aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorPaul Eggert2015-07-26 00:01:34 -0700
committerPaul Eggert2015-07-26 12:44:54 -0700
commitaf32fa956267af40db61051c248597144d41521c (patch)
treeafd650b9f9805474df149081e51cc8abae3bdb87 /lib
parent4c55786d9b2a5d571f3e543cc261ce0702c7341e (diff)
downloademacs-af32fa956267af40db61051c248597144d41521c.tar.gz
emacs-af32fa956267af40db61051c248597144d41521c.zip
New optional ZONE arg for format-time-string etc.
This simplifies time conversions in other time zones. It also prevents display-time-world tampering with TZ (Bug#21020). * admin/admin.el (add-release-logs): Use improved add-log-time-format API. * admin/merge-gnulib (GNULIB_MODULES): Add time_rz, timegm. (GNULIB_TOOL_FLAGS): Avoid flexmember, setenv, unsetenv. * configure.ac (tzalloc): Remove test for this, since Emacs no longer uses HAVE_TZALLOC directly. * doc/lispref/os.texi (Time of Day, Time Conversion) (Time Parsing): * etc/NEWS: Document the new behavior. Merge from gnulib, incorporating: 2015-07-25 strftime: fix newly-introduced bug on Solaris 2015-07-23 fprintftime, strftime: use timezone_t args * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. * lib/strftime.c, lib/strftime.h, lib/time.in.h, m4/sys_time_h.m4: * m4/time_h.m4: Update from gnulib. * lib/time_rz.c, lib/timegm.c, m4/time_rz.m4, m4/timegm.m4: New files from gnulib. * lisp/time-stamp.el (time-stamp-string): * lisp/time.el (display-time-world-list) (display-time-world-display): Use new API, with time zone arg. * lisp/time.el (display-time-world-display): Fix race when current-time advances while we're running. * lisp/vc/add-log.el (add-log-iso8601-time-zone) (add-log-iso8601-time-string): Accept optional time zone arg. * lisp/vc/add-log.el (add-change-log-entry): * lisp/vc/log-edit.el (log-edit-changelog-ours-p): Use new arg. * nt/gnulib.mk: Propagate lib/gnulib.mk changes here. Add rules for the time module, since they're now needed for tzalloc etc. * src/conf_post.h (getenv_TZ, setenv_TZ): New macros. (emacs_getenv_TZ, emacs_setenv_TZ): New decls. * src/editfns.c: Include errno.h. (set_time_zone_rule): Omit unnecessary forward decl. (initial_tz): Remove, replacing with ... (local_tz, wall_clock_tz, utc_tz): New static vars and constants. (tzeqlen): New constant; prefer it to (sizeof "TZ=" - 1). (emacs_localtime_rz, emacs_mktime_z, xtzalloc, xtzfree) (tzlookup): New static functions. (init_editfns): New arg DUMPING. All uses changed. (init_editfns): Omit most initialization if dumping, not if !initialized. Initialize wall_clock_tz and local_tz. (emacs_nmemftime, format_time_string): Time zone argument can now be any time zone, not just a boolean for UTC or local time. All callers changed. (Fformat_time_string, Fencode_time, Fcurrent_time_string) (Fcurrent_time_zone): New optional arg ZONE. (Fdecode_time, Fset_time_zone_rule): ZONE arg can now also take the same form as with the other new additions. (decode_time_zone): Remove; no longer needed. (tzvalbuf): Now file-scope. (emacs_getenv_TZ, emacs_setenv_TZ): New functions. (syms_of_editfns): Define Qwall. * src/editfns.c (mktime_z) [!HAVE_TZALLOC]: * src/systime.h (mktime_z, timezone_t, tzalloc, tzfree) [!HAVE_TZALLOC]: Remove; now supplied by gnulib. * src/emacs.c (main): * src/lisp.h (init_editfns): Adjust to init_editfns API change.
Diffstat (limited to 'lib')
-rw-r--r--lib/gnulib.mk42
-rw-r--r--lib/strftime.c64
-rw-r--r--lib/strftime.h7
-rw-r--r--lib/time.in.h19
-rw-r--r--lib/time_rz.c374
-rw-r--r--lib/timegm.c38
6 files changed, 483 insertions, 61 deletions
diff --git a/lib/gnulib.mk b/lib/gnulib.mk
index 2dd0ef82c3c..1ca12a28beb 100644
--- a/lib/gnulib.mk
+++ b/lib/gnulib.mk
@@ -21,7 +21,7 @@
21# the same distribution terms as the rest of that program. 21# the same distribution terms as the rest of that program.
22# 22#
23# Generated by gnulib-tool. 23# Generated by gnulib-tool.
24# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=close --avoid=dup --avoid=fchdir --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=sigprocmask --avoid=stdarg --avoid=stdbool --avoid=threadlib --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt binary-io byteswap c-ctype c-strcase careadlinkat close-stream count-one-bits count-trailing-zeros crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog intprops largefile lstat manywarnings memrchr mkostemp mktime pipe2 pselect pthread_sigmask putenv qcopy-acl readlink readlinkat sig2str socklen stat-time stdalign stddef stdio stpcpy strftime strtoimax strtoumax symlink sys_stat sys_time time time_r timer-time timespec-add timespec-sub unsetenv update-copyright utimens vla warnings 24# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --avoid=close --avoid=dup --avoid=fchdir --avoid=flexmember --avoid=fstat --avoid=malloc-posix --avoid=msvc-inval --avoid=msvc-nothrow --avoid=open --avoid=openat-die --avoid=opendir --avoid=raise --avoid=save-cwd --avoid=select --avoid=setenv --avoid=sigprocmask --avoid=stdarg --avoid=stdbool --avoid=threadlib --avoid=unsetenv --makefile-name=gnulib.mk --conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files alloca-opt binary-io byteswap c-ctype c-strcase careadlinkat close-stream count-one-bits count-trailing-zeros crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 dtoastr dtotimespec dup2 environ execinfo faccessat fcntl fcntl-h fdatasync fdopendir filemode fstatat fsync getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog intprops largefile lstat manywarnings memrchr mkostemp mktime pipe2 pselect pthread_sigmask putenv qcopy-acl readlink readlinkat sig2str socklen stat-time stdalign stddef stdio stpcpy strftime strtoimax strtoumax symlink sys_stat sys_time time time_r time_rz timegm timer-time timespec-add timespec-sub unsetenv update-copyright utimens vla warnings
25 25
26 26
27MOSTLYCLEANFILES += core *.stackdump 27MOSTLYCLEANFILES += core *.stackdump
@@ -655,6 +655,17 @@ EXTRA_libgnu_a_SOURCES += mktime.c
655 655
656## end gnulib module mktime 656## end gnulib module mktime
657 657
658## begin gnulib module mktime-internal
659
660if gl_GNULIB_ENABLED_5264294aa0a5557541b53c8c741f7f31
661
662endif
663EXTRA_DIST += mktime-internal.h mktime.c
664
665EXTRA_libgnu_a_SOURCES += mktime.c
666
667## end gnulib module mktime-internal
668
658## begin gnulib module openat-h 669## begin gnulib module openat-h
659 670
660if gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7 671if gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7
@@ -1589,10 +1600,12 @@ time.h: time.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(
1589 -e 's/@''GNULIB_STRPTIME''@/$(GNULIB_STRPTIME)/g' \ 1600 -e 's/@''GNULIB_STRPTIME''@/$(GNULIB_STRPTIME)/g' \
1590 -e 's/@''GNULIB_TIMEGM''@/$(GNULIB_TIMEGM)/g' \ 1601 -e 's/@''GNULIB_TIMEGM''@/$(GNULIB_TIMEGM)/g' \
1591 -e 's/@''GNULIB_TIME_R''@/$(GNULIB_TIME_R)/g' \ 1602 -e 's/@''GNULIB_TIME_R''@/$(GNULIB_TIME_R)/g' \
1603 -e 's/@''GNULIB_TIME_RZ''@/$(GNULIB_TIME_RZ)/g' \
1592 -e 's|@''HAVE_DECL_LOCALTIME_R''@|$(HAVE_DECL_LOCALTIME_R)|g' \ 1604 -e 's|@''HAVE_DECL_LOCALTIME_R''@|$(HAVE_DECL_LOCALTIME_R)|g' \
1593 -e 's|@''HAVE_NANOSLEEP''@|$(HAVE_NANOSLEEP)|g' \ 1605 -e 's|@''HAVE_NANOSLEEP''@|$(HAVE_NANOSLEEP)|g' \
1594 -e 's|@''HAVE_STRPTIME''@|$(HAVE_STRPTIME)|g' \ 1606 -e 's|@''HAVE_STRPTIME''@|$(HAVE_STRPTIME)|g' \
1595 -e 's|@''HAVE_TIMEGM''@|$(HAVE_TIMEGM)|g' \ 1607 -e 's|@''HAVE_TIMEGM''@|$(HAVE_TIMEGM)|g' \
1608 -e 's|@''HAVE_TIMEZONE_T''@|$(HAVE_TIMEZONE_T)|g' \
1596 -e 's|@''REPLACE_GMTIME''@|$(REPLACE_GMTIME)|g' \ 1609 -e 's|@''REPLACE_GMTIME''@|$(REPLACE_GMTIME)|g' \
1597 -e 's|@''REPLACE_LOCALTIME''@|$(REPLACE_LOCALTIME)|g' \ 1610 -e 's|@''REPLACE_LOCALTIME''@|$(REPLACE_LOCALTIME)|g' \
1598 -e 's|@''REPLACE_LOCALTIME_R''@|$(REPLACE_LOCALTIME_R)|g' \ 1611 -e 's|@''REPLACE_LOCALTIME_R''@|$(REPLACE_LOCALTIME_R)|g' \
@@ -1624,6 +1637,24 @@ EXTRA_libgnu_a_SOURCES += time_r.c
1624 1637
1625## end gnulib module time_r 1638## end gnulib module time_r
1626 1639
1640## begin gnulib module time_rz
1641
1642
1643EXTRA_DIST += time_rz.c
1644
1645EXTRA_libgnu_a_SOURCES += time_rz.c
1646
1647## end gnulib module time_rz
1648
1649## begin gnulib module timegm
1650
1651
1652EXTRA_DIST += mktime-internal.h timegm.c
1653
1654EXTRA_libgnu_a_SOURCES += timegm.c
1655
1656## end gnulib module timegm
1657
1627## begin gnulib module timespec 1658## begin gnulib module timespec
1628 1659
1629libgnu_a_SOURCES += timespec.c 1660libgnu_a_SOURCES += timespec.c
@@ -1806,15 +1837,6 @@ EXTRA_DIST += unistd.in.h
1806 1837
1807## end gnulib module unistd 1838## end gnulib module unistd
1808 1839
1809## begin gnulib module unsetenv
1810
1811
1812EXTRA_DIST += unsetenv.c
1813
1814EXTRA_libgnu_a_SOURCES += unsetenv.c
1815
1816## end gnulib module unsetenv
1817
1818## begin gnulib module update-copyright 1840## begin gnulib module update-copyright
1819 1841
1820 1842
diff --git a/lib/strftime.c b/lib/strftime.c
index 2426aae7052..c7cec2621c9 100644
--- a/lib/strftime.c
+++ b/lib/strftime.c
@@ -121,22 +121,11 @@ extern char *tzname[];
121 121
122 122
123#ifdef _LIBC 123#ifdef _LIBC
124# define mktime_z(tz, tm) mktime (tm)
124# define tzname __tzname 125# define tzname __tzname
125# define tzset __tzset 126# define tzset __tzset
126#endif 127#endif
127 128
128#if !HAVE_TM_GMTOFF
129/* Portable standalone applications should supply a "time.h" that
130 declares a POSIX-compliant localtime_r, for the benefit of older
131 implementations that lack localtime_r or have a nonstandard one.
132 See the gnulib time_r module for one way to implement this. */
133# undef __gmtime_r
134# undef __localtime_r
135# define __gmtime_r gmtime_r
136# define __localtime_r localtime_r
137#endif
138
139
140#ifndef FPRINTFTIME 129#ifndef FPRINTFTIME
141# define FPRINTFTIME 0 130# define FPRINTFTIME 0
142#endif 131#endif
@@ -385,12 +374,7 @@ iso_week_days (int yday, int wday)
385 374
386/* When compiling this file, GNU applications can #define my_strftime 375/* When compiling this file, GNU applications can #define my_strftime
387 to a symbol (typically nstrftime) to get an extended strftime with 376 to a symbol (typically nstrftime) to get an extended strftime with
388 extra arguments UT and NS. Emacs is a special case for now, but 377 extra arguments TZ and NS. */
389 this Emacs-specific code can be removed once Emacs's config.h
390 defines my_strftime. */
391#if defined emacs && !defined my_strftime
392# define my_strftime nstrftime
393#endif
394 378
395#if FPRINTFTIME 379#if FPRINTFTIME
396# undef my_strftime 380# undef my_strftime
@@ -398,8 +382,9 @@ iso_week_days (int yday, int wday)
398#endif 382#endif
399 383
400#ifdef my_strftime 384#ifdef my_strftime
401# define extra_args , ut, ns 385# undef HAVE_TZSET
402# define extra_args_spec , int ut, int ns 386# define extra_args , tz, ns
387# define extra_args_spec , timezone_t tz, int ns
403#else 388#else
404# if defined COMPILE_WIDE 389# if defined COMPILE_WIDE
405# define my_strftime wcsftime 390# define my_strftime wcsftime
@@ -411,7 +396,7 @@ iso_week_days (int yday, int wday)
411# define extra_args 396# define extra_args
412# define extra_args_spec 397# define extra_args_spec
413/* We don't have this information in general. */ 398/* We don't have this information in general. */
414# define ut 0 399# define tz 1
415# define ns 0 400# define ns 0
416#endif 401#endif
417 402
@@ -483,7 +468,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
483 zone = (const char *) tp->tm_zone; 468 zone = (const char *) tp->tm_zone;
484#endif 469#endif
485#if HAVE_TZNAME 470#if HAVE_TZNAME
486 if (ut) 471 if (!tz)
487 { 472 {
488 if (! (zone && *zone)) 473 if (! (zone && *zone))
489 zone = "GMT"; 474 zone = "GMT";
@@ -496,7 +481,12 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
496 tzset (); 481 tzset ();
497# endif 482# endif
498 } 483 }
484 /* The tzset() call might have changed the value. */
485 if (!(zone && *zone) && tp->tm_isdst >= 0)
486 zone = tzname[tp->tm_isdst != 0];
499#endif 487#endif
488 if (! zone)
489 zone = "";
500 490
501 if (hour12 > 12) 491 if (hour12 > 12)
502 hour12 -= 12; 492 hour12 -= 12;
@@ -1144,7 +1134,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
1144 time_t t; 1134 time_t t;
1145 1135
1146 ltm = *tp; 1136 ltm = *tp;
1147 t = mktime (&ltm); 1137 t = mktime_z (tz, &ltm);
1148 1138
1149 /* Generate string value for T using time_t arithmetic; 1139 /* Generate string value for T using time_t arithmetic;
1150 this works even if sizeof (long) < sizeof (time_t). */ 1140 this works even if sizeof (long) < sizeof (time_t). */
@@ -1319,14 +1309,6 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
1319 to_lowcase = true; 1309 to_lowcase = true;
1320 } 1310 }
1321 1311
1322#if HAVE_TZNAME
1323 /* The tzset() call might have changed the value. */
1324 if (!(zone && *zone) && tp->tm_isdst >= 0)
1325 zone = tzname[tp->tm_isdst != 0];
1326#endif
1327 if (! zone)
1328 zone = "";
1329
1330#ifdef COMPILE_WIDE 1312#ifdef COMPILE_WIDE
1331 { 1313 {
1332 /* The zone string is always given in multibyte form. We have 1314 /* The zone string is always given in multibyte form. We have
@@ -1366,7 +1348,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
1366#if HAVE_TM_GMTOFF 1348#if HAVE_TM_GMTOFF
1367 diff = tp->tm_gmtoff; 1349 diff = tp->tm_gmtoff;
1368#else 1350#else
1369 if (ut) 1351 if (!tz)
1370 diff = 0; 1352 diff = 0;
1371 else 1353 else
1372 { 1354 {
@@ -1375,7 +1357,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
1375 time_t lt; 1357 time_t lt;
1376 1358
1377 ltm = *tp; 1359 ltm = *tp;
1378 lt = mktime (&ltm); 1360 lt = mktime_z (tz, &ltm);
1379 1361
1380 if (lt == (time_t) -1) 1362 if (lt == (time_t) -1)
1381 { 1363 {
@@ -1384,7 +1366,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
1384 occurred. */ 1366 occurred. */
1385 struct tm tm; 1367 struct tm tm;
1386 1368
1387 if (! __localtime_r (&lt, &tm) 1369 if (! localtime_rz (tz, &lt, &tm)
1388 || ((ltm.tm_sec ^ tm.tm_sec) 1370 || ((ltm.tm_sec ^ tm.tm_sec)
1389 | (ltm.tm_min ^ tm.tm_min) 1371 | (ltm.tm_min ^ tm.tm_min)
1390 | (ltm.tm_hour ^ tm.tm_hour) 1372 | (ltm.tm_hour ^ tm.tm_hour)
@@ -1394,7 +1376,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
1394 break; 1376 break;
1395 } 1377 }
1396 1378
1397 if (! __gmtime_r (&lt, &gtm)) 1379 if (! localtime_rz (0, &lt, &gtm))
1398 break; 1380 break;
1399 1381
1400 diff = tm_diff (&ltm, &gtm); 1382 diff = tm_diff (&ltm, &gtm);
@@ -1473,15 +1455,3 @@ my_strftime (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
1473#if defined _LIBC && ! FPRINTFTIME 1455#if defined _LIBC && ! FPRINTFTIME
1474libc_hidden_def (my_strftime) 1456libc_hidden_def (my_strftime)
1475#endif 1457#endif
1476
1477
1478#if defined emacs && ! FPRINTFTIME
1479/* For Emacs we have a separate interface which corresponds to the normal
1480 strftime function plus the ut argument, but without the ns argument. */
1481size_t
1482emacs_strftimeu (char *s, size_t maxsize, const char *format,
1483 const struct tm *tp, int ut)
1484{
1485 return my_strftime (s, maxsize, format, tp, ut, 0);
1486}
1487#endif
diff --git a/lib/strftime.h b/lib/strftime.h
index 3967afc4941..2ce6cc57687 100644
--- a/lib/strftime.h
+++ b/lib/strftime.h
@@ -23,11 +23,10 @@ extern "C" {
23 23
24/* Just like strftime, but with two more arguments: 24/* Just like strftime, but with two more arguments:
25 POSIX requires that strftime use the local timezone information. 25 POSIX requires that strftime use the local timezone information.
26 When __UTC is nonzero and tm->tm_zone is NULL or the empty string, 26 Use the timezone __TZ instead. Use __NS as the number of
27 use UTC instead. Use __NS as the number of nanoseconds in the 27 nanoseconds in the %N directive. */
28 %N directive. */
29size_t nstrftime (char *, size_t, char const *, struct tm const *, 28size_t nstrftime (char *, size_t, char const *, struct tm const *,
30 int __utc, int __ns); 29 timezone_t __tz, int __ns);
31 30
32#ifdef __cplusplus 31#ifdef __cplusplus
33} 32}
diff --git a/lib/time.in.h b/lib/time.in.h
index 1a6b746ccab..a983f498e51 100644
--- a/lib/time.in.h
+++ b/lib/time.in.h
@@ -231,6 +231,25 @@ _GL_CXXALIAS_SYS (strptime, char *, (char const *restrict __buf,
231_GL_CXXALIASWARN (strptime); 231_GL_CXXALIASWARN (strptime);
232# endif 232# endif
233 233
234# if defined _GNU_SOURCE && @GNULIB_TIME_RZ@ && ! @HAVE_TIMEZONE_T@
235typedef struct tm_zone *timezone_t;
236_GL_FUNCDECL_SYS (tzalloc, timezone_t, (char const *__name));
237_GL_CXXALIAS_SYS (tzalloc, timezone_t, (char const *__name));
238_GL_FUNCDECL_SYS (tzfree, void, (timezone_t __tz));
239_GL_CXXALIAS_SYS (tzfree, void, (timezone_t __tz));
240_GL_FUNCDECL_SYS (localtime_rz, struct tm *,
241 (timezone_t __tz, time_t const *restrict __timer,
242 struct tm *restrict __result) _GL_ARG_NONNULL ((2, 3)));
243_GL_CXXALIAS_SYS (localtime_rz, struct tm *,
244 (timezone_t __tz, time_t const *restrict __timer,
245 struct tm *restrict __result));
246_GL_FUNCDECL_SYS (mktime_z, time_t,
247 (timezone_t __tz, struct tm *restrict __result)
248 _GL_ARG_NONNULL ((2)));
249_GL_CXXALIAS_SYS (mktime_z, time_t,
250 (timezone_t __tz, struct tm *restrict __result));
251# endif
252
234/* Convert TM to a time_t value, assuming UTC. */ 253/* Convert TM to a time_t value, assuming UTC. */
235# if @GNULIB_TIMEGM@ 254# if @GNULIB_TIMEGM@
236# if @REPLACE_TIMEGM@ 255# if @REPLACE_TIMEGM@
diff --git a/lib/time_rz.c b/lib/time_rz.c
new file mode 100644
index 00000000000..8a4d7d13a2c
--- /dev/null
+++ b/lib/time_rz.c
@@ -0,0 +1,374 @@
1/* Time zone functions such as tzalloc and localtime_rz
2
3 Copyright 2015 Free Software Foundation, Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, see <http://www.gnu.org/licenses/>. */
17
18/* Written by Paul Eggert. */
19
20/* Although this module is not thread-safe, any races should be fairly
21 rare and reasonably benign. For complete thread-safety, use a C
22 library with a working timezone_t type, so that this module is not
23 needed. */
24
25#include <config.h>
26
27#include <time.h>
28
29#include <errno.h>
30#include <stdbool.h>
31#include <stddef.h>
32#include <stdlib.h>
33#include <string.h>
34
35#if !HAVE_TZSET
36static void tzset (void) { }
37#endif
38
39/* A time zone rule. */
40struct tm_zone
41{
42 /* More abbreviations, should they be needed. Their TZ_IS_SET
43 members are zero. */
44 timezone_t next;
45
46 /* If nonzero, the rule represents the TZ environment variable set
47 to the first "abbreviation" (this may be the empty string).
48 Otherwise, it represents an unset TZ. */
49 char tz_is_set;
50
51 /* A sequence of null-terminated strings packed next to each other.
52 The strings are followed by an extra null byte. If TZ_IS_SET,
53 there must be at least one string and the first string (which is
54 actually a TZ environment value value) may be empty. Otherwise
55 all strings must be nonempty.
56
57 Abbreviations are stored here because otherwise the values of
58 tm_zone and/or tzname would be dead after changing TZ and calling
59 tzset. Abbreviations never move once allocated, and are live
60 until tzfree is called. */
61 char abbrs[FLEXIBLE_ARRAY_MEMBER];
62};
63
64/* The approximate size to use for small allocation requests. This is
65 the largest "small" request for the GNU C library malloc. */
66enum { DEFAULT_MXFAST = 64 * sizeof (size_t) / 4 };
67
68/* Minimum size of the ABBRS member of struct abbr. ABBRS is larger
69 only in the unlikely case where an abbreviation longer than this is
70 used. */
71enum { ABBR_SIZE_MIN = DEFAULT_MXFAST - offsetof (struct tm_zone, abbrs) };
72
73static char const TZ[] = "TZ";
74
75/* Magic cookie timezone_t value, for local time. It differs from
76 NULL and from all other timezone_t values. Only the address
77 matters; the pointer is never dereferenced. */
78static timezone_t const local_tz = (timezone_t) 1;
79
80#if HAVE_TM_ZONE || HAVE_TZNAME
81
82/* Return true if the values A and B differ according to the rules for
83 tm_isdst: A and B differ if one is zero and the other positive. */
84static bool
85isdst_differ (int a, int b)
86{
87 return !a != !b && 0 <= a && 0 <= b;
88}
89
90/* Return true if A and B are equal. */
91static int
92equal_tm (const struct tm *a, const struct tm *b)
93{
94 return ! ((a->tm_sec ^ b->tm_sec)
95 | (a->tm_min ^ b->tm_min)
96 | (a->tm_hour ^ b->tm_hour)
97 | (a->tm_mday ^ b->tm_mday)
98 | (a->tm_mon ^ b->tm_mon)
99 | (a->tm_year ^ b->tm_year)
100 | isdst_differ (a->tm_isdst, b->tm_isdst));
101}
102
103#endif
104
105/* Copy to ABBRS the abbreviation at ABBR with size ABBR_SIZE (this
106 includes its trailing null byte). Append an extra null byte to
107 mark the end of ABBRS. */
108static void
109extend_abbrs (char *abbrs, char const *abbr, size_t abbr_size)
110{
111 memcpy (abbrs, abbr, abbr_size);
112 abbrs[abbr_size] = '\0';
113}
114
115/* Return a newly allocated time zone for NAME, or NULL on failure.
116 As a special case, return a nonzero constant for wall clock time, a
117 constant that survives freeing. */
118timezone_t
119tzalloc (char const *name)
120{
121 size_t name_size = name ? strlen (name) + 1 : 0;
122 size_t abbr_size = name_size < ABBR_SIZE_MIN ? ABBR_SIZE_MIN : name_size + 1;
123 timezone_t tz = malloc (offsetof (struct tm_zone, abbrs) + abbr_size);
124 if (tz)
125 {
126 tz->next = NULL;
127 tz->tz_is_set = !!name;
128 extend_abbrs (tz->abbrs, name, name_size);
129 }
130 return tz;
131}
132
133#if HAVE_TZNAME
134/* If TZNAME_ADDRESS is nonnull, an assignment of a saved abbreviation.
135 TZNAME_ADDRESS should be either null, or &tzname[0], or &tzname[1].
136 *TZNAME_ADDRESS = TZNAME_VALUE should be done after revert_tz
137 (indirectly) calls tzset, so that revert_tz can overwrite tzset's
138 assignment to tzname. Also, it should be done at the start of
139 the next localtime_tz or mktime_z, to undo the overwrite. */
140static char **tzname_address;
141static char *tzname_value;
142#endif
143
144/* Save into TZ any nontrivial time zone abbreviation used by TM,
145 and update *TM (or prepare to update tzname) if they use the abbreviation.
146 Return true if successful, false (setting errno) otherwise. */
147static bool
148save_abbr (timezone_t tz, struct tm *tm)
149{
150#if HAVE_TM_ZONE || HAVE_TZNAME
151 char const *zone = NULL;
152 char **tzname_zone = NULL;
153 char *zone_copy = (char *) "";
154# if HAVE_TM_ZONE
155 zone = tm->tm_zone;
156# endif
157# if HAVE_TZNAME
158 if (! (zone && *zone) && 0 <= tm->tm_isdst)
159 zone = *(tzname_zone = &tzname[0 < tm->tm_isdst]);
160# endif
161
162 /* No need to replace null zones, or zones within the struct tm. */
163 if (!zone || ((char *) tm <= zone && zone < (char *) (tm + 1)))
164 return true;
165
166 if (*zone)
167 {
168 zone_copy = tz->abbrs;
169
170 while (strcmp (zone_copy, zone) != 0)
171 {
172 if (! (*zone_copy || (zone_copy == tz->abbrs && tz->tz_is_set)))
173 {
174 size_t zone_size = strlen (zone) + 1;
175 if (zone_size < tz->abbrs + ABBR_SIZE_MIN - zone_copy)
176 extend_abbrs (zone_copy, zone, zone_size);
177 else
178 {
179 tz = tz->next = tzalloc (zone);
180 if (!tz)
181 return false;
182 tz->tz_is_set = 0;
183 zone_copy = tz->abbrs;
184 }
185 break;
186 }
187
188 zone_copy += strlen (zone_copy) + 1;
189 if (!*zone_copy && tz->next)
190 {
191 tz = tz->next;
192 zone_copy = tz->abbrs;
193 }
194 }
195 }
196
197 /* Replace the zone name so that its lifetime matches that of TZ. */
198# if HAVE_TM_ZONE
199 if (!tzname_zone)
200 tm->tm_zone = zone_copy;
201# endif
202# if HAVE_TZNAME
203 tzname_address = tzname_zone;
204 tzname_value = zone_copy;
205# endif
206#endif
207 return true;
208}
209
210/* Free a time zone. */
211void
212tzfree (timezone_t tz)
213{
214 if (tz != local_tz)
215 while (tz)
216 {
217 timezone_t next = tz->next;
218 free (tz);
219 tz = next;
220 }
221}
222
223/* Get and set the TZ environment variable. These functions can be
224 overridden by programs like Emacs that manage their own environment. */
225
226#ifndef getenv_TZ
227static char *
228getenv_TZ (void)
229{
230 return getenv (TZ);
231}
232#endif
233
234#ifndef setenv_TZ
235static int
236setenv_TZ (char const *tz)
237{
238 return tz ? setenv (TZ, tz, 1) : unsetenv (TZ);
239}
240#endif
241
242/* Change the environment to match the specified timezone_t value.
243 Return true if successful, false (setting errno) otherwise. */
244static bool
245change_env (timezone_t tz)
246{
247 if (setenv_TZ (tz->tz_is_set ? tz->abbrs : NULL) != 0)
248 return false;
249 tzset ();
250 return true;
251}
252
253/* Temporarily set the time zone to TZ, which must not be null.
254 Return LOCAL_TZ if the time zone setting is already correct.
255 Otherwise return a newly allocated time zone representing the old
256 setting, or NULL (setting errno) on failure. */
257static timezone_t
258set_tz (timezone_t tz)
259{
260 char *env_tz = getenv_TZ ();
261 if (env_tz
262 ? tz->tz_is_set && strcmp (tz->abbrs, env_tz) == 0
263 : !tz->tz_is_set)
264 return local_tz;
265 else
266 {
267 timezone_t old_tz = tzalloc (env_tz);
268 if (!old_tz)
269 return old_tz;
270 if (! change_env (tz))
271 {
272 int saved_errno = errno;
273 tzfree (old_tz);
274 errno = saved_errno;
275 return NULL;
276 }
277 return old_tz;
278 }
279}
280
281/* Restore an old setting returned by set_tz. It must not be null.
282 Return true (preserving errno) if successful, false (setting errno)
283 otherwise. */
284static bool
285revert_tz (timezone_t tz)
286{
287 if (tz == local_tz)
288 return true;
289 else
290 {
291 int saved_errno = errno;
292 bool ok = change_env (tz);
293 if (!ok)
294 saved_errno = errno;
295#if HAVE_TZNAME
296 if (!ok)
297 tzname_address = NULL;
298 if (tzname_address)
299 {
300 char *old_value = *tzname_address;
301 *tzname_address = tzname_value;
302 tzname_value = old_value;
303 }
304#endif
305 tzfree (tz);
306 errno = saved_errno;
307 return ok;
308 }
309}
310
311/* Restore an old tzname setting that was temporarily munged by revert_tz. */
312static void
313restore_tzname (void)
314{
315#if HAVE_TZNAME
316 if (tzname_address)
317 {
318 *tzname_address = tzname_value;
319 tzname_address = NULL;
320 }
321#endif
322}
323
324/* Use time zone TZ to compute localtime_r (T, TM). */
325struct tm *
326localtime_rz (timezone_t tz, time_t const *t, struct tm *tm)
327{
328 restore_tzname ();
329
330 if (!tz)
331 return gmtime_r (t, tm);
332 else
333 {
334 timezone_t old_tz = set_tz (tz);
335 if (old_tz)
336 {
337 tm = localtime_r (t, tm);
338 if (tm && !save_abbr (tz, tm))
339 tm = NULL;
340 if (revert_tz (old_tz))
341 return tm;
342 }
343 return NULL;
344 }
345}
346
347/* Use time zone TZ to compute mktime (TM). */
348time_t
349mktime_z (timezone_t tz, struct tm *tm)
350{
351 restore_tzname ();
352
353 if (!tz)
354 return timegm (tm);
355 else
356 {
357 timezone_t old_tz = set_tz (tz);
358 if (old_tz)
359 {
360 time_t t = mktime (tm);
361#if HAVE_TM_ZONE || HAVE_TZNAME
362 time_t badtime = -1;
363 struct tm tm_1;
364 if ((t != badtime
365 || (localtime_r (&t, &tm_1) && equal_tm (tm, &tm_1)))
366 && !save_abbr (tz, tm))
367 t = badtime;
368#endif
369 if (revert_tz (old_tz))
370 return t;
371 }
372 return -1;
373 }
374}
diff --git a/lib/timegm.c b/lib/timegm.c
new file mode 100644
index 00000000000..11c485ff05a
--- /dev/null
+++ b/lib/timegm.c
@@ -0,0 +1,38 @@
1/* Convert UTC calendar time to simple time. Like mktime but assumes UTC.
2
3 Copyright (C) 1994, 1997, 2003-2004, 2006-2007, 2009-2015 Free Software
4 Foundation, Inc. This file is part of the GNU C Library.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>. */
18
19#ifndef _LIBC
20# include <config.h>
21#endif
22
23#include <time.h>
24
25#ifndef _LIBC
26# undef __gmtime_r
27# define __gmtime_r gmtime_r
28# define __mktime_internal mktime_internal
29# include "mktime-internal.h"
30#endif
31
32time_t
33timegm (struct tm *tmp)
34{
35 static time_t gmtime_offset;
36 tmp->tm_isdst = 0;
37 return __mktime_internal (tmp, __gmtime_r, &gmtime_offset);
38}