emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master af32fa9: New optional ZONE arg for format-time-stri


From: Paul Eggert
Subject: [Emacs-diffs] master af32fa9: New optional ZONE arg for format-time-string etc.
Date: Sun, 26 Jul 2015 19:44:59 +0000

branch: master
commit af32fa956267af40db61051c248597144d41521c
Author: Paul Eggert <address@hidden>
Commit: Paul Eggert <address@hidden>

    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.
---
 admin/admin.el      |    6 +-
 admin/merge-gnulib  |    8 +-
 configure.ac        |    2 +-
 doc/lispref/os.texi |   80 +++++++-----
 etc/NEWS            |    9 ++
 lib/gnulib.mk       |   42 +++++--
 lib/strftime.c      |   64 +++-------
 lib/strftime.h      |    7 +-
 lib/time.in.h       |   19 +++
 lib/time_rz.c       |  374 +++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/timegm.c        |   38 +++++
 lisp/time-stamp.el  |   12 +--
 lisp/time.el        |   33 ++---
 lisp/vc/add-log.el  |   23 +--
 lisp/vc/log-edit.el |    3 +-
 m4/gnulib-comp.m4   |   43 +++++--
 m4/sys_time_h.m4    |    1 +
 m4/time_h.m4        |    1 +
 m4/time_rz.m4       |   21 +++
 m4/timegm.m4        |   26 ++++
 nt/gnulib.mk        |   88 +++++++++++--
 src/conf_post.h     |    7 +
 src/editfns.c       |  324 +++++++++++++++++++++++++++-----------------
 src/emacs.c         |    2 +-
 src/lisp.h          |    2 +-
 src/systime.h       |   14 --
 26 files changed, 941 insertions(+), 308 deletions(-)

