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