>From 37257d6acadff17bd1e52cfa460950bcb684c5c3 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Fri, 16 Aug 2019 22:09:04 -0700 Subject: [PATCH] More-compatible subsecond calendrical timestamps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- doc/lispref/os.texi | 65 +++++---- doc/misc/emacs-mime.texi | 2 +- etc/NEWS | 22 ++- lisp/calendar/icalendar.el | 2 +- lisp/calendar/iso8601.el | 14 +- lisp/calendar/parse-time.el | 8 +- lisp/calendar/time-date.el | 75 ++++------ lisp/net/soap-client.el | 4 +- lisp/org/org.el | 4 +- lisp/simple.el | 8 +- src/timefns.c | 178 +++++++++++++---------- test/lisp/calendar/icalendar-tests.el | 8 +- test/lisp/calendar/iso8601-tests.el | 190 ++++++++++++------------- test/lisp/calendar/parse-time-tests.el | 18 +-- test/src/timefns-tests.el | 46 +++--- 15 files changed, 337 insertions(+), 307 deletions(-) diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index 70ae39e6ab..49c07380c5 100644 --- a/doc/lispref/os.texi +++ b/doc/lispref/os.texi @@ -1478,23 +1478,23 @@ Time Conversion @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 @@ Time Conversion @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 @@ Time Conversion 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 @@ Time Conversion 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 @@ Time Conversion @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. diff --git a/doc/misc/emacs-mime.texi b/doc/misc/emacs-mime.texi index c411bf3d68..eb829b0612 100644 --- a/doc/misc/emacs-mime.texi +++ b/doc/misc/emacs-mime.texi @@ -1535,7 +1535,7 @@ time-date @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") diff --git a/etc/NEWS b/etc/NEWS index edce7b3e57..53408a871e 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -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' diff --git a/lisp/calendar/icalendar.el b/lisp/calendar/icalendar.el index 84f579ad44..c2688705e3 100644 --- a/lisp/calendar/icalendar.el +++ b/lisp/calendar/icalendar.el @@ -644,7 +644,7 @@ icalendar--decode-isodatetime ;; 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 diff --git a/lisp/calendar/iso8601.el b/lisp/calendar/iso8601.el index 51f5dff909..30352c7e75 100644 --- a/lisp/calendar/iso8601.el +++ b/lisp/calendar/iso8601.el @@ -129,8 +129,7 @@ iso8601-parse (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 @@ iso8601-parse-time (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 @@ iso8601-parse-duration :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 @@ iso8601--value (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 @@ iso8601--value (iso8601--value year) nil dst - zone - subsec)) + zone)) (defun iso8601--encode-time (time) "Like `encode-time', but fill in nil values in TIME." diff --git a/lisp/calendar/parse-time.el b/lisp/calendar/parse-time.el index 9af93b5b1e..b0b277db77 100644 --- a/lisp/calendar/parse-time.el +++ b/lisp/calendar/parse-time.el @@ -148,7 +148,7 @@ parse-time-rules ;;;###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 @@ parse-time-string 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 @@ parse-time-string (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) diff --git a/lisp/calendar/time-date.el b/lisp/calendar/time-date.el index fa5e886869..f3d252f03c 100644 --- a/lisp/calendar/time-date.el +++ b/lisp/calendar/time-date.el @@ -420,26 +420,13 @@ decoded-time-add ;; 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 @@ decoded-time--alter-day (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 @@ decoded-time-set-defaults (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) diff --git a/lisp/net/soap-client.el b/lisp/net/soap-client.el index eb08511171..7ce7d79c74 100644 --- a/lisp/net/soap-client.el +++ b/lisp/net/soap-client.el @@ -561,8 +561,8 @@ soap-decode-date-time 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 diff --git a/lisp/org/org.el b/lisp/org/org.el index 336c413c8c..ab29353ae8 100644 --- a/lisp/org/org.el +++ b/lisp/org/org.el @@ -17775,12 +17775,14 @@ org-parse-time-string (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)))) diff --git a/lisp/simple.el b/lisp/simple.el index cb938bb341..fdf7d893cd 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -9082,8 +9082,9 @@ capitalize-dwim (: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 @@ capitalize-dwim (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.") ) diff --git a/src/timefns.c b/src/timefns.c index 979550c843..16c39c8349 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -1374,8 +1374,8 @@ usage: (format-time-string FORMAT-STRING &optional TIME ZONE) */) t, zone, &tm); } -DEFUN ("decode-time", Fdecode_time, Sdecode_time, 0, 2, 0, - doc: /* Decode a time value as (SEC MINUTE HOUR DAY MONTH YEAR DOW DST UTCOFF SUBSEC). +DEFUN ("decode-time", Fdecode_time, Sdecode_time, 0, 3, 0, + doc: /* Decode a time value as (SEC MINUTE HOUR DAY MONTH YEAR DOW DST UTCOFF). The optional TIME is the time value to convert. See `format-time-string' for the various forms of a time value. @@ -1385,29 +1385,33 @@ the TZ environment variable. It can also be a list (as from `current-time-zone') or an integer (the UTC offset in seconds) applied without consideration for daylight saving time. +The optional FORM specifies the form of the SEC member. If `integer', +SEC is an integer; if t, SEC uses the same resolution as TIME. An +omitted or nil FORM is currently treated like `integer', but this may +change in future Emacs versions. + To access (or alter) the elements in the 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 list has the following ten members: SEC is an integer between 0 -and 60; SEC is 60 for a leap second, which only some operating systems -support. MINUTE is an integer between 0 and 59. HOUR is an integer +The list has the following nine members: SEC is an integer or +Lisp timestamp representing a nonnegative value less than 60 +\(or less than 61 if the operating system supports leap seconds). +MINUTE is an integer between 0 and 59. HOUR is an integer between 0 and 23. DAY is an integer between 1 and 31. MONTH is an integer between 1 and 12. YEAR is an integer indicating the four-digit year. DOW is the day of week, an integer between 0 and 6, where 0 is Sunday. DST is t if daylight saving time is in effect, nil if it is not in effect, and -1 if daylight saving information is not available. UTCOFF is an integer indicating the UTC offset in -seconds, i.e., the number of seconds east of Greenwich. SUBSEC is -is either 0 or (TICKS . HZ) where HZ is a positive integer clock -resolution and TICKS is a nonnegative integer less than HZ. (Note -that Common Lisp has different meanings for DOW and UTCOFF, and lacks -SUBSEC.) +seconds, i.e., the number of seconds east of Greenwich. (Note that +Common Lisp has different meanings for DOW and UTCOFF, and its +SEC is always an integer between 0 and 59.) -usage: (decode-time &optional TIME ZONE) */) - (Lisp_Object specified_time, Lisp_Object zone) +usage: (decode-time &optional TIME ZONE FORM) */) + (Lisp_Object specified_time, Lisp_Object zone, Lisp_Object form) { struct lisp_time lt = lisp_time_struct (specified_time, 0); struct timespec ts = lisp_to_timespec (lt); @@ -1439,8 +1443,35 @@ usage: (decode-time &optional TIME ZONE) */) year = make_integer_mpz (); } + Lisp_Object hz = lt.hz, sec; + if (EQ (hz, make_fixnum (1)) || !EQ (form, Qt)) + sec = make_fixnum (local_tm.tm_sec); + else + { + Lisp_Object ticks; /* hz * tm_sec + mod (lt.ticks, hz) */ + intmax_t n; + if (FASTER_TIMEFNS && FIXNUMP (lt.ticks) && FIXNUMP (hz) + && !INT_MULTIPLY_WRAPV (XFIXNUM (hz), local_tm.tm_sec, &n) + && ! (INT_ADD_WRAPV + (n, (XFIXNUM (lt.ticks) % XFIXNUM (hz) + + (XFIXNUM (lt.ticks) % XFIXNUM (hz) < 0 + ? XFIXNUM (hz) : 0)), + &n))) + ticks = make_int (n); + else + { + mpz_fdiv_r (mpz[0], + *bignum_integer (&mpz[0], lt.ticks), + *bignum_integer (&mpz[1], hz)); + mpz_addmul_ui (mpz[0], *bignum_integer (&mpz[1], hz), + local_tm.tm_sec); + ticks = make_integer_mpz (); + } + sec = Fcons (ticks, hz); + } + return CALLN (Flist, - make_fixnum (local_tm.tm_sec), + sec, make_fixnum (local_tm.tm_min), make_fixnum (local_tm.tm_hour), make_fixnum (local_tm.tm_mday), @@ -1453,10 +1484,7 @@ usage: (decode-time &optional TIME ZONE) */) ? make_fixnum (tm_gmtoff (&local_tm)) : gmtime_r (&time_spec, &gmt_tm) ? make_fixnum (tm_diff (&local_tm, &gmt_tm)) - : Qnil), - (EQ (lt.hz, make_fixnum (1)) - ? make_fixnum (0) - : Fcons (integer_mod (lt.ticks, lt.hz), lt.hz))); + : Qnil)); } /* Return OBJ - OFFSET, checking that OBJ is a valid integer and that @@ -1487,7 +1515,7 @@ check_tm_member (Lisp_Object obj, int offset) DEFUN ("encode-time", Fencode_time, Sencode_time, 1, MANY, 0, doc: /* Convert TIME to a timestamp. -TIME is a list (SECOND MINUTE HOUR DAY MONTH YEAR IGNORED DST ZONE SUBSEC). +TIME is a list (SECOND MINUTE HOUR DAY MONTH YEAR IGNORED DST ZONE). in the style of `decode-time', so that (encode-time (decode-time ...)) works. In this list, ZONE can be nil for Emacs local time, t for Universal Time, `wall' for system wall clock time, or a string as in the TZ @@ -1496,23 +1524,16 @@ environment variable. It can also be a list (as from without consideration for daylight saving time. If ZONE specifies a time zone with daylight-saving transitions, DST is t for daylight saving time, nil for standard time, and -1 to cause the daylight -saving flag to be guessed. SUBSEC is either 0 or a Lisp timestamp -in (TICKS . HZ) form. +saving flag to be guessed. As an obsolescent calling convention, if this function is called with -6 through 10 arguments, the first 6 arguments are SECOND, MINUTE, -HOUR, DAY, MONTH, and YEAR, and specify the components of a decoded -time. If there are 7 through 9 arguments the *last* argument -specifies ZONE, and if there are 10 arguments the 9th specifies ZONE -and the 10th specifies SUBSEC; in either case any other extra -arguments are ignored, so that (apply #\\='encode-time (decode-time -...)) works. In this obsolescent convention, DST, ZONE, and SUBSEC -default to -1, nil and 0 respectively. - -Out-of-range values for SECOND, MINUTE, HOUR, DAY, or MONTH are allowed; -for example, a DAY of 0 means the day preceding the given month. -Year numbers less than 100 are treated just like other year numbers. -If you want them to stand for years in this century, you must do that yourself. +6 or more arguments, the first 6 arguments are SECOND, MINUTE, HOUR, +DAY, MONTH, and YEAR, and specify the components of a decoded time, +where DST assumed to be -1 and FORM is omitted. If there are more +than 6 arguments the *last* argument is used as ZONE and any other +extra arguments are ignored, so that (apply #\\='encode-time +(decode-time ...)) works. In this obsolescent convention, DST and +ZONE default to -1 and nil respectively. Years before 1970 are not guaranteed to work. On some systems, year values as low as 1901 do work. @@ -1521,27 +1542,27 @@ usage: (encode-time TIME &rest OBSOLESCENT-ARGUMENTS) */) (ptrdiff_t nargs, Lisp_Object *args) { struct tm tm; - Lisp_Object zone = Qnil, subsec = make_fixnum (0); + Lisp_Object zone = Qnil; Lisp_Object a = args[0]; + Lisp_Object secarg, minarg, hourarg, mdayarg, monarg, yeararg; tm.tm_isdst = -1; if (nargs == 1) { Lisp_Object tail = a; - for (int i = 0; i < 10; i++, tail = XCDR (tail)) + for (int i = 0; i < 9; i++, tail = XCDR (tail)) CHECK_CONS (tail); - tm.tm_sec = check_tm_member (XCAR (a), 0); a = XCDR (a); - tm.tm_min = check_tm_member (XCAR (a), 0); a = XCDR (a); - tm.tm_hour = check_tm_member (XCAR (a), 0); a = XCDR (a); - tm.tm_mday = check_tm_member (XCAR (a), 0); a = XCDR (a); - tm.tm_mon = check_tm_member (XCAR (a), 1); a = XCDR (a); - tm.tm_year = check_tm_member (XCAR (a), TM_YEAR_BASE); a = XCDR (a); + secarg = XCAR (a); a = XCDR (a); + minarg = XCAR (a); a = XCDR (a); + hourarg = XCAR (a); a = XCDR (a); + mdayarg = XCAR (a); a = XCDR (a); + monarg = XCAR (a); a = XCDR (a); + yeararg = XCAR (a); a = XCDR (a); a = XCDR (a); Lisp_Object dstflag = XCAR (a); a = XCDR (a); - zone = XCAR (a); a = XCDR (a); + zone = XCAR (a); if (SYMBOLP (dstflag) && !FIXNUMP (zone) && !CONSP (zone)) tm.tm_isdst = !NILP (dstflag); - subsec = XCAR (a); } else if (nargs < 6) xsignal2 (Qwrong_number_of_arguments, Qencode_time, make_fixnum (nargs)); @@ -1549,18 +1570,37 @@ usage: (encode-time TIME &rest OBSOLESCENT-ARGUMENTS) */) { if (6 < nargs) zone = args[nargs - 1]; - if (9 < nargs) - { - zone = args[8]; - subsec = args[9]; - } - tm.tm_sec = check_tm_member (a, 0); - tm.tm_min = check_tm_member (args[1], 0); - tm.tm_hour = check_tm_member (args[2], 0); - tm.tm_mday = check_tm_member (args[3], 0); - tm.tm_mon = check_tm_member (args[4], 1); - tm.tm_year = check_tm_member (args[5], TM_YEAR_BASE); + secarg = a; + minarg = args[1]; + hourarg = args[2]; + mdayarg = args[3]; + monarg = args[4]; + yeararg = args[5]; + } + + struct lisp_time lt; + decode_lisp_time (secarg, 0, <, 0); + Lisp_Object hz = lt.hz, sec, subsecticks; + if (FASTER_TIMEFNS && EQ (hz, make_fixnum (1))) + { + sec = lt.ticks; + subsecticks = make_fixnum (0); + } + else + { + mpz_fdiv_qr (mpz[0], mpz[1], + *bignum_integer (&mpz[0], lt.ticks), + *bignum_integer (&mpz[1], hz)); + sec = make_integer_mpz (); + mpz_swap (mpz[0], mpz[1]); + subsecticks = make_integer_mpz (); } + tm.tm_sec = check_tm_member (sec, 0); + tm.tm_min = check_tm_member (minarg, 0); + tm.tm_hour = check_tm_member (hourarg, 0); + tm.tm_mday = check_tm_member (mdayarg, 0); + tm.tm_mon = check_tm_member (monarg, 1); + tm.tm_year = check_tm_member (yeararg, TM_YEAR_BASE); timezone_t tz = tzlookup (zone, false); tm.tm_wday = -1; @@ -1571,25 +1611,17 @@ usage: (encode-time TIME &rest OBSOLESCENT-ARGUMENTS) */) if (tm.tm_wday < 0) time_error (mktime_errno); - if (CONSP (subsec)) + if (EQ (hz, make_fixnum (1))) + return (CURRENT_TIME_LIST + ? list2 (hi_time (value), lo_time (value)) + : INT_TO_INTEGER (value)); + else { - Lisp_Object subsecticks = XCAR (subsec); - if (INTEGERP (subsecticks)) - { - struct lisp_time val1 = { INT_TO_INTEGER (value), make_fixnum (1) }; - Lisp_Object - hz = XCDR (subsec), - secticks = lisp_time_hz_ticks (val1, hz), - ticks = lispint_arith (secticks, subsecticks, false); - return Fcons (ticks, hz); - } + struct lisp_time val1 = { INT_TO_INTEGER (value), make_fixnum (1) }; + Lisp_Object secticks = lisp_time_hz_ticks (val1, hz); + Lisp_Object ticks = lispint_arith (secticks, subsecticks, false); + return Fcons (ticks, hz); } - else if (INTEGERP (subsec)) - return (CURRENT_TIME_LIST && EQ (subsec, make_fixnum (0)) - ? list2 (hi_time (value), lo_time (value)) - : lispint_arith (INT_TO_INTEGER (value), subsec, false)); - - xsignal2 (Qerror, build_string ("Invalid subsec"), subsec); } DEFUN ("time-convert", Ftime_convert, Stime_convert, 1, 2, 0, diff --git a/test/lisp/calendar/icalendar-tests.el b/test/lisp/calendar/icalendar-tests.el index 060cd8c909..baea480404 100644 --- a/test/lisp/calendar/icalendar-tests.el +++ b/test/lisp/calendar/icalendar-tests.el @@ -477,18 +477,18 @@ icalendar-tests--trim ;; 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"))) ) diff --git a/test/lisp/calendar/iso8601-tests.el b/test/lisp/calendar/iso8601-tests.el index 3f1149c864..35c319ed03 100644 --- a/test/lisp/calendar/iso8601-tests.el +++ b/test/lisp/calendar/iso8601-tests.el @@ -24,65 +24,65 @@ (ert-deftest test-iso8601-date-years () (should (equal (iso8601-parse-date "1985") - '(nil nil nil nil nil 1985 nil nil nil nil))) + '(nil nil nil nil nil 1985 nil nil nil))) (should (equal (iso8601-parse-date "-0003") - '(nil nil nil nil nil -4 nil nil nil nil))) + '(nil nil nil nil nil -4 nil nil nil))) (should (equal (iso8601-parse-date "+1985") - '(nil nil nil nil nil 1985 nil nil nil nil)))) + '(nil nil nil nil nil 1985 nil nil nil)))) (ert-deftest test-iso8601-date-dates () (should (equal (iso8601-parse-date "1985-03-14") - '(nil nil nil 14 3 1985 nil nil nil nil))) + '(nil nil nil 14 3 1985 nil nil nil))) (should (equal (iso8601-parse-date "19850314") - '(nil nil nil 14 3 1985 nil nil nil nil))) + '(nil nil nil 14 3 1985 nil nil nil))) (should (equal (iso8601-parse-date "1985-02") - '(nil nil nil nil 2 1985 nil nil nil nil)))) + '(nil nil nil nil 2 1985 nil nil nil)))) (ert-deftest test-iso8601-date-obsolete () (should (equal (iso8601-parse-date "--02-01") - '(nil nil nil 1 2 nil nil nil nil nil))) + '(nil nil nil 1 2 nil nil nil nil))) (should (equal (iso8601-parse-date "--0201") - '(nil nil nil 1 2 nil nil nil nil nil)))) + '(nil nil nil 1 2 nil nil nil nil)))) (ert-deftest test-iso8601-date-weeks () (should (equal (iso8601-parse-date "2008W39-6") - '(nil nil nil 27 9 2008 nil nil nil nil))) + '(nil nil nil 27 9 2008 nil nil nil))) (should (equal (iso8601-parse-date "2009W01-1") - '(nil nil nil 29 12 2008 nil nil nil nil))) + '(nil nil nil 29 12 2008 nil nil nil))) (should (equal (iso8601-parse-date "2009W53-7") - '(nil nil nil 3 1 2010 nil nil nil nil)))) + '(nil nil nil 3 1 2010 nil nil nil)))) (ert-deftest test-iso8601-date-ordinals () (should (equal (iso8601-parse-date "1981-095") - '(nil nil nil 5 4 1981 nil nil nil nil)))) + '(nil nil nil 5 4 1981 nil nil nil)))) (ert-deftest test-iso8601-time () (should (equal (iso8601-parse-time "13:47:30") - '(30 47 13 nil nil nil nil nil nil 0))) + '(30 47 13 nil nil nil nil nil nil))) (should (equal (iso8601-parse-time "134730") - '(30 47 13 nil nil nil nil nil nil 0))) + '(30 47 13 nil nil nil nil nil nil))) (should (equal (iso8601-parse-time "1347") - '(0 47 13 nil nil nil nil nil nil 0)))) + '(0 47 13 nil nil nil nil nil nil)))) (ert-deftest test-iso8601-combined () (should (equal (iso8601-parse "2008-03-02T13:47:30") - '(30 47 13 2 3 2008 nil nil nil 0))) + '(30 47 13 2 3 2008 nil nil nil))) (should (equal (iso8601-parse "2008-03-02T13:47:30Z") - '(30 47 13 2 3 2008 nil nil 0 0))) + '(30 47 13 2 3 2008 nil nil 0))) (should (equal (iso8601-parse "2008-03-02T13:47:30+01:00") - '(30 47 13 2 3 2008 nil nil 3600 0))) + '(30 47 13 2 3 2008 nil nil 3600))) (should (equal (iso8601-parse "2008-03-02T13:47:30-01") - '(30 47 13 2 3 2008 nil nil -3600 0)))) + '(30 47 13 2 3 2008 nil nil -3600)))) (ert-deftest test-iso8601-duration () (should (equal (iso8601-parse-duration "P3Y6M4DT12H30M5S") - '(5 30 12 4 6 3 nil nil nil 0))) + '(5 30 12 4 6 3 nil nil nil))) (should (equal (iso8601-parse-duration "P1M") - '(0 0 0 0 1 0 nil nil nil 0))) + '(0 0 0 0 1 0 nil nil nil))) (should (equal (iso8601-parse-duration "PT1M") - '(0 1 0 0 0 0 nil nil nil 0))) + '(0 1 0 0 0 0 nil nil nil))) (should (equal (iso8601-parse-duration "P0003-06-04T12:30:05") - '(5 30 12 4 6 3 nil nil nil 0)))) + '(5 30 12 4 6 3 nil nil nil)))) (ert-deftest test-iso8601-invalid () (should-not (iso8601-valid-p " 2008-03-02T13:47:30-01")) @@ -94,149 +94,149 @@ (ert-deftest test-iso8601-intervals () (should (equal (iso8601-parse-interval "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z") - '((0 0 13 1 3 2007 nil nil 0 0) - (0 30 15 11 5 2008 nil nil 0 0) + '((0 0 13 1 3 2007 nil nil 0) + (0 30 15 11 5 2008 nil nil 0) ;; Hm... can't really use decode-time for time differences... - (0 30 2 14 3 1971 0 nil 0 0)))) + (0 30 2 14 3 1971 0 nil 0)))) (should (equal (iso8601-parse-interval "2007-03-01T13:00:00Z/P1Y2M10DT2H30M") - '((0 0 13 1 3 2007 nil nil 0 0) - (0 30 15 11 5 2008 nil nil 0 0) - (0 30 2 10 2 1 nil nil nil 0)))) + '((0 0 13 1 3 2007 nil nil 0) + (0 30 15 11 5 2008 nil nil 0) + (0 30 2 10 2 1 nil nil nil)))) (should (equal (iso8601-parse-interval "P1Y2M10DT2H30M/2008-05-11T15:30:00Z") - '((0 0 13 1 3 2007 nil nil 0 0) - (0 30 15 11 5 2008 nil nil 0 0) - (0 30 2 10 2 1 nil nil nil 0))))) + '((0 0 13 1 3 2007 nil nil 0) + (0 30 15 11 5 2008 nil nil 0) + (0 30 2 10 2 1 nil nil nil))))) (ert-deftest standard-test-dates () (should (equal (iso8601-parse-date "19850412") - '(nil nil nil 12 4 1985 nil nil nil nil))) + '(nil nil nil 12 4 1985 nil nil nil))) (should (equal (iso8601-parse-date "1985-04-12") - '(nil nil nil 12 4 1985 nil nil nil nil))) + '(nil nil nil 12 4 1985 nil nil nil))) (should (equal (iso8601-parse-date "1985102") - '(nil nil nil 12 4 1985 nil nil nil nil))) + '(nil nil nil 12 4 1985 nil nil nil))) (should (equal (iso8601-parse-date "1985-102") - '(nil nil nil 12 4 1985 nil nil nil nil))) + '(nil nil nil 12 4 1985 nil nil nil))) (should (equal (iso8601-parse-date "1985W155") - '(nil nil nil 12 4 1985 nil nil nil nil))) + '(nil nil nil 12 4 1985 nil nil nil))) (should (equal (iso8601-parse-date "1985-W15-5") - '(nil nil nil 12 4 1985 nil nil nil nil))) + '(nil nil nil 12 4 1985 nil nil nil))) (should (equal (iso8601-parse-date "1985W15") - '(nil nil nil 7 4 1985 nil nil nil nil))) + '(nil nil nil 7 4 1985 nil nil nil))) (should (equal (iso8601-parse-date "1985-W15") - '(nil nil nil 7 4 1985 nil nil nil nil))) + '(nil nil nil 7 4 1985 nil nil nil))) (should (equal (iso8601-parse-date "1985-04") - '(nil nil nil nil 4 1985 nil nil nil nil))) + '(nil nil nil nil 4 1985 nil nil nil))) (should (equal (iso8601-parse-date "1985") - '(nil nil nil nil nil 1985 nil nil nil nil))) + '(nil nil nil nil nil 1985 nil nil nil))) (should (equal (iso8601-parse-date "+1985-04-12") - '(nil nil nil 12 4 1985 nil nil nil nil))) + '(nil nil nil 12 4 1985 nil nil nil))) (should (equal (iso8601-parse-date "+19850412") - '(nil nil nil 12 4 1985 nil nil nil nil)))) + '(nil nil nil 12 4 1985 nil nil nil)))) (ert-deftest standard-test-time-of-day-local-time () (should (equal (iso8601-parse-time "152746") - '(46 27 15 nil nil nil nil nil nil 0))) + '(46 27 15 nil nil nil nil nil nil))) (should (equal (iso8601-parse-time "15:27:46") - '(46 27 15 nil nil nil nil nil nil 0))) + '(46 27 15 nil nil nil nil nil nil))) (should (equal (iso8601-parse-time "1528") - '(0 28 15 nil nil nil nil nil nil 0))) + '(0 28 15 nil nil nil nil nil nil))) (should (equal (iso8601-parse-time "15:28") - '(0 28 15 nil nil nil nil nil nil 0))) + '(0 28 15 nil nil nil nil nil nil))) (should (equal (iso8601-parse-time "15") - '(0 0 15 nil nil nil nil nil nil 0)))) + '(0 0 15 nil nil nil nil nil nil)))) (ert-deftest standard-test-time-of-day-fractions () ;; decoded-time doesn't support sub-second times. ;; (should (equal (iso8601-parse-time "152735,5") - ;; '(46 27 15 nil nil nil nil nil nil (5 . 10)))) + ;; '(46 27 15 nil nil nil nil nil nil))) ;; (should (equal (iso8601-parse-time "15:27:35,5") - ;; '(46 27 15 nil nil nil nil nil nil (5 . 10)))) + ;; '(46 27 15 nil nil nil nil nil nil))) ) (ert-deftest standard-test-time-of-day-beginning-of-day () (should (equal (iso8601-parse-time "000000") - '(0 0 0 nil nil nil nil nil nil 0))) + '(0 0 0 nil nil nil nil nil nil))) (should (equal (iso8601-parse-time "00:00:00") - '(0 0 0 nil nil nil nil nil nil 0))) + '(0 0 0 nil nil nil nil nil nil))) (should (equal (iso8601-parse-time "0000") - '(0 0 0 nil nil nil nil nil nil 0))) + '(0 0 0 nil nil nil nil nil nil))) (should (equal (iso8601-parse-time "00:00") - '(0 0 0 nil nil nil nil nil nil 0)))) + '(0 0 0 nil nil nil nil nil nil)))) (ert-deftest standard-test-time-of-day-utc () (should (equal (iso8601-parse-time "232030Z") - '(30 20 23 nil nil nil nil nil 0 0))) + '(30 20 23 nil nil nil nil nil 0))) (should (equal (iso8601-parse-time "23:20:30Z") - '(30 20 23 nil nil nil nil nil 0 0))) + '(30 20 23 nil nil nil nil nil 0))) (should (equal (iso8601-parse-time "2320Z") - '(0 20 23 nil nil nil nil nil 0 0))) + '(0 20 23 nil nil nil nil nil 0))) (should (equal (iso8601-parse-time "23:20Z") - '(0 20 23 nil nil nil nil nil 0 0))) + '(0 20 23 nil nil nil nil nil 0))) (should (equal (iso8601-parse-time "23Z") - '(0 0 23 nil nil nil nil nil 0 0)))) + '(0 0 23 nil nil nil nil nil 0)))) (ert-deftest standard-test-time-of-day-zone () (should (equal (iso8601-parse-time "152746+0100") - '(46 27 15 nil nil nil nil nil 3600 0))) + '(46 27 15 nil nil nil nil nil 3600))) (should (equal (iso8601-parse-time "15:27:46+0100") - '(46 27 15 nil nil nil nil nil 3600 0))) + '(46 27 15 nil nil nil nil nil 3600))) (should (equal (iso8601-parse-time "152746+01") - '(46 27 15 nil nil nil nil nil 3600 0))) + '(46 27 15 nil nil nil nil nil 3600))) (should (equal (iso8601-parse-time "15:27:46+01") - '(46 27 15 nil nil nil nil nil 3600 0))) + '(46 27 15 nil nil nil nil nil 3600))) (should (equal (iso8601-parse-time "152746-0500") - '(46 27 15 nil nil nil nil nil -18000 0))) + '(46 27 15 nil nil nil nil nil -18000))) (should (equal (iso8601-parse-time "15:27:46-0500") - '(46 27 15 nil nil nil nil nil -18000 0))) + '(46 27 15 nil nil nil nil nil -18000))) (should (equal (iso8601-parse-time "152746-05") - '(46 27 15 nil nil nil nil nil -18000 0))) + '(46 27 15 nil nil nil nil nil -18000))) (should (equal (iso8601-parse-time "15:27:46-05") - '(46 27 15 nil nil nil nil nil -18000 0)))) + '(46 27 15 nil nil nil nil nil -18000)))) (ert-deftest standard-test-date-and-time-of-day () (should (equal (iso8601-parse "19850412T101530") - '(30 15 10 12 4 1985 nil nil nil 0))) + '(30 15 10 12 4 1985 nil nil nil))) (should (equal (iso8601-parse "1985-04-12T10:15:30") - '(30 15 10 12 4 1985 nil nil nil 0))) + '(30 15 10 12 4 1985 nil nil nil))) (should (equal (iso8601-parse "1985102T235030Z") - '(30 50 23 12 4 1985 nil nil 0 0))) + '(30 50 23 12 4 1985 nil nil 0))) (should (equal (iso8601-parse "1985-102T23:50:30Z") - '(30 50 23 12 4 1985 nil nil 0 0))) + '(30 50 23 12 4 1985 nil nil 0))) (should (equal (iso8601-parse "1985W155T235030") - '(30 50 23 12 4 1985 nil nil nil 0))) + '(30 50 23 12 4 1985 nil nil nil))) (should (equal (iso8601-parse "1985-W155T23:50:30") - '(30 50 23 12 4 1985 nil nil nil 0)))) + '(30 50 23 12 4 1985 nil nil nil)))) (ert-deftest standard-test-interval () ;; A time interval starting at 20 minutes and 50 seconds past 23 ;; hours on 12 April 1985 and ending at 30 minutes past 10 hours on ;; 25 June 1985. (should (equal (iso8601-parse-interval "19850412T232050Z/19850625T103000Z") - '((50 20 23 12 4 1985 nil nil 0 0) - (0 30 10 25 6 1985 nil nil 0 0) - (10 9 11 15 3 1970 0 nil 0 0)))) + '((50 20 23 12 4 1985 nil nil 0) + (0 30 10 25 6 1985 nil nil 0) + (10 9 11 15 3 1970 0 nil 0)))) (should (equal (iso8601-parse-interval "1985-04-12T23:20:50Z/1985-06-25T10:30:00Z") - '((50 20 23 12 4 1985 nil nil 0 0) - (0 30 10 25 6 1985 nil nil 0 0) - (10 9 11 15 3 1970 0 nil 0 0)))) + '((50 20 23 12 4 1985 nil nil 0) + (0 30 10 25 6 1985 nil nil 0) + (10 9 11 15 3 1970 0 nil 0)))) ;; A time interval starting at 12 April 1985 and ending on 25 June ;; 1985. @@ -251,41 +251,41 @@ ;; A time interval of 2 years, 10 months, 15 days, 10 hours, 20 ;; minutes and 30 seconds. (should (equal (iso8601-parse-duration "P2Y10M15DT10H20M30S") - '(30 20 10 15 10 2 nil nil nil 0))) + '(30 20 10 15 10 2 nil nil nil))) (should (equal (iso8601-parse-duration "P00021015T102030") - '(30 20 10 15 10 2 nil nil nil 0))) + '(30 20 10 15 10 2 nil nil nil))) (should (equal (iso8601-parse-duration "P0002-10-15T10:20:30") - '(30 20 10 15 10 2 nil nil nil 0))) + '(30 20 10 15 10 2 nil nil nil))) ;; A time interval of 1 year and 6 months. (should (equal (iso8601-parse-duration "P1Y6M") - '(0 0 0 0 6 1 nil nil nil 0))) + '(0 0 0 0 6 1 nil nil nil))) (should (equal (iso8601-parse-duration "P0001-06") - '(nil nil nil nil 6 1 nil nil nil nil))) + '(nil nil nil nil 6 1 nil nil nil))) ;; A time interval of seventy-two hours. (should (equal (iso8601-parse-duration "PT72H") - '(0 0 72 0 0 0 nil nil nil 0))) + '(0 0 72 0 0 0 nil nil nil))) ;; Defined by start and duration ;; A time interval of 1 year, 2 months, 15 days and 12 hours, ;; beginning on 12 April 1985 at 20 minutes past 23 hours. (should (equal (iso8601-parse-interval "19850412T232000/P1Y2M15DT12H") - '((0 20 23 12 4 1985 nil nil nil 0) - (0 20 11 28 6 1986 nil nil nil 0) - (0 0 12 15 2 1 nil nil nil 0)))) + '((0 20 23 12 4 1985 nil nil nil) + (0 20 11 28 6 1986 nil nil nil) + (0 0 12 15 2 1 nil nil nil)))) (should (equal (iso8601-parse-interval "1985-04-12T23:20:00/P1Y2M15DT12H") - '((0 20 23 12 4 1985 nil nil nil 0) - (0 20 11 28 6 1986 nil nil nil 0) - (0 0 12 15 2 1 nil nil nil 0)))) + '((0 20 23 12 4 1985 nil nil nil) + (0 20 11 28 6 1986 nil nil nil) + (0 0 12 15 2 1 nil nil nil)))) ;; Defined by duration and end ;; A time interval of 1 year, 2 months, 15 days and 12 hours, ending ;; on 12 April 1985 at 20 minutes past 23 hour. (should (equal (iso8601-parse-interval "P1Y2M15DT12H/19850412T232000") - '((0 20 11 28 1 1984 nil nil nil 0) - (0 20 23 12 4 1985 nil nil nil 0) - (0 0 12 15 2 1 nil nil nil 0))))) + '((0 20 11 28 1 1984 nil nil nil) + (0 20 23 12 4 1985 nil nil nil) + (0 0 12 15 2 1 nil nil nil))))) ;;; iso8601-tests.el ends here diff --git a/test/lisp/calendar/parse-time-tests.el b/test/lisp/calendar/parse-time-tests.el index 61a3838a52..7435620b71 100644 --- a/test/lisp/calendar/parse-time-tests.el +++ b/test/lisp/calendar/parse-time-tests.el @@ -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) diff --git a/test/src/timefns-tests.el b/test/src/timefns-tests.el index 362e7655a9..13ab7d83c3 100644 --- a/test/src/timefns-tests.el +++ b/test/src/timefns-tests.el @@ -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 @@ timefns-tests--have-leap-seconds (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 () -- 2.17.1