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.
This commit is contained in:
parent
7c2c2196fd
commit
fdb1ba144c
7 changed files with 108 additions and 82 deletions
|
@ -1325,7 +1325,12 @@ 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
|
||||
@code{wall}, the conversion uses the system wall clock time. If it is
|
||||
a string, the conversion uses the time zone rule equivalent to setting
|
||||
@env{TZ} to that string.
|
||||
@env{TZ} to that string. If it is an integer @var{offset}, the
|
||||
conversion uses a fixed time zone with the given offset and a numeric
|
||||
abbreviation. If it is a list (@var{offset} @var{abbr}), where
|
||||
@var{offset} is an integer number of seconds east of Universal Time
|
||||
and @var{abbr} is a string, the conversion uses a fixed time zone with
|
||||
the given offset and abbreviation.
|
||||
|
||||
@defun current-time-zone &optional time zone
|
||||
@cindex time zone, current
|
||||
|
@ -1423,10 +1428,6 @@ yourself before you call @code{encode-time}.
|
|||
|
||||
The optional argument @var{zone} defaults to the current time zone rule.
|
||||
@xref{Time Zone Rules}.
|
||||
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
|
||||
|
|
8
etc/NEWS
8
etc/NEWS
|
@ -228,6 +228,14 @@ two objects are 'eq' ('eql'), then the result of 'sxhash-eq'
|
|||
consistency with the new functions. For compatibility, 'sxhash'
|
||||
remains as an alias to 'sxhash-equal'.
|
||||
|
||||
+++
|
||||
** Time conversion functions that accept a time zone rule argument now
|
||||
allow it to be OFFSET or a list (OFFSET ABBR), where the integer
|
||||
OFFSET is a count of seconds east of Universal Time, and the string
|
||||
ABBR is a time zone abbreviation. The affected functions are
|
||||
'current-time-string', 'current-time-zone', 'decode-time',
|
||||
'format-time-string', and 'set-time-zone-rule'.
|
||||
|
||||
|
||||
* Changes in Emacs 25.2 on Non-Free Operating Systems
|
||||
|
||||
|
|
|
@ -256,37 +256,8 @@ If mode is nil, use `major-mode' of the current buffer."
|
|||
(string-match "^\\(.+\\)-mode$" mode)
|
||||
(match-string 1 mode))))))
|
||||
|
||||
(defun gmm-format-time-string (format-string &optional time tz)
|
||||
"Use FORMAT-STRING to format the time TIME, or now if omitted.
|
||||
The optional TZ specifies the time zone in a number of seconds; any
|
||||
other non-nil value will be treated as 0. Note that both the format
|
||||
specifiers `%Z' and `%z' will be replaced with a numeric form. "
|
||||
;; FIXME: is there a smart way to replace %Z with a time zone name?
|
||||
(if (and (numberp tz) (not (zerop tz)))
|
||||
(let ((st 0)
|
||||
(case-fold-search t)
|
||||
ls nd rest)
|
||||
(setq time (if time
|
||||
(copy-sequence time)
|
||||
(current-time)))
|
||||
(if (>= (setq ls (- (cadr time) (car (current-time-zone)) (- tz))) 0)
|
||||
(setcar (cdr time) ls)
|
||||
(setcar (cdr time) (+ ls 65536))
|
||||
(setcar time (1- (car time))))
|
||||
(setq tz (format "%s%02d%02d"
|
||||
(if (>= tz 0) "+" "-")
|
||||
(/ (abs tz) 3600)
|
||||
(/ (% (abs tz) 3600) 60)))
|
||||
(while (string-match "%+z" format-string st)
|
||||
(if (zerop (% (- (setq nd (match-end 0)) (match-beginning 0)) 2))
|
||||
(progn
|
||||
(push (substring format-string st (- nd 2)) rest)
|
||||
(push tz rest))
|
||||
(push (substring format-string st nd) rest))
|
||||
(setq st nd))
|
||||
(push (substring format-string st) rest)
|
||||
(format-time-string (apply 'concat (nreverse rest)) time))
|
||||
(format-time-string format-string time t)))
|
||||
(define-obsolete-function-alias 'gmm-format-time-string 'format-time-string
|
||||
"25.2")
|
||||
|
||||
(provide 'gmm-utils)
|
||||
|
||||
|
|
|
@ -3879,8 +3879,13 @@ This function uses `mail-citation-hook' if that is non-nil."
|
|||
(defun message-insert-formatted-citation-line (&optional from date tz)
|
||||
"Function that inserts a formatted citation line.
|
||||
The optional FROM, and DATE are strings containing the contents of
|
||||
the From header and the Date header respectively. The optional TZ
|
||||
is a number of seconds, overrides the time zone of DATE.
|
||||
the From header and the Date header respectively.
|
||||
|
||||
The optional TZ is omitted or nil for Emacs local time, t for
|
||||
Universal Time, `wall' for system wall clock time, or a string as
|
||||
in the TZ environment variable. 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.
|
||||
|
||||
See `message-citation-line-format'."
|
||||
;; The optional args are for testing/debugging. They will disappear later.
|
||||
|
|
|
@ -22673,8 +22673,10 @@ When optional argument END is non-nil, use end of date-range or
|
|||
time-range, if possible.
|
||||
|
||||
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
|
||||
the TZ environment variable."
|
||||
Universal Time, `wall' for system wall clock time, or a string as
|
||||
in the TZ environment variable. 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."
|
||||
(format-time-string
|
||||
format
|
||||
(apply 'encode-time
|
||||
|
|
|
@ -590,25 +590,14 @@ If a string, interpret as the ZONE argument of `format-time-string'.")
|
|||
(lambda (x) (or (booleanp x) (stringp x))))
|
||||
|
||||
(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))
|
||||
(min (/ sec 60))
|
||||
(mm (% min 60))
|
||||
(hh (/ min 60)))
|
||||
(format (cond ((not (zerop ss)) "%c%02d:%02d:%02d")
|
||||
((not (zerop mm)) "%c%02d:%02d")
|
||||
(t "%c%02d"))
|
||||
sign hh mm ss)))
|
||||
(declare (obsolete nil "25.2"))
|
||||
(format-time-string "%:::z" time zone))
|
||||
|
||||
(defvar add-log-iso8601-with-time-zone nil)
|
||||
|
||||
(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 date " " (add-log-iso8601-time-zone time zone))
|
||||
date)))
|
||||
(format-time-string
|
||||
(if add-log-iso8601-with-time-zone "%Y-%m-%d %:::z" "%Y-%m-%d") time zone))
|
||||
|
||||
(defun change-log-name ()
|
||||
"Return (system-dependent) default name for a change log file."
|
||||
|
|
104
src/editfns.c
104
src/editfns.c
|
@ -146,8 +146,6 @@ xtzfree (timezone_t tz)
|
|||
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;
|
||||
|
||||
|
@ -160,16 +158,53 @@ tzlookup (Lisp_Object zone, bool settz)
|
|||
}
|
||||
else
|
||||
{
|
||||
static char const tzbuf_format[] = "<%+.*"pI"d>%s%"pI"d:%02d:%02d";
|
||||
char const *trailing_tzbuf_format = tzbuf_format + sizeof "<%+.*"pI"d" - 1;
|
||||
char tzbuf[sizeof tzbuf_format + 2 * INT_STRLEN_BOUND (EMACS_INT)];
|
||||
bool plain_integer = INTEGERP (zone);
|
||||
|
||||
if (EQ (zone, Qwall))
|
||||
zone_string = 0;
|
||||
else if (STRINGP (zone))
|
||||
zone_string = SSDATA (zone);
|
||||
else if (INTEGERP (zone))
|
||||
zone_string = SSDATA (ENCODE_SYSTEM (zone));
|
||||
else if (plain_integer || (CONSP (zone) && INTEGERP (XCAR (zone))
|
||||
&& CONSP (XCDR (zone))))
|
||||
{
|
||||
Lisp_Object abbr;
|
||||
if (!plain_integer)
|
||||
{
|
||||
abbr = XCAR (XCDR (zone));
|
||||
zone = XCAR (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;
|
||||
int hour_remainder = abszone % (60 * 60);
|
||||
int min = hour_remainder / 60, sec = hour_remainder % 60;
|
||||
|
||||
if (plain_integer)
|
||||
{
|
||||
int prec = 2;
|
||||
EMACS_INT numzone = hour;
|
||||
if (hour_remainder != 0)
|
||||
{
|
||||
prec += 2, numzone = 100 * numzone + min;
|
||||
if (sec != 0)
|
||||
prec += 2, numzone = 100 * numzone + sec;
|
||||
}
|
||||
sprintf (tzbuf, tzbuf_format, prec, numzone,
|
||||
&"-"[XINT (zone) < 0], hour, min, sec);
|
||||
zone_string = tzbuf;
|
||||
}
|
||||
else
|
||||
{
|
||||
AUTO_STRING (leading, "<");
|
||||
AUTO_STRING_WITH_LEN (trailing, tzbuf,
|
||||
sprintf (tzbuf, trailing_tzbuf_format,
|
||||
&"-"[XINT (zone) < 0],
|
||||
hour, min, sec));
|
||||
zone_string = SSDATA (concat3 (leading, ENCODE_SYSTEM (abbr),
|
||||
trailing));
|
||||
}
|
||||
}
|
||||
else
|
||||
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,
|
|||
doc: /* Use FORMAT-STRING to format the time TIME, or now if omitted.
|
||||
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 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 the TZ environment variable.
|
||||
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
|
||||
the TZ environment variable. 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.
|
||||
|
||||
The value is a copy of FORMAT-STRING, but with certain constructs replaced
|
||||
by text that describes the specified date and time in TIME:
|
||||
|
@ -2085,9 +2124,12 @@ DEFUN ("decode-time", Fdecode_time, Sdecode_time, 0, 2, 0,
|
|||
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
|
||||
the TZ environment variable.
|
||||
the TZ environment variable. 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.
|
||||
|
||||
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
|
||||
|
@ -2150,6 +2192,7 @@ check_tm_member (Lisp_Object obj, int offset)
|
|||
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.
|
||||
|
||||
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
|
||||
the 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) */)
|
|||
tm.tm_year = check_tm_member (args[5], TM_YEAR_BASE);
|
||||
tm.tm_isdst = -1;
|
||||
|
||||
if (CONSP (zone))
|
||||
zone = XCAR (zone);
|
||||
timezone_t tz = tzlookup (zone, false);
|
||||
value = emacs_mktime_z (tz, &tm);
|
||||
xtzfree (tz);
|
||||
|
@ -2214,7 +2255,9 @@ 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
|
||||
the TZ environment variable. */)
|
||||
the TZ environment variable. 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. */)
|
||||
(Lisp_Object specified_time, Lisp_Object zone)
|
||||
{
|
||||
time_t value = lisp_seconds_argument (specified_time);
|
||||
|
@ -2290,8 +2333,12 @@ 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 the TZ environment variable.
|
||||
|
||||
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
|
||||
the TZ environment variable. 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.
|
||||
|
||||
Some operating systems cannot provide all this information to Emacs;
|
||||
in this case, `current-time-zone' returns a list containing nil for
|
||||
|
@ -2315,15 +2362,18 @@ the data it can't find. */)
|
|||
zone_offset = make_number (offset);
|
||||
if (SCHARS (zone_name) == 0)
|
||||
{
|
||||
/* No local time zone name is available; use "+-NNNN" instead. */
|
||||
long int m = offset / 60;
|
||||
long int am = offset < 0 ? - m : m;
|
||||
long int hour = am / 60;
|
||||
int min = am % 60;
|
||||
char buf[sizeof "+00" + INT_STRLEN_BOUND (long int)];
|
||||
zone_name = make_formatted_string (buf, "%c%02ld%02d",
|
||||
/* No local time zone name is available; use numeric zone instead. */
|
||||
long int hour = offset / 3600;
|
||||
int min_sec = offset % 3600;
|
||||
int amin_sec = min_sec < 0 ? - min_sec : min_sec;
|
||||
int min = amin_sec / 60;
|
||||
int sec = amin_sec % 60;
|
||||
int min_prec = min_sec ? 2 : 0;
|
||||
int sec_prec = sec ? 2 : 0;
|
||||
char buf[sizeof "+0000" + INT_STRLEN_BOUND (long int)];
|
||||
zone_name = make_formatted_string (buf, "%c%.2ld%.*d%.*d",
|
||||
(offset < 0 ? '-' : '+'),
|
||||
hour, min);
|
||||
hour, min_prec, min, sec_prec, sec);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2332,11 +2382,11 @@ 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 Emacs local time zone using TZ, a string specifying a time zone rule.
|
||||
|
||||
If TZ is nil or `wall', use system wall clock time; this differs from
|
||||
the usual Emacs convention where nil means current local time. If TZ
|
||||
is t, use Universal Time. If TZ is an integer, treat it as in
|
||||
`encode-time'.
|
||||
is t, use Universal Time. If TZ is a list (as from
|
||||
`current-time-zone') or an integer (as from `decode-time'), use the
|
||||
specified time zone without consideration for daylight saving time.
|
||||
|
||||
Instead of calling this function, you typically want something else.
|
||||
To temporarily use a different time zone rule for just one invocation
|
||||
|
|
Loading…
Add table
Reference in a new issue