diff --git a/admin/admin.el b/admin/admin.el
index 93e9124..267f2c4 100644
--- a/admin/admin.el
+++ b/admin/admin.el
@@ -38,14 +38,12 @@ Optional argument DATE is the release date, default today."
                                          emacs-minor-version))
                     (read-string "Release date: "
                                  (progn (require 'add-log)
-                                        (let ((add-log-time-zone-rule t))
-                                          (funcall add-log-time-format))))))
+                                         (funcall add-log-time-format nil 
t)))))
   (setq root (expand-file-name root))
   (unless (file-exists-p (expand-file-name "src/emacs.c" root))
     (user-error "%s doesn't seem to be the root of an Emacs source tree" root))
   (require 'add-log)
-  (or date (setq date (let ((add-log-time-zone-rule t))
-                       (funcall add-log-time-format))))
+  (or date (setq date (funcall add-log-time-format nil t)))
   (let* ((logs (process-lines "find" root "-name" "ChangeLog"))
         (entry (format "%s  %s  <%s>\n\n\t* Version %s released.\n\n"
                        date
diff --git a/admin/merge-gnulib b/admin/merge-gnulib
index e7910a6..963c3a0 100755
--- a/admin/merge-gnulib
+++ b/admin/merge-gnulib
@@ -37,20 +37,20 @@ GNULIB_MODULES='
   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
+  sys_time time time_r time_rz timegm timer-time timespec-add timespec-sub
   unsetenv update-copyright utimens
   vla warnings
 '
 
 GNULIB_TOOL_FLAGS='
   --avoid=close --avoid=dup
-  --avoid=fchdir --avoid=fstat
+  --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=sigprocmask
+  --avoid=save-cwd --avoid=select --avoid=setenv --avoid=sigprocmask
   --avoid=stdarg --avoid=stdbool
-  --avoid=threadlib
+  --avoid=threadlib --avoid=unsetenv
   --conditional-dependencies --import --no-changelog --no-vc-files
   --makefile-name=gnulib.mk
 '
diff --git a/configure.ac b/configure.ac
index b58c7de..19b8b9d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4000,7 +4000,7 @@ AC_SUBST(KRB4LIB)
 
 AC_CHECK_HEADERS(valgrind/valgrind.h)
 
-AC_CHECK_FUNCS_ONCE(tzalloc tzset)
+AC_CHECK_FUNCS_ONCE(tzset)
 
 ok_so_far=yes
 AC_CHECK_FUNC(socket, , ok_so_far=no)
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index 0c39be9..4b5a1b4 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -1234,7 +1234,7 @@ three-element lists, with omitted @var{microsec} and 
@var{picosec}
 components defaulting to zero.
 
 @cindex time value
-  Function arguments, e.g., the @var{time-value} argument to
+  Function arguments, e.g., the @var{time} argument to
 @code{current-time-string}, accept a more-general @dfn{time value}
 format, which can be a list of integers as above, or a single number
 for seconds since the epoch, or @code{nil} for the current time.  You
@@ -1244,7 +1244,7 @@ of integers using @code{seconds-to-time}, and into other 
forms using
 @code{decode-time} and @code{float-time}.  These functions are
 described in the following sections.
 
address@hidden current-time-string &optional time-value
address@hidden current-time-string &optional time zone
 This function returns the current time and date as a human-readable
 string.  The format does not vary for the initial part of the string,
 which contains the day of week, month, day of month, and time of day
@@ -1255,8 +1255,9 @@ characters from the beginning of the string rather than 
from the end,
 as the year might not have exactly four digits, and additional
 information may some day be added at the end.
 
-The argument @var{time-value}, if given, specifies a time to format,
-instead of the current time.
+The argument @var{time}, if given, specifies a time to format,
+instead of the current time.  The optional argument @var{zone}
+defaults to the current time zone rule.
 
 @example
 @group
@@ -1275,9 +1276,9 @@ multiple of 1000, but this may change as 
higher-resolution clocks
 become available.
 @end defun
 
address@hidden float-time &optional time-value
address@hidden float-time &optional time
 This function returns the current time as a floating-point number of
-seconds since the epoch.  The optional argument @var{time-value}, if
+seconds since the epoch.  The optional argument @var{time}, if
 given, specifies a time to convert instead of the current time.
 
 @emph{Warning}: Since the result is floating point, it may not be
@@ -1286,14 +1287,14 @@ exact.  Do not use this function if precise time stamps 
are required.
 @code{time-to-seconds} is an alias for this function.
 @end defun
 
address@hidden seconds-to-time time-value
address@hidden seconds-to-time time
 This function converts a time value to list-of-integer form.
-For example, if @var{time-value} is a number, @code{(time-to-seconds
-(seconds-to-time @var{time-value}))} equals the number unless overflow
+For example, if @var{time} is a number, @code{(time-to-seconds
+(seconds-to-time @var{time}))} equals the number unless overflow
 or rounding errors occur.
 @end defun
 
address@hidden current-time-zone &optional time-value
address@hidden current-time-zone &optional time zone
 @cindex time zone, current
 This function returns a list describing the time zone that the user is
 in.
@@ -1309,15 +1310,27 @@ adjustment, then the value is constant through time.
 If the operating system doesn't supply all the information necessary to
 compute the value, the unknown elements of the list are @code{nil}.
 
-The argument @var{time-value}, if given, specifies a time value to
-analyze instead of the current time.
+The argument @var{time}, if given, specifies a time value to
+analyze instead of the current time.  The optional argument @var{zone}
+defaults to the current time zone rule.
 @end defun
 
-The current time zone is determined by the @env{TZ} environment
address@hidden TZ, environment variable
+The default time zone is determined by the @env{TZ} environment
 variable.  @xref{System Environment}.  For example, you can tell Emacs
-to use universal time with @code{(setenv "TZ" "UTC0")}.  If @env{TZ}
-is not in the environment, Emacs uses a platform-dependent default
-time zone.
+to default to universal time with @code{(setenv "TZ" "UTC0")}.  If
address@hidden is not in the environment, Emacs uses system wall clock time,
+which is a platform-dependent default time zone.
+
address@hidden time zone rule
+Functions that convert to and from local time accept an optional
address@hidden zone rule} argument, which specifies the conversion's time
+zone and daylight saving time history.  If the time zone rule is
+omitted or @code{nil}, the conversion uses Emacs's default time zone.
+If it is @code{t}, the conversion uses Universal Time.  If it is
address@hidden, the conversion uses the system wall clock time.  If it is
+a string, the conversion uses the time zone rule equivalent to setting
address@hidden to that string.
 
 @node Time Conversion
 @section Time Conversion
@@ -1340,13 +1353,14 @@ count the number of years since the year 1 B.C., and do 
not skip zero
 as traditional Gregorian years do; for example, the year number
 @minus{}37 represents the Gregorian year 38 address@hidden
 
address@hidden decode-time &optional time-value
address@hidden decode-time &optional time zone
 This function converts a time value into calendrical information.  If
-you don't specify @var{time-value}, it decodes the current time.  The return
+you don't specify @var{time}, it decodes the current time, and similarly
address@hidden defaults to the current time zone rule.  The return
 value is a list of nine elements, as follows:
 
 @example
-(@var{seconds} @var{minutes} @var{hour} @var{day} @var{month} @var{year} 
@var{dow} @var{dst} @var{zone})
+(@var{seconds} @var{minutes} @var{hour} @var{day} @var{month} @var{year} 
@var{dow} @var{dst} @var{utcoff})
 @end example
 
 Here is what the elements mean:
@@ -1370,13 +1384,13 @@ The day of week, as an integer between 0 and 6, where 0 
stands for
 Sunday.
 @item dst
 @code{t} if daylight saving time is effect, otherwise @code{nil}.
address@hidden zone
-An integer indicating the time zone, as the number of seconds east of
-Greenwich.
address@hidden utcoff
+An integer indicating the UTC offset in seconds, i.e., the number of
+seconds east of Greenwich.
 @end table
 
 @strong{Common Lisp Note:} Common Lisp has different meanings for
address@hidden and @var{zone}.
address@hidden and @var{utcoff}.
 @end defun
 
 @defun encode-time seconds minutes hour day month year &optional zone
@@ -1389,12 +1403,11 @@ Year numbers less than 100 are not treated specially.  
If you want them
 to stand for years above 1900, or years above 2000, you must alter them
 yourself before you call @code{encode-time}.
 
-The optional argument @var{zone} defaults to the current time zone and
-its daylight saving time rules.  If specified, it can be either a list
-(as you would get from @code{current-time-zone}), a string as in the
address@hidden environment variable, @code{t} for Universal Time, or an
-integer (as you would get from @code{decode-time}).  The specified
-zone is used without any further alteration for daylight saving time.
+The optional argument @var{zone} defaults to the current time zone rule.
+In addition to the usual time zone rule values, it can also be a list
+(as you would get from @code{current-time-zone}) or an integer (as
+from @code{decode-time}), applied without any further alteration for
+daylight saving time.
 
 If you pass more than seven arguments to @code{encode-time}, the first
 six are used as @var{seconds} through @var{year}, the last argument is
@@ -1430,11 +1443,12 @@ This function parses the time-string @var{string} and 
returns the
 corresponding time value.
 @end defun
 
address@hidden format-time-string format-string &optional time-value universal
address@hidden format-time-string format-string &optional time zone
 
-This function converts @var{time-value} (or the current time, if
address@hidden is omitted) to a string according to
address@hidden  The argument
+This function converts @var{time} (or the current time, if
address@hidden is omitted) to a string according to
address@hidden  The conversion uses the time zone rule @var{zone}
+(or the current time zone rule, if omitted).  The argument
 @var{format-string} may contain @samp{%}-sequences which say to
 substitute parts of the time.  Here is a table of what the
 @samp{%}-sequences mean:
diff --git a/etc/NEWS b/etc/NEWS
index 666cccf..5bb7a00 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1014,6 +1014,15 @@ key works) by typing ‘A-[’ and ‘A-]’.
 +++
 ** Time-related changes:
 
+*** Time conversion functions now accept an optional ZONE argument
+that specifies the time zone rules for conversion.  ZONE is omitted or
+nil for Emacs local time, t for Universal Time, ‘wall’ for system wall
+clock time, or a string as in ‘set-time-zone-rule’ for a time zone
+rule.  The affected functions are ‘current-time-string’,
+‘current-time-zone’, ‘decode-time’, and ‘format-time-string’.  The
+function ‘encode-time’, which already accepted a simple time zone rule
+argument, has been extended to accept all the new forms.
+
 *** Time-related functions now consistently accept numbers
 (representing seconds since the epoch) and nil (representing the
 current time) as well as the usual list-of-integer representation.
diff --git a/lib/gnulib.mk b/lib/gnulib.mk
index 2dd0ef8..1ca12a2 100644
--- a/lib/gnulib.mk
+++ b/lib/gnulib.mk
@@ -21,7 +21,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# 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 --ma [...]
+# 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=gnu [...]
 
 
 MOSTLYCLEANFILES += core *.stackdump
@@ -655,6 +655,17 @@ EXTRA_libgnu_a_SOURCES += mktime.c
 
 ## end   gnulib module mktime
 
+## begin gnulib module mktime-internal
+
+if gl_GNULIB_ENABLED_5264294aa0a5557541b53c8c741f7f31
+
+endif
+EXTRA_DIST += mktime-internal.h mktime.c
+
+EXTRA_libgnu_a_SOURCES += mktime.c
+
+## end   gnulib module mktime-internal
+
 ## begin gnulib module openat-h
 
 if gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7
@@ -1589,10 +1600,12 @@ time.h: time.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H) $(
              -e 's/@''GNULIB_STRPTIME''@/$(GNULIB_STRPTIME)/g' \
              -e 's/@''GNULIB_TIMEGM''@/$(GNULIB_TIMEGM)/g' \
              -e 's/@''GNULIB_TIME_R''@/$(GNULIB_TIME_R)/g' \
+             -e 's/@''GNULIB_TIME_RZ''@/$(GNULIB_TIME_RZ)/g' \
              -e 's|@''HAVE_DECL_LOCALTIME_R''@|$(HAVE_DECL_LOCALTIME_R)|g' \
              -e 's|@''HAVE_NANOSLEEP''@|$(HAVE_NANOSLEEP)|g' \
              -e 's|@''HAVE_STRPTIME''@|$(HAVE_STRPTIME)|g' \
              -e 's|@''HAVE_TIMEGM''@|$(HAVE_TIMEGM)|g' \
+             -e 's|@''HAVE_TIMEZONE_T''@|$(HAVE_TIMEZONE_T)|g' \
              -e 's|@''REPLACE_GMTIME''@|$(REPLACE_GMTIME)|g' \
              -e 's|@''REPLACE_LOCALTIME''@|$(REPLACE_LOCALTIME)|g' \
              -e 's|@''REPLACE_LOCALTIME_R''@|$(REPLACE_LOCALTIME_R)|g' \
@@ -1624,6 +1637,24 @@ EXTRA_libgnu_a_SOURCES += time_r.c
 
 ## end   gnulib module time_r
 
+## begin gnulib module time_rz
+
+
+EXTRA_DIST += time_rz.c
+
+EXTRA_libgnu_a_SOURCES += time_rz.c
+
+## end   gnulib module time_rz
+
+## begin gnulib module timegm
+
+
+EXTRA_DIST += mktime-internal.h timegm.c
+
+EXTRA_libgnu_a_SOURCES += timegm.c
+
+## end   gnulib module timegm
+
 ## begin gnulib module timespec
 
 libgnu_a_SOURCES += timespec.c
@@ -1806,15 +1837,6 @@ EXTRA_DIST += unistd.in.h
 
 ## end   gnulib module unistd
 
-## begin gnulib module unsetenv
-
-
-EXTRA_DIST += unsetenv.c
-
-EXTRA_libgnu_a_SOURCES += unsetenv.c
-
-## end   gnulib module unsetenv
-
 ## begin gnulib module update-copyright
 
 
diff --git a/lib/strftime.c b/lib/strftime.c
index 2426aae..c7cec26 100644
--- a/lib/strftime.c
+++ b/lib/strftime.c
@@ -121,22 +121,11 @@ extern char *tzname[];
 
 
 #ifdef _LIBC
+# define mktime_z(tz, tm) mktime (tm)
 # define tzname __tzname
 # define tzset __tzset
 #endif
 
-#if !HAVE_TM_GMTOFF
-/* Portable standalone applications should supply a "time.h" that
-   declares a POSIX-compliant localtime_r, for the benefit of older
-   implementations that lack localtime_r or have a nonstandard one.
-   See the gnulib time_r module for one way to implement this.  */
-# undef __gmtime_r
-# undef __localtime_r
-# define __gmtime_r gmtime_r
-# define __localtime_r localtime_r
-#endif
-
-
 #ifndef FPRINTFTIME
 # define FPRINTFTIME 0
 #endif
@@ -385,12 +374,7 @@ iso_week_days (int yday, int wday)
 
 /* When compiling this file, GNU applications can #define my_strftime
    to a symbol (typically nstrftime) to get an extended strftime with
-   extra arguments UT and NS.  Emacs is a special case for now, but
-   this Emacs-specific code can be removed once Emacs's config.h
-   defines my_strftime.  */
-#if defined emacs && !defined my_strftime
-# define my_strftime nstrftime
-#endif
+   extra arguments TZ and NS.  */
 
 #if FPRINTFTIME
 # undef my_strftime
@@ -398,8 +382,9 @@ iso_week_days (int yday, int wday)
 #endif
 
 #ifdef my_strftime
-# define extra_args , ut, ns
-# define extra_args_spec , int ut, int ns
+# undef HAVE_TZSET
+# define extra_args , tz, ns
+# define extra_args_spec , timezone_t tz, int ns
 #else
 # if defined COMPILE_WIDE
 #  define my_strftime wcsftime
@@ -411,7 +396,7 @@ iso_week_days (int yday, int wday)
 # define extra_args
 # define extra_args_spec
 /* We don't have this information in general.  */
-# define ut 0
+# define tz 1
 # define ns 0
 #endif
 
@@ -483,7 +468,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
   zone = (const char *) tp->tm_zone;
 #endif
 #if HAVE_TZNAME
-  if (ut)
+  if (!tz)
     {
       if (! (zone && *zone))
         zone = "GMT";
@@ -496,7 +481,12 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
       tzset ();
 # endif
     }
+  /* The tzset() call might have changed the value.  */
+  if (!(zone && *zone) && tp->tm_isdst >= 0)
+    zone = tzname[tp->tm_isdst != 0];
 #endif
+  if (! zone)
+    zone = "";
 
   if (hour12 > 12)
     hour12 -= 12;
@@ -1144,7 +1134,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
             time_t t;
 
             ltm = *tp;
-            t = mktime (&ltm);
+            t = mktime_z (tz, &ltm);
 
             /* Generate string value for T using time_t arithmetic;
                this works even if sizeof (long) < sizeof (time_t).  */
@@ -1319,14 +1309,6 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
               to_lowcase = true;
             }
 
-#if HAVE_TZNAME
-          /* The tzset() call might have changed the value.  */
-          if (!(zone && *zone) && tp->tm_isdst >= 0)
-            zone = tzname[tp->tm_isdst != 0];
-#endif
-          if (! zone)
-            zone = "";
-
 #ifdef COMPILE_WIDE
           {
             /* The zone string is always given in multibyte form.  We have
@@ -1366,7 +1348,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
 #if HAVE_TM_GMTOFF
             diff = tp->tm_gmtoff;
 #else
-            if (ut)
+            if (!tz)
               diff = 0;
             else
               {
@@ -1375,7 +1357,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
                 time_t lt;
 
                 ltm = *tp;
-                lt = mktime (&ltm);
+                lt = mktime_z (tz, &ltm);
 
                 if (lt == (time_t) -1)
                   {
@@ -1384,7 +1366,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
                        occurred.  */
                     struct tm tm;
 
-                    if (! __localtime_r (&lt, &tm)
+                    if (! localtime_rz (tz, &lt, &tm)
                         || ((ltm.tm_sec ^ tm.tm_sec)
                             | (ltm.tm_min ^ tm.tm_min)
                             | (ltm.tm_hour ^ tm.tm_hour)
@@ -1394,7 +1376,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
                       break;
                   }
 
-                if (! __gmtime_r (&lt, &gtm))
+                if (! localtime_rz (0, &lt, &gtm))
                   break;
 
                 diff = tm_diff (&ltm, &gtm);
@@ -1473,15 +1455,3 @@ my_strftime (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t 
maxsize)
 #if defined _LIBC && ! FPRINTFTIME
 libc_hidden_def (my_strftime)
 #endif
-
-
-#if defined emacs && ! FPRINTFTIME
-/* For Emacs we have a separate interface which corresponds to the normal
-   strftime function plus the ut argument, but without the ns argument.  */
-size_t
-emacs_strftimeu (char *s, size_t maxsize, const char *format,
-                 const struct tm *tp, int ut)
-{
-  return my_strftime (s, maxsize, format, tp, ut, 0);
-}
-#endif
diff --git a/lib/strftime.h b/lib/strftime.h
index 3967afc..2ce6cc5 100644
--- a/lib/strftime.h
+++ b/lib/strftime.h
@@ -23,11 +23,10 @@ extern "C" {
 
 /* Just like strftime, but with two more arguments:
    POSIX requires that strftime use the local timezone information.
-   When __UTC is nonzero and tm->tm_zone is NULL or the empty string,
-   use UTC instead.  Use __NS as the number of nanoseconds in the
-   %N directive.  */
+   Use the timezone __TZ instead.  Use __NS as the number of
+   nanoseconds in the %N directive.  */
 size_t nstrftime (char *, size_t, char const *, struct tm const *,
-                  int __utc, int __ns);
+                  timezone_t __tz, int __ns);
 
 #ifdef __cplusplus
 }
diff --git a/lib/time.in.h b/lib/time.in.h
index 1a6b746..a983f49 100644
--- a/lib/time.in.h
+++ b/lib/time.in.h
@@ -231,6 +231,25 @@ _GL_CXXALIAS_SYS (strptime, char *, (char const *restrict 
__buf,
 _GL_CXXALIASWARN (strptime);
 # endif
 
+# if defined _GNU_SOURCE && @GNULIB_TIME_RZ@ && ! @HAVE_TIMEZONE_T@
+typedef struct tm_zone *timezone_t;
+_GL_FUNCDECL_SYS (tzalloc, timezone_t, (char const *__name));
+_GL_CXXALIAS_SYS (tzalloc, timezone_t, (char const *__name));
+_GL_FUNCDECL_SYS (tzfree, void, (timezone_t __tz));
+_GL_CXXALIAS_SYS (tzfree, void, (timezone_t __tz));
+_GL_FUNCDECL_SYS (localtime_rz, struct tm *,
+                  (timezone_t __tz, time_t const *restrict __timer,
+                   struct tm *restrict __result) _GL_ARG_NONNULL ((2, 3)));
+_GL_CXXALIAS_SYS (localtime_rz, struct tm *,
+                  (timezone_t __tz, time_t const *restrict __timer,
+                   struct tm *restrict __result));
+_GL_FUNCDECL_SYS (mktime_z, time_t,
+                  (timezone_t __tz, struct tm *restrict __result)
+                  _GL_ARG_NONNULL ((2)));
+_GL_CXXALIAS_SYS (mktime_z, time_t,
+                  (timezone_t __tz, struct tm *restrict __result));
+# endif
+
 /* Convert TM to a time_t value, assuming UTC.  */
 # if @GNULIB_TIMEGM@
 #  if @REPLACE_TIMEGM@
diff --git a/lib/time_rz.c b/lib/time_rz.c
new file mode 100644
index 0000000..8a4d7d1
--- /dev/null
+++ b/lib/time_rz.c
@@ -0,0 +1,374 @@
+/* Time zone functions such as tzalloc and localtime_rz
+
+   Copyright 2015 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Paul Eggert.  */
+
+/* Although this module is not thread-safe, any races should be fairly
+   rare and reasonably benign.  For complete thread-safety, use a C
+   library with a working timezone_t type, so that this module is not
+   needed.  */
+
+#include <config.h>
+
+#include <time.h>
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if !HAVE_TZSET
+static void tzset (void) { }
+#endif
+
+/* A time zone rule.  */
+struct tm_zone
+{
+  /* More abbreviations, should they be needed.  Their TZ_IS_SET
+     members are zero.  */
+  timezone_t next;
+
+  /* If nonzero, the rule represents the TZ environment variable set
+     to the first "abbreviation" (this may be the empty string).
+     Otherwise, it represents an unset TZ.  */
+  char tz_is_set;
+
+  /* A sequence of null-terminated strings packed next to each other.
+     The strings are followed by an extra null byte.  If TZ_IS_SET,
+     there must be at least one string and the first string (which is
+     actually a TZ environment value value) may be empty.  Otherwise
+     all strings must be nonempty.
+
+     Abbreviations are stored here because otherwise the values of
+     tm_zone and/or tzname would be dead after changing TZ and calling
+     tzset.  Abbreviations never move once allocated, and are live
+     until tzfree is called.  */
+  char abbrs[FLEXIBLE_ARRAY_MEMBER];
+};
+
+/* The approximate size to use for small allocation requests.  This is
+   the largest "small" request for the GNU C library malloc.  */
+enum { DEFAULT_MXFAST = 64 * sizeof (size_t) / 4 };
+
+/* Minimum size of the ABBRS member of struct abbr.  ABBRS is larger
+   only in the unlikely case where an abbreviation longer than this is
+   used.  */
+enum { ABBR_SIZE_MIN = DEFAULT_MXFAST - offsetof (struct tm_zone, abbrs) };
+
+static char const TZ[] = "TZ";
+
+/* Magic cookie timezone_t value, for local time.  It differs from
+   NULL and from all other timezone_t values.  Only the address
+   matters; the pointer is never dereferenced.  */
+static timezone_t const local_tz = (timezone_t) 1;
+
+#if HAVE_TM_ZONE || HAVE_TZNAME
+
+/* Return true if the values A and B differ according to the rules for
+   tm_isdst: A and B differ if one is zero and the other positive.  */
+static bool
+isdst_differ (int a, int b)
+{
+  return !a != !b && 0 <= a && 0 <= b;
+}
+
+/* Return true if A and B are equal.  */
+static int
+equal_tm (const struct tm *a, const struct tm *b)
+{
+  return ! ((a->tm_sec ^ b->tm_sec)
+            | (a->tm_min ^ b->tm_min)
+            | (a->tm_hour ^ b->tm_hour)
+            | (a->tm_mday ^ b->tm_mday)
+            | (a->tm_mon ^ b->tm_mon)
+            | (a->tm_year ^ b->tm_year)
+            | isdst_differ (a->tm_isdst, b->tm_isdst));
+}
+
+#endif
+
+/* Copy to ABBRS the abbreviation at ABBR with size ABBR_SIZE (this
+   includes its trailing null byte).  Append an extra null byte to
+   mark the end of ABBRS.  */
+static void
+extend_abbrs (char *abbrs, char const *abbr, size_t abbr_size)
+{
+  memcpy (abbrs, abbr, abbr_size);
+  abbrs[abbr_size] = '\0';
+}
+
+/* Return a newly allocated time zone for NAME, or NULL on failure.
+   As a special case, return a nonzero constant for wall clock time, a
+   constant that survives freeing.  */
+timezone_t
+tzalloc (char const *name)
+{
+  size_t name_size = name ? strlen (name) + 1 : 0;
+  size_t abbr_size = name_size < ABBR_SIZE_MIN ? ABBR_SIZE_MIN : name_size + 1;
+  timezone_t tz = malloc (offsetof (struct tm_zone, abbrs) + abbr_size);
+  if (tz)
+    {
+      tz->next = NULL;
+      tz->tz_is_set = !!name;
+      extend_abbrs (tz->abbrs, name, name_size);
+    }
+  return tz;
+}
+
+#if HAVE_TZNAME
+/* If TZNAME_ADDRESS is nonnull, an assignment of a saved abbreviation.
+   TZNAME_ADDRESS should be either null, or &tzname[0], or &tzname[1].
+   *TZNAME_ADDRESS = TZNAME_VALUE should be done after revert_tz
+   (indirectly) calls tzset, so that revert_tz can overwrite tzset's
+   assignment to tzname.  Also, it should be done at the start of
+   the next localtime_tz or mktime_z, to undo the overwrite.  */
+static char **tzname_address;
+static char *tzname_value;
+#endif
+
+/* Save into TZ any nontrivial time zone abbreviation used by TM,
+   and update *TM (or prepare to update tzname) if they use the abbreviation.
+   Return true if successful, false (setting errno) otherwise.  */
+static bool
+save_abbr (timezone_t tz, struct tm *tm)
+{
+#if HAVE_TM_ZONE || HAVE_TZNAME
+  char const *zone = NULL;
+  char **tzname_zone = NULL;
+  char *zone_copy = (char *) "";
+# if HAVE_TM_ZONE
+  zone = tm->tm_zone;
+# endif
+# if HAVE_TZNAME
+  if (! (zone && *zone) && 0 <= tm->tm_isdst)
+    zone = *(tzname_zone = &tzname[0 < tm->tm_isdst]);
+# endif
+
+  /* No need to replace null zones, or zones within the struct tm.  */
+  if (!zone || ((char *) tm <= zone && zone < (char *) (tm + 1)))
+    return true;
+
+  if (*zone)
+    {
+      zone_copy = tz->abbrs;
+
+      while (strcmp (zone_copy, zone) != 0)
+        {
+          if (! (*zone_copy || (zone_copy == tz->abbrs && tz->tz_is_set)))
+            {
+              size_t zone_size = strlen (zone) + 1;
+              if (zone_size < tz->abbrs + ABBR_SIZE_MIN - zone_copy)
+                extend_abbrs (zone_copy, zone, zone_size);
+              else
+                {
+                  tz = tz->next = tzalloc (zone);
+                  if (!tz)
+                    return false;
+                  tz->tz_is_set = 0;
+                  zone_copy = tz->abbrs;
+                }
+              break;
+            }
+
+          zone_copy += strlen (zone_copy) + 1;
+          if (!*zone_copy && tz->next)
+            {
+              tz = tz->next;
+              zone_copy = tz->abbrs;
+            }
+        }
+    }
+
+  /* Replace the zone name so that its lifetime matches that of TZ.  */
+# if HAVE_TM_ZONE
+  if (!tzname_zone)
+    tm->tm_zone = zone_copy;
+# endif
+# if HAVE_TZNAME
+  tzname_address = tzname_zone;
+  tzname_value = zone_copy;
+# endif
+#endif
+  return true;
+}
+
+/* Free a time zone.  */
+void
+tzfree (timezone_t tz)
+{
+  if (tz != local_tz)
+    while (tz)
+      {
+        timezone_t next = tz->next;
+        free (tz);
+        tz = next;
+      }
+}
+
+/* Get and set the TZ environment variable.  These functions can be
+   overridden by programs like Emacs that manage their own environment.  */
+
+#ifndef getenv_TZ
+static char *
+getenv_TZ (void)
+{
+  return getenv (TZ);
+}
+#endif
+
+#ifndef setenv_TZ
+static int
+setenv_TZ (char const *tz)
+{
+  return tz ? setenv (TZ, tz, 1) : unsetenv (TZ);
+}
+#endif
+
+/* Change the environment to match the specified timezone_t value.
+   Return true if successful, false (setting errno) otherwise.  */
+static bool
+change_env (timezone_t tz)
+{
+  if (setenv_TZ (tz->tz_is_set ? tz->abbrs : NULL) != 0)
+    return false;
+  tzset ();
+  return true;
+}
+
+/* Temporarily set the time zone to TZ, which must not be null.
+   Return LOCAL_TZ if the time zone setting is already correct.
+   Otherwise return a newly allocated time zone representing the old
+   setting, or NULL (setting errno) on failure.  */
+static timezone_t
+set_tz (timezone_t tz)
+{
+  char *env_tz = getenv_TZ ();
+  if (env_tz
+      ? tz->tz_is_set && strcmp (tz->abbrs, env_tz) == 0
+      : !tz->tz_is_set)
+    return local_tz;
+  else
+    {
+      timezone_t old_tz = tzalloc (env_tz);
+      if (!old_tz)
+        return old_tz;
+      if (! change_env (tz))
+        {
+          int saved_errno = errno;
+          tzfree (old_tz);
+          errno = saved_errno;
+          return NULL;
+        }
+      return old_tz;
+    }
+}
+
+/* Restore an old setting returned by set_tz.  It must not be null.
+   Return true (preserving errno) if successful, false (setting errno)
+   otherwise.  */
+static bool
+revert_tz (timezone_t tz)
+{
+  if (tz == local_tz)
+    return true;
+  else
+    {
+      int saved_errno = errno;
+      bool ok = change_env (tz);
+      if (!ok)
+        saved_errno = errno;
+#if HAVE_TZNAME
+      if (!ok)
+        tzname_address = NULL;
+      if (tzname_address)
+        {
+          char *old_value = *tzname_address;
+          *tzname_address = tzname_value;
+          tzname_value = old_value;
+        }
+#endif
+      tzfree (tz);
+      errno = saved_errno;
+      return ok;
+    }
+}
+
+/* Restore an old tzname setting that was temporarily munged by revert_tz.  */
+static void
+restore_tzname (void)
+{
+#if HAVE_TZNAME
+  if (tzname_address)
+    {
+      *tzname_address = tzname_value;
+      tzname_address = NULL;
+    }
+#endif
+}
+
+/* Use time zone TZ to compute localtime_r (T, TM).  */
+struct tm *
+localtime_rz (timezone_t tz, time_t const *t, struct tm *tm)
+{
+  restore_tzname ();
+
+  if (!tz)
+    return gmtime_r (t, tm);
+  else
+    {
+      timezone_t old_tz = set_tz (tz);
+      if (old_tz)
+        {
+          tm = localtime_r (t, tm);
+          if (tm && !save_abbr (tz, tm))
+            tm = NULL;
+          if (revert_tz (old_tz))
+            return tm;
+        }
+      return NULL;
+    }
+}
+
+/* Use time zone TZ to compute mktime (TM).  */
+time_t
+mktime_z (timezone_t tz, struct tm *tm)
+{
+  restore_tzname ();
+
+  if (!tz)
+    return timegm (tm);
+  else
+    {
+      timezone_t old_tz = set_tz (tz);
+      if (old_tz)
+        {
+          time_t t = mktime (tm);
+#if HAVE_TM_ZONE || HAVE_TZNAME
+          time_t badtime = -1;
+          struct tm tm_1;
+          if ((t != badtime
+               || (localtime_r (&t, &tm_1) && equal_tm (tm, &tm_1)))
+              && !save_abbr (tz, tm))
+            t = badtime;
+#endif
+          if (revert_tz (old_tz))
+            return t;
+        }
+      return -1;
+    }
+}
diff --git a/lib/timegm.c b/lib/timegm.c
new file mode 100644
index 0000000..11c485f
--- /dev/null
+++ b/lib/timegm.c
@@ -0,0 +1,38 @@
+/* Convert UTC calendar time to simple time.  Like mktime but assumes UTC.
+
+   Copyright (C) 1994, 1997, 2003-2004, 2006-2007, 2009-2015 Free Software
+   Foundation, Inc.  This file is part of the GNU C Library.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBC
+# include <config.h>
+#endif
+
+#include <time.h>
+
+#ifndef _LIBC
+# undef __gmtime_r
+# define __gmtime_r gmtime_r
+# define __mktime_internal mktime_internal
+# include "mktime-internal.h"
+#endif
+
+time_t
+timegm (struct tm *tmp)
+{
+  static time_t gmtime_offset;
+  tmp->tm_isdst = 0;
+  return __mktime_internal (tmp, __gmtime_r, &gmtime_offset);
+}
diff --git a/lisp/time-stamp.el b/lisp/time-stamp.el
index 24e5ef4..1e0fe99 100644
--- a/lisp/time-stamp.el
+++ b/lisp/time-stamp.el
@@ -420,16 +420,8 @@ format the string."
   (or ts-format
       (setq ts-format time-stamp-format))
   (if (stringp ts-format)
-      (if (stringp time-stamp-time-zone)
-         (let ((ts-real-time-zone (getenv "TZ")))
-           (unwind-protect
-               (progn
-                 (setenv "TZ" time-stamp-time-zone)
-                 (format-time-string
-                  (time-stamp-string-preprocess ts-format)))
-             (setenv "TZ" ts-real-time-zone)))
-       (format-time-string
-        (time-stamp-string-preprocess ts-format)))
+      (format-time-string (time-stamp-string-preprocess ts-format)
+                          nil time-stamp-time-zone)
     ;; handle version 1 compatibility
     (cond ((or (eq time-stamp-old-format-warn 'error)
               (and (eq time-stamp-old-format-warn 'ask)
diff --git a/lisp/time.el b/lisp/time.el
index ae0e598..d35f5b9 100644
--- a/lisp/time.el
+++ b/lisp/time.el
@@ -160,15 +160,8 @@ LABEL is a string to display as the label of that 
TIMEZONE's time."
 (defcustom display-time-world-list
   ;; Determine if zoneinfo style timezones are supported by testing that
   ;; America/New York and Europe/London return different timezones.
-  (let ((old-tz (getenv "TZ"))
-       gmt nyt)
-    (unwind-protect
-       (progn
-         (setenv "TZ" "America/New_York")
-         (setq nyt (format-time-string "%z"))
-         (setenv "TZ" "Europe/London")
-         (setq gmt (format-time-string "%z")))
-      (setenv "TZ" old-tz))
+  (let ((nyt (format-time-string "%z" nil "America/New_York"))
+        (gmt (format-time-string "%z" nil "Europe/London")))
     (if (string-equal nyt gmt)
         legacy-style-world-list
       zoneinfo-style-world-list))
@@ -523,21 +516,19 @@ See `display-time-world'."
   "Replace current buffer text with times in various zones, based on ALIST."
   (let ((inhibit-read-only t)
        (buffer-undo-list t)
-       (old-tz (getenv "TZ"))
+       (now (current-time))
        (max-width 0)
        result fmt)
     (erase-buffer)
-    (unwind-protect
-       (dolist (zone alist)
-         (let* ((label (cadr zone))
-                (width (string-width label)))
-           (setenv "TZ" (car zone))
-           (push (cons label
-                       (format-time-string display-time-world-time-format))
-                 result)
-           (when (> width max-width)
-             (setq max-width width))))
-      (setenv "TZ" old-tz))
+    (dolist (zone alist)
+      (let* ((label (cadr zone))
+            (width (string-width label)))
+       (push (cons label
+                   (format-time-string display-time-world-time-format
+                                       now (car zone)))
+             result)
+       (when (> width max-width)
+         (setq max-width width))))
     (setq fmt (concat "%-" (int-to-string max-width) "s %s\n"))
     (dolist (timedata (nreverse result))
       (insert (format fmt (car timedata) (cdr timedata))))
diff --git a/lisp/vc/add-log.el b/lisp/vc/add-log.el
index eb7e5bf..c90413c 100644
--- a/lisp/vc/add-log.el
+++ b/lisp/vc/add-log.el
@@ -581,8 +581,8 @@ If t, use universal time.")
 (put 'add-log-time-zone-rule 'safe-local-variable
      (lambda (x) (or (booleanp x) (stringp x))))
 
-(defun add-log-iso8601-time-zone (&optional time)
-  (let* ((utc-offset (or (car (current-time-zone time)) 0))
+(defun add-log-iso8601-time-zone (&optional time zone)
+  (let* ((utc-offset (or (car (current-time-zone time zone)) 0))
         (sign (if (< utc-offset 0) ?- ?+))
         (sec (abs utc-offset))
         (ss (% sec 60))
@@ -596,12 +596,11 @@ If t, use universal time.")
 
 (defvar add-log-iso8601-with-time-zone nil)
 
-(defun add-log-iso8601-time-string ()
-  (let ((time (format-time-string "%Y-%m-%d"
-                                  nil (eq t add-log-time-zone-rule))))
+(defun add-log-iso8601-time-string (&optional time zone)
+  (let ((date (format-time-string "%Y-%m-%d" time zone)))
     (if add-log-iso8601-with-time-zone
-        (concat time " " (add-log-iso8601-time-zone))
-      time)))
+        (concat date " " (add-log-iso8601-time-zone time zone))
+      date)))
 
 (defun change-log-name ()
   "Return (system-dependent) default name for a change log file."
@@ -848,14 +847,8 @@ non-nil, otherwise in local time."
       (let ((new-entries
              (mapcar (lambda (addr)
                        (concat
-                        (if (stringp add-log-time-zone-rule)
-                            (let ((tz (getenv "TZ")))
-                              (unwind-protect
-                                  (progn
-                                    (setenv "TZ" add-log-time-zone-rule)
-                                    (funcall add-log-time-format))
-                                (setenv "TZ" tz)))
-                          (funcall add-log-time-format))
+                        (funcall add-log-time-format
+                                 nil add-log-time-zone-rule)
                         "  " full-name
                         "  <" addr ">"))
                      (if (consp mailing-address)
diff --git a/lisp/vc/log-edit.el b/lisp/vc/log-edit.el
index d595497..acbd9c0 100644
--- a/lisp/vc/log-edit.el
+++ b/lisp/vc/log-edit.el
@@ -872,7 +872,8 @@ Return non-nil if it is."
                  (and (boundp 'user-mail-address) user-mail-address)))
        (time (or (and (boundp 'add-log-time-format)
                       (functionp add-log-time-format)
-                      (funcall add-log-time-format))
+                      (funcall add-log-time-format
+                               nil add-log-time-zone-rule))
                  (format-time-string "%Y-%m-%d"))))
     (if (null log-edit-changelog-use-first)
         (looking-at (regexp-quote (format "%s  %s  <%s>" time name mail)))
diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4
index 0425d02..cf71d7e 100644
--- a/m4/gnulib-comp.m4
+++ b/m4/gnulib-comp.m4
@@ -98,6 +98,7 @@ AC_DEFUN([gl_EARLY],
   # Code from module memrchr:
   # Code from module mkostemp:
   # Code from module mktime:
+  # Code from module mktime-internal:
   # Code from module multiarch:
   # Code from module nocrash:
   # Code from module openat-h:
@@ -141,13 +142,14 @@ AC_DEFUN([gl_EARLY],
   # Code from module tempname:
   # Code from module time:
   # Code from module time_r:
+  # Code from module time_rz:
+  # Code from module timegm:
   # Code from module timer-time:
   # Code from module timespec:
   # Code from module timespec-add:
   # Code from module timespec-sub:
   # Code from module u64:
   # Code from module unistd:
-  # Code from module unsetenv:
   # Code from module update-copyright:
   # Code from module utimens:
   # Code from module vararrays:
@@ -385,15 +387,20 @@ AC_DEFUN([gl_INIT],
     gl_PREREQ_TIME_R
   fi
   gl_TIME_MODULE_INDICATOR([time_r])
+  gl_TIME_RZ
+  if test "$HAVE_TIMEZONE_T" = 0; then
+    AC_LIBOBJ([time_rz])
+  fi
+  gl_TIME_MODULE_INDICATOR([time_rz])
+  gl_FUNC_TIMEGM
+  if test $HAVE_TIMEGM = 0 || test $REPLACE_TIMEGM = 1; then
+    AC_LIBOBJ([timegm])
+    gl_PREREQ_TIMEGM
+  fi
+  gl_TIME_MODULE_INDICATOR([timegm])
   gl_TIMER_TIME
   gl_TIMESPEC
   gl_UNISTD_H
-  gl_FUNC_UNSETENV
-  if test $HAVE_UNSETENV = 0 || test $REPLACE_UNSETENV = 1; then
-    AC_LIBOBJ([unsetenv])
-    gl_PREREQ_UNSETENV
-  fi
-  gl_STDLIB_MODULE_INDICATOR([unsetenv])
   gl_UTIMENS
   AC_C_VARARRAYS
   gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=false
@@ -404,6 +411,7 @@ AC_DEFUN([gl_INIT],
   gl_gnulib_enabled_getgroups=false
   gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
   gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false
+  gl_gnulib_enabled_5264294aa0a5557541b53c8c741f7f31=false
   gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=false
   gl_gnulib_enabled_pathmax=false
   gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false
@@ -506,6 +514,17 @@ AC_DEFUN([gl_INIT],
       fi
     fi
   }
+  func_gl_gnulib_m4code_5264294aa0a5557541b53c8c741f7f31 ()
+  {
+    if ! $gl_gnulib_enabled_5264294aa0a5557541b53c8c741f7f31; then
+      gl_FUNC_MKTIME_INTERNAL
+      if test $REPLACE_MKTIME = 1; then
+        AC_LIBOBJ([mktime])
+        gl_PREREQ_MKTIME
+      fi
+      gl_gnulib_enabled_5264294aa0a5557541b53c8c741f7f31=true
+    fi
+  }
   func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 ()
   {
     if ! $gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7; then
@@ -653,6 +672,9 @@ AC_DEFUN([gl_INIT],
   if { test $HAVE_DECL_STRTOUMAX = 0 || test $REPLACE_STRTOUMAX = 1; } && test 
$ac_cv_type_unsigned_long_long_int = yes; then
     func_gl_gnulib_m4code_strtoull
   fi
+  if test $HAVE_TIMEGM = 0 || test $REPLACE_TIMEGM = 1; then
+    func_gl_gnulib_m4code_5264294aa0a5557541b53c8c741f7f31
+  fi
   m4_pattern_allow([^gl_GNULIB_ENABLED_])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b], 
[$gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_dirfd], [$gl_gnulib_enabled_dirfd])
@@ -662,6 +684,7 @@ AC_DEFUN([gl_INIT],
   AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], 
[$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], 
[$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_5264294aa0a5557541b53c8c741f7f31], 
[$gl_gnulib_enabled_5264294aa0a5557541b53c8c741f7f31])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7], 
[$gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_pathmax], [$gl_gnulib_enabled_pathmax])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], 
[$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c])
@@ -938,6 +961,8 @@ AC_DEFUN([gl_FILE_LIST], [
   lib/tempname.h
   lib/time.in.h
   lib/time_r.c
+  lib/time_rz.c
+  lib/timegm.c
   lib/timespec-add.c
   lib/timespec-sub.c
   lib/timespec.c
@@ -946,7 +971,6 @@ AC_DEFUN([gl_FILE_LIST], [
   lib/u64.h
   lib/unistd.c
   lib/unistd.in.h
-  lib/unsetenv.c
   lib/utimens.c
   lib/utimens.h
   lib/verify.h
@@ -1011,7 +1035,6 @@ AC_DEFUN([gl_FILE_LIST], [
   m4/readlink.m4
   m4/readlinkat.m4
   m4/secure_getenv.m4
-  m4/setenv.m4
   m4/sha1.m4
   m4/sha256.m4
   m4/sha512.m4
@@ -1043,6 +1066,8 @@ AC_DEFUN([gl_FILE_LIST], [
   m4/tempname.m4
   m4/time_h.m4
   m4/time_r.m4
+  m4/time_rz.m4
+  m4/timegm.m4
   m4/timer_time.m4
   m4/timespec.m4
   m4/tm_gmtoff.m4
diff --git a/m4/sys_time_h.m4 b/m4/sys_time_h.m4
index 50133b9..28c8b1a 100644
--- a/m4/sys_time_h.m4
+++ b/m4/sys_time_h.m4
@@ -105,6 +105,7 @@ AC_DEFUN([gl_HEADER_SYS_TIME_H_DEFAULTS],
   HAVE_GETTIMEOFDAY=1;       AC_SUBST([HAVE_GETTIMEOFDAY])
   HAVE_STRUCT_TIMEVAL=1;     AC_SUBST([HAVE_STRUCT_TIMEVAL])
   HAVE_SYS_TIME_H=1;         AC_SUBST([HAVE_SYS_TIME_H])
+  HAVE_TIMEZONE_T=0;         AC_SUBST([HAVE_TIMEZONE_T])
   REPLACE_GETTIMEOFDAY=0;    AC_SUBST([REPLACE_GETTIMEOFDAY])
   REPLACE_STRUCT_TIMEVAL=0;  AC_SUBST([REPLACE_STRUCT_TIMEVAL])
 ])
diff --git a/m4/time_h.m4 b/m4/time_h.m4
index d9c41a4..754b469 100644
--- a/m4/time_h.m4
+++ b/m4/time_h.m4
@@ -109,6 +109,7 @@ AC_DEFUN([gl_HEADER_TIME_H_DEFAULTS],
   GNULIB_STRPTIME=0;                     AC_SUBST([GNULIB_STRPTIME])
   GNULIB_TIMEGM=0;                       AC_SUBST([GNULIB_TIMEGM])
   GNULIB_TIME_R=0;                       AC_SUBST([GNULIB_TIME_R])
+  GNULIB_TIME_RZ=0;                      AC_SUBST([GNULIB_TIME_RZ])
   dnl Assume proper GNU behavior unless another module says otherwise.
   HAVE_DECL_LOCALTIME_R=1;               AC_SUBST([HAVE_DECL_LOCALTIME_R])
   HAVE_NANOSLEEP=1;                      AC_SUBST([HAVE_NANOSLEEP])
diff --git a/m4/time_rz.m4 b/m4/time_rz.m4
new file mode 100644
index 0000000..0c1f2c3
--- /dev/null
+++ b/m4/time_rz.m4
@@ -0,0 +1,21 @@
+dnl Time zone functions: tzalloc, localtime_rz, etc.
+
+dnl Copyright (C) 2015 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Written by Paul Eggert.
+
+AC_DEFUN([gl_TIME_RZ],
+[
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+  AC_REQUIRE([gl_HEADER_SYS_TIME_H_DEFAULTS])
+  AC_REQUIRE([AC_STRUCT_TIMEZONE])
+  AC_CHECK_FUNCS_ONCE([tzset])
+
+  AC_CHECK_TYPES([timezone_t], [], [], [[#include <time.h>]])
+  if test "$ac_cv_type_timezone_t" = yes; then
+    HAVE_TIMEZONE_T=1
+  fi
+])
diff --git a/m4/timegm.m4 b/m4/timegm.m4
new file mode 100644
index 0000000..8e68b99
--- /dev/null
+++ b/m4/timegm.m4
@@ -0,0 +1,26 @@
+# timegm.m4 serial 11
+dnl Copyright (C) 2003, 2007, 2009-2015 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_TIMEGM],
+[
+  AC_REQUIRE([gl_HEADER_TIME_H_DEFAULTS])
+  AC_REQUIRE([gl_FUNC_MKTIME])
+  REPLACE_TIMEGM=0
+  AC_CHECK_FUNCS_ONCE([timegm])
+  if test $ac_cv_func_timegm = yes; then
+    if test $gl_cv_func_working_mktime = no; then
+      # Assume that timegm is buggy if mktime is.
+      REPLACE_TIMEGM=1
+    fi
+  else
+    HAVE_TIMEGM=0
+  fi
+])
+
+# Prerequisites of lib/timegm.c.
+AC_DEFUN([gl_PREREQ_TIMEGM], [
+  :
+])
diff --git a/nt/gnulib.mk b/nt/gnulib.mk
index 0c2b786..8a57d64 100644
--- a/nt/gnulib.mk
+++ b/nt/gnulib.mk
@@ -43,7 +43,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# 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 --ma [...]
+# 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=gnu [...]
 
 
 MOSTLYCLEANFILES += core *.stackdump
@@ -487,6 +487,17 @@ EXTRA_libgnu_a_SOURCES += mktime.c
 
 ## end   gnulib module mktime
 
+## begin gnulib module mktime-internal
+
+if gl_GNULIB_ENABLED_5264294aa0a5557541b53c8c741f7f31
+
+endif
+EXTRA_DIST += mktime-internal.h mktime.c
+
+EXTRA_libgnu_a_SOURCES += mktime.c
+
+## end   gnulib module mktime-internal
+
 ## begin gnulib module openat-h
 
 if gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7
@@ -944,6 +955,54 @@ EXTRA_libgnu_a_SOURCES += symlink.c
 
 ## end   gnulib module symlink
 
+## begin gnulib module time
+
+BUILT_SOURCES += time.h
+
+# We need the following in order to create <time.h> when the system
+# doesn't have one that works with the given compiler.
+time.h: time.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) 
$(WARN_ON_USE_H)
+       $(AM_V_GEN)rm -f address@hidden $@ && \
+       { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' && \
+         sed -e 's|@''GUARD_PREFIX''@|GL|g' \
+             -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+             -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+             -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
+             -e 's|@''NEXT_TIME_H''@|$(NEXT_TIME_H)|g' \
+             -e 's/@''GNULIB_GETTIMEOFDAY''@/$(GNULIB_GETTIMEOFDAY)/g' \
+             -e 's/@''GNULIB_MKTIME''@/$(GNULIB_MKTIME)/g' \
+             -e 's/@''GNULIB_NANOSLEEP''@/$(GNULIB_NANOSLEEP)/g' \
+             -e 's/@''GNULIB_STRPTIME''@/$(GNULIB_STRPTIME)/g' \
+             -e 's/@''GNULIB_TIMEGM''@/$(GNULIB_TIMEGM)/g' \
+             -e 's/@''GNULIB_TIME_R''@/$(GNULIB_TIME_R)/g' \
+             -e 's/@''GNULIB_TIME_RZ''@/$(GNULIB_TIME_RZ)/g' \
+             -e 's|@''HAVE_DECL_LOCALTIME_R''@|$(HAVE_DECL_LOCALTIME_R)|g' \
+             -e 's|@''HAVE_NANOSLEEP''@|$(HAVE_NANOSLEEP)|g' \
+             -e 's|@''HAVE_STRPTIME''@|$(HAVE_STRPTIME)|g' \
+             -e 's|@''HAVE_TIMEGM''@|$(HAVE_TIMEGM)|g' \
+             -e 's|@''HAVE_TIMEZONE_T''@|$(HAVE_TIMEZONE_T)|g' \
+             -e 's|@''REPLACE_GMTIME''@|$(REPLACE_GMTIME)|g' \
+             -e 's|@''REPLACE_LOCALTIME''@|$(REPLACE_LOCALTIME)|g' \
+             -e 's|@''REPLACE_LOCALTIME_R''@|$(REPLACE_LOCALTIME_R)|g' \
+             -e 's|@''REPLACE_MKTIME''@|$(REPLACE_MKTIME)|g' \
+             -e 's|@''REPLACE_NANOSLEEP''@|$(REPLACE_NANOSLEEP)|g' \
+             -e 's|@''REPLACE_TIMEGM''@|$(REPLACE_TIMEGM)|g' \
+             -e 
's|@''PTHREAD_H_DEFINES_STRUCT_TIMESPEC''@|$(PTHREAD_H_DEFINES_STRUCT_TIMESPEC)|g'
 \
+             -e 
's|@''SYS_TIME_H_DEFINES_STRUCT_TIMESPEC''@|$(SYS_TIME_H_DEFINES_STRUCT_TIMESPEC)|g'
 \
+             -e 
's|@''TIME_H_DEFINES_STRUCT_TIMESPEC''@|$(TIME_H_DEFINES_STRUCT_TIMESPEC)|g' \
+             -e 
's|@''UNISTD_H_DEFINES_STRUCT_TIMESPEC''@|$(UNISTD_H_DEFINES_STRUCT_TIMESPEC)|g'
 \
+             -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
+             -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
+             -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
+             < $(srcdir)/time.in.h; \
+       } > address@hidden && \
+       mv address@hidden $@
+MOSTLYCLEANFILES += time.h time.h-t
+
+EXTRA_DIST += time.in.h
+
+## end   gnulib module time
+
 ## begin gnulib module time_r
 
 
@@ -953,6 +1012,24 @@ EXTRA_libgnu_a_SOURCES += time_r.c
 
 ## end   gnulib module time_r
 
+## begin gnulib module time_rz
+
+
+EXTRA_DIST += time_rz.c
+
+EXTRA_libgnu_a_SOURCES += time_rz.c
+
+## end   gnulib module time_rz
+
+## begin gnulib module timegm
+
+
+EXTRA_DIST += mktime-internal.h timegm.c
+
+EXTRA_libgnu_a_SOURCES += timegm.c
+
+## end   gnulib module timegm
+
 ## begin gnulib module timespec
 
 libgnu_a_SOURCES += timespec.c
@@ -981,15 +1058,6 @@ EXTRA_DIST += u64.h
 
 ## end   gnulib module u64
 
-## begin gnulib module unsetenv
-
-
-EXTRA_DIST += unsetenv.c
-
-EXTRA_libgnu_a_SOURCES += unsetenv.c
-
-## end   gnulib module unsetenv
-
 ## begin gnulib module update-copyright
 
 
diff --git a/src/conf_post.h b/src/conf_post.h
index 1a080fa..785e5d7 100644
--- a/src/conf_post.h
+++ b/src/conf_post.h
@@ -206,6 +206,13 @@ extern void _DebPrint (const char *fmt, ...);
 #define RE_TRANSLATE_P(TBL) (!EQ (TBL, make_number (0)))
 #endif
 
+/* Tell time_rz.c to use Emacs's getter and setter for TZ.
+   Only Emacs uses time_rz so this is OK.  */
+#define getenv_TZ emacs_getenv_TZ
+#define setenv_TZ emacs_setenv_TZ
+extern char *emacs_getenv_TZ (void);
+extern int emacs_setenv_TZ (char const *);
+
 #include <string.h>
 #include <stdlib.h>
 
diff --git a/src/editfns.c b/src/editfns.c
index e39eed6..9ff39f9 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -44,8 +44,10 @@ along with GNU Emacs.  If not, see 
<http://www.gnu.org/licenses/>.  */
 #include <sys/resource.h>
 #endif
 
+#include <errno.h>
 #include <float.h>
 #include <limits.h>
+
 #include <intprops.h>
 #include <strftime.h>
 #include <verify.h>
@@ -65,9 +67,8 @@ extern Lisp_Object w32_get_internal_run_time (void);
 #endif
 
 static struct lisp_time lisp_time_struct (Lisp_Object, int *);
-static void set_time_zone_rule (char const *);
 static Lisp_Object format_time_string (char const *, ptrdiff_t, struct 
timespec,
-                                      bool, struct tm *);
+                                      Lisp_Object, struct tm *);
 static long int tm_gmtoff (struct tm *);
 static int tm_diff (struct tm *, struct tm *);
 static void update_buffer_properties (ptrdiff_t, ptrdiff_t);
@@ -76,8 +77,13 @@ static void update_buffer_properties (ptrdiff_t, ptrdiff_t);
 # define HAVE_TM_GMTOFF false
 #endif
 
-/* The startup value of the TZ environment variable; null if unset.  */
-static char const *initial_tz;
+enum { tzeqlen = sizeof "TZ=" - 1 };
+
+/* Time zones equivalent to current local time, to wall clock time,
+   and to UTC, respectively.  */
+static timezone_t local_tz;
+static timezone_t wall_clock_tz;
+static timezone_t const utc_tz = 0;
 
 /* A valid but unlikely setting for the TZ environment variable.
    It is OK (though a bit slower) if the user chooses this value.  */
@@ -94,8 +100,97 @@ init_and_cache_system_name (void)
   cached_system_name = Vsystem_name;
 }
 
+static struct tm *
+emacs_localtime_rz (timezone_t tz, time_t const *t, struct tm *tm)
+{
+  tm = localtime_rz (tz, t, tm);
+  if (!tm && errno == ENOMEM)
+    memory_full (SIZE_MAX);
+  return tm;
+}
+
+static time_t
+emacs_mktime_z (timezone_t tz, struct tm *tm)
+{
+  errno = 0;
+  time_t t = mktime_z (tz, tm);
+  if (t == (time_t) -1 && errno == ENOMEM)
+    memory_full (SIZE_MAX);
+  return t;
+}
+
+/* Allocate a timezone, signaling on failure.  */
+static timezone_t
+xtzalloc (char const *name)
+{
+  timezone_t tz = tzalloc (name);
+  if (!tz)
+    memory_full (SIZE_MAX);
+  return tz;
+}
+
+/* Free a timezone, except do not free the time zone for local time.
+   Freeing utc_tz is also a no-op.  */
+static void
+xtzfree (timezone_t tz)
+{
+  if (tz != local_tz)
+    tzfree (tz);
+}
+
+/* Convert the Lisp time zone rule ZONE to a timezone_t object.
+   The returned value either is 0, or is LOCAL_TZ, or is newly allocated.
+   If SETTZ, set Emacs local time to the time zone rule; otherwise,
+   the caller should eventually pass the returned value to xtzfree.  */
+static timezone_t
+tzlookup (Lisp_Object zone, bool settz)
+{
+  static char const tzbuf_format[] = "XXX%s%"pI"d:%02d:%02d";
+  char tzbuf[sizeof tzbuf_format + INT_STRLEN_BOUND (EMACS_INT)];
+  char const *zone_string;
+  timezone_t new_tz;
+
+  if (NILP (zone))
+    return local_tz;
+  else if (EQ (zone, Qt))
+    {
+      zone_string = "UTC0";
+      new_tz = utc_tz;
+    }
+  else
+    {
+      if (EQ (zone, Qwall))
+       zone_string = 0;
+      else if (STRINGP (zone))
+       zone_string = SSDATA (zone);
+      else if (INTEGERP (zone))
+       {
+         EMACS_INT abszone = eabs (XINT (zone)), hour = abszone / (60 * 60);
+         int min = (abszone / 60) % 60, sec = abszone % 60;
+         sprintf (tzbuf, tzbuf_format, &"-"[XINT (zone) < 0], hour, min, sec);
+         zone_string = tzbuf;
+       }
+      else
+       xsignal2 (Qerror, build_string ("Invalid time zone specification"),
+                 zone);
+      new_tz = xtzalloc (zone_string);
+    }
+
+  if (settz)
+    {
+      block_input ();
+      emacs_setenv_TZ (zone_string);
+      timezone_t old_tz = local_tz;
+      local_tz = new_tz;
+      tzfree (old_tz);
+      unblock_input ();
+    }
+
+  return new_tz;
+}
+
 void
-init_editfns (void)
+init_editfns (bool dumping)
 {
   const char *user_name;
   register char *p;
@@ -108,7 +203,7 @@ init_editfns (void)
 #ifndef CANNOT_DUMP
   /* When just dumping out, set the time zone to a known unlikely value
      and skip the rest of this function.  */
-  if (!initialized)
+  if (dumping)
     {
 # ifdef HAVE_TZSET
       xputenv (dump_tz_string);
@@ -119,7 +214,6 @@ init_editfns (void)
 #endif
 
   char *tz = getenv ("TZ");
-  initial_tz = tz;
 
 #if !defined CANNOT_DUMP && defined HAVE_TZSET
   /* If the execution TZ happens to be the same as the dump TZ,
@@ -127,7 +221,7 @@ init_editfns (void)
      to force the underlying implementation to reload the TZ info.
      This is needed on implementations that load TZ info from files,
      since the TZ file contents may differ between dump and execution.  */
-  if (tz && strcmp (tz, &dump_tz_string[sizeof "TZ=" - 1]) == 0)
+  if (tz && strcmp (tz, &dump_tz_string[tzeqlen]) == 0)
     {
       ++*tz;
       tzset ();
@@ -135,9 +229,10 @@ init_editfns (void)
     }
 #endif
 
-  /* Call set_time_zone_rule now, so that its call to putenv is done
+  /* Set the time zone rule now, so that the call to putenv is done
      before multiple threads are active.  */
-  set_time_zone_rule (tz);
+  wall_clock_tz = xtzalloc (0);
+  tzlookup (tz ? build_string (tz) : Qwall, true);
 
   pw = getpwuid (getuid ());
 #ifdef MSDOS
@@ -1206,7 +1301,7 @@ of the user with that uid, or nil if there is no such 
user.  */)
      (That can happen if Emacs is dumpable
      but you decide to run `temacs -l loadup' and not dump.  */
   if (NILP (Vuser_login_name))
-    init_editfns ();
+    init_editfns (false);
 
   if (NILP (uid))
     return Vuser_login_name;
@@ -1229,7 +1324,7 @@ This ignores the environment variables LOGNAME and USER, 
so it differs from
      (That can happen if Emacs is dumpable
      but you decide to run `temacs -l loadup' and not dump.  */
   if (NILP (Vuser_login_name))
-    init_editfns ();
+    init_editfns (false);
   return Vuser_real_login_name;
 }
 
@@ -1384,30 +1479,6 @@ check_time_validity (int validity)
     }
 }
 
-/* A substitute for mktime_z on platforms that lack it.  It's not
-   thread-safe, but should be good enough for Emacs in typical use.  */
-#ifndef HAVE_TZALLOC
-static time_t
-mktime_z (timezone_t tz, struct tm *tm)
-{
-  char *oldtz = getenv ("TZ");
-  USE_SAFE_ALLOCA;
-  if (oldtz)
-    {
-      size_t oldtzsize = strlen (oldtz) + 1;
-      char *oldtzcopy = SAFE_ALLOCA (oldtzsize);
-      oldtz = strcpy (oldtzcopy, oldtz);
-    }
-  block_input ();
-  set_time_zone_rule (tz);
-  time_t t = mktime (tm);
-  set_time_zone_rule (oldtz);
-  unblock_input ();
-  SAFE_FREE ();
-  return t;
-}
-#endif
-
 /* Return the upper part of the time T (everything but the bottom 16 bits).  */
 static EMACS_INT
 hi_time (time_t t)
@@ -1848,7 +1919,7 @@ or (if you need time as a string) `format-time-string'.  
*/)
 
 /* Write information into buffer S of size MAXSIZE, according to the
    FORMAT of length FORMAT_LEN, using time information taken from *TP.
-   Default to Universal Time if UT, local time otherwise.
+   Use the time zone specified by TZ.
    Use NS as the number of nanoseconds in the %N directive.
    Return the number of bytes written, not including the terminating
    '\0'.  If S is NULL, nothing will be written anywhere; so to
@@ -1859,7 +1930,7 @@ or (if you need time as a string) `format-time-string'.  
*/)
    bytes in FORMAT and it does not support nanoseconds.  */
 static size_t
 emacs_nmemftime (char *s, size_t maxsize, const char *format,
-                size_t format_len, const struct tm *tp, bool ut, int ns)
+                size_t format_len, const struct tm *tp, timezone_t tz, int ns)
 {
   size_t total = 0;
 
@@ -1876,7 +1947,7 @@ emacs_nmemftime (char *s, size_t maxsize, const char 
*format,
       if (s)
        s[0] = '\1';
 
-      result = nstrftime (s, maxsize, format, tp, ut, ns);
+      result = nstrftime (s, maxsize, format, tp, tz, ns);
 
       if (s)
        {
@@ -1901,8 +1972,9 @@ DEFUN ("format-time-string", Fformat_time_string, 
Sformat_time_string, 1, 3, 0,
 TIME is specified as (HIGH LOW USEC PSEC), as returned by
 `current-time' or `file-attributes'.  The obsolete form (HIGH . LOW)
 is also still accepted.
-The third, optional, argument UNIVERSAL, if non-nil, means describe TIME
-as Universal Time; nil means describe TIME in the local time zone.
+The optional ZONE is omitted or nil for Emacs local time, t for
+Universal Time, `wall' for system wall clock time, or a string as in
+`set-time-zone-rule' for a time zone rule.
 The value is a copy of FORMAT-STRING, but with certain constructs replaced
 by text that describes the specified date and time in TIME:
 
@@ -1951,8 +2023,8 @@ The modifiers are `E' and `O'.  For certain characters X,
 
 For example, to produce full ISO 8601 format, use "%FT%T%z".
 
-usage: (format-time-string FORMAT-STRING &optional TIME UNIVERSAL)  */)
-  (Lisp_Object format_string, Lisp_Object timeval, Lisp_Object universal)
+usage: (format-time-string FORMAT-STRING &optional TIME ZONE)  */)
+  (Lisp_Object format_string, Lisp_Object timeval, Lisp_Object zone)
 {
   struct timespec t = lisp_time_argument (timeval);
   struct tm tm;
@@ -1961,12 +2033,12 @@ usage: (format-time-string FORMAT-STRING &optional TIME 
UNIVERSAL)  */)
   format_string = code_convert_string_norecord (format_string,
                                                Vlocale_coding_system, 1);
   return format_time_string (SSDATA (format_string), SBYTES (format_string),
-                            t, ! NILP (universal), &tm);
+                            t, zone, &tm);
 }
 
 static Lisp_Object
 format_time_string (char const *format, ptrdiff_t formatlen,
-                   struct timespec t, bool ut, struct tm *tmp)
+                   struct timespec t, Lisp_Object zone, struct tm *tmp)
 {
   char buffer[4000];
   char *buf = buffer;
@@ -1976,36 +2048,48 @@ format_time_string (char const *format, ptrdiff_t 
formatlen,
   int ns = t.tv_nsec;
   USE_SAFE_ALLOCA;
 
-  tmp = ut ? gmtime_r (&t.tv_sec, tmp) : localtime_r (&t.tv_sec, tmp);
+  timezone_t tz = tzlookup (zone, false);
+  tmp = emacs_localtime_rz (tz, &t.tv_sec, tmp);
   if (! tmp)
-    time_overflow ();
+    {
+      xtzfree (tz);
+      time_overflow ();
+    }
   synchronize_system_time_locale ();
 
   while (true)
     {
       buf[0] = '\1';
-      len = emacs_nmemftime (buf, size, format, formatlen, tmp, ut, ns);
+      len = emacs_nmemftime (buf, size, format, formatlen, tmp, tz, ns);
       if ((0 < len && len < size) || (len == 0 && buf[0] == '\0'))
        break;
 
       /* Buffer was too small, so make it bigger and try again.  */
-      len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tmp, ut, ns);
+      len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tmp, tz, ns);
       if (STRING_BYTES_BOUND <= len)
-       string_overflow ();
+       {
+         xtzfree (tz);
+         string_overflow ();
+       }
       size = len + 1;
       buf = SAFE_ALLOCA (size);
     }
 
