time_rz.c 8.2 KB

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