aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2016-04-12 09:19:11 -0700
committerPaul Eggert2016-04-12 09:19:38 -0700
commitfdb1ba144ca61185e6457f092f38f59dd9bbe6a0 (patch)
tree5048d1fbb946faf0b6059e4d4375bb514b3bfa74
parent7c2c2196fd4be0b656bdf0e0b68f3d7c4a5eca08 (diff)
downloademacs-fdb1ba144ca61185e6457f092f38f59dd9bbe6a0.tar.gz
emacs-fdb1ba144ca61185e6457f092f38f59dd9bbe6a0.zip
Support OFFSET and (OFFSET ABBR) time zone rules
This simplifies Gnus and VC time zone support, by letting them feed the output of ‘current-time-zone’ and ‘decode time’ to primitives that accept time zone arguments. * doc/lispref/os.texi (Time Zone Rules, Time Conversion): * etc/NEWS: * lisp/gnus/message.el (message-insert-formatted-citation-line): * lisp/org/org.el (org-timestamp-format): * src/editfns.c (Fformat_time_string, Fdecode_time): (Fcurrent_time_string, Fcurrent_time_zone, Fset_time_zone_rule): Document new behavior. * lisp/gnus/gmm-utils.el (gmm-format-time-string): * lisp/vc/add-log.el (add-log-iso8601-time-zone): Mark as obsolete, as it is now just an alias or narrow wrapper around format-time-string. * src/editfns.c (tzlookup): Also support integer OFFSET and list (OFFSET ABBR) as time zone rules. (Fencode_time): No longer need a special case for a cons ZONE. (Fcurrent_time_zone): If the time zone string is missing, compute it the same way the other new code does.
-rw-r--r--doc/lispref/os.texi11
-rw-r--r--etc/NEWS8
-rw-r--r--lisp/gnus/gmm-utils.el33
-rw-r--r--lisp/gnus/message.el9
-rw-r--r--lisp/org/org.el6
-rw-r--r--lisp/vc/add-log.el19
-rw-r--r--src/editfns.c104
7 files changed, 108 insertions, 82 deletions
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index 6e0edec2943..becb691581b 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -1325,7 +1325,12 @@ omitted or @code{nil}, the conversion uses Emacs's default time zone.
1325If it is @code{t}, the conversion uses Universal Time. If it is 1325If it is @code{t}, the conversion uses Universal Time. If it is
1326@code{wall}, the conversion uses the system wall clock time. If it is 1326@code{wall}, the conversion uses the system wall clock time. If it is
1327a string, the conversion uses the time zone rule equivalent to setting 1327a string, the conversion uses the time zone rule equivalent to setting
1328@env{TZ} to that string. 1328@env{TZ} to that string. If it is an integer @var{offset}, the
1329conversion uses a fixed time zone with the given offset and a numeric
1330abbreviation. If it is a list (@var{offset} @var{abbr}), where
1331@var{offset} is an integer number of seconds east of Universal Time
1332and @var{abbr} is a string, the conversion uses a fixed time zone with
1333the given offset and abbreviation.
1329 1334
1330@defun current-time-zone &optional time zone 1335@defun current-time-zone &optional time zone
1331@cindex time zone, current 1336@cindex time zone, current
@@ -1423,10 +1428,6 @@ yourself before you call @code{encode-time}.
1423 1428
1424The optional argument @var{zone} defaults to the current time zone rule. 1429The optional argument @var{zone} defaults to the current time zone rule.
1425@xref{Time Zone Rules}. 1430@xref{Time Zone Rules}.
1426In addition to the usual time zone rule values, it can also be a list
1427(as you would get from @code{current-time-zone}) or an integer (as
1428from @code{decode-time}), applied without any further alteration for
1429daylight saving time.
1430 1431
1431If you pass more than seven arguments to @code{encode-time}, the first 1432If you pass more than seven arguments to @code{encode-time}, the first
1432six are used as @var{seconds} through @var{year}, the last argument is 1433six are used as @var{seconds} through @var{year}, the last argument is
diff --git a/etc/NEWS b/etc/NEWS
index 00f5aadd856..5ebff6267fe 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -228,6 +228,14 @@ two objects are 'eq' ('eql'), then the result of 'sxhash-eq'
228consistency with the new functions. For compatibility, 'sxhash' 228consistency with the new functions. For compatibility, 'sxhash'
229remains as an alias to 'sxhash-equal'. 229remains as an alias to 'sxhash-equal'.
230 230
231+++
232** Time conversion functions that accept a time zone rule argument now
233allow it to be OFFSET or a list (OFFSET ABBR), where the integer
234OFFSET is a count of seconds east of Universal Time, and the string
235ABBR is a time zone abbreviation. The affected functions are
236'current-time-string', 'current-time-zone', 'decode-time',
237'format-time-string', and 'set-time-zone-rule'.
238
231 239
232* Changes in Emacs 25.2 on Non-Free Operating Systems 240* Changes in Emacs 25.2 on Non-Free Operating Systems
233 241
diff --git a/lisp/gnus/gmm-utils.el b/lisp/gnus/gmm-utils.el
index f6455cf9f1a..7aa52794e4c 100644
--- a/lisp/gnus/gmm-utils.el
+++ b/lisp/gnus/gmm-utils.el
@@ -256,37 +256,8 @@ If mode is nil, use `major-mode' of the current buffer."
256 (string-match "^\\(.+\\)-mode$" mode) 256 (string-match "^\\(.+\\)-mode$" mode)
257 (match-string 1 mode)))))) 257 (match-string 1 mode))))))
258 258
259(defun gmm-format-time-string (format-string &optional time tz) 259(define-obsolete-function-alias 'gmm-format-time-string 'format-time-string
260 "Use FORMAT-STRING to format the time TIME, or now if omitted. 260 "25.2")
261The optional TZ specifies the time zone in a number of seconds; any
262other non-nil value will be treated as 0. Note that both the format
263specifiers `%Z' and `%z' will be replaced with a numeric form. "
264;; FIXME: is there a smart way to replace %Z with a time zone name?
265 (if (and (numberp tz) (not (zerop tz)))
266 (let ((st 0)
267 (case-fold-search t)
268 ls nd rest)
269 (setq time (if time
270 (copy-sequence time)
271 (current-time)))
272 (if (>= (setq ls (- (cadr time) (car (current-time-zone)) (- tz))) 0)
273 (setcar (cdr time) ls)
274 (setcar (cdr time) (+ ls 65536))
275 (setcar time (1- (car time))))
276 (setq tz (format "%s%02d%02d"
277 (if (>= tz 0) "+" "-")
278 (/ (abs tz) 3600)
279 (/ (% (abs tz) 3600) 60)))
280 (while (string-match "%+z" format-string st)
281 (if (zerop (% (- (setq nd (match-end 0)) (match-beginning 0)) 2))
282 (progn
283 (push (substring format-string st (- nd 2)) rest)
284 (push tz rest))
285 (push (substring format-string st nd) rest))
286 (setq st nd))
287 (push (substring format-string st) rest)
288 (format-time-string (apply 'concat (nreverse rest)) time))
289 (format-time-string format-string time t)))
290 261
291(provide 'gmm-utils) 262(provide 'gmm-utils)
292 263
diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el
index 14d8d30f8af..32d740b0190 100644
--- a/lisp/gnus/message.el
+++ b/lisp/gnus/message.el
@@ -3879,8 +3879,13 @@ This function uses `mail-citation-hook' if that is non-nil."
3879(defun message-insert-formatted-citation-line (&optional from date tz) 3879(defun message-insert-formatted-citation-line (&optional from date tz)
3880 "Function that inserts a formatted citation line. 3880 "Function that inserts a formatted citation line.
3881The optional FROM, and DATE are strings containing the contents of 3881The optional FROM, and DATE are strings containing the contents of
3882the From header and the Date header respectively. The optional TZ 3882the From header and the Date header respectively.
3883is a number of seconds, overrides the time zone of DATE. 3883
3884The optional TZ is omitted or nil for Emacs local time, t for
3885Universal Time, `wall' for system wall clock time, or a string as
3886in the TZ environment variable. It can also be a list (as from
3887`current-time-zone') or an integer (as from `decode-time')
3888applied without consideration for daylight saving time.
3884 3889
3885See `message-citation-line-format'." 3890See `message-citation-line-format'."
3886 ;; The optional args are for testing/debugging. They will disappear later. 3891 ;; The optional args are for testing/debugging. They will disappear later.
diff --git a/lisp/org/org.el b/lisp/org/org.el
index 231daa9a6a7..3abf62704bb 100644
--- a/lisp/org/org.el
+++ b/lisp/org/org.el
@@ -22673,8 +22673,10 @@ When optional argument END is non-nil, use end of date-range or
22673time-range, if possible. 22673time-range, if possible.
22674 22674
22675The optional ZONE is omitted or nil for Emacs local time, t for 22675The optional ZONE is omitted or nil for Emacs local time, t for
22676Universal Time, `wall' for system wall clock time, or a string as in 22676Universal Time, `wall' for system wall clock time, or a string as
22677the TZ environment variable." 22677in the TZ environment variable. It can also be a list (as from
22678`current-time-zone') or an integer (as from `decode-time')
22679applied without consideration for daylight saving time."
22678 (format-time-string 22680 (format-time-string
22679 format 22681 format
22680 (apply 'encode-time 22682 (apply 'encode-time
diff --git a/lisp/vc/add-log.el b/lisp/vc/add-log.el
index 58a4e77a602..9076d834c7c 100644
--- a/lisp/vc/add-log.el
+++ b/lisp/vc/add-log.el
@@ -590,25 +590,14 @@ If a string, interpret as the ZONE argument of `format-time-string'.")
590 (lambda (x) (or (booleanp x) (stringp x)))) 590 (lambda (x) (or (booleanp x) (stringp x))))
591 591
592(defun add-log-iso8601-time-zone (&optional time zone) 592(defun add-log-iso8601-time-zone (&optional time zone)
593 (let* ((utc-offset (or (car (current-time-zone time zone)) 0)) 593 (declare (obsolete nil "25.2"))
594 (sign (if (< utc-offset 0) ?- ?+)) 594 (format-time-string "%:::z" time zone))
595 (sec (abs utc-offset))
596 (ss (% sec 60))
597 (min (/ sec 60))
598 (mm (% min 60))
599 (hh (/ min 60)))
600 (format (cond ((not (zerop ss)) "%c%02d:%02d:%02d")
601 ((not (zerop mm)) "%c%02d:%02d")
602 (t "%c%02d"))
603 sign hh mm ss)))
604 595
605(defvar add-log-iso8601-with-time-zone nil) 596(defvar add-log-iso8601-with-time-zone nil)
606 597
607(defun add-log-iso8601-time-string (&optional time zone) 598(defun add-log-iso8601-time-string (&optional time zone)
608 (let ((date (format-time-string "%Y-%m-%d" time zone))) 599 (format-time-string
609 (if add-log-iso8601-with-time-zone 600 (if add-log-iso8601-with-time-zone "%Y-%m-%d %:::z" "%Y-%m-%d") time zone))
610 (concat date " " (add-log-iso8601-time-zone time zone))
611 date)))
612 601
613(defun change-log-name () 602(defun change-log-name ()
614 "Return (system-dependent) default name for a change log file." 603 "Return (system-dependent) default name for a change log file."
diff --git a/src/editfns.c b/src/editfns.c
index 70285e6d5db..48f2a8de126 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -146,8 +146,6 @@ xtzfree (timezone_t tz)
146static timezone_t 146static timezone_t
147tzlookup (Lisp_Object zone, bool settz) 147tzlookup (Lisp_Object zone, bool settz)
148{ 148{
149 static char const tzbuf_format[] = "XXX%s%"pI"d:%02d:%02d";
150 char tzbuf[sizeof tzbuf_format + INT_STRLEN_BOUND (EMACS_INT)];
151 char const *zone_string; 149 char const *zone_string;
152 timezone_t new_tz; 150 timezone_t new_tz;
153 151
@@ -160,16 +158,53 @@ tzlookup (Lisp_Object zone, bool settz)
160 } 158 }
161 else 159 else
162 { 160 {
161 static char const tzbuf_format[] = "<%+.*"pI"d>%s%"pI"d:%02d:%02d";
162 char const *trailing_tzbuf_format = tzbuf_format + sizeof "<%+.*"pI"d" - 1;
163 char tzbuf[sizeof tzbuf_format + 2 * INT_STRLEN_BOUND (EMACS_INT)];
164 bool plain_integer = INTEGERP (zone);
165
163 if (EQ (zone, Qwall)) 166 if (EQ (zone, Qwall))
164 zone_string = 0; 167 zone_string = 0;
165 else if (STRINGP (zone)) 168 else if (STRINGP (zone))
166 zone_string = SSDATA (zone); 169 zone_string = SSDATA (ENCODE_SYSTEM (zone));
167 else if (INTEGERP (zone)) 170 else if (plain_integer || (CONSP (zone) && INTEGERP (XCAR (zone))
171 && CONSP (XCDR (zone))))
168 { 172 {
173 Lisp_Object abbr;
174 if (!plain_integer)
175 {
176 abbr = XCAR (XCDR (zone));
177 zone = XCAR (zone);
178 }
179
169 EMACS_INT abszone = eabs (XINT (zone)), hour = abszone / (60 * 60); 180 EMACS_INT abszone = eabs (XINT (zone)), hour = abszone / (60 * 60);
170 int min = (abszone / 60) % 60, sec = abszone % 60; 181 int hour_remainder = abszone % (60 * 60);
171 sprintf (tzbuf, tzbuf_format, &"-"[XINT (zone) < 0], hour, min, sec); 182 int min = hour_remainder / 60, sec = hour_remainder % 60;
172 zone_string = tzbuf; 183
184 if (plain_integer)
185 {
186 int prec = 2;
187 EMACS_INT numzone = hour;
188 if (hour_remainder != 0)
189 {
190 prec += 2, numzone = 100 * numzone + min;
191 if (sec != 0)
192 prec += 2, numzone = 100 * numzone + sec;
193 }
194 sprintf (tzbuf, tzbuf_format, prec, numzone,
195 &"-"[XINT (zone) < 0], hour, min, sec);
196 zone_string = tzbuf;
197 }
198 else
199 {
200 AUTO_STRING (leading, "<");
201 AUTO_STRING_WITH_LEN (trailing, tzbuf,
202 sprintf (tzbuf, trailing_tzbuf_format,
203 &"-"[XINT (zone) < 0],
204 hour, min, sec));
205 zone_string = SSDATA (concat3 (leading, ENCODE_SYSTEM (abbr),
206 trailing));
207 }
173 } 208 }
174 else 209 else
175 xsignal2 (Qerror, build_string ("Invalid time zone specification"), 210 xsignal2 (Qerror, build_string ("Invalid time zone specification"),
@@ -1969,9 +2004,13 @@ DEFUN ("format-time-string", Fformat_time_string, Sformat_time_string, 1, 3, 0,
1969 doc: /* Use FORMAT-STRING to format the time TIME, or now if omitted. 2004 doc: /* Use FORMAT-STRING to format the time TIME, or now if omitted.
1970TIME is specified as (HIGH LOW USEC PSEC), as returned by 2005TIME is specified as (HIGH LOW USEC PSEC), as returned by
1971`current-time' or `file-attributes'. The obsolete form (HIGH . LOW) 2006`current-time' or `file-attributes'. The obsolete form (HIGH . LOW)
1972is also still accepted. The optional ZONE is omitted or nil for Emacs 2007is also still accepted.
1973local time, t for Universal Time, `wall' for system wall clock time, 2008
1974or a string as in the TZ environment variable. 2009The optional ZONE is omitted or nil for Emacs local time, t for
2010Universal Time, `wall' for system wall clock time, or a string as in
2011the TZ environment variable. It can also be a list (as from
2012`current-time-zone') or an integer (as from `decode-time') applied
2013without consideration for daylight saving time.
1975 2014
1976The value is a copy of FORMAT-STRING, but with certain constructs replaced 2015The value is a copy of FORMAT-STRING, but with certain constructs replaced
1977by text that describes the specified date and time in TIME: 2016by text that describes the specified date and time in TIME:
@@ -2085,9 +2124,12 @@ DEFUN ("decode-time", Fdecode_time, Sdecode_time, 0, 2, 0,
2085The optional SPECIFIED-TIME should be a list of (HIGH LOW . IGNORED), 2124The optional SPECIFIED-TIME should be a list of (HIGH LOW . IGNORED),
2086as from `current-time' and `file-attributes', or nil to use the 2125as from `current-time' and `file-attributes', or nil to use the
2087current time. The obsolete form (HIGH . LOW) is also still accepted. 2126current time. The obsolete form (HIGH . LOW) is also still accepted.
2127
2088The optional ZONE is omitted or nil for Emacs local time, t for 2128The optional ZONE is omitted or nil for Emacs local time, t for
2089Universal Time, `wall' for system wall clock time, or a string as in 2129Universal Time, `wall' for system wall clock time, or a string as in
2090the TZ environment variable. 2130the TZ environment variable. It can also be a list (as from
2131`current-time-zone') or an integer (as from `decode-time') applied
2132without consideration for daylight saving time.
2091 2133
2092The list has the following nine members: SEC is an integer between 0 2134The list has the following nine members: SEC is an integer between 0
2093and 60; SEC is 60 for a leap second, which only some operating systems 2135and 60; SEC is 60 for a leap second, which only some operating systems
@@ -2150,6 +2192,7 @@ check_tm_member (Lisp_Object obj, int offset)
2150DEFUN ("encode-time", Fencode_time, Sencode_time, 6, MANY, 0, 2192DEFUN ("encode-time", Fencode_time, Sencode_time, 6, MANY, 0,
2151 doc: /* Convert SECOND, MINUTE, HOUR, DAY, MONTH, YEAR and ZONE to internal time. 2193 doc: /* Convert SECOND, MINUTE, HOUR, DAY, MONTH, YEAR and ZONE to internal time.
2152This is the reverse operation of `decode-time', which see. 2194This is the reverse operation of `decode-time', which see.
2195
2153The optional ZONE is omitted or nil for Emacs local time, t for 2196The optional ZONE is omitted or nil for Emacs local time, t for
2154Universal Time, `wall' for system wall clock time, or a string as in 2197Universal Time, `wall' for system wall clock time, or a string as in
2155the TZ environment variable. It can also be a list (as from 2198the TZ environment variable. It can also be a list (as from
@@ -2184,8 +2227,6 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */)
2184 tm.tm_year = check_tm_member (args[5], TM_YEAR_BASE); 2227 tm.tm_year = check_tm_member (args[5], TM_YEAR_BASE);
2185 tm.tm_isdst = -1; 2228 tm.tm_isdst = -1;
2186 2229
2187 if (CONSP (zone))
2188 zone = XCAR (zone);
2189 timezone_t tz = tzlookup (zone, false); 2230 timezone_t tz = tzlookup (zone, false);
2190 value = emacs_mktime_z (tz, &tm); 2231 value = emacs_mktime_z (tz, &tm);
2191 xtzfree (tz); 2232 xtzfree (tz);
@@ -2214,7 +2255,9 @@ but this is considered obsolete.
2214 2255
2215The optional ZONE is omitted or nil for Emacs local time, t for 2256The optional ZONE is omitted or nil for Emacs local time, t for
2216Universal Time, `wall' for system wall clock time, or a string as in 2257Universal Time, `wall' for system wall clock time, or a string as in
2217the TZ environment variable. */) 2258the TZ environment variable. It can also be a list (as from
2259`current-time-zone') or an integer (as from `decode-time') applied
2260without consideration for daylight saving time. */)
2218 (Lisp_Object specified_time, Lisp_Object zone) 2261 (Lisp_Object specified_time, Lisp_Object zone)
2219{ 2262{
2220 time_t value = lisp_seconds_argument (specified_time); 2263 time_t value = lisp_seconds_argument (specified_time);
@@ -2290,8 +2333,12 @@ instead of using the current time. The argument should have the form
2290\(HIGH LOW . IGNORED). Thus, you can use times obtained from 2333\(HIGH LOW . IGNORED). Thus, you can use times obtained from
2291`current-time' and from `file-attributes'. SPECIFIED-TIME can also 2334`current-time' and from `file-attributes'. SPECIFIED-TIME can also
2292have the form (HIGH . LOW), but this is considered obsolete. 2335have the form (HIGH . LOW), but this is considered obsolete.
2293Optional second arg ZONE is omitted or nil for the local time zone, or 2336
2294a string as in the TZ environment variable. 2337The optional ZONE is omitted or nil for Emacs local time, t for
2338Universal Time, `wall' for system wall clock time, or a string as in
2339the TZ environment variable. It can also be a list (as from
2340`current-time-zone') or an integer (as from `decode-time') applied
2341without consideration for daylight saving time.
2295 2342
2296Some operating systems cannot provide all this information to Emacs; 2343Some operating systems cannot provide all this information to Emacs;
2297in this case, `current-time-zone' returns a list containing nil for 2344in this case, `current-time-zone' returns a list containing nil for
@@ -2315,15 +2362,18 @@ the data it can't find. */)
2315 zone_offset = make_number (offset); 2362 zone_offset = make_number (offset);
2316 if (SCHARS (zone_name) == 0) 2363 if (SCHARS (zone_name) == 0)
2317 { 2364 {
2318 /* No local time zone name is available; use "+-NNNN" instead. */ 2365 /* No local time zone name is available; use numeric zone instead. */
2319 long int m = offset / 60; 2366 long int hour = offset / 3600;
2320 long int am = offset < 0 ? - m : m; 2367 int min_sec = offset % 3600;
2321 long int hour = am / 60; 2368 int amin_sec = min_sec < 0 ? - min_sec : min_sec;
2322 int min = am % 60; 2369 int min = amin_sec / 60;
2323 char buf[sizeof "+00" + INT_STRLEN_BOUND (long int)]; 2370 int sec = amin_sec % 60;
2324 zone_name = make_formatted_string (buf, "%c%02ld%02d", 2371 int min_prec = min_sec ? 2 : 0;
2372 int sec_prec = sec ? 2 : 0;
2373 char buf[sizeof "+0000" + INT_STRLEN_BOUND (long int)];
2374 zone_name = make_formatted_string (buf, "%c%.2ld%.*d%.*d",
2325 (offset < 0 ? '-' : '+'), 2375 (offset < 0 ? '-' : '+'),
2326 hour, min); 2376 hour, min_prec, min, sec_prec, sec);
2327 } 2377 }
2328 } 2378 }
2329 2379
@@ -2332,11 +2382,11 @@ the data it can't find. */)
2332 2382
2333DEFUN ("set-time-zone-rule", Fset_time_zone_rule, Sset_time_zone_rule, 1, 1, 0, 2383DEFUN ("set-time-zone-rule", Fset_time_zone_rule, Sset_time_zone_rule, 1, 1, 0,
2334 doc: /* Set the Emacs local time zone using TZ, a string specifying a time zone rule. 2384 doc: /* Set the Emacs local time zone using TZ, a string specifying a time zone rule.
2335
2336If TZ is nil or `wall', use system wall clock time; this differs from 2385If TZ is nil or `wall', use system wall clock time; this differs from
2337the usual Emacs convention where nil means current local time. If TZ 2386the usual Emacs convention where nil means current local time. If TZ
2338is t, use Universal Time. If TZ is an integer, treat it as in 2387is t, use Universal Time. If TZ is a list (as from
2339`encode-time'. 2388`current-time-zone') or an integer (as from `decode-time'), use the
2389specified time zone without consideration for daylight saving time.
2340 2390
2341Instead of calling this function, you typically want something else. 2391Instead of calling this function, you typically want something else.
2342To temporarily use a different time zone rule for just one invocation 2392To temporarily use a different time zone rule for just one invocation