+  xtzfree (tz);
   bufstring = make_unibyte_string (buf, len);
   SAFE_FREE ();
   return code_convert_string_norecord (bufstring, Vlocale_coding_system, 0);
 }
 
-DEFUN ("decode-time", Fdecode_time, Sdecode_time, 0, 1, 0,
-       doc: /* Decode a time value as (SEC MINUTE HOUR DAY MONTH YEAR DOW DST 
ZONE).
+DEFUN ("decode-time", Fdecode_time, Sdecode_time, 0, 2, 0,
+       doc: /* Decode a time value as (SEC MINUTE HOUR DAY MONTH YEAR DOW DST 
UTCOFF).
 The optional SPECIFIED-TIME should be a list of (HIGH LOW . IGNORED),
 as from `current-time' and `file-attributes', or nil to use the
 current time.  The obsolete form (HIGH . LOW) is also still accepted.
+The optional ZONE is omitted or nil for Emacs local time, t for
+Universal Time, `wall' for system wall clock time, or a string as in
+`set-time-zone-rule' for a time zone rule.
+
 The list has the following nine members: SEC is an integer between 0
 and 60; SEC is 60 for a leap second, which only some operating systems
 support.  MINUTE is an integer between 0 and 59.  HOUR is an integer
@@ -2013,15 +2097,20 @@ between 0 and 23.  DAY is an integer between 1 and 31.  
MONTH is an
 integer between 1 and 12.  YEAR is an integer indicating the
 four-digit year.  DOW is the day of week, an integer between 0 and 6,
 where 0 is Sunday.  DST is t if daylight saving time is in effect,
-otherwise nil.  ZONE is an integer indicating the number of seconds
-east of Greenwich.  (Note that Common Lisp has different meanings for
-DOW and ZONE.)  */)
-  (Lisp_Object specified_time)
+otherwise nil.  UTCOFF is an integer indicating the UTC offset in
+seconds, i.e., the number of seconds east of Greenwich.  (Note that
+Common Lisp has different meanings for DOW and UTCOFF.)
+
+usage: (decode-time &optional TIME ZONE)  */)
+  (Lisp_Object specified_time, Lisp_Object zone)
 {
   time_t time_spec = lisp_seconds_argument (specified_time);
   struct tm local_tm, gmt_tm;
+  timezone_t tz = tzlookup (zone, false);
+  struct tm *tm = emacs_localtime_rz (tz, &time_spec, &local_tm);
+  xtzfree (tz);
 
-  if (! (localtime_r (&time_spec, &local_tm)
+  if (! (tm
         && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= local_tm.tm_year
         && local_tm.tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE))
     time_overflow ();
@@ -2059,35 +2148,13 @@ check_tm_member (Lisp_Object obj, int offset)
   return n - offset;
 }
 
-/* Decode ZONE as a time zone specification.  */
-
-static Lisp_Object
-decode_time_zone (Lisp_Object zone)
-{
-  if (EQ (zone, Qt))
-    return build_string ("UTC0");
-  else if (STRINGP (zone))
-    return zone;
-  else if (INTEGERP (zone))
-    {
-      static char const tzbuf_format[] = "XXX%s%"pI"d:%02d:%02d";
-      char tzbuf[sizeof tzbuf_format + INT_STRLEN_BOUND (EMACS_INT)];
-      EMACS_INT abszone = eabs (XINT (zone)), zone_hr = abszone / (60 * 60);
-      int zone_min = (abszone / 60) % 60, zone_sec = abszone % 60;
-
-      return make_formatted_string (tzbuf, tzbuf_format, &"-"[XINT (zone) < 0],
-                                   zone_hr, zone_min, zone_sec);
-    }
-  else
-    xsignal2 (Qerror, build_string ("Invalid time zone specification"), zone);
-}
-
 DEFUN ("encode-time", Fencode_time, Sencode_time, 6, MANY, 0,
        doc: /* Convert SECOND, MINUTE, HOUR, DAY, MONTH, YEAR and ZONE to 
internal time.
 This is the reverse operation of `decode-time', which see.
-ZONE defaults to the current time zone rule.  This can
-be a string or t (as from `set-time-zone-rule'), or it can be a list
-\(as from `current-time-zone') or an integer (as from `decode-time')
+The optional ZONE is omitted or nil for Emacs local time, t for
+Universal Time, `wall' for system wall clock time, or a string as in
+`set-time-zone-rule' for a time zone rule.  It can also be a list (as
+from `current-time-zone') or an integer (as from `decode-time')
 applied without consideration for daylight saving time.
 
 You can pass more than 7 arguments; then the first six arguments
@@ -2120,14 +2187,9 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR 
&optional ZONE)  */)
 
   if (CONSP (zone))
     zone = XCAR (zone);
-  if (NILP (zone))
-    value = mktime (&tm);
-  else
-    {
-      timezone_t tz = tzalloc (SSDATA (decode_time_zone (zone)));
-      value = mktime_z (tz, &tm);
-      tzfree (tz);
-    }
+  timezone_t tz = tzlookup (zone, false);
+  value = emacs_mktime_z (tz, &tm);
+  xtzfree (tz);
 
   if (value == (time_t) -1)
     time_overflow ();
@@ -2135,7 +2197,8 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR 
&optional ZONE)  */)
   return list2i (hi_time (value), lo_time (value));
 }
 
