time_rz.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. /* Time zone functions such as tzalloc and localtime_rz
  2. Copyright 2015-2017 Free Software Foundation, Inc.
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU Lesser General Public License as published by
  5. the Free Software Foundation; either version 2, or (at your option)
  6. any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License along
  12. with this program; if not, see <http://www.gnu.org/licenses/>. */
  13. /* Written by Paul Eggert. */
  14. /* Although this module is not thread-safe, any races should be fairly
  15. rare and reasonably benign. For complete thread-safety, use a C
  16. library with a working timezone_t type, so that this module is not
  17. needed. */
  18. #include <config.h>
  19. #include <time.h>
  20. #include <errno.h>
  21. #include <stdbool.h>
  22. #include <stddef.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include "flexmember.h"
  26. #include "time-internal.h"
  27. #if !HAVE_TZSET
  28. static void tzset (void) { }
  29. #endif
  30. /* The approximate size to use for small allocation requests. This is
  31. the largest "small" request for the GNU C library malloc. */
  32. enum { DEFAULT_MXFAST = 64 * sizeof (size_t) / 4 };
  33. /* Minimum size of the ABBRS member of struct abbr. ABBRS is larger
  34. only in the unlikely case where an abbreviation longer than this is
  35. used. */
  36. enum { ABBR_SIZE_MIN = DEFAULT_MXFAST - offsetof (struct tm_zone, abbrs) };
  37. /* Magic cookie timezone_t value, for local time. It differs from
  38. NULL and from all other timezone_t values. Only the address
  39. matters; the pointer is never dereferenced. */
  40. static timezone_t const local_tz = (timezone_t) 1;
  41. #if HAVE_TM_ZONE || HAVE_TZNAME
  42. /* Return true if the values A and B differ according to the rules for
  43. tm_isdst: A and B differ if one is zero and the other positive. */
  44. static bool
  45. isdst_differ (int a, int b)
  46. {
  47. return !a != !b && 0 <= a && 0 <= b;
  48. }
  49. /* Return true if A and B are equal. */
  50. static int
  51. equal_tm (const struct tm *a, const struct tm *b)
  52. {
  53. return ! ((a->tm_sec ^ b->tm_sec)
  54. | (a->tm_min ^ b->tm_min)
  55. | (a->tm_hour ^ b->tm_hour)
  56. | (a->tm_mday ^ b->tm_mday)
  57. | (a->tm_mon ^ b->tm_mon)
  58. | (a->tm_year ^ b->tm_year)
  59. | isdst_differ (a->tm_isdst, b->tm_isdst));
  60. }
  61. #endif
  62. /* Copy to ABBRS the abbreviation at ABBR with size ABBR_SIZE (this
  63. includes its trailing null byte). Append an extra null byte to
  64. mark the end of ABBRS. */
  65. static void
  66. extend_abbrs (char *abbrs, char const *abbr, size_t abbr_size)
  67. {
  68. memcpy (abbrs, abbr, abbr_size);
  69. abbrs[abbr_size] = '\0';
  70. }
  71. /* Return a newly allocated time zone for NAME, or NULL on failure.
  72. A null NAME stands for wall clock time (which is like unset TZ). */
  73. timezone_t
  74. tzalloc (char const *name)
  75. {
  76. size_t name_size = name ? strlen (name) + 1 : 0;
  77. size_t abbr_size = name_size < ABBR_SIZE_MIN ? ABBR_SIZE_MIN : name_size + 1;
  78. timezone_t tz = malloc (FLEXSIZEOF (struct tm_zone, abbrs, abbr_size));
  79. if (tz)
  80. {
  81. tz->next = NULL;
  82. #if HAVE_TZNAME && !HAVE_TM_ZONE
  83. tz->tzname_copy[0] = tz->tzname_copy[1] = NULL;
  84. #endif
  85. tz->tz_is_set = !!name;
  86. tz->abbrs[0] = '\0';
  87. if (name)
  88. extend_abbrs (tz->abbrs, name, name_size);
  89. }
  90. return tz;
  91. }
  92. /* Save into TZ any nontrivial time zone abbreviation used by TM, and
  93. update *TM (if HAVE_TM_ZONE) or *TZ (if !HAVE_TM_ZONE &&
  94. HAVE_TZNAME) if they use the abbreviation. Return true if
  95. successful, false (setting errno) otherwise. */
  96. static bool
  97. save_abbr (timezone_t tz, struct tm *tm)
  98. {
  99. #if HAVE_TM_ZONE || HAVE_TZNAME
  100. char const *zone = NULL;
  101. char *zone_copy = (char *) "";
  102. # if HAVE_TZNAME
  103. int tzname_index = -1;
  104. # endif
  105. # if HAVE_TM_ZONE
  106. zone = tm->tm_zone;
  107. # endif
  108. # if HAVE_TZNAME
  109. if (! (zone && *zone) && 0 <= tm->tm_isdst)
  110. {
  111. tzname_index = tm->tm_isdst != 0;
  112. zone = tzname[tzname_index];
  113. }
  114. # endif
  115. /* No need to replace null zones, or zones within the struct tm. */
  116. if (!zone || ((char *) tm <= zone && zone < (char *) (tm + 1)))
  117. return true;
  118. if (*zone)
  119. {
  120. zone_copy = tz->abbrs;
  121. while (strcmp (zone_copy, zone) != 0)
  122. {
  123. if (! (*zone_copy || (zone_copy == tz->abbrs && tz->tz_is_set)))
  124. {
  125. size_t zone_size = strlen (zone) + 1;
  126. if (zone_size < tz->abbrs + ABBR_SIZE_MIN - zone_copy)
  127. extend_abbrs (zone_copy, zone, zone_size);
  128. else
  129. {
  130. tz = tz->next = tzalloc (zone);
  131. if (!tz)
  132. return false;
  133. tz->tz_is_set = 0;
  134. zone_copy = tz->abbrs;
  135. }
  136. break;
  137. }
  138. zone_copy += strlen (zone_copy) + 1;
  139. if (!*zone_copy && tz->next)
  140. {
  141. tz = tz->next;
  142. zone_copy = tz->abbrs;
  143. }
  144. }
  145. }
  146. /* Replace the zone name so that its lifetime matches that of TZ. */
  147. # if HAVE_TM_ZONE
  148. tm->tm_zone = zone_copy;
  149. # else
  150. if (0 <= tzname_index)
  151. tz->tzname_copy[tzname_index] = zone_copy;
  152. # endif
  153. #endif
  154. return true;
  155. }
  156. /* Free a time zone. */
  157. void
  158. tzfree (timezone_t tz)
  159. {
  160. if (tz != local_tz)
  161. while (tz)
  162. {
  163. timezone_t next = tz->next;
  164. free (tz);
  165. tz = next;
  166. }
  167. }
  168. /* Get and set the TZ environment variable. These functions can be
  169. overridden by programs like Emacs that manage their own environment. */
  170. #ifndef getenv_TZ
  171. static char *
  172. getenv_TZ (void)
  173. {
  174. return getenv ("TZ");
  175. }
  176. #endif
  177. #ifndef setenv_TZ
  178. static int
  179. setenv_TZ (char const *tz)
  180. {
  181. return tz ? setenv ("TZ", tz, 1) : unsetenv ("TZ");
  182. }
  183. #endif
  184. /* Change the environment to match the specified timezone_t value.
  185. Return true if successful, false (setting errno) otherwise. */
  186. static bool
  187. change_env (timezone_t tz)
  188. {
  189. if (setenv_TZ (tz->tz_is_set ? tz->abbrs : NULL) != 0)
  190. return false;
  191. tzset ();
  192. return true;
  193. }
  194. /* Temporarily set the time zone to TZ, which must not be null.
  195. Return LOCAL_TZ if the time zone setting is already correct.
  196. Otherwise return a newly allocated time zone representing the old
  197. setting, or NULL (setting errno) on failure. */
  198. static timezone_t
  199. set_tz (timezone_t tz)
  200. {
  201. char *env_tz = getenv_TZ ();
  202. if (env_tz
  203. ? tz->tz_is_set && strcmp (tz->abbrs, env_tz) == 0
  204. : !tz->tz_is_set)
  205. return local_tz;
  206. else
  207. {
  208. timezone_t old_tz = tzalloc (env_tz);
  209. if (!old_tz)
  210. return old_tz;
  211. if (! change_env (tz))
  212. {
  213. int saved_errno = errno;
  214. tzfree (old_tz);
  215. errno = saved_errno;
  216. return NULL;
  217. }
  218. return old_tz;
  219. }
  220. }
  221. /* Restore an old setting returned by set_tz. It must not be null.
  222. Return true (preserving errno) if successful, false (setting errno)
  223. otherwise. */
  224. static bool
  225. revert_tz (timezone_t tz)
  226. {
  227. if (tz == local_tz)
  228. return true;
  229. else
  230. {
  231. int saved_errno = errno;
  232. bool ok = change_env (tz);
  233. if (!ok)
  234. saved_errno = errno;
  235. tzfree (tz);
  236. errno = saved_errno;
  237. return ok;
  238. }
  239. }
  240. /* Use time zone TZ to compute localtime_r (T, TM). */
  241. struct tm *
  242. localtime_rz (timezone_t tz, time_t const *t, struct tm *tm)
  243. {
  244. if (!tz)
  245. return gmtime_r (t, tm);
  246. else
  247. {
  248. timezone_t old_tz = set_tz (tz);
  249. if (old_tz)
  250. {
  251. bool abbr_saved = localtime_r (t, tm) && save_abbr (tz, tm);
  252. if (revert_tz (old_tz) && abbr_saved)
  253. return tm;
  254. }
  255. return NULL;
  256. }
  257. }
  258. /* Use time zone TZ to compute mktime (TM). */
  259. time_t
  260. mktime_z (timezone_t tz, struct tm *tm)
  261. {
  262. if (!tz)
  263. return timegm (tm);
  264. else
  265. {
  266. timezone_t old_tz = set_tz (tz);
  267. if (old_tz)
  268. {
  269. time_t t = mktime (tm);
  270. #if HAVE_TM_ZONE || HAVE_TZNAME
  271. time_t badtime = -1;
  272. struct tm tm_1;
  273. if ((t != badtime
  274. || (localtime_r (&t, &tm_1) && equal_tm (tm, &tm_1)))
  275. && !save_abbr (tz, tm))
  276. t = badtime;
  277. #endif
  278. if (revert_tz (old_tz))
  279. return t;
  280. }
  281. return -1;
  282. }
  283. }