]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_subr/time.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_subr / time.c
1 /*
2  * time.c:  time/date utilities
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24
25 \f
26 #include <string.h>
27 #include <stdlib.h>
28 #include <apr_pools.h>
29 #include <apr_time.h>
30 #include <apr_strings.h>
31 #include "svn_io.h"
32 #include "svn_time.h"
33 #include "svn_utf.h"
34 #include "svn_error.h"
35 #include "svn_private_config.h"
36
37
38 \f
39 /*** Code. ***/
40
41 /* Our timestamp strings look like this:
42  *
43  *    "2002-05-07Thh:mm:ss.uuuuuuZ"
44  *
45  * The format is conformant with ISO-8601 and the date format required
46  * by RFC2518 for creationdate. It is a direct conversion between
47  * apr_time_t and a string, so converting to string and back retains
48  * the exact value.
49  */
50 #define TIMESTAMP_FORMAT "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ"
51
52 /* Our old timestamp strings looked like this:
53  *
54  *    "Tue 3 Oct 2000 HH:MM:SS.UUU (day 277, dst 1, gmt_off -18000)"
55  *
56  * The idea is that they are conventionally human-readable for the
57  * first part, and then in parentheses comes everything else required
58  * to completely fill in an apr_time_exp_t: tm_yday, tm_isdst,
59  * and tm_gmtoff.
60  *
61  * This format is still recognized on input, for backward
62  * compatibility, but no longer generated.
63  */
64 #define OLD_TIMESTAMP_FORMAT \
65         "%3s %d %3s %d %02d:%02d:%02d.%06d (day %03d, dst %d, gmt_off %06d)"
66
67 /* Our human representation of dates looks like this:
68  *
69  *    "2002-06-23 11:13:02 +0300 (Sun, 23 Jun 2002)"
70  *
71  * This format is used whenever time is shown to the user. It consists
72  * of a machine parseable, almost ISO-8601, part in the beginning -
73  * and a human explanatory part at the end. The machine parseable part
74  * is generated strictly by APR and our code, with a apr_snprintf. The
75  * human explanatory part is generated by apr_strftime, which means
76  * that its generation can be affected by locale, it can fail and it
77  * doesn't need to be constant in size. In other words, perfect to be
78  * converted to a configuration option later on.
79  */
80 /* Maximum length for the date string. */
81 #define SVN_TIME__MAX_LENGTH 80
82 /* Machine parseable part, generated by apr_snprintf. */
83 #define HUMAN_TIMESTAMP_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %+.2d%.2d"
84 /* Human explanatory part, generated by apr_strftime as "Sat, 01 Jan 2000" */
85 #define human_timestamp_format_suffix _(" (%a, %d %b %Y)")
86
87 const char *
88 svn_time_to_cstring(apr_time_t when, apr_pool_t *pool)
89 {
90   apr_time_exp_t exploded_time;
91
92   /* We toss apr_status_t return value here -- for one thing, caller
93      should pass in good information.  But also, where APR's own code
94      calls these functions it tosses the return values, and
95      furthermore their current implementations can only return success
96      anyway. */
97
98   /* We get the date in GMT now -- and expect the tm_gmtoff and
99      tm_isdst to be not set. We also ignore the weekday and yearday,
100      since those are not needed. */
101
102   apr_time_exp_gmt(&exploded_time, when);
103
104   /* It would be nice to use apr_strftime(), but APR doesn't give a
105      way to convert back, so we wouldn't be able to share the format
106      string between the writer and reader. */
107   return apr_psprintf(pool,
108                       TIMESTAMP_FORMAT,
109                       exploded_time.tm_year + 1900,
110                       exploded_time.tm_mon + 1,
111                       exploded_time.tm_mday,
112                       exploded_time.tm_hour,
113                       exploded_time.tm_min,
114                       exploded_time.tm_sec,
115                       exploded_time.tm_usec);
116 }
117
118
119 static apr_int32_t
120 find_matching_string(char *str, apr_size_t size, const char strings[][4])
121 {
122   apr_size_t i;
123
124   for (i = 0; i < size; i++)
125     if (strings[i] && (strcmp(str, strings[i]) == 0))
126       return (apr_int32_t) i;
127
128   return -1;
129 }
130
131
132 svn_error_t *
133 svn_time_from_cstring(apr_time_t *when, const char *data, apr_pool_t *pool)
134 {
135   apr_time_exp_t exploded_time;
136   apr_status_t apr_err;
137   char wday[4], month[4];
138   char *c;
139
140   /* Open-code parsing of the new timestamp format, as this
141      is a hot path for reading the entries file.  This format looks
142      like:  "2001-08-31T04:24:14.966996Z"  */
143   exploded_time.tm_year = (apr_int32_t) strtol(data, &c, 10);
144   if (*c++ != '-') goto fail;
145   exploded_time.tm_mon = (apr_int32_t) strtol(c, &c, 10);
146   if (*c++ != '-') goto fail;
147   exploded_time.tm_mday = (apr_int32_t) strtol(c, &c, 10);
148   if (*c++ != 'T') goto fail;
149   exploded_time.tm_hour = (apr_int32_t) strtol(c, &c, 10);
150   if (*c++ != ':') goto fail;
151   exploded_time.tm_min = (apr_int32_t) strtol(c, &c, 10);
152   if (*c++ != ':') goto fail;
153   exploded_time.tm_sec = (apr_int32_t) strtol(c, &c, 10);
154   if (*c++ != '.') goto fail;
155   exploded_time.tm_usec = (apr_int32_t) strtol(c, &c, 10);
156   if (*c++ != 'Z') goto fail;
157
158   exploded_time.tm_year  -= 1900;
159   exploded_time.tm_mon   -= 1;
160   exploded_time.tm_wday   = 0;
161   exploded_time.tm_yday   = 0;
162   exploded_time.tm_isdst  = 0;
163   exploded_time.tm_gmtoff = 0;
164
165   apr_err = apr_time_exp_gmt_get(when, &exploded_time);
166   if (apr_err == APR_SUCCESS)
167     return SVN_NO_ERROR;
168
169   return svn_error_create(SVN_ERR_BAD_DATE, NULL, NULL);
170
171  fail:
172   /* Try the compatibility option.  This does not need to be fast,
173      as this format is no longer generated and the client will convert
174      an old-format entries file the first time it reads it.  */
175   if (sscanf(data,
176              OLD_TIMESTAMP_FORMAT,
177              wday,
178              &exploded_time.tm_mday,
179              month,
180              &exploded_time.tm_year,
181              &exploded_time.tm_hour,
182              &exploded_time.tm_min,
183              &exploded_time.tm_sec,
184              &exploded_time.tm_usec,
185              &exploded_time.tm_yday,
186              &exploded_time.tm_isdst,
187              &exploded_time.tm_gmtoff) == 11)
188     {
189       exploded_time.tm_year -= 1900;
190       exploded_time.tm_yday -= 1;
191       /* Using hard coded limits for the arrays - they are going away
192          soon in any case. */
193       exploded_time.tm_wday = find_matching_string(wday, 7, apr_day_snames);
194       exploded_time.tm_mon = find_matching_string(month, 12, apr_month_snames);
195
196       apr_err = apr_time_exp_gmt_get(when, &exploded_time);
197       if (apr_err != APR_SUCCESS)
198         return svn_error_create(SVN_ERR_BAD_DATE, NULL, NULL);
199
200       return SVN_NO_ERROR;
201     }
202   /* Timestamp is something we do not recognize. */
203   else
204     return svn_error_create(SVN_ERR_BAD_DATE, NULL, NULL);
205 }
206
207
208 const char *
209 svn_time_to_human_cstring(apr_time_t when, apr_pool_t *pool)
210 {
211   apr_time_exp_t exploded_time;
212   apr_size_t len, retlen;
213   apr_status_t ret;
214   char *datestr, *curptr, human_datestr[SVN_TIME__MAX_LENGTH];
215
216   /* Get the time into parts */
217   ret = apr_time_exp_lt(&exploded_time, when);
218   if (ret)
219     return NULL;
220
221   /* Make room for datestring */
222   datestr = apr_palloc(pool, SVN_TIME__MAX_LENGTH);
223
224   /* Put in machine parseable part */
225   len = apr_snprintf(datestr,
226                      SVN_TIME__MAX_LENGTH,
227                      HUMAN_TIMESTAMP_FORMAT,
228                      exploded_time.tm_year + 1900,
229                      exploded_time.tm_mon + 1,
230                      exploded_time.tm_mday,
231                      exploded_time.tm_hour,
232                      exploded_time.tm_min,
233                      exploded_time.tm_sec,
234                      exploded_time.tm_gmtoff / (60 * 60),
235                      (abs(exploded_time.tm_gmtoff) / 60) % 60);
236
237   /* If we overfilled the buffer, just return what we got. */
238   if (len >= SVN_TIME__MAX_LENGTH)
239     return datestr;
240
241   /* Calculate offset to the end of the machine parseable part. */
242   curptr = datestr + len;
243
244   /* Put in human explanatory part */
245   ret = apr_strftime(human_datestr,
246                      &retlen,
247                      SVN_TIME__MAX_LENGTH - len,
248                      human_timestamp_format_suffix,
249                      &exploded_time);
250
251   /* If there was an error, ensure that the string is zero-terminated. */
252   if (ret || retlen == 0)
253     *curptr = '\0';
254   else
255     {
256       const char *utf8_string;
257       svn_error_t *err;
258
259       err = svn_utf_cstring_to_utf8(&utf8_string, human_datestr, pool);
260       if (err)
261         {
262           *curptr = '\0';
263           svn_error_clear(err);
264         }
265       else
266         apr_cpystrn(curptr, utf8_string, SVN_TIME__MAX_LENGTH - len);
267     }
268
269   return datestr;
270 }
271
272
273 void
274 svn_sleep_for_timestamps(void)
275 {
276   svn_io_sleep_for_timestamps(NULL, NULL);
277 }