PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/subversion/subversion/libsvn_subr/time.c

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