-DEFUN ("current-time-string", Fcurrent_time_string, Scurrent_time_string, 0, 
1, 0,
+DEFUN ("current-time-string", Fcurrent_time_string, Scurrent_time_string,
+       0, 2, 0,
        doc: /* Return the current local time, as a human-readable string.
 Programs can use this function to decode a time,
 since the number of columns in each field is fixed
@@ -2148,17 +2211,24 @@ If SPECIFIED-TIME is given, it is a time to format 
instead of the
 current time.  The argument should have the form (HIGH LOW . IGNORED).
 Thus, you can use times obtained from `current-time' and from
 `file-attributes'.  SPECIFIED-TIME can also have the form (HIGH . LOW),
-but this is considered obsolete.  */)
-  (Lisp_Object specified_time)
+but this is considered obsolete.
+
+The optional ZONE is omitted or nil for Emacs local time, t for
+Universal Time, `wall' for system wall clock time, or a string as in
+`set-time-zone-rule' for a time zone rule.  */)
+  (Lisp_Object specified_time, Lisp_Object zone)
 {
   time_t value = lisp_seconds_argument (specified_time);
+  timezone_t tz = tzlookup (zone, false);
 
   /* Convert to a string in ctime format, except without the trailing
      newline, and without the 4-digit year limit.  Don't use asctime
      or ctime, as they might dump core if the year is outside the
      range -999 .. 9999.  */
   struct tm tm;
-  if (! localtime_r (&value, &tm))
+  struct tm *tmp = emacs_localtime_rz (tz, &value, &tm);
+  xtzfree (tz);
+  if (! tmp)
     time_overflow ();
 
   static char const wday_name[][4] =
@@ -2210,7 +2280,7 @@ tm_gmtoff (struct tm *a)
 #endif
 }
 
-DEFUN ("current-time-zone", Fcurrent_time_zone, Scurrent_time_zone, 0, 1, 0,
+DEFUN ("current-time-zone", Fcurrent_time_zone, Scurrent_time_zone, 0, 2, 0,
        doc: /* Return the offset and name for the local time zone.
 This returns a list of the form (OFFSET NAME).
 OFFSET is an integer number of seconds ahead of UTC (east of Greenwich).
@@ -2221,11 +2291,13 @@ instead of using the current time.  The argument should 
have the form
 (HIGH LOW . IGNORED).  Thus, you can use times obtained from
 `current-time' and from `file-attributes'.  SPECIFIED-TIME can also
 have the form (HIGH . LOW), but this is considered obsolete.
+Optional second arg ZONE is omitted or nil for the local time zone, or
+a string as in `set-time-zone-rule'.
 
 Some operating systems cannot provide all this information to Emacs;
 in this case, `current-time-zone' returns a list containing nil for
 the data it can't find.  */)
-  (Lisp_Object specified_time)
+  (Lisp_Object specified_time, Lisp_Object zone)
 {
   struct timespec value;
   struct tm local_tm, gmt_tm;
@@ -2233,7 +2305,8 @@ the data it can't find.  */)
 
   zone_offset = Qnil;
   value = make_timespec (lisp_seconds_argument (specified_time), 0);
-  zone_name = format_time_string ("%Z", sizeof "%Z" - 1, value, 0, &local_tm);
+  zone_name = format_time_string ("%Z", sizeof "%Z" - 1, value,
+                                 zone, &local_tm);
 
   if (HAVE_TM_GMTOFF || gmtime_r (&value.tv_sec, &gmt_tm))
     {
@@ -2259,42 +2332,48 @@ the data it can't find.  */)
 }
 
 DEFUN ("set-time-zone-rule", Fset_time_zone_rule, Sset_time_zone_rule, 1, 1, 0,
-       doc: /* Set the local time zone using TZ, a string specifying a time 
zone rule.
-If TZ is nil, use implementation-defined default time zone information.
-If TZ is t, use Universal Time.  If TZ is an integer, it is treated as in
-`encode-time'.
-
-Instead of calling this function, you typically want (setenv "TZ" TZ).
-That changes both the environment of the Emacs process and the
-variable `process-environment', whereas `set-time-zone-rule' affects
-only the former.  */)
+       doc: /* Set the Emacs local time zone using TZ, a string specifying a 
time zone rule.
+If TZ is nil or `wall', use system wall clock time.  If TZ is t, use
+Universal Time.  If TZ is an integer, treat it as in `encode-time'.
+
+Instead of calling this function, you typically want something else.
+To temporarily use a different time zone rule for just one invocation
+of `decode-time', `encode-time', or `format-time-string', pass the
+function a ZONE argument.  To change local time consistently
+throughout Emacs, call (setenv "TZ" TZ): this changes both the
+environment of the Emacs process and the variable
+`process-environment', whereas `set-time-zone-rule' affects only the
+former.  */)
   (Lisp_Object tz)
 {
-  const char *tzstring = NILP (tz) ? initial_tz : SSDATA (decode_time_zone 
(tz));
+  tzlookup (NILP (tz) ? Qwall : tz, true);
+  return Qnil;
+}
 
-  block_input ();
-  set_time_zone_rule (tzstring);
-  unblock_input ();
+/* A buffer holding a string of the form "TZ=value", intended
+   to be part of the environment.  If TZ is supposed to be unset,
+   the buffer string is "tZ=".  */
+ static char *tzvalbuf;
 
-  return Qnil;
+/* Get the local time zone rule.  */
+char *
+emacs_getenv_TZ (void)
+{
+  return tzvalbuf[0] == 'T' ? tzvalbuf + tzeqlen : 0;
 }
 
-/* Set the local time zone rule to TZSTRING.
+/* Set the local time zone rule to TZSTRING, which can be null to
+   denote wall clock time.  Do not record the setting in LOCAL_TZ.
 
    This function is not thread-safe, in theory because putenv is not,
    but mostly because of the static storage it updates.  Other threads
    that invoke localtime etc. may be adversely affected while this
    function is executing.  */
 
-static void
-set_time_zone_rule (const char *tzstring)
+int
+emacs_setenv_TZ (const char *tzstring)
 {
-  /* A buffer holding a string of the form "TZ=value", intended
-     to be part of the environment.  */
-  static char *tzvalbuf;
   static ptrdiff_t tzvalbufsize;
-
-  int tzeqlen = sizeof "TZ=" - 1;
   ptrdiff_t tzstringlen = tzstring ? strlen (tzstring) : 0;
   char *tzval = tzvalbuf;
   bool new_tzvalbuf = tzvalbufsize <= tzeqlen + tzstringlen;
@@ -2346,9 +2425,7 @@ set_time_zone_rule (const char *tzstring)
       xputenv (tzval);
     }
 
-#ifdef HAVE_TZSET
-  tzset ();
-#endif
+  return 0;
 }
 
 /* Insert NARGS Lisp objects in the array ARGS by calling INSERT_FUNC
@@ -4943,6 +5020,7 @@ void
 syms_of_editfns (void)
 {
   DEFSYM (Qbuffer_access_fontify_functions, "buffer-access-fontify-functions");
+  DEFSYM (Qwall, "wall");
 
   DEFVAR_LISP ("inhibit-field-text-motion", Vinhibit_field_text_motion,
               doc: /* Non-nil means text motion commands don't notice fields.  
*/);
diff --git a/src/emacs.c b/src/emacs.c
index 93fb587..6e35496 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -1552,7 +1552,7 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
 
   /* This calls putenv and so must precede init_process_emacs.  Also,
      it sets Voperating_system_release, which init_process_emacs uses.  */
