aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2015-07-26 00:01:34 -0700
committerPaul Eggert2015-07-26 12:44:54 -0700
commitaf32fa956267af40db61051c248597144d41521c (patch)
treeafd650b9f9805474df149081e51cc8abae3bdb87 /src
parent4c55786d9b2a5d571f3e543cc261ce0702c7341e (diff)
downloademacs-af32fa956267af40db61051c248597144d41521c.tar.gz
emacs-af32fa956267af40db61051c248597144d41521c.zip
New optional ZONE arg for format-time-string etc.
This simplifies time conversions in other time zones. It also prevents display-time-world tampering with TZ (Bug#21020). * admin/admin.el (add-release-logs): Use improved add-log-time-format API. * admin/merge-gnulib (GNULIB_MODULES): Add time_rz, timegm. (GNULIB_TOOL_FLAGS): Avoid flexmember, setenv, unsetenv. * configure.ac (tzalloc): Remove test for this, since Emacs no longer uses HAVE_TZALLOC directly. * doc/lispref/os.texi (Time of Day, Time Conversion) (Time Parsing): * etc/NEWS: Document the new behavior. Merge from gnulib, incorporating: 2015-07-25 strftime: fix newly-introduced bug on Solaris 2015-07-23 fprintftime, strftime: use timezone_t args * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate. * lib/strftime.c, lib/strftime.h, lib/time.in.h, m4/sys_time_h.m4: * m4/time_h.m4: Update from gnulib. * lib/time_rz.c, lib/timegm.c, m4/time_rz.m4, m4/timegm.m4: New files from gnulib. * lisp/time-stamp.el (time-stamp-string): * lisp/time.el (display-time-world-list) (display-time-world-display): Use new API, with time zone arg. * lisp/time.el (display-time-world-display): Fix race when current-time advances while we're running. * lisp/vc/add-log.el (add-log-iso8601-time-zone) (add-log-iso8601-time-string): Accept optional time zone arg. * lisp/vc/add-log.el (add-change-log-entry): * lisp/vc/log-edit.el (log-edit-changelog-ours-p): Use new arg. * nt/gnulib.mk: Propagate lib/gnulib.mk changes here. Add rules for the time module, since they're now needed for tzalloc etc. * src/conf_post.h (getenv_TZ, setenv_TZ): New macros. (emacs_getenv_TZ, emacs_setenv_TZ): New decls. * src/editfns.c: Include errno.h. (set_time_zone_rule): Omit unnecessary forward decl. (initial_tz): Remove, replacing with ... (local_tz, wall_clock_tz, utc_tz): New static vars and constants. (tzeqlen): New constant; prefer it to (sizeof "TZ=" - 1). (emacs_localtime_rz, emacs_mktime_z, xtzalloc, xtzfree) (tzlookup): New static functions. (init_editfns): New arg DUMPING. All uses changed. (init_editfns): Omit most initialization if dumping, not if !initialized. Initialize wall_clock_tz and local_tz. (emacs_nmemftime, format_time_string): Time zone argument can now be any time zone, not just a boolean for UTC or local time. All callers changed. (Fformat_time_string, Fencode_time, Fcurrent_time_string) (Fcurrent_time_zone): New optional arg ZONE. (Fdecode_time, Fset_time_zone_rule): ZONE arg can now also take the same form as with the other new additions. (decode_time_zone): Remove; no longer needed. (tzvalbuf): Now file-scope. (emacs_getenv_TZ, emacs_setenv_TZ): New functions. (syms_of_editfns): Define Qwall. * src/editfns.c (mktime_z) [!HAVE_TZALLOC]: * src/systime.h (mktime_z, timezone_t, tzalloc, tzfree) [!HAVE_TZALLOC]: Remove; now supplied by gnulib. * src/emacs.c (main): * src/lisp.h (init_editfns): Adjust to init_editfns API change.
Diffstat (limited to 'src')
-rw-r--r--src/conf_post.h7
-rw-r--r--src/editfns.c324
-rw-r--r--src/emacs.c2
-rw-r--r--src/lisp.h2
-rw-r--r--src/systime.h14
5 files changed, 210 insertions, 139 deletions
diff --git a/src/conf_post.h b/src/conf_post.h
index 1a080fad635..785e5d7554b 100644
--- a/src/conf_post.h
+++ b/src/conf_post.h
@@ -206,6 +206,13 @@ extern void _DebPrint (const char *fmt, ...);
206#define RE_TRANSLATE_P(TBL) (!EQ (TBL, make_number (0))) 206#define RE_TRANSLATE_P(TBL) (!EQ (TBL, make_number (0)))
207#endif 207#endif
208 208
209/* Tell time_rz.c to use Emacs's getter and setter for TZ.
210 Only Emacs uses time_rz so this is OK. */
211#define getenv_TZ emacs_getenv_TZ
212#define setenv_TZ emacs_setenv_TZ
213extern char *emacs_getenv_TZ (void);
214extern int emacs_setenv_TZ (char const *);
215
209#include <string.h> 216#include <string.h>
210#include <stdlib.h> 217#include <stdlib.h>
211 218
diff --git a/src/editfns.c b/src/editfns.c
index e39eed6e870..9ff39f9bf19 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/>. */
44#include <sys/resource.h> 44#include <sys/resource.h>
45#endif 45#endif
46 46
47#include <errno.h>
47#include <float.h> 48#include <float.h>
48#include <limits.h> 49#include <limits.h>
50
49#include <intprops.h> 51#include <intprops.h>
50#include <strftime.h> 52#include <strftime.h>
51#include <verify.h> 53#include <verify.h>
@@ -65,9 +67,8 @@ extern Lisp_Object w32_get_internal_run_time (void);
65#endif 67#endif
66 68
67static struct lisp_time lisp_time_struct (Lisp_Object, int *); 69static struct lisp_time lisp_time_struct (Lisp_Object, int *);
68static void set_time_zone_rule (char const *);
69static Lisp_Object format_time_string (char const *, ptrdiff_t, struct timespec, 70static Lisp_Object format_time_string (char const *, ptrdiff_t, struct timespec,
70 bool, struct tm *); 71 Lisp_Object, struct tm *);
71static long int tm_gmtoff (struct tm *); 72static long int tm_gmtoff (struct tm *);
72static int tm_diff (struct tm *, struct tm *); 73static int tm_diff (struct tm *, struct tm *);
73static void update_buffer_properties (ptrdiff_t, ptrdiff_t); 74static void update_buffer_properties (ptrdiff_t, ptrdiff_t);
@@ -76,8 +77,13 @@ static void update_buffer_properties (ptrdiff_t, ptrdiff_t);
76# define HAVE_TM_GMTOFF false 77# define HAVE_TM_GMTOFF false
77#endif 78#endif
78 79
79/* The startup value of the TZ environment variable; null if unset. */ 80enum { tzeqlen = sizeof "TZ=" - 1 };
80static char const *initial_tz; 81
82/* Time zones equivalent to current local time, to wall clock time,
83 and to UTC, respectively. */
84static timezone_t local_tz;
85static timezone_t wall_clock_tz;
86static timezone_t const utc_tz = 0;
81 87
82/* A valid but unlikely setting for the TZ environment variable. 88/* A valid but unlikely setting for the TZ environment variable.
83 It is OK (though a bit slower) if the user chooses this value. */ 89 It is OK (though a bit slower) if the user chooses this value. */
@@ -94,8 +100,97 @@ init_and_cache_system_name (void)
94 cached_system_name = Vsystem_name; 100 cached_system_name = Vsystem_name;
95} 101}
96 102
103static struct tm *
104emacs_localtime_rz (timezone_t tz, time_t const *t, struct tm *tm)
105{
106 tm = localtime_rz (tz, t, tm);
107 if (!tm && errno == ENOMEM)
108 memory_full (SIZE_MAX);
109 return tm;
110}
111
112static time_t
113emacs_mktime_z (timezone_t tz, struct tm *tm)
114{
115 errno = 0;
116 time_t t = mktime_z (tz, tm);
117 if (t == (time_t) -1 && errno == ENOMEM)
118 memory_full (SIZE_MAX);
119 return t;
120}
121
122/* Allocate a timezone, signaling on failure. */
123static timezone_t
124xtzalloc (char const *name)
125{
126 timezone_t tz = tzalloc (name);
127 if (!tz)
128 memory_full (SIZE_MAX);
129 return tz;
130}
131
132/* Free a timezone, except do not free the time zone for local time.
133 Freeing utc_tz is also a no-op. */
134static void
135xtzfree (timezone_t tz)
136{
137 if (tz != local_tz)
138 tzfree (tz);
139}
140
141/* Convert the Lisp time zone rule ZONE to a timezone_t object.
142 The returned value either is 0, or is LOCAL_TZ, or is newly allocated.
143 If SETTZ, set Emacs local time to the time zone rule; otherwise,
144 the caller should eventually pass the returned value to xtzfree. */
145static timezone_t
146tzlookup (Lisp_Object zone, bool settz)
147{
148 static char const tzbuf_format[] = "XXX%s%"pI"d:%02d:%02d";
149 char tzbuf[sizeof tzbuf_format + INT_STRLEN_BOUND (EMACS_INT)];
150 char const *zone_string;
151 timezone_t new_tz;
152
153 if (NILP (zone))
154 return local_tz;
155 else if (EQ (zone, Qt))
156 {
157 zone_string = "UTC0";
158 new_tz = utc_tz;
159 }
160 else
161 {
162 if (EQ (zone, Qwall))
163 zone_string = 0;
164 else if (STRINGP (zone))
165 zone_string = SSDATA (zone);
166 else if (INTEGERP (zone))
167 {
168 EMACS_INT abszone = eabs (XINT (zone)), hour = abszone / (60 * 60);
169 int min = (abszone / 60) % 60, sec = abszone % 60;
170 sprintf (tzbuf, tzbuf_format, &"-"[XINT (zone) < 0], hour, min, sec);
171 zone_string = tzbuf;
172 }
173 else
174 xsignal2 (Qerror, build_string ("Invalid time zone specification"),
175 zone);
176 new_tz = xtzalloc (zone_string);
177 }
178
179 if (settz)
180 {
181 block_input ();
182 emacs_setenv_TZ (zone_string);
183 timezone_t old_tz = local_tz;
184 local_tz = new_tz;
185 tzfree (old_tz);
186 unblock_input ();
187 }
188
189 return new_tz;
190}
191
97void 192void
98init_editfns (void) 193init_editfns (bool dumping)
99{ 194{
100 const char *user_name; 195 const char *user_name;
101 register char *p; 196 register char *p;
@@ -108,7 +203,7 @@ init_editfns (void)
108#ifndef CANNOT_DUMP 203#ifndef CANNOT_DUMP
109 /* When just dumping out, set the time zone to a known unlikely value 204 /* When just dumping out, set the time zone to a known unlikely value
110 and skip the rest of this function. */ 205 and skip the rest of this function. */
111 if (!initialized) 206 if (dumping)
112 { 207 {
113# ifdef HAVE_TZSET 208# ifdef HAVE_TZSET
114 xputenv (dump_tz_string); 209 xputenv (dump_tz_string);
@@ -119,7 +214,6 @@ init_editfns (void)
119#endif 214#endif
120 215
121 char *tz = getenv ("TZ"); 216 char *tz = getenv ("TZ");
122 initial_tz = tz;
123 217
124#if !defined CANNOT_DUMP && defined HAVE_TZSET 218#if !defined CANNOT_DUMP && defined HAVE_TZSET
125 /* If the execution TZ happens to be the same as the dump TZ, 219 /* If the execution TZ happens to be the same as the dump TZ,
@@ -127,7 +221,7 @@ init_editfns (void)
127 to force the underlying implementation to reload the TZ info. 221 to force the underlying implementation to reload the TZ info.
128 This is needed on implementations that load TZ info from files, 222 This is needed on implementations that load TZ info from files,
129 since the TZ file contents may differ between dump and execution. */ 223 since the TZ file contents may differ between dump and execution. */
130 if (tz && strcmp (tz, &dump_tz_string[sizeof "TZ=" - 1]) == 0) 224 if (tz && strcmp (tz, &dump_tz_string[tzeqlen]) == 0)
131 { 225 {
132 ++*tz; 226 ++*tz;
133 tzset (); 227 tzset ();
@@ -135,9 +229,10 @@ init_editfns (void)
135 } 229 }
136#endif 230#endif
137 231
138 /* Call set_time_zone_rule now, so that its call to putenv is done 232 /* Set the time zone rule now, so that the call to putenv is done
139 before multiple threads are active. */ 233 before multiple threads are active. */
140 set_time_zone_rule (tz); 234 wall_clock_tz = xtzalloc (0);
235 tzlookup (tz ? build_string (tz) : Qwall, true);
141 236
142 pw = getpwuid (getuid ()); 237 pw = getpwuid (getuid ());
143#ifdef MSDOS 238#ifdef MSDOS
@@ -1206,7 +1301,7 @@ of the user with that uid, or nil if there is no such user. */)
1206 (That can happen if Emacs is dumpable 1301 (That can happen if Emacs is dumpable
1207 but you decide to run `temacs -l loadup' and not dump. */ 1302 but you decide to run `temacs -l loadup' and not dump. */
1208 if (NILP (Vuser_login_name)) 1303 if (NILP (Vuser_login_name))
1209 init_editfns (); 1304 init_editfns (false);
1210 1305
1211 if (NILP (uid)) 1306 if (NILP (uid))
1212 return Vuser_login_name; 1307 return Vuser_login_name;
@@ -1229,7 +1324,7 @@ This ignores the environment variables LOGNAME and USER, so it differs from
1229 (That can happen if Emacs is dumpable 1324 (That can happen if Emacs is dumpable
1230 but you decide to run `temacs -l loadup' and not dump. */ 1325 but you decide to run `temacs -l loadup' and not dump. */
1231 if (NILP (Vuser_login_name)) 1326 if (NILP (Vuser_login_name))
1232 init_editfns (); 1327 init_editfns (false);
1233 return Vuser_real_login_name; 1328 return Vuser_real_login_name;
1234} 1329}
1235 1330
@@ -1384,30 +1479,6 @@ check_time_validity (int validity)
1384 } 1479 }
1385} 1480}
1386 1481
1387/* A substitute for mktime_z on platforms that lack it. It's not
1388 thread-safe, but should be good enough for Emacs in typical use. */
1389#ifndef HAVE_TZALLOC
1390static time_t
1391mktime_z (timezone_t tz, struct tm *tm)
1392{
1393 char *oldtz = getenv ("TZ");
1394 USE_SAFE_ALLOCA;
1395 if (oldtz)
1396 {
1397 size_t oldtzsize = strlen (oldtz) + 1;
1398 char *oldtzcopy = SAFE_ALLOCA (oldtzsize);
1399 oldtz = strcpy (oldtzcopy, oldtz);
1400 }
1401 block_input ();
1402 set_time_zone_rule (tz);
1403 time_t t = mktime (tm);
1404 set_time_zone_rule (oldtz);
1405 unblock_input ();
1406 SAFE_FREE ();
1407 return t;
1408}
1409#endif
1410
1411/* Return the upper part of the time T (everything but the bottom 16 bits). */ 1482/* Return the upper part of the time T (everything but the bottom 16 bits). */
1412static EMACS_INT 1483static EMACS_INT
1413hi_time (time_t t) 1484hi_time (time_t t)
@@ -1848,7 +1919,7 @@ or (if you need time as a string) `format-time-string'. */)
1848 1919
1849/* Write information into buffer S of size MAXSIZE, according to the 1920/* Write information into buffer S of size MAXSIZE, according to the
1850 FORMAT of length FORMAT_LEN, using time information taken from *TP. 1921 FORMAT of length FORMAT_LEN, using time information taken from *TP.
1851 Default to Universal Time if UT, local time otherwise. 1922 Use the time zone specified by TZ.
1852 Use NS as the number of nanoseconds in the %N directive. 1923 Use NS as the number of nanoseconds in the %N directive.
1853 Return the number of bytes written, not including the terminating 1924 Return the number of bytes written, not including the terminating
1854 '\0'. If S is NULL, nothing will be written anywhere; so to 1925 '\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'. */)
1859 bytes in FORMAT and it does not support nanoseconds. */ 1930 bytes in FORMAT and it does not support nanoseconds. */
1860static size_t 1931static size_t
1861emacs_nmemftime (char *s, size_t maxsize, const char *format, 1932emacs_nmemftime (char *s, size_t maxsize, const char *format,
1862 size_t format_len, const struct tm *tp, bool ut, int ns) 1933 size_t format_len, const struct tm *tp, timezone_t tz, int ns)
1863{ 1934{
1864 size_t total = 0; 1935 size_t total = 0;
1865 1936
@@ -1876,7 +1947,7 @@ emacs_nmemftime (char *s, size_t maxsize, const char *format,
1876 if (s) 1947 if (s)
1877 s[0] = '\1'; 1948 s[0] = '\1';
1878 1949
1879 result = nstrftime (s, maxsize, format, tp, ut, ns); 1950 result = nstrftime (s, maxsize, format, tp, tz, ns);
1880 1951
1881 if (s) 1952 if (s)
1882 { 1953 {
@@ -1901,8 +1972,9 @@ DEFUN ("format-time-string", Fformat_time_string, Sformat_time_string, 1, 3, 0,
1901TIME is specified as (HIGH LOW USEC PSEC), as returned by 1972TIME is specified as (HIGH LOW USEC PSEC), as returned by
1902`current-time' or `file-attributes'. The obsolete form (HIGH . LOW) 1973`current-time' or `file-attributes'. The obsolete form (HIGH . LOW)
1903is also still accepted. 1974is also still accepted.
1904The third, optional, argument UNIVERSAL, if non-nil, means describe TIME 1975The optional ZONE is omitted or nil for Emacs local time, t for
1905as Universal Time; nil means describe TIME in the local time zone. 1976Universal Time, `wall' for system wall clock time, or a string as in
1977`set-time-zone-rule' for a time zone rule.
1906The value is a copy of FORMAT-STRING, but with certain constructs replaced 1978The value is a copy of FORMAT-STRING, but with certain constructs replaced
1907by text that describes the specified date and time in TIME: 1979by text that describes the specified date and time in TIME:
1908 1980
@@ -1951,8 +2023,8 @@ The modifiers are `E' and `O'. For certain characters X,
1951 2023
1952For example, to produce full ISO 8601 format, use "%FT%T%z". 2024For example, to produce full ISO 8601 format, use "%FT%T%z".
1953 2025
1954usage: (format-time-string FORMAT-STRING &optional TIME UNIVERSAL) */) 2026usage: (format-time-string FORMAT-STRING &optional TIME ZONE) */)
1955 (Lisp_Object format_string, Lisp_Object timeval, Lisp_Object universal) 2027 (Lisp_Object format_string, Lisp_Object timeval, Lisp_Object zone)
1956{ 2028{
1957 struct timespec t = lisp_time_argument (timeval); 2029 struct timespec t = lisp_time_argument (timeval);
1958 struct tm tm; 2030 struct tm tm;
@@ -1961,12 +2033,12 @@ usage: (format-time-string FORMAT-STRING &optional TIME UNIVERSAL) */)
1961 format_string = code_convert_string_norecord (format_string, 2033 format_string = code_convert_string_norecord (format_string,
1962 Vlocale_coding_system, 1); 2034 Vlocale_coding_system, 1);
1963 return format_time_string (SSDATA (format_string), SBYTES (format_string), 2035 return format_time_string (SSDATA (format_string), SBYTES (format_string),
1964 t, ! NILP (universal), &tm); 2036 t, zone, &tm);
1965} 2037}
1966 2038
1967static Lisp_Object 2039static Lisp_Object
1968format_time_string (char const *format, ptrdiff_t formatlen, 2040format_time_string (char const *format, ptrdiff_t formatlen,
1969 struct timespec t, bool ut, struct tm *tmp) 2041 struct timespec t, Lisp_Object zone, struct tm *tmp)
1970{ 2042{
1971 char buffer[4000]; 2043 char buffer[4000];
1972 char *buf = buffer; 2044 char *buf = buffer;
@@ -1976,36 +2048,48 @@ format_time_string (char const *format, ptrdiff_t formatlen,
1976 int ns = t.tv_nsec; 2048 int ns = t.tv_nsec;
1977 USE_SAFE_ALLOCA; 2049 USE_SAFE_ALLOCA;
1978 2050
1979 tmp = ut ? gmtime_r (&t.tv_sec, tmp) : localtime_r (&t.tv_sec, tmp); 2051 timezone_t tz = tzlookup (zone, false);
2052 tmp = emacs_localtime_rz (tz, &t.tv_sec, tmp);
1980 if (! tmp) 2053 if (! tmp)
1981 time_overflow (); 2054 {
2055 xtzfree (tz);
2056 time_overflow ();
2057 }
1982 synchronize_system_time_locale (); 2058 synchronize_system_time_locale ();
1983 2059
1984 while (true) 2060 while (true)
1985 { 2061 {
1986 buf[0] = '\1'; 2062 buf[0] = '\1';
1987 len = emacs_nmemftime (buf, size, format, formatlen, tmp, ut, ns); 2063 len = emacs_nmemftime (buf, size, format, formatlen, tmp, tz, ns);
1988 if ((0 < len && len < size) || (len == 0 && buf[0] == '\0')) 2064 if ((0 < len && len < size) || (len == 0 && buf[0] == '\0'))
1989 break; 2065 break;
1990 2066
1991 /* Buffer was too small, so make it bigger and try again. */ 2067 /* Buffer was too small, so make it bigger and try again. */
1992 len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tmp, ut, ns); 2068 len = emacs_nmemftime (NULL, SIZE_MAX, format, formatlen, tmp, tz, ns);
1993 if (STRING_BYTES_BOUND <= len) 2069 if (STRING_BYTES_BOUND <= len)
1994 string_overflow (); 2070 {
2071 xtzfree (tz);
2072 string_overflow ();
2073 }
1995 size = len + 1; 2074 size = len + 1;
1996 buf = SAFE_ALLOCA (size); 2075 buf = SAFE_ALLOCA (size);
1997 } 2076 }
1998 2077
2078 xtzfree (tz);
1999 bufstring = make_unibyte_string (buf, len); 2079 bufstring = make_unibyte_string (buf, len);
2000 SAFE_FREE (); 2080 SAFE_FREE ();
2001 return code_convert_string_norecord (bufstring, Vlocale_coding_system, 0); 2081 return code_convert_string_norecord (bufstring, Vlocale_coding_system, 0);
2002} 2082}
2003 2083
2004DEFUN ("decode-time", Fdecode_time, Sdecode_time, 0, 1, 0, 2084DEFUN ("decode-time", Fdecode_time, Sdecode_time, 0, 2, 0,
2005 doc: /* Decode a time value as (SEC MINUTE HOUR DAY MONTH YEAR DOW DST ZONE). 2085 doc: /* Decode a time value as (SEC MINUTE HOUR DAY MONTH YEAR DOW DST UTCOFF).
2006The optional SPECIFIED-TIME should be a list of (HIGH LOW . IGNORED), 2086The optional SPECIFIED-TIME should be a list of (HIGH LOW . IGNORED),
2007as from `current-time' and `file-attributes', or nil to use the 2087as from `current-time' and `file-attributes', or nil to use the
2008current time. The obsolete form (HIGH . LOW) is also still accepted. 2088current time. The obsolete form (HIGH . LOW) is also still accepted.
2089The optional ZONE is omitted or nil for Emacs local time, t for
2090Universal Time, `wall' for system wall clock time, or a string as in
2091`set-time-zone-rule' for a time zone rule.
2092
2009The list has the following nine members: SEC is an integer between 0 2093The list has the following nine members: SEC is an integer between 0
2010and 60; SEC is 60 for a leap second, which only some operating systems 2094and 60; SEC is 60 for a leap second, which only some operating systems
2011support. MINUTE is an integer between 0 and 59. HOUR is an integer 2095support. 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
2013integer between 1 and 12. YEAR is an integer indicating the 2097integer between 1 and 12. YEAR is an integer indicating the
2014four-digit year. DOW is the day of week, an integer between 0 and 6, 2098four-digit year. DOW is the day of week, an integer between 0 and 6,
2015where 0 is Sunday. DST is t if daylight saving time is in effect, 2099where 0 is Sunday. DST is t if daylight saving time is in effect,
2016otherwise nil. ZONE is an integer indicating the number of seconds 2100otherwise nil. UTCOFF is an integer indicating the UTC offset in
2017east of Greenwich. (Note that Common Lisp has different meanings for 2101seconds, i.e., the number of seconds east of Greenwich. (Note that
2018DOW and ZONE.) */) 2102Common Lisp has different meanings for DOW and UTCOFF.)
2019 (Lisp_Object specified_time) 2103
2104usage: (decode-time &optional TIME ZONE) */)
2105 (Lisp_Object specified_time, Lisp_Object zone)
2020{ 2106{
2021 time_t time_spec = lisp_seconds_argument (specified_time); 2107 time_t time_spec = lisp_seconds_argument (specified_time);
2022 struct tm local_tm, gmt_tm; 2108 struct tm local_tm, gmt_tm;
2109 timezone_t tz = tzlookup (zone, false);
2110 struct tm *tm = emacs_localtime_rz (tz, &time_spec, &local_tm);
2111 xtzfree (tz);
2023 2112
2024 if (! (localtime_r (&time_spec, &local_tm) 2113 if (! (tm
2025 && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= local_tm.tm_year 2114 && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= local_tm.tm_year
2026 && local_tm.tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE)) 2115 && local_tm.tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE))
2027 time_overflow (); 2116 time_overflow ();
@@ -2059,35 +2148,13 @@ check_tm_member (Lisp_Object obj, int offset)
2059 return n - offset; 2148 return n - offset;
2060} 2149}
2061 2150
2062/* Decode ZONE as a time zone specification. */
2063
2064static Lisp_Object
2065decode_time_zone (Lisp_Object zone)
2066{
2067 if (EQ (zone, Qt))
2068 return build_string ("UTC0");
2069 else if (STRINGP (zone))
2070 return zone;
2071 else if (INTEGERP (zone))
2072 {
2073 static char const tzbuf_format[] = "XXX%s%"pI"d:%02d:%02d";
2074 char tzbuf[sizeof tzbuf_format + INT_STRLEN_BOUND (EMACS_INT)];
2075 EMACS_INT abszone = eabs (XINT (zone)), zone_hr = abszone / (60 * 60);
2076 int zone_min = (abszone / 60) % 60, zone_sec = abszone % 60;
2077
2078 return make_formatted_string (tzbuf, tzbuf_format, &"-"[XINT (zone) < 0],
2079 zone_hr, zone_min, zone_sec);
2080 }
2081 else
2082 xsignal2 (Qerror, build_string ("Invalid time zone specification"), zone);
2083}
2084
2085DEFUN ("encode-time", Fencode_time, Sencode_time, 6, MANY, 0, 2151DEFUN ("encode-time", Fencode_time, Sencode_time, 6, MANY, 0,
2086 doc: /* Convert SECOND, MINUTE, HOUR, DAY, MONTH, YEAR and ZONE to internal time. 2152 doc: /* Convert SECOND, MINUTE, HOUR, DAY, MONTH, YEAR and ZONE to internal time.
2087This is the reverse operation of `decode-time', which see. 2153This is the reverse operation of `decode-time', which see.
2088ZONE defaults to the current time zone rule. This can 2154The optional ZONE is omitted or nil for Emacs local time, t for
2089be a string or t (as from `set-time-zone-rule'), or it can be a list 2155Universal Time, `wall' for system wall clock time, or a string as in
2090\(as from `current-time-zone') or an integer (as from `decode-time') 2156`set-time-zone-rule' for a time zone rule. It can also be a list (as
2157from `current-time-zone') or an integer (as from `decode-time')
2091applied without consideration for daylight saving time. 2158applied without consideration for daylight saving time.
2092 2159
2093You can pass more than 7 arguments; then the first six arguments 2160You 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) */)
2120 2187
2121 if (CONSP (zone)) 2188 if (CONSP (zone))
2122 zone = XCAR (zone); 2189 zone = XCAR (zone);
2123 if (NILP (zone)) 2190 timezone_t tz = tzlookup (zone, false);
2124 value = mktime (&tm); 2191 value = emacs_mktime_z (tz, &tm);
2125 else 2192 xtzfree (tz);
2126 {
2127 timezone_t tz = tzalloc (SSDATA (decode_time_zone (zone)));
2128 value = mktime_z (tz, &tm);
2129 tzfree (tz);
2130 }
2131 2193
2132 if (value == (time_t) -1) 2194 if (value == (time_t) -1)
2133 time_overflow (); 2195 time_overflow ();
@@ -2135,7 +2197,8 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */)
2135 return list2i (hi_time (value), lo_time (value)); 2197 return list2i (hi_time (value), lo_time (value));
2136} 2198}
2137 2199
2138DEFUN ("current-time-string", Fcurrent_time_string, Scurrent_time_string, 0, 1, 0, 2200DEFUN ("current-time-string", Fcurrent_time_string, Scurrent_time_string,
2201 0, 2, 0,
2139 doc: /* Return the current local time, as a human-readable string. 2202 doc: /* Return the current local time, as a human-readable string.
2140Programs can use this function to decode a time, 2203Programs can use this function to decode a time,
2141since the number of columns in each field is fixed 2204since 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
2148current time. The argument should have the form (HIGH LOW . IGNORED). 2211current time. The argument should have the form (HIGH LOW . IGNORED).
2149Thus, you can use times obtained from `current-time' and from 2212Thus, you can use times obtained from `current-time' and from
2150`file-attributes'. SPECIFIED-TIME can also have the form (HIGH . LOW), 2213`file-attributes'. SPECIFIED-TIME can also have the form (HIGH . LOW),
2151but this is considered obsolete. */) 2214but this is considered obsolete.
2152 (Lisp_Object specified_time) 2215
2216The optional ZONE is omitted or nil for Emacs local time, t for
2217Universal Time, `wall' for system wall clock time, or a string as in
2218`set-time-zone-rule' for a time zone rule. */)
2219 (Lisp_Object specified_time, Lisp_Object zone)
2153{ 2220{
2154 time_t value = lisp_seconds_argument (specified_time); 2221 time_t value = lisp_seconds_argument (specified_time);
2222 timezone_t tz = tzlookup (zone, false);
2155 2223
2156 /* Convert to a string in ctime format, except without the trailing 2224 /* Convert to a string in ctime format, except without the trailing
2157 newline, and without the 4-digit year limit. Don't use asctime 2225 newline, and without the 4-digit year limit. Don't use asctime
2158 or ctime, as they might dump core if the year is outside the 2226 or ctime, as they might dump core if the year is outside the
2159 range -999 .. 9999. */ 2227 range -999 .. 9999. */
2160 struct tm tm; 2228 struct tm tm;
2161 if (! localtime_r (&value, &tm)) 2229 struct tm *tmp = emacs_localtime_rz (tz, &value, &tm);
2230 xtzfree (tz);
2231 if (! tmp)
2162 time_overflow (); 2232 time_overflow ();
2163 2233
2164 static char const wday_name[][4] = 2234 static char const wday_name[][4] =
@@ -2210,7 +2280,7 @@ tm_gmtoff (struct tm *a)
2210#endif 2280#endif
2211} 2281}
2212 2282
2213DEFUN ("current-time-zone", Fcurrent_time_zone, Scurrent_time_zone, 0, 1, 0, 2283DEFUN ("current-time-zone", Fcurrent_time_zone, Scurrent_time_zone, 0, 2, 0,
2214 doc: /* Return the offset and name for the local time zone. 2284 doc: /* Return the offset and name for the local time zone.
2215This returns a list of the form (OFFSET NAME). 2285This returns a list of the form (OFFSET NAME).
2216OFFSET is an integer number of seconds ahead of UTC (east of Greenwich). 2286OFFSET 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
2221(HIGH LOW . IGNORED). Thus, you can use times obtained from 2291(HIGH LOW . IGNORED). Thus, you can use times obtained from
2222`current-time' and from `file-attributes'. SPECIFIED-TIME can also 2292`current-time' and from `file-attributes'. SPECIFIED-TIME can also
2223have the form (HIGH . LOW), but this is considered obsolete. 2293have the form (HIGH . LOW), but this is considered obsolete.
2294Optional second arg ZONE is omitted or nil for the local time zone, or
2295a string as in `set-time-zone-rule'.
2224 2296
2225Some operating systems cannot provide all this information to Emacs; 2297Some operating systems cannot provide all this information to Emacs;
2226in this case, `current-time-zone' returns a list containing nil for 2298in this case, `current-time-zone' returns a list containing nil for
2227the data it can't find. */) 2299the data it can't find. */)
2228 (Lisp_Object specified_time) 2300 (Lisp_Object specified_time, Lisp_Object zone)
2229{ 2301{
2230 struct timespec value; 2302 struct timespec value;
2231 struct tm local_tm, gmt_tm; 2303 struct tm local_tm, gmt_tm;
@@ -2233,7 +2305,8 @@ the data it can't find. */)
2233 2305
2234 zone_offset = Qnil; 2306 zone_offset = Qnil;
2235 value = make_timespec (lisp_seconds_argument (specified_time), 0); 2307 value = make_timespec (lisp_seconds_argument (specified_time), 0);
2236 zone_name = format_time_string ("%Z", sizeof "%Z" - 1, value, 0, &local_tm); 2308 zone_name = format_time_string ("%Z", sizeof "%Z" - 1, value,
2309 zone, &local_tm);
2237 2310
2238 if (HAVE_TM_GMTOFF || gmtime_r (&value.tv_sec, &gmt_tm)) 2311 if (HAVE_TM_GMTOFF || gmtime_r (&value.tv_sec, &gmt_tm))
2239 { 2312 {
@@ -2259,42 +2332,48 @@ the data it can't find. */)
2259} 2332}
2260 2333
2261DEFUN ("set-time-zone-rule", Fset_time_zone_rule, Sset_time_zone_rule, 1, 1, 0, 2334DEFUN ("set-time-zone-rule", Fset_time_zone_rule, Sset_time_zone_rule, 1, 1, 0,
2262 doc: /* Set the local time zone using TZ, a string specifying a time zone rule. 2335 doc: /* Set the Emacs local time zone using TZ, a string specifying a time zone rule.
2263If TZ is nil, use implementation-defined default time zone information. 2336If TZ is nil or `wall', use system wall clock time. If TZ is t, use
2264If TZ is t, use Universal Time. If TZ is an integer, it is treated as in 2337Universal Time. If TZ is an integer, treat it as in `encode-time'.
2265`encode-time'. 2338
2266 2339Instead of calling this function, you typically want something else.
2267Instead of calling this function, you typically want (setenv "TZ" TZ). 2340To temporarily use a different time zone rule for just one invocation
2268That changes both the environment of the Emacs process and the 2341of `decode-time', `encode-time', or `format-time-string', pass the
2269variable `process-environment', whereas `set-time-zone-rule' affects 2342function a ZONE argument. To change local time consistently
2270only the former. */) 2343throughout Emacs, call (setenv "TZ" TZ): this changes both the
2344environment of the Emacs process and the variable
2345`process-environment', whereas `set-time-zone-rule' affects only the
2346former. */)
2271 (Lisp_Object tz) 2347 (Lisp_Object tz)
2272{ 2348{
2273 const char *tzstring = NILP (tz) ? initial_tz : SSDATA (decode_time_zone (tz)); 2349 tzlookup (NILP (tz) ? Qwall : tz, true);
2350 return Qnil;
2351}
2274 2352
2275 block_input (); 2353/* A buffer holding a string of the form "TZ=value", intended
2276 set_time_zone_rule (tzstring); 2354 to be part of the environment. If TZ is supposed to be unset,
2277 unblock_input (); 2355 the buffer string is "tZ=". */
2356 static char *tzvalbuf;
2278 2357
2279 return Qnil; 2358/* Get the local time zone rule. */
2359char *
2360emacs_getenv_TZ (void)
2361{
2362 return tzvalbuf[0] == 'T' ? tzvalbuf + tzeqlen : 0;
2280} 2363}
2281 2364
2282/* Set the local time zone rule to TZSTRING. 2365/* Set the local time zone rule to TZSTRING, which can be null to
2366 denote wall clock time. Do not record the setting in LOCAL_TZ.
2283 2367
2284 This function is not thread-safe, in theory because putenv is not, 2368 This function is not thread-safe, in theory because putenv is not,
2285 but mostly because of the static storage it updates. Other threads 2369 but mostly because of the static storage it updates. Other threads
2286 that invoke localtime etc. may be adversely affected while this 2370 that invoke localtime etc. may be adversely affected while this
2287 function is executing. */ 2371 function is executing. */
2288 2372
2289static void 2373int
2290set_time_zone_rule (const char *tzstring) 2374emacs_setenv_TZ (const char *tzstring)
2291{ 2375{
2292 /* A buffer holding a string of the form "TZ=value", intended
2293 to be part of the environment. */
2294 static char *tzvalbuf;
2295 static ptrdiff_t tzvalbufsize; 2376 static ptrdiff_t tzvalbufsize;
2296
2297 int tzeqlen = sizeof "TZ=" - 1;
2298 ptrdiff_t tzstringlen = tzstring ? strlen (tzstring) : 0; 2377 ptrdiff_t tzstringlen = tzstring ? strlen (tzstring) : 0;
2299 char *tzval = tzvalbuf; 2378 char *tzval = tzvalbuf;
2300 bool new_tzvalbuf = tzvalbufsize <= tzeqlen + tzstringlen; 2379 bool new_tzvalbuf = tzvalbufsize <= tzeqlen + tzstringlen;
@@ -2346,9 +2425,7 @@ set_time_zone_rule (const char *tzstring)
2346 xputenv (tzval); 2425 xputenv (tzval);
2347 } 2426 }
2348 2427
2349#ifdef HAVE_TZSET 2428 return 0;
2350 tzset ();
2351#endif
2352} 2429}
2353 2430
2354/* Insert NARGS Lisp objects in the array ARGS by calling INSERT_FUNC 2431/* Insert NARGS Lisp objects in the array ARGS by calling INSERT_FUNC
@@ -4943,6 +5020,7 @@ void
4943syms_of_editfns (void) 5020syms_of_editfns (void)
4944{ 5021{
4945 DEFSYM (Qbuffer_access_fontify_functions, "buffer-access-fontify-functions"); 5022 DEFSYM (Qbuffer_access_fontify_functions, "buffer-access-fontify-functions");
5023 DEFSYM (Qwall, "wall");
4946 5024
4947 DEFVAR_LISP ("inhibit-field-text-motion", Vinhibit_field_text_motion, 5025 DEFVAR_LISP ("inhibit-field-text-motion", Vinhibit_field_text_motion,
4948 doc: /* Non-nil means text motion commands don't notice fields. */); 5026 doc: /* Non-nil means text motion commands don't notice fields. */);
diff --git a/src/emacs.c b/src/emacs.c
index 93fb5870247..6e35496eb8a 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
1552 1552
1553 /* This calls putenv and so must precede init_process_emacs. Also, 1553 /* This calls putenv and so must precede init_process_emacs. Also,
1554 it sets Voperating_system_release, which init_process_emacs uses. */ 1554 it sets Voperating_system_release, which init_process_emacs uses. */
1555 init_editfns (); 1555 init_editfns (dumping);
1556 1556
1557 /* These two call putenv. */ 1557 /* These two call putenv. */
1558#ifdef HAVE_DBUS 1558#ifdef HAVE_DBUS
diff --git a/src/lisp.h b/src/lisp.h
index 341603f311f..02109d72174 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4055,7 +4055,7 @@ extern _Noreturn void time_overflow (void);
4055extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool); 4055extern Lisp_Object make_buffer_string (ptrdiff_t, ptrdiff_t, bool);
4056extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t, 4056extern Lisp_Object make_buffer_string_both (ptrdiff_t, ptrdiff_t, ptrdiff_t,
4057 ptrdiff_t, bool); 4057 ptrdiff_t, bool);
4058extern void init_editfns (void); 4058extern void init_editfns (bool);
4059extern void syms_of_editfns (void); 4059extern void syms_of_editfns (void);
4060 4060
4061/* Defined in buffer.c. */ 4061/* Defined in buffer.c. */
diff --git a/src/systime.h b/src/systime.h
index 744af17b640..abbe60114d5 100644
--- a/src/systime.h
+++ b/src/systime.h
@@ -106,20 +106,6 @@ extern struct timespec lisp_to_timespec (struct lisp_time);
106extern struct timespec lisp_time_argument (Lisp_Object); 106extern struct timespec lisp_time_argument (Lisp_Object);
107#endif 107#endif
108 108
109#ifndef HAVE_TZALLOC
110# undef mktime_z
111# undef timezone_t
112# undef tzalloc
113# undef tzfree
114# define mktime_z emacs_mktime_z
115# define timezone_t emacs_timezone_t
116# define tzalloc emacs_tzalloc
117# define tzfree emacs_tzfree
118typedef char const *timezone_t;
119INLINE timezone_t tzalloc (char const *name) { return name; }
120INLINE void tzfree (timezone_t tz) { }
121#endif
122
123INLINE_HEADER_END 109INLINE_HEADER_END
124 110
125#endif /* EMACS_SYSTIME_H */ 111#endif /* EMACS_SYSTIME_H */