diff options
| author | Paul Eggert | 2015-07-26 00:01:34 -0700 |
|---|---|---|
| committer | Paul Eggert | 2015-07-26 12:44:54 -0700 |
| commit | af32fa956267af40db61051c248597144d41521c (patch) | |
| tree | afd650b9f9805474df149081e51cc8abae3bdb87 /lib | |
| parent | 4c55786d9b2a5d571f3e543cc261ce0702c7341e (diff) | |
| download | emacs-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.mk | 42 | ||||
| -rw-r--r-- | lib/strftime.c | 64 | ||||
| -rw-r--r-- | lib/strftime.h | 7 | ||||
| -rw-r--r-- | lib/time.in.h | 19 | ||||
| -rw-r--r-- | lib/time_rz.c | 374 | ||||
| -rw-r--r-- | lib/timegm.c | 38 |
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 | ||
| 27 | MOSTLYCLEANFILES += core *.stackdump | 27 | MOSTLYCLEANFILES += 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 | |||
| 660 | if gl_GNULIB_ENABLED_5264294aa0a5557541b53c8c741f7f31 | ||
| 661 | |||
| 662 | endif | ||
| 663 | EXTRA_DIST += mktime-internal.h mktime.c | ||
| 664 | |||
| 665 | EXTRA_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 | ||
| 660 | if gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7 | 671 | if 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 | |||
| 1643 | EXTRA_DIST += time_rz.c | ||
| 1644 | |||
| 1645 | EXTRA_libgnu_a_SOURCES += time_rz.c | ||
| 1646 | |||
| 1647 | ## end gnulib module time_rz | ||
| 1648 | |||
| 1649 | ## begin gnulib module timegm | ||
| 1650 | |||
| 1651 | |||
| 1652 | EXTRA_DIST += mktime-internal.h timegm.c | ||
| 1653 | |||
| 1654 | EXTRA_libgnu_a_SOURCES += timegm.c | ||
| 1655 | |||
| 1656 | ## end gnulib module timegm | ||
| 1657 | |||
| 1627 | ## begin gnulib module timespec | 1658 | ## begin gnulib module timespec |
| 1628 | 1659 | ||
| 1629 | libgnu_a_SOURCES += timespec.c | 1660 | libgnu_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 | |||
| 1812 | EXTRA_DIST += unsetenv.c | ||
| 1813 | |||
| 1814 | EXTRA_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 (<m); | 1137 | t = mktime_z (tz, <m); |
| 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 (<m); | 1360 | lt = mktime_z (tz, <m); |
| 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 (<, &tm) | 1369 | if (! localtime_rz (tz, <, &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 (<, >m)) | 1379 | if (! localtime_rz (0, <, >m)) |
| 1398 | break; | 1380 | break; |
| 1399 | 1381 | ||
| 1400 | diff = tm_diff (<m, >m); | 1382 | diff = tm_diff (<m, >m); |
| @@ -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 |
| 1474 | libc_hidden_def (my_strftime) | 1456 | libc_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. */ | ||
| 1481 | size_t | ||
| 1482 | emacs_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. */ | ||
| 29 | size_t nstrftime (char *, size_t, char const *, struct tm const *, | 28 | size_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@ | ||
| 235 | typedef 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 | ||
| 36 | static void tzset (void) { } | ||
| 37 | #endif | ||
| 38 | |||
| 39 | /* A time zone rule. */ | ||
| 40 | struct 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. */ | ||
| 66 | enum { 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. */ | ||
| 71 | enum { ABBR_SIZE_MIN = DEFAULT_MXFAST - offsetof (struct tm_zone, abbrs) }; | ||
| 72 | |||
| 73 | static 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. */ | ||
| 78 | static 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. */ | ||
| 84 | static bool | ||
| 85 | isdst_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. */ | ||
| 91 | static int | ||
| 92 | equal_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. */ | ||
| 108 | static void | ||
| 109 | extend_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. */ | ||
| 118 | timezone_t | ||
| 119 | tzalloc (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. */ | ||
| 140 | static char **tzname_address; | ||
| 141 | static 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. */ | ||
| 147 | static bool | ||
| 148 | save_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. */ | ||
| 211 | void | ||
| 212 | tzfree (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 | ||
| 227 | static char * | ||
| 228 | getenv_TZ (void) | ||
| 229 | { | ||
| 230 | return getenv (TZ); | ||
| 231 | } | ||
| 232 | #endif | ||
| 233 | |||
| 234 | #ifndef setenv_TZ | ||
| 235 | static int | ||
| 236 | setenv_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. */ | ||
| 244 | static bool | ||
| 245 | change_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. */ | ||
| 257 | static timezone_t | ||
| 258 | set_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. */ | ||
| 284 | static bool | ||
| 285 | revert_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. */ | ||
| 312 | static void | ||
| 313 | restore_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). */ | ||
| 325 | struct tm * | ||
| 326 | localtime_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). */ | ||
| 348 | time_t | ||
| 349 | mktime_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 | |||
| 32 | time_t | ||
| 33 | timegm (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 | } | ||