-  init_editfns ();
+  init_editfns (dumping);
 
   /* These two call putenv.  */
 #ifdef HAVE_DBUS
diff --git a/src/lisp.h b/src/lisp.h
index 341603f..02109d7 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4055,7 +4055,7 @@ extern _Noreturn void time_overflow (void);
 extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool);
 extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t,
                                            ptrdiff_t, bool);
-extern void init_editfns (void);
+extern void init_editfns (bool);
 extern void syms_of_editfns (void);
 
 /* Defined in buffer.c.  */
diff --git a/src/systime.h b/src/systime.h
index 744af17..abbe601 100644
--- a/src/systime.h
+++ b/src/systime.h
@@ -106,20 +106,6 @@ extern struct timespec lisp_to_timespec (struct lisp_time);
 extern struct timespec lisp_time_argument (Lisp_Object);
 #endif
 
-#ifndef HAVE_TZALLOC
-# undef mktime_z
-# undef timezone_t
-# undef tzalloc
-# undef tzfree
-# define mktime_z emacs_mktime_z
-# define timezone_t emacs_timezone_t
-# define tzalloc emacs_tzalloc
-# define tzfree emacs_tzfree
-typedef char const *timezone_t;
-INLINE timezone_t tzalloc (char const *name) { return name; }
-INLINE void tzfree (timezone_t tz) { }
-#endif
-
 INLINE_HEADER_END
 
 #endif /* EMACS_SYSTIME_H */



reply via email to

[Prev in Thread] Current Thread [Next in Thread]