time_to_tm.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /* Copyright (c) 2003-2004, Roger Dingledine
  2. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
  3. * Copyright (c) 2007-2019, The Tor Project, Inc. */
  4. /* See LICENSE for licensing information */
  5. /**
  6. * \file time_to_tm.c
  7. * \brief Convert to struct tm, portably.
  8. **/
  9. #include "orconfig.h"
  10. #include "lib/cc/torint.h"
  11. #include "lib/cc/compat_compiler.h"
  12. #include "lib/wallclock/time_to_tm.h"
  13. #include "lib/string/printf.h"
  14. #include "lib/err/torerr.h"
  15. #include <errno.h>
  16. #include <time.h>
  17. #include <string.h>
  18. #include <stdlib.h>
  19. #if !defined(_WIN32)
  20. /** Defined iff we need to add locks when defining fake versions of reentrant
  21. * versions of time-related functions. */
  22. #define TIME_FNS_NEED_LOCKS
  23. #endif
  24. /** Helper: Deal with confused or out-of-bounds values from localtime_r and
  25. * friends. (On some platforms, they can give out-of-bounds values or can
  26. * return NULL.) If <b>islocal</b>, this is a localtime result; otherwise
  27. * it's from gmtime. The function returns <b>r</b>, when given <b>timep</b>
  28. * as its input. If we need to store new results, store them in
  29. * <b>resultbuf</b>. */
  30. static struct tm *
  31. correct_tm(int islocal, const time_t *timep, struct tm *resultbuf,
  32. struct tm *r, char **err_out)
  33. {
  34. const char *outcome;
  35. if (PREDICT_LIKELY(r)) {
  36. /* We can't strftime dates after 9999 CE, and we want to avoid dates
  37. * before 1 CE (avoiding the year 0 issue and negative years). */
  38. if (r->tm_year > 8099) {
  39. r->tm_year = 8099;
  40. r->tm_mon = 11;
  41. r->tm_mday = 31;
  42. r->tm_yday = 364;
  43. r->tm_wday = 6;
  44. r->tm_hour = 23;
  45. r->tm_min = 59;
  46. r->tm_sec = 59;
  47. } else if (r->tm_year < (1-1900)) {
  48. r->tm_year = (1-1900);
  49. r->tm_mon = 0;
  50. r->tm_mday = 1;
  51. r->tm_yday = 0;
  52. r->tm_wday = 0;
  53. r->tm_hour = 0;
  54. r->tm_min = 0;
  55. r->tm_sec = 0;
  56. }
  57. return r;
  58. }
  59. /* If we get here, gmtime or localtime returned NULL. It might have done
  60. * this because of overrun or underrun, or it might have done it because of
  61. * some other weird issue. */
  62. if (timep) {
  63. if (*timep < 0) {
  64. r = resultbuf;
  65. r->tm_year = 70; /* 1970 CE */
  66. r->tm_mon = 0;
  67. r->tm_mday = 1;
  68. r->tm_yday = 0;
  69. r->tm_wday = 0;
  70. r->tm_hour = 0;
  71. r->tm_min = 0 ;
  72. r->tm_sec = 0;
  73. outcome = "Rounding up to 1970";
  74. goto done;
  75. } else if (*timep >= INT32_MAX) {
  76. /* Rounding down to INT32_MAX isn't so great, but keep in mind that we
  77. * only do it if gmtime/localtime tells us NULL. */
  78. r = resultbuf;
  79. r->tm_year = 137; /* 2037 CE */
  80. r->tm_mon = 11;
  81. r->tm_mday = 31;
  82. r->tm_yday = 364;
  83. r->tm_wday = 6;
  84. r->tm_hour = 23;
  85. r->tm_min = 59;
  86. r->tm_sec = 59;
  87. outcome = "Rounding down to 2037";
  88. goto done;
  89. }
  90. }
  91. /* If we get here, then gmtime/localtime failed without getting an extreme
  92. * value for *timep */
  93. /* LCOV_EXCL_START */
  94. r = resultbuf;
  95. memset(resultbuf, 0, sizeof(struct tm));
  96. outcome="can't recover";
  97. /* LCOV_EXCL_STOP */
  98. done:
  99. if (err_out) {
  100. tor_asprintf(err_out, "%s(%"PRId64") failed with error %s: %s",
  101. islocal?"localtime":"gmtime",
  102. timep?((int64_t)*timep):0,
  103. strerror(errno),
  104. outcome);
  105. }
  106. return r;
  107. }
  108. /** @{ */
  109. /** As localtime_r, but defined for platforms that don't have it:
  110. *
  111. * Convert *<b>timep</b> to a struct tm in local time, and store the value in
  112. * *<b>result</b>. Return the result on success, or NULL on failure.
  113. */
  114. #ifdef HAVE_LOCALTIME_R
  115. struct tm *
  116. tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
  117. {
  118. struct tm *r;
  119. r = localtime_r(timep, result);
  120. return correct_tm(1, timep, result, r, err_out);
  121. }
  122. #elif defined(TIME_FNS_NEED_LOCKS)
  123. struct tm *
  124. tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
  125. {
  126. struct tm *r;
  127. static tor_mutex_t *m=NULL;
  128. if (!m) { m=tor_mutex_new(); }
  129. raw_assert(result);
  130. tor_mutex_acquire(m);
  131. r = localtime(timep);
  132. if (r)
  133. memcpy(result, r, sizeof(struct tm));
  134. tor_mutex_release(m);
  135. return correct_tm(1, timep, result, r, err_out);
  136. }
  137. #else
  138. struct tm *
  139. tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
  140. {
  141. struct tm *r;
  142. raw_assert(result);
  143. r = localtime(timep);
  144. if (r)
  145. memcpy(result, r, sizeof(struct tm));
  146. return correct_tm(1, timep, result, r, err_out);
  147. }
  148. #endif /* defined(HAVE_LOCALTIME_R) || ... */
  149. /** @} */
  150. /** @{ */
  151. /** As gmtime_r, but defined for platforms that don't have it:
  152. *
  153. * Convert *<b>timep</b> to a struct tm in UTC, and store the value in
  154. * *<b>result</b>. Return the result on success, or NULL on failure.
  155. */
  156. #ifdef HAVE_GMTIME_R
  157. struct tm *
  158. tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
  159. {
  160. struct tm *r;
  161. r = gmtime_r(timep, result);
  162. return correct_tm(0, timep, result, r, err_out);
  163. }
  164. #elif defined(TIME_FNS_NEED_LOCKS)
  165. struct tm *
  166. tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
  167. {
  168. struct tm *r;
  169. static tor_mutex_t *m=NULL;
  170. if (!m) { m=tor_mutex_new(); }
  171. raw_assert(result);
  172. tor_mutex_acquire(m);
  173. r = gmtime(timep);
  174. if (r)
  175. memcpy(result, r, sizeof(struct tm));
  176. tor_mutex_release(m);
  177. return correct_tm(0, timep, result, r, err_out);
  178. }
  179. #else
  180. struct tm *
  181. tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
  182. {
  183. struct tm *r;
  184. raw_assert(result);
  185. r = gmtime(timep);
  186. if (r)
  187. memcpy(result, r, sizeof(struct tm));
  188. return correct_tm(0, timep, result, r, err_out);
  189. }
  190. #endif /* defined(HAVE_GMTIME_R) || ... */