time_fmt.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. /* Copyright (c) 2001, Matej Pfajfar.
  2. * Copyright (c) 2001-2004, Roger Dingledine.
  3. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
  4. * Copyright (c) 2007-2019, The Tor Project, Inc. */
  5. /* See LICENSE for licensing information */
  6. /**
  7. * \file time_fmt.c
  8. *
  9. * \brief Encode and decode time in various formats.
  10. *
  11. * This module is higher-level than the conversion functions in "wallclock",
  12. * and handles a larger variety of types. It converts between different time
  13. * formats, and encodes and decodes them from strings.
  14. **/
  15. #include "lib/encoding/time_fmt.h"
  16. #include "lib/log/log.h"
  17. #include "lib/log/escape.h"
  18. #include "lib/log/util_bug.h"
  19. #include "lib/malloc/malloc.h"
  20. #include "lib/string/printf.h"
  21. #include "lib/string/scanf.h"
  22. #include "lib/wallclock/time_to_tm.h"
  23. #include <string.h>
  24. #include <time.h>
  25. #ifdef HAVE_SYS_TIME_H
  26. #include <sys/time.h>
  27. #endif
  28. #ifdef _WIN32
  29. /* For struct timeval */
  30. #include <winsock2.h>
  31. #endif
  32. /** As localtime_r, but defined for platforms that don't have it:
  33. *
  34. * Convert *<b>timep</b> to a struct tm in local time, and store the value in
  35. * *<b>result</b>. Return the result on success, or NULL on failure.
  36. *
  37. * Treat malformatted inputs localtime outputs as a BUG.
  38. */
  39. struct tm *
  40. tor_localtime_r(const time_t *timep, struct tm *result)
  41. {
  42. char *err = NULL;
  43. struct tm *r = tor_localtime_r_msg(timep, result, &err);
  44. if (err) {
  45. log_warn(LD_BUG, "%s", err);
  46. tor_free(err);
  47. }
  48. return r;
  49. }
  50. /** As gmtime_r, but defined for platforms that don't have it:
  51. *
  52. * Convert *<b>timep</b> to a struct tm in UTC, and store the value in
  53. * *<b>result</b>. Return the result on success, or NULL on failure.
  54. *
  55. * Treat malformatted inputs or gmtime outputs as a BUG.
  56. */
  57. struct tm *
  58. tor_gmtime_r(const time_t *timep, struct tm *result)
  59. {
  60. char *err = NULL;
  61. struct tm *r = tor_gmtime_r_msg(timep, result, &err);
  62. if (err) {
  63. log_warn(LD_BUG, "%s", err);
  64. tor_free(err);
  65. }
  66. return r;
  67. }
  68. /** Yield true iff <b>y</b> is a leap-year. */
  69. #define IS_LEAPYEAR(y) (!(y % 4) && ((y % 100) || !(y % 400)))
  70. /** Helper: Return the number of leap-days between Jan 1, y1 and Jan 1, y2. */
  71. static int
  72. n_leapdays(int year1, int year2)
  73. {
  74. --year1;
  75. --year2;
  76. return (year2/4 - year1/4) - (year2/100 - year1/100)
  77. + (year2/400 - year1/400);
  78. }
  79. /** Number of days per month in non-leap year; used by tor_timegm and
  80. * parse_rfc1123_time. */
  81. static const int days_per_month[] =
  82. { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  83. /** Compute a time_t given a struct tm. The result is given in UTC, and
  84. * does not account for leap seconds. Return 0 on success, -1 on failure.
  85. */
  86. int
  87. tor_timegm(const struct tm *tm, time_t *time_out)
  88. {
  89. /* This is a pretty ironclad timegm implementation, snarfed from Python2.2.
  90. * It's way more brute-force than fiddling with tzset().
  91. *
  92. * We use int64_t rather than time_t to avoid overflow on multiplication on
  93. * platforms with 32-bit time_t. Since year is clipped to INT32_MAX, and
  94. * since 365 * 24 * 60 * 60 is approximately 31 million, it's not possible
  95. * for INT32_MAX years to overflow int64_t when converted to seconds. */
  96. int64_t year, days, hours, minutes, seconds;
  97. int i, invalid_year, dpm;
  98. /* Initialize time_out to 0 for now, to avoid bad usage in case this function
  99. fails and the caller ignores the return value. */
  100. tor_assert(time_out);
  101. *time_out = 0;
  102. /* avoid int overflow on addition */
  103. if (tm->tm_year < INT32_MAX-1900) {
  104. year = tm->tm_year + 1900;
  105. } else {
  106. /* clamp year */
  107. year = INT32_MAX;
  108. }
  109. invalid_year = (year < 1970 || tm->tm_year >= INT32_MAX-1900);
  110. if (tm->tm_mon >= 0 && tm->tm_mon <= 11) {
  111. dpm = days_per_month[tm->tm_mon];
  112. if (tm->tm_mon == 1 && !invalid_year && IS_LEAPYEAR(tm->tm_year)) {
  113. dpm = 29;
  114. }
  115. } else {
  116. /* invalid month - default to 0 days per month */
  117. dpm = 0;
  118. }
  119. if (invalid_year ||
  120. tm->tm_mon < 0 || tm->tm_mon > 11 ||
  121. tm->tm_mday < 1 || tm->tm_mday > dpm ||
  122. tm->tm_hour < 0 || tm->tm_hour > 23 ||
  123. tm->tm_min < 0 || tm->tm_min > 59 ||
  124. tm->tm_sec < 0 || tm->tm_sec > 60) {
  125. log_warn(LD_BUG, "Out-of-range argument to tor_timegm");
  126. return -1;
  127. }
  128. days = 365 * (year-1970) + n_leapdays(1970,(int)year);
  129. for (i = 0; i < tm->tm_mon; ++i)
  130. days += days_per_month[i];
  131. if (tm->tm_mon > 1 && IS_LEAPYEAR(year))
  132. ++days;
  133. days += tm->tm_mday - 1;
  134. hours = days*24 + tm->tm_hour;
  135. minutes = hours*60 + tm->tm_min;
  136. seconds = minutes*60 + tm->tm_sec;
  137. /* Check that "seconds" will fit in a time_t. On platforms where time_t is
  138. * 32-bit, this check will fail for dates in and after 2038.
  139. *
  140. * We already know that "seconds" can't be negative because "year" >= 1970 */
  141. #if SIZEOF_TIME_T < 8
  142. if (seconds < TIME_MIN || seconds > TIME_MAX) {
  143. log_warn(LD_BUG, "Result does not fit in tor_timegm");
  144. return -1;
  145. }
  146. #endif /* SIZEOF_TIME_T < 8 */
  147. *time_out = (time_t)seconds;
  148. return 0;
  149. }
  150. /* strftime is locale-specific, so we need to replace those parts */
  151. /** A c-locale array of 3-letter names of weekdays, starting with Sun. */
  152. static const char *WEEKDAY_NAMES[] =
  153. { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  154. /** A c-locale array of 3-letter names of months, starting with Jan. */
  155. static const char *MONTH_NAMES[] =
  156. { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  157. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  158. /** Set <b>buf</b> to the RFC1123 encoding of the UTC value of <b>t</b>.
  159. * The buffer must be at least RFC1123_TIME_LEN+1 bytes long.
  160. *
  161. * (RFC1123 format is "Fri, 29 Sep 2006 15:54:20 GMT". Note the "GMT"
  162. * rather than "UTC".)
  163. */
  164. void
  165. format_rfc1123_time(char *buf, time_t t)
  166. {
  167. struct tm tm;
  168. tor_gmtime_r(&t, &tm);
  169. strftime(buf, RFC1123_TIME_LEN+1, "___, %d ___ %Y %H:%M:%S GMT", &tm);
  170. tor_assert(tm.tm_wday >= 0);
  171. tor_assert(tm.tm_wday <= 6);
  172. memcpy(buf, WEEKDAY_NAMES[tm.tm_wday], 3);
  173. tor_assert(tm.tm_mon >= 0);
  174. tor_assert(tm.tm_mon <= 11);
  175. memcpy(buf+8, MONTH_NAMES[tm.tm_mon], 3);
  176. }
  177. /** Parse the (a subset of) the RFC1123 encoding of some time (in UTC) from
  178. * <b>buf</b>, and store the result in *<b>t</b>.
  179. *
  180. * Note that we only accept the subset generated by format_rfc1123_time above,
  181. * not the full range of formats suggested by RFC 1123.
  182. *
  183. * Return 0 on success, -1 on failure.
  184. */
  185. int
  186. parse_rfc1123_time(const char *buf, time_t *t)
  187. {
  188. struct tm tm;
  189. char month[4];
  190. char weekday[4];
  191. int i, m, invalid_year;
  192. unsigned tm_mday, tm_year, tm_hour, tm_min, tm_sec;
  193. unsigned dpm;
  194. if (strlen(buf) != RFC1123_TIME_LEN)
  195. return -1;
  196. memset(&tm, 0, sizeof(tm));
  197. if (tor_sscanf(buf, "%3s, %2u %3s %u %2u:%2u:%2u GMT", weekday,
  198. &tm_mday, month, &tm_year, &tm_hour,
  199. &tm_min, &tm_sec) < 7) {
  200. char *esc = esc_for_log(buf);
  201. log_warn(LD_GENERAL, "Got invalid RFC1123 time %s", esc);
  202. tor_free(esc);
  203. return -1;
  204. }
  205. m = -1;
  206. for (i = 0; i < 12; ++i) {
  207. if (!strcmp(month, MONTH_NAMES[i])) {
  208. m = i;
  209. break;
  210. }
  211. }
  212. if (m<0) {
  213. char *esc = esc_for_log(buf);
  214. log_warn(LD_GENERAL, "Got invalid RFC1123 time %s: No such month", esc);
  215. tor_free(esc);
  216. return -1;
  217. }
  218. tm.tm_mon = m;
  219. invalid_year = (tm_year >= INT32_MAX || tm_year < 1970);
  220. tor_assert(m >= 0 && m <= 11);
  221. dpm = days_per_month[m];
  222. if (m == 1 && !invalid_year && IS_LEAPYEAR(tm_year)) {
  223. dpm = 29;
  224. }
  225. if (invalid_year || tm_mday < 1 || tm_mday > dpm ||
  226. tm_hour > 23 || tm_min > 59 || tm_sec > 60) {
  227. char *esc = esc_for_log(buf);
  228. log_warn(LD_GENERAL, "Got invalid RFC1123 time %s", esc);
  229. tor_free(esc);
  230. return -1;
  231. }
  232. tm.tm_mday = (int)tm_mday;
  233. tm.tm_year = (int)tm_year;
  234. tm.tm_hour = (int)tm_hour;
  235. tm.tm_min = (int)tm_min;
  236. tm.tm_sec = (int)tm_sec;
  237. if (tm.tm_year < 1970) {
  238. /* LCOV_EXCL_START
  239. * XXXX I think this is dead code; we already checked for
  240. * invalid_year above. */
  241. tor_assert_nonfatal_unreached();
  242. char *esc = esc_for_log(buf);
  243. log_warn(LD_GENERAL,
  244. "Got invalid RFC1123 time %s. (Before 1970)", esc);
  245. tor_free(esc);
  246. return -1;
  247. /* LCOV_EXCL_STOP */
  248. }
  249. tm.tm_year -= 1900;
  250. return tor_timegm(&tm, t);
  251. }
  252. /** Set <b>buf</b> to the ISO8601 encoding of the local value of <b>t</b>.
  253. * The buffer must be at least ISO_TIME_LEN+1 bytes long.
  254. *
  255. * (ISO8601 format is 2006-10-29 10:57:20)
  256. */
  257. void
  258. format_local_iso_time(char *buf, time_t t)
  259. {
  260. struct tm tm;
  261. strftime(buf, ISO_TIME_LEN+1, "%Y-%m-%d %H:%M:%S", tor_localtime_r(&t, &tm));
  262. }
  263. /** Set <b>buf</b> to the ISO8601 encoding of the GMT value of <b>t</b>.
  264. * The buffer must be at least ISO_TIME_LEN+1 bytes long.
  265. */
  266. void
  267. format_iso_time(char *buf, time_t t)
  268. {
  269. struct tm tm;
  270. strftime(buf, ISO_TIME_LEN+1, "%Y-%m-%d %H:%M:%S", tor_gmtime_r(&t, &tm));
  271. }
  272. /** As format_local_iso_time, but use the yyyy-mm-ddThh:mm:ss format to avoid
  273. * embedding an internal space. */
  274. void
  275. format_local_iso_time_nospace(char *buf, time_t t)
  276. {
  277. format_local_iso_time(buf, t);
  278. buf[10] = 'T';
  279. }
  280. /** As format_iso_time, but use the yyyy-mm-ddThh:mm:ss format to avoid
  281. * embedding an internal space. */
  282. void
  283. format_iso_time_nospace(char *buf, time_t t)
  284. {
  285. format_iso_time(buf, t);
  286. buf[10] = 'T';
  287. }
  288. /** As format_iso_time_nospace, but include microseconds in decimal
  289. * fixed-point format. Requires that buf be at least ISO_TIME_USEC_LEN+1
  290. * bytes long. */
  291. void
  292. format_iso_time_nospace_usec(char *buf, const struct timeval *tv)
  293. {
  294. tor_assert(tv);
  295. format_iso_time_nospace(buf, (time_t)tv->tv_sec);
  296. tor_snprintf(buf+ISO_TIME_LEN, 8, ".%06d", (int)tv->tv_usec);
  297. }
  298. /** Given an ISO-formatted UTC time value (after the epoch) in <b>cp</b>,
  299. * parse it and store its value in *<b>t</b>. Return 0 on success, -1 on
  300. * failure. Ignore extraneous stuff in <b>cp</b> after the end of the time
  301. * string, unless <b>strict</b> is set. If <b>nospace</b> is set,
  302. * expect the YYYY-MM-DDTHH:MM:SS format. */
  303. int
  304. parse_iso_time_(const char *cp, time_t *t, int strict, int nospace)
  305. {
  306. struct tm st_tm;
  307. unsigned int year=0, month=0, day=0, hour=0, minute=0, second=0;
  308. int n_fields;
  309. char extra_char, separator_char;
  310. n_fields = tor_sscanf(cp, "%u-%2u-%2u%c%2u:%2u:%2u%c",
  311. &year, &month, &day,
  312. &separator_char,
  313. &hour, &minute, &second, &extra_char);
  314. if (strict ? (n_fields != 7) : (n_fields < 7)) {
  315. char *esc = esc_for_log(cp);
  316. log_warn(LD_GENERAL, "ISO time %s was unparseable", esc);
  317. tor_free(esc);
  318. return -1;
  319. }
  320. if (separator_char != (nospace ? 'T' : ' ')) {
  321. char *esc = esc_for_log(cp);
  322. log_warn(LD_GENERAL, "ISO time %s was unparseable", esc);
  323. tor_free(esc);
  324. return -1;
  325. }
  326. if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
  327. hour > 23 || minute > 59 || second > 60 || year >= INT32_MAX) {
  328. char *esc = esc_for_log(cp);
  329. log_warn(LD_GENERAL, "ISO time %s was nonsensical", esc);
  330. tor_free(esc);
  331. return -1;
  332. }
  333. st_tm.tm_year = (int)year-1900;
  334. st_tm.tm_mon = month-1;
  335. st_tm.tm_mday = day;
  336. st_tm.tm_hour = hour;
  337. st_tm.tm_min = minute;
  338. st_tm.tm_sec = second;
  339. st_tm.tm_wday = 0; /* Should be ignored. */
  340. if (st_tm.tm_year < 70) {
  341. /* LCOV_EXCL_START
  342. * XXXX I think this is dead code; we already checked for
  343. * year < 1970 above. */
  344. tor_assert_nonfatal_unreached();
  345. char *esc = esc_for_log(cp);
  346. log_warn(LD_GENERAL, "Got invalid ISO time %s. (Before 1970)", esc);
  347. tor_free(esc);
  348. return -1;
  349. /* LCOV_EXCL_STOP */
  350. }
  351. return tor_timegm(&st_tm, t);
  352. }
  353. /** Given an ISO-formatted UTC time value (after the epoch) in <b>cp</b>,
  354. * parse it and store its value in *<b>t</b>. Return 0 on success, -1 on
  355. * failure. Reject the string if any characters are present after the time.
  356. */
  357. int
  358. parse_iso_time(const char *cp, time_t *t)
  359. {
  360. return parse_iso_time_(cp, t, 1, 0);
  361. }
  362. /**
  363. * As parse_iso_time, but parses a time encoded by format_iso_time_nospace().
  364. */
  365. int
  366. parse_iso_time_nospace(const char *cp, time_t *t)
  367. {
  368. return parse_iso_time_(cp, t, 1, 1);
  369. }
  370. /** Given a <b>date</b> in one of the three formats allowed by HTTP (ugh),
  371. * parse it into <b>tm</b>. Return 0 on success, negative on failure. */
  372. int
  373. parse_http_time(const char *date, struct tm *tm)
  374. {
  375. const char *cp;
  376. char month[4];
  377. char wkday[4];
  378. int i;
  379. unsigned tm_mday, tm_year, tm_hour, tm_min, tm_sec;
  380. tor_assert(tm);
  381. memset(tm, 0, sizeof(*tm));
  382. /* First, try RFC1123 or RFC850 format: skip the weekday. */
  383. if ((cp = strchr(date, ','))) {
  384. ++cp;
  385. if (*cp != ' ')
  386. return -1;
  387. ++cp;
  388. if (tor_sscanf(cp, "%2u %3s %4u %2u:%2u:%2u GMT",
  389. &tm_mday, month, &tm_year,
  390. &tm_hour, &tm_min, &tm_sec) == 6) {
  391. /* rfc1123-date */
  392. tm_year -= 1900;
  393. } else if (tor_sscanf(cp, "%2u-%3s-%2u %2u:%2u:%2u GMT",
  394. &tm_mday, month, &tm_year,
  395. &tm_hour, &tm_min, &tm_sec) == 6) {
  396. /* rfc850-date */
  397. } else {
  398. return -1;
  399. }
  400. } else {
  401. /* No comma; possibly asctime() format. */
  402. if (tor_sscanf(date, "%3s %3s %2u %2u:%2u:%2u %4u",
  403. wkday, month, &tm_mday,
  404. &tm_hour, &tm_min, &tm_sec, &tm_year) == 7) {
  405. tm_year -= 1900;
  406. } else {
  407. return -1;
  408. }
  409. }
  410. tm->tm_mday = (int)tm_mday;
  411. tm->tm_year = (int)tm_year;
  412. tm->tm_hour = (int)tm_hour;
  413. tm->tm_min = (int)tm_min;
  414. tm->tm_sec = (int)tm_sec;
  415. tm->tm_wday = 0; /* Leave this unset. */
  416. month[3] = '\0';
  417. /* Okay, now decode the month. */
  418. /* set tm->tm_mon to dummy value so the check below fails. */
  419. tm->tm_mon = -1;
  420. for (i = 0; i < 12; ++i) {
  421. if (!strcasecmp(MONTH_NAMES[i], month)) {
  422. tm->tm_mon = i;
  423. }
  424. }
  425. if (tm->tm_year < 0 ||
  426. tm->tm_mon < 0 || tm->tm_mon > 11 ||
  427. tm->tm_mday < 1 || tm->tm_mday > 31 ||
  428. tm->tm_hour < 0 || tm->tm_hour > 23 ||
  429. tm->tm_min < 0 || tm->tm_min > 59 ||
  430. tm->tm_sec < 0 || tm->tm_sec > 60)
  431. return -1; /* Out of range, or bad month. */
  432. return 0;
  433. }
  434. /** Given an <b>interval</b> in seconds, try to write it to the
  435. * <b>out_len</b>-byte buffer in <b>out</b> in a human-readable form.
  436. * Returns a non-negative integer on success, -1 on failure.
  437. */
  438. int
  439. format_time_interval(char *out, size_t out_len, long interval)
  440. {
  441. /* We only report seconds if there's no hours. */
  442. long sec = 0, min = 0, hour = 0, day = 0;
  443. /* -LONG_MIN is LONG_MAX + 1, which causes signed overflow */
  444. if (interval < -LONG_MAX)
  445. interval = LONG_MAX;
  446. else if (interval < 0)
  447. interval = -interval;
  448. if (interval >= 86400) {
  449. day = interval / 86400;
  450. interval %= 86400;
  451. }
  452. if (interval >= 3600) {
  453. hour = interval / 3600;
  454. interval %= 3600;
  455. }
  456. if (interval >= 60) {
  457. min = interval / 60;
  458. interval %= 60;
  459. }
  460. sec = interval;
  461. if (day) {
  462. return tor_snprintf(out, out_len, "%ld days, %ld hours, %ld minutes",
  463. day, hour, min);
  464. } else if (hour) {
  465. return tor_snprintf(out, out_len, "%ld hours, %ld minutes", hour, min);
  466. } else if (min) {
  467. return tor_snprintf(out, out_len, "%ld minutes, %ld seconds", min, sec);
  468. } else {
  469. return tor_snprintf(out, out_len, "%ld seconds", sec);
  470. }
  471. }