Commit 37257d6a authored by Paul Eggert's avatar Paul Eggert

More-compatible subsecond calendrical timestamps

Instead of appending a subseconds member to the result of
‘decode-time’, this keeps the format unchanged unless you give
a new optional argument to ‘decode-time’.  Also, the augmented
format now puts the subsecond info in the SECONDS element, so
the total number of elements is unchanged; this is more
compatible with code that expects the traditional 9 elements,
such as ‘(pcase decoded-time (`(,SEC ,MIN ,HOUR ,DAY ,MON
,YEAR ,DOW ,DST ,ZONE) ...) ...)’.
* doc/lispref/os.texi, doc/misc/emacs-mime.texi, etc/NEWS:
* lisp/net/soap-client.el (soap-decode-date-time):
* lisp/simple.el (decoded-time):
Document the new behavior.
* lisp/calendar/icalendar.el (icalendar--decode-isodatetime):
* lisp/calendar/iso8601.el (iso8601-parse)
(iso8601-parse-time, iso8601-parse-duration)
(iso8601--decoded-time):
* lisp/calendar/parse-time.el (parse-time-string):
* lisp/calendar/time-date.el (decoded-time-add)
(decoded-time--alter-second):
* lisp/org/org.el (org-parse-time-string):
* lisp/simple.el (decoded-time):
* src/timefns.c (Fdecode_time, Fencode_time):
* test/lisp/calendar/icalendar-tests.el:
(icalendar--decode-isodatetime):
* test/lisp/calendar/iso8601-tests.el (test-iso8601-date-years)
(test-iso8601-date-dates, test-iso8601-date-obsolete)
(test-iso8601-date-weeks, test-iso8601-date-ordinals)
(test-iso8601-time, test-iso8601-combined)
(test-iso8601-duration, test-iso8601-intervals)
(standard-test-dates, standard-test-time-of-day-fractions)
(standard-test-time-of-day-beginning-of-day)
(standard-test-time-of-day-utc)
(standard-test-time-of-day-zone)
(standard-test-date-and-time-of-day, standard-test-interval):
* test/lisp/calendar/parse-time-tests.el (parse-time-tests):
* test/src/timefns-tests.el (format-time-string-with-zone)
(encode-time-dst-numeric-zone):
Revert recent changes that added a SUBSECS member to
calendrical timestamps, since that component is no longer
present (the info, if any, is now in the SECONDS member).
* lisp/calendar/time-date.el (decoded-time-add)
(decoded-time--alter-second):
Support fractional seconds in the new form.  Simplify.
* src/timefns.c (Fdecode_time): Support new arg FORM.
(Fencode_time): Support subsecond resolution.
* test/src/timefns-tests.el (format-time-string-with-zone)
(decode-then-encode-time): Test subsecond calendrical timestamps.
parent d7c9ed84
Pipeline #2868 failed with stage
in 55 minutes and 7 seconds
......@@ -1478,23 +1478,23 @@ Although @code{(time-convert nil nil)} is equivalent to
@end example
@end defun
@defun decode-time &optional time zone
@defun decode-time &optional time zone form
This function converts a time value into calendrical information. If
you don't specify @var{time}, it decodes the current time, and similarly
@var{zone} defaults to the current time zone rule. @xref{Time Zone Rules}.
The return value is a list of ten elements, as follows:
The @var{form} argument controls the form of the returned
@var{seconds} element, as described below.
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{utcoff} @var{subsec})
(@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:
@table @var
@item seconds
The number of seconds past the minute, as an integer between 0 and 59.
On some operating systems, this is 60 for leap seconds.
The number of seconds past the minute, with form described below.
@item minutes
The number of minutes past the hour, as an integer between 0 and 59.
@item hour
......@@ -1514,22 +1514,33 @@ in effect, and @minus{}1 if this information is not available.
@item utcoff
An integer indicating the Universal Time offset in seconds, i.e., the number of
seconds east of Greenwich.
@item subsec
The number of subseconds past the second, as either 0 or a Lisp
timestamp @code{(@var{ticks} . @var{hz})} representing a nonnegative
fraction less than 1.
@end table
The @var{seconds} element is a Lisp timestamp that is nonnegative and
less than 61; it is less than 60 except during positive leap seconds
(assuming the operating system supports leap seconds). If the
optional @var{form} argument is @code{t}, @var{seconds} uses the same
precision as @var{time}; if @var{form} is @code{integer},
@var{seconds} is truncated to an integer. For example, if @var{time}
is the timestamp @code{(1566009571321 . 1000)}, which represents
2019-08-17 02:39:31.321 UTC on typical systems that lack leap seconds,
then @code{(decode-time @var{time} t t)} returns @code{((31321 . 1000)
39 2 17 8 2019 6 nil 0)}, whereas @code{(decode-time @var{time} t
'integer)} returns @code{(31 39 2 17 8 2019 6 nil 0)}. If @var{form}
is omitted or @code{nil}, it currently defaults to @code{integer} but
this default may change in future Emacs releases, so callers requiring
a particular form should specify @var{form}.
@strong{Common Lisp Note:} Common Lisp has different meanings for
@var{dow} and @var{utcoff}, and lacks @var{subsec}.
@var{dow} and @var{utcoff}, and its @var{second} is an integer between
0 and 59 inclusive.
To access (or alter) the elements in the time value, the
@code{decoded-time-second}, @code{decoded-time-minute},
@code{decoded-time-hour}, @code{decoded-time-day},
@code{decoded-time-month}, @code{decoded-time-year},
@code{decoded-time-weekday}, @code{decoded-time-dst},
@code{decoded-time-zone} and @code{decoded-time-subsec}
accessors can be used.
@code{decoded-time-weekday}, @code{decoded-time-dst} and
@code{decoded-time-zone} accessors can be used.
For instance, to increase the year in a decoded time, you could say:
......@@ -1551,7 +1562,7 @@ For instance, if you want ``same time next month'', you
could say:
@lisp
(let ((time (decode-time))
(let ((time (decode-time nil nil t))
(delta (make-decoded-time :month 2)))
(encode-time (decoded-time-add time delta)))
@end lisp
......@@ -1585,22 +1596,21 @@ It can act as the inverse of @code{decode-time}.
Ordinarily the first argument is a list
@code{(@var{second} @var{minute} @var{hour} @var{day} @var{month}
@var{year} @var{ignored} @var{dst} @var{zone} @var{subsec})} that specifies a
@var{year} @var{ignored} @var{dst} @var{zone})} that specifies a
decoded time in the style of @code{decode-time}, so that
@code{(encode-time (decode-time ...))} works. For the meanings of
these list members, see the table under @code{decode-time}.
As an obsolescent calling convention, this function can be given six
through ten arguments. The first six arguments @var{second},
or more arguments. The first six arguments @var{second},
@var{minute}, @var{hour}, @var{day}, @var{month}, and @var{year}
specify most of the components of a decoded time. If there are seven
through nine arguments the @emph{last} argument is used as @var{zone},
and if there are ten arguments the ninth specifies @var{zone} and the
tenth specifies @var{subsec}; in either case any other extra arguments
are ignored, so that @code{(apply #'encode-time (decode-time ...))}
works. In this obsolescent convention, @var{zone} defaults to the
current time zone rule (@pxref{Time Zone Rules}), @var{subsec}
defaults to 0, and @var{dst} is treated as if it was @minus{}1.
specify most of the components of a decoded time. If there are more
than six arguments the @emph{last} argument is used as @var{zone} and
any other extra arguments are ignored, so that @code{(apply
#'encode-time (decode-time ...))} works. In this obsolescent
convention, @var{zone} defaults to the current time zone rule
(@pxref{Time Zone Rules}), and @var{dst} is treated as if it was
@minus{}1.
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
......@@ -1615,9 +1625,8 @@ the latter to the former as follows:
@end example
You can perform simple date arithmetic by using out-of-range values for
@var{seconds}, @var{minutes}, @var{hour}, @var{day}, @var{month}, and
@var{subsec}; for example, day 0 means the day preceding the given
month.
@var{seconds}, @var{minutes}, @var{hour}, @var{day}, and @var{month};
for example, day 0 means the day preceding the given month.
The operating system puts limits on the range of possible time values;
if the limits are exceeded while encoding the time, an error results.
......
......@@ -1535,7 +1535,7 @@ Here's a bunch of time/date/second/day examples:
@example
(parse-time-string "Sat Sep 12 12:21:54 1998 +0200")
@result{} (54 21 12 12 9 1998 6 -1 7200 0)
@result{} (54 21 12 12 9 1998 6 -1 7200)
(time-convert
(date-to-time "Sat Sep 12 12:21:54 1998 +0200")
......
......@@ -2116,10 +2116,20 @@ probing the innards of a timestamp directly, or creating a timestamp
by hand.
+++
*** Decoded (calendrical) timestamps now have a new subsecond member.
This affects functions like decode-time and parse-time-string that
generate these timestamps, and functions like encode-time that accept
them.
*** Decoded (calendrical) timestamps now have subsecond resolution.
This affects decode-time, which generates these timestamps, as well as
functions like encode-time that accept them. The subsecond info is
present as a (TICKS . HZ) value in the seconds element of a decoded
timestamp, and decode-time has a new optional FORM argument specifying
the form of the seconds member. For example, if X is the timestamp
(1566009571321878186 . 1000000000), which represents 2019-08-17
02:39:31.321878186 UTC, (decode-time X t t) returns ((31321878186
. 1000000000) 39 2 17 8 2019 6 nil 0) instead of the traditional (31
39 2 17 8 2019 6 nil 0) returned by plain (decode-time X t). Although
the default FORM is currently 'integer', which truncates the seconds
to an integer and is the traditional behavior, this default may change
in future Emacs versions, so callers requiring an integer should
specify FORM explicitly.
+++
*** 'encode-time' supports a new API '(encode-time TIME)'.
......@@ -2152,8 +2162,8 @@ with POSIX.1-2017.
*** To access (or alter) the elements a decoded time value, the
'decoded-time-second', 'decoded-time-minute', 'decoded-time-hour',
'decoded-time-day', 'decoded-time-month', 'decoded-time-year',
'decoded-time-weekday', 'decoded-time-dst', 'decoded-time-zone',
and 'decoded-time-subsec' accessors can be used.
'decoded-time-weekday', 'decoded-time-dst' and 'decoded-time-zone'
accessors can be used.
*** The new functions 'date-days-in-month' (which will say how many
days there are in a month in a specific year), 'date-ordinal-to-time'
......
......@@ -644,7 +644,7 @@ FIXME: multiple comma-separated values should be allowed!"
;; create the decoded date-time
;; FIXME!?!
(let ((decoded-time (list second minute hour day month year
nil -1 zone 0)))
nil -1 zone)))
(condition-case nil
(decode-time (encode-time decoded-time))
(error
......
......@@ -129,8 +129,7 @@ well as variants like \"2008W32\" (week number) and
(let ((time (iso8601-parse-time time-string)))
(setf (decoded-time-hour date) (decoded-time-hour time))
(setf (decoded-time-minute date) (decoded-time-minute time))
(setf (decoded-time-second date) (decoded-time-second time))
(setf (decoded-time-subsec date) (decoded-time-subsec time))))
(setf (decoded-time-second date) (decoded-time-second time))))
;; The time zone is optional.
(when zone-string
(setf (decoded-time-zone date)
......@@ -237,8 +236,6 @@ well as variants like \"2008W32\" (week number) and
(iso8601--decoded-time :hour hour
:minute (or minute 0)
:second (or second 0)
;; FIXME: Support subsec.
:subsec 0
:zone (and zone
(* 60 (iso8601-parse-zone
zone)))))))))
......@@ -277,9 +274,7 @@ Return the number of minutes."
:day (or (match-string 3 string) 0)
:hour (or (match-string 5 string) 0)
:minute (or (match-string 6 string) 0)
:second (or (match-string 7 string) 0)
;; FIXME: Support subsec.
:subsec 0))
:second (or (match-string 7 string) 0)))
;; PnW: Weeks.
((iso8601--match iso8601--duration-week-match string)
(let ((weeks (string-to-number (match-string 1 string))))
......@@ -341,7 +336,7 @@ Return the number of minutes."
(cl-defun iso8601--decoded-time (&key second minute hour
day month year
dst zone subsec)
dst zone)
(list (iso8601--value second)
(iso8601--value minute)
(iso8601--value hour)
......@@ -350,8 +345,7 @@ Return the number of minutes."
(iso8601--value year)
nil
dst
zone
subsec))
zone))
(defun iso8601--encode-time (time)
"Like `encode-time', but fill in nil values in TIME."
......
......@@ -148,7 +148,7 @@ letters, digits, plus or minus signs or colons."
;;;###autoload
(defun parse-time-string (string)
"Parse the time in STRING into (SEC MIN HOUR DAY MON YEAR DOW DST TZ SUBSEC).
"Parse the time in STRING into (SEC MIN HOUR DAY MON YEAR DOW DST TZ).
STRING should be something resembling an RFC 822 (or later) date-time, e.g.,
\"Fri, 25 Mar 2016 16:24:56 +0100\", but this function is
somewhat liberal in what format it accepts, and will attempt to
......@@ -156,7 +156,7 @@ return a \"likely\" value even for somewhat malformed strings.
The values returned are identical to those of `decode-time', but
any unknown values other than DST are returned as nil, and an
unknown DST value is returned as -1."
(let ((time (list nil nil nil nil nil nil nil -1 nil nil))
(let ((time (list nil nil nil nil nil nil nil -1 nil))
(temp (parse-time-tokenize (downcase string))))
(while temp
(let ((parse-time-elt (pop temp))
......@@ -193,10 +193,6 @@ unknown DST value is returned as -1."
(funcall this)))
parse-time-val)))
(setf (nth (pop slots) time) new-val))))))))
;; FIXME: Currently parse-time-string does not parse subseconds.
;; So if seconds were found, set subseconds to zero.
(when (nth 0 time)
(setf (nth 9 time) 0))
time))
(defun parse-iso8601-time-string (date-string)
......
......@@ -420,26 +420,13 @@ changes in daylight saving time are not taken into account."
;; Do the time part, which is pretty simple (except for leap
;; seconds, I guess).
(setq seconds (+ (* (or (decoded-time-hour delta) 0) 3600)
(* (or (decoded-time-minute delta) 0) 60)
(or (decoded-time-second delta) 0)))
(when (decoded-time-subsec delta)
(let* ((subsec (time-convert (time-add (decoded-time-subsec time)
(decoded-time-subsec delta))
t))
(s (time-convert subsec 'integer)))
(setq seconds (+ seconds s))
(setf (decoded-time-subsec time) (time-subtract subsec s))))
;; Time zone adjustments are basically the same as time adjustments.
(setq seconds (+ seconds (or (decoded-time-zone delta) 0)))
(cond
((> seconds 0)
(decoded-time--alter-second time seconds t))
((< seconds 0)
(decoded-time--alter-second time (abs seconds) nil)))
(setq seconds (time-add (+ (* (or (decoded-time-hour delta) 0) 3600)
(* (or (decoded-time-minute delta) 0) 60)
(or (decoded-time-zone delta) 0))
(or (decoded-time-second delta) 0)))
(decoded-time--alter-second time seconds)
time))
(defun decoded-time--alter-month (time increase)
......@@ -472,38 +459,31 @@ changes in daylight saving time are not taken into account."
(date-days-in-month (decoded-time-year time)
(decoded-time-month time))))))
(defun decoded-time--alter-second (time seconds increase)
"Increase or decrease the time in TIME by SECONDS."
(let ((old (+ (* (or (decoded-time-hour time) 0) 3600)
(* (or (decoded-time-minute time) 0) 60)
(or (decoded-time-second time) 0))))
(if increase
(progn
(setq old (+ old seconds))
(setf (decoded-time-second time) (% old 60)
(decoded-time-minute time) (% (/ old 60) 60)
(decoded-time-hour time) (% (/ old 3600) 24))
;; Hm... DST...
(let ((days (/ old (* 60 60 24))))
(while (> days 0)
(decoded-time--alter-day time t)
(cl-decf days))))
(setq old (abs (- old seconds)))
(setf (decoded-time-second time) (% old 60)
(decoded-time-minute time) (% (/ old 60) 60)
(decoded-time-hour time) (% (/ old 3600) 24))
;; Hm... DST...
(let ((days (/ old (* 60 60 24))))
(while (> days 0)
(decoded-time--alter-day time nil)
(cl-decf days))))))
(defun decoded-time--alter-second (time seconds)
"Increase the time in TIME by SECONDS."
(let* ((secsperday 86400)
(old (time-add (+ (* 3600 (or (decoded-time-hour time) 0))
(* 60 (or (decoded-time-minute time) 0)))
(or (decoded-time-second time) 0)))
(new (time-add old seconds)))
;; Hm... DST...
(while (time-less-p new 0)
(decoded-time--alter-day time nil)
(setq new (time-add new secsperday)))
(while (not (time-less-p new secsperday))
(decoded-time--alter-day time t)
(setq new (time-subtract new secsperday)))
(let ((sec (time-convert new 'integer)))
(setf (decoded-time-second time) (time-add (% sec 60)
(time-subtract new sec))
(decoded-time-minute time) (% (/ sec 60) 60)
(decoded-time-hour time) (/ sec 3600)))))
(cl-defun make-decoded-time (&key second minute hour
day month year
dst zone subsec)
dst zone)
"Return a `decoded-time' structure with only the keywords given filled out."
(list second minute hour day month year nil dst zone subsec))
(list second minute hour day month year nil dst zone))
(defun decoded-time-set-defaults (time &optional default-zone)
"Set any nil values in `decoded-time' TIME to default values.
......@@ -533,9 +513,6 @@ TIME is modified and returned."
(when (and (not (decoded-time-zone time))
default-zone)
(setf (decoded-time-zone time) 0))
(unless (decoded-time-subsec time)
(setf (decoded-time-subsec time) 0))
time)
(provide 'time-date)
......
......@@ -561,8 +561,8 @@ gMonthDay, gDay or gMonth.
Return a list in a format (SEC MINUTE HOUR DAY MONTH YEAR
SEC-FRACTION DATATYPE ZONE). This format is meant to be similar
to that returned by `decode-time' (and compatible with
`encode-time'). The differences are the SUBSEC (fractional
seconds) field is omitted, the DOW (day-of-week) field
`encode-time'). The differences are the SEC (seconds)
field is always an integer, the DOW (day-of-week) field
is replaced with SEC-FRACTION, a float representing the
fractional seconds, and the DST (daylight savings time) field is
replaced with DATATYPE, a symbol representing the XSD primitive
......
......@@ -17775,12 +17775,14 @@ NODEFAULT, hour and minute fields will be nil if not given."
(string-to-number (match-string 4 s))
(string-to-number (match-string 3 s))
(string-to-number (match-string 2 s))
nil nil nil 0))
nil nil nil))
((string-match "^<[^>]+>$" s)
;; FIXME: `decode-time' needs to be called with ZONE as its
;; second argument. However, this requires at least Emacs
;; 25.1. We can do it when we switch to this version as our
;; minimal requirement.
;; FIXME: decode-time needs to be called with t as its
;; third argument, but this requires at least Emacs 27.
(decode-time (org-matcher-time s)))
(t (error "Not a standard Org time string: %s" s))))
......
......@@ -9082,8 +9082,9 @@ to capitalize ARG words."
(:copier nil)
(:type list))
(second nil :documentation "\
This is an integer between 0 and 60 (inclusive). (60 is a leap
second, which only some operating systems support.)")
This is an integer or a Lisp timestamp (TICKS . HZ) representing a nonnegative
number of seconds less than 61. (If not less than 60, it is a leap second,
which only some operating systems support.)")
(minute nil :documentation "This is an integer between 0 and 59 (inclusive).")
(hour nil :documentation "This is an integer between 0 and 23 (inclusive).")
(day nil :documentation "This is an integer between 1 and 31 (inclusive).")
......@@ -9099,9 +9100,6 @@ available.")
(zone nil :documentation "\
This is an integer indicating the UTC offset in seconds, i.e.,
the number of seconds east of Greenwich.")
(subsec nil :documentation "\
This is 0, or is an integer pair (TICKS . HZ) indicating TICKS/HZ seconds,
where HZ is positive and TICKS is nonnegative and less than HZ.")
)
......
This diff is collapsed.
......@@ -477,18 +477,18 @@ END:VEVENT
;; testcase: no time zone in input -> keep time as is
;; 1 Jan 2013 10:00
(should (equal '(0 0 10 1 1 2013 2 nil 7200 0)
(should (equal '(0 0 10 1 1 2013 2 nil 7200)
(icalendar--decode-isodatetime "20130101T100000")))
;; 1 Aug 2013 10:00 (DST)
(should (equal '(0 0 10 1 8 2013 4 t 10800 0)
(should (equal '(0 0 10 1 8 2013 4 t 10800)
(icalendar--decode-isodatetime "20130801T100000")))
;; testcase: UTC time zone specifier in input -> convert to local time
;; 31 Dec 2013 23:00 UTC -> 1 Jan 2013 01:00 EET
(should (equal '(0 0 1 1 1 2014 3 nil 7200 0)
(should (equal '(0 0 1 1 1 2014 3 nil 7200)
(icalendar--decode-isodatetime "20131231T230000Z")))
;; 1 Aug 2013 10:00 UTC -> 1 Aug 2013 13:00 EEST
(should (equal '(0 0 13 1 8 2013 4 t 10800 0)
(should (equal '(0 0 13 1 8 2013 4 t 10800)
(icalendar--decode-isodatetime "20130801T100000Z")))
)
......
This diff is collapsed.
......@@ -28,23 +28,23 @@
(ert-deftest parse-time-tests ()
(should (equal (parse-time-string "Mon, 22 Feb 2016 19:35:42 +0100")
'(42 35 19 22 2 2016 1 -1 3600 0)))
'(42 35 19 22 2 2016 1 -1 3600)))
(should (equal (parse-time-string "22 Feb 2016 19:35:42 +0100")
'(42 35 19 22 2 2016 nil -1 3600 0)))
'(42 35 19 22 2 2016 nil -1 3600)))
(should (equal (parse-time-string "22 Feb 2016 +0100")
'(nil nil nil 22 2 2016 nil -1 3600 nil)))
'(nil nil nil 22 2 2016 nil -1 3600)))
(should (equal (parse-time-string "Mon, 22 Feb 16 19:35:42 +0100")
'(42 35 19 22 2 2016 1 -1 3600 0)))
'(42 35 19 22 2 2016 1 -1 3600)))
(should (equal (parse-time-string "Mon, 22 February 2016 19:35:42 +0100")
'(42 35 19 22 2 2016 1 -1 3600 0)))
'(42 35 19 22 2 2016 1 -1 3600)))
(should (equal (parse-time-string "Mon, 22 feb 2016 19:35:42 +0100")
'(42 35 19 22 2 2016 1 -1 3600 0)))
'(42 35 19 22 2 2016 1 -1 3600)))
(should (equal (parse-time-string "Monday, 22 february 2016 19:35:42 +0100")
'(42 35 19 22 2 2016 1 -1 3600 0)))
'(42 35 19 22 2 2016 1 -1 3600)))
(should (equal (parse-time-string "Monday, 22 february 2016 19:35:42 PST")
'(42 35 19 22 2 2016 1 nil -28800 0)))
'(42 35 19 22 2 2016 1 nil -28800)))
(should (equal (parse-time-string "Friday, 21 Sep 2018 13:47:58 PDT")
'(58 47 13 21 9 2018 5 t -25200 0)))
'(58 47 13 21 9 2018 5 t -25200)))
(should (equal (format-time-string
"%Y-%m-%d %H:%M:%S"
(parse-iso8601-time-string "1998-09-12T12:21:54-0200") t)
......
......@@ -40,25 +40,31 @@
(7879679999900 . 100000)
(78796799999999999999 . 1000000000000)))
;; UTC.
(let ((subsec (time-subtract (time-convert look t)
(time-convert look 'integer))))
(let ((sec (time-add 59 (time-subtract (time-convert look t)
(time-convert look 'integer)))))
(should (string-equal
(format-time-string "%Y-%m-%d %H:%M:%S.%3N %z" look t)
"1972-06-30 23:59:59.999 +0000"))
(should (equal (decode-time look t)
(list 59 59 23 30 6 1972 5 nil 0 subsec)))
(should (equal (decode-time look t 'integer)
'(59 59 23 30 6 1972 5 nil 0)))
(should (equal (decode-time look t t)
(list sec 59 23 30 6 1972 5 nil 0)))
;; "UTC0".
(should (string-equal
(format-time-string format look "UTC0")
"1972-06-30 23:59:59.999 +0000 (UTC)"))
(should (equal (decode-time look "UTC0")
(list 59 59 23 30 6 1972 5 nil 0 subsec)))
(should (equal (decode-time look "UTC0" 'integer)
'(59 59 23 30 6 1972 5 nil 0)))
(should (equal (decode-time look "UTC0" t)
(list sec 59 23 30 6 1972 5 nil 0)))
;; Negative UTC offset, as a Lisp list.
(should (string-equal
(format-time-string format look '(-28800 "PST"))
"1972-06-30 15:59:59.999 -0800 (PST)"))
(should (equal (decode-time look '(-28800 "PST"))
(list 59 59 15 30 6 1972 5 nil -28800 subsec)))
(should (equal (decode-time look '(-28800 "PST") 'integer)
'(59 59 15 30 6 1972 5 nil -28800)))
(should (equal (decode-time look '(-28800 "PST") t)
(list sec 59 15 30 6 1972 5 nil -28800)))
;; Negative UTC offset, as a Lisp integer.
(should (string-equal
(format-time-string format look -28800)
......@@ -67,14 +73,18 @@
(if (eq system-type 'windows-nt)
"1972-06-30 15:59:59.999 -0800 (ZZZ)"
"1972-06-30 15:59:59.999 -0800 (-08)")))
(should (equal (decode-time look -28800)
(list 59 59 15 30 6 1972 5 nil -28800 subsec)))
(should (equal (decode-time look -28800 'integer)
'(59 59 15 30 6 1972 5 nil -28800)))
(should (equal (decode-time look -28800 t)
(list sec 59 15 30 6 1972 5 nil -28800)))
;; Positive UTC offset that is not an hour multiple, as a string.
(should (string-equal
(format-time-string format look "IST-5:30")
"1972-07-01 05:29:59.999 +0530 (IST)"))
(should (equal (decode-time look "IST-5:30")
(list 59 29 5 1 7 1972 6 nil 19800 subsec)))))))
(should (equal (decode-time look "IST-5:30" 'integer)
'(59 29 5 1 7 1972 6 nil 19800)))
(should (equal (decode-time look "IST-5:30" t)
(list sec 29 5 1 7 1972 6 nil 19800)))))))
(ert-deftest decode-then-encode-time ()
(let ((time-values (list 0 -2 1 0.0 -0.0 -2.0 1.0
......@@ -87,11 +97,13 @@
(cons (1+ most-positive-fixnum) 1000000000000)
(cons 1000000000000 (1+ most-positive-fixnum)))))
(dolist (a time-values)
(let* ((d (ignore-errors (decode-time a t)))
(let* ((d (ignore-errors (decode-time a t t)))
(d-integer (ignore-errors (decode-time a t 'integer)))
(e (if d (encode-time d)))
(diff (float-time (time-subtract a e))))
(should (or (not d)
(and (<= 0 diff) (< diff 1))))))))
(e-integer (if d-integer (encode-time d-integer))))
(should (or (not d) (time-equal-p a e)))
(should (or (not d-integer) (time-equal-p (time-convert a 'integer)
e-integer)))))))
;;; This should not dump core.
(ert-deftest format-time-string-with-outlandish-zone ()
......@@ -151,7 +163,7 @@
(ert-deftest encode-time-dst-numeric-zone ()
"Check for Bug#35502."
(should (time-equal-p
(encode-time '(29 31 17 30 4 2019 2 t 7200 0))
(encode-time '(29 31 17 30 4 2019 2 t 7200))
'(23752 27217))))
(ert-deftest float-time-precision ()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment