diff options
| author | Paul Eggert | 2019-08-16 22:09:04 -0700 |
|---|---|---|
| committer | Paul Eggert | 2019-08-16 23:25:07 -0700 |
| commit | 37257d6acadff17bd1e52cfa460950bcb684c5c3 (patch) | |
| tree | ab7088cfa725561c8456f388cff79466948b3532 /lisp | |
| parent | d7c9ed8445d13de7350be3360d68717362f89929 (diff) | |
| download | emacs-37257d6acadff17bd1e52cfa460950bcb684c5c3.tar.gz emacs-37257d6acadff17bd1e52cfa460950bcb684c5c3.zip | |
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.
Diffstat (limited to 'lisp')
| -rw-r--r-- | lisp/calendar/icalendar.el | 2 | ||||
| -rw-r--r-- | lisp/calendar/iso8601.el | 14 | ||||
| -rw-r--r-- | lisp/calendar/parse-time.el | 8 | ||||
| -rw-r--r-- | lisp/calendar/time-date.el | 75 | ||||
| -rw-r--r-- | lisp/net/soap-client.el | 4 | ||||
| -rw-r--r-- | lisp/org/org.el | 4 | ||||
| -rw-r--r-- | lisp/simple.el | 8 |
7 files changed, 41 insertions, 74 deletions
diff --git a/lisp/calendar/icalendar.el b/lisp/calendar/icalendar.el index 84f579ad44e..c2688705e30 100644 --- a/lisp/calendar/icalendar.el +++ b/lisp/calendar/icalendar.el | |||
| @@ -644,7 +644,7 @@ FIXME: multiple comma-separated values should be allowed!" | |||
| 644 | ;; create the decoded date-time | 644 | ;; create the decoded date-time |
| 645 | ;; FIXME!?! | 645 | ;; FIXME!?! |
| 646 | (let ((decoded-time (list second minute hour day month year | 646 | (let ((decoded-time (list second minute hour day month year |
| 647 | nil -1 zone 0))) | 647 | nil -1 zone))) |
| 648 | (condition-case nil | 648 | (condition-case nil |
| 649 | (decode-time (encode-time decoded-time)) | 649 | (decode-time (encode-time decoded-time)) |
| 650 | (error | 650 | (error |
diff --git a/lisp/calendar/iso8601.el b/lisp/calendar/iso8601.el index 51f5dff9091..30352c7e75f 100644 --- a/lisp/calendar/iso8601.el +++ b/lisp/calendar/iso8601.el | |||
| @@ -129,8 +129,7 @@ well as variants like \"2008W32\" (week number) and | |||
| 129 | (let ((time (iso8601-parse-time time-string))) | 129 | (let ((time (iso8601-parse-time time-string))) |
| 130 | (setf (decoded-time-hour date) (decoded-time-hour time)) | 130 | (setf (decoded-time-hour date) (decoded-time-hour time)) |
| 131 | (setf (decoded-time-minute date) (decoded-time-minute time)) | 131 | (setf (decoded-time-minute date) (decoded-time-minute time)) |
| 132 | (setf (decoded-time-second date) (decoded-time-second time)) | 132 | (setf (decoded-time-second date) (decoded-time-second time)))) |
| 133 | (setf (decoded-time-subsec date) (decoded-time-subsec time)))) | ||
| 134 | ;; The time zone is optional. | 133 | ;; The time zone is optional. |
| 135 | (when zone-string | 134 | (when zone-string |
| 136 | (setf (decoded-time-zone date) | 135 | (setf (decoded-time-zone date) |
| @@ -237,8 +236,6 @@ well as variants like \"2008W32\" (week number) and | |||
| 237 | (iso8601--decoded-time :hour hour | 236 | (iso8601--decoded-time :hour hour |
| 238 | :minute (or minute 0) | 237 | :minute (or minute 0) |
| 239 | :second (or second 0) | 238 | :second (or second 0) |
| 240 | ;; FIXME: Support subsec. | ||
| 241 | :subsec 0 | ||
| 242 | :zone (and zone | 239 | :zone (and zone |
| 243 | (* 60 (iso8601-parse-zone | 240 | (* 60 (iso8601-parse-zone |
| 244 | zone))))))))) | 241 | zone))))))))) |
| @@ -277,9 +274,7 @@ Return the number of minutes." | |||
| 277 | :day (or (match-string 3 string) 0) | 274 | :day (or (match-string 3 string) 0) |
| 278 | :hour (or (match-string 5 string) 0) | 275 | :hour (or (match-string 5 string) 0) |
| 279 | :minute (or (match-string 6 string) 0) | 276 | :minute (or (match-string 6 string) 0) |
| 280 | :second (or (match-string 7 string) 0) | 277 | :second (or (match-string 7 string) 0))) |
| 281 | ;; FIXME: Support subsec. | ||
| 282 | :subsec 0)) | ||
| 283 | ;; PnW: Weeks. | 278 | ;; PnW: Weeks. |
| 284 | ((iso8601--match iso8601--duration-week-match string) | 279 | ((iso8601--match iso8601--duration-week-match string) |
| 285 | (let ((weeks (string-to-number (match-string 1 string)))) | 280 | (let ((weeks (string-to-number (match-string 1 string)))) |
| @@ -341,7 +336,7 @@ Return the number of minutes." | |||
| 341 | 336 | ||
| 342 | (cl-defun iso8601--decoded-time (&key second minute hour | 337 | (cl-defun iso8601--decoded-time (&key second minute hour |
| 343 | day month year | 338 | day month year |
| 344 | dst zone subsec) | 339 | dst zone) |
| 345 | (list (iso8601--value second) | 340 | (list (iso8601--value second) |
| 346 | (iso8601--value minute) | 341 | (iso8601--value minute) |
| 347 | (iso8601--value hour) | 342 | (iso8601--value hour) |
| @@ -350,8 +345,7 @@ Return the number of minutes." | |||
| 350 | (iso8601--value year) | 345 | (iso8601--value year) |
| 351 | nil | 346 | nil |
| 352 | dst | 347 | dst |
| 353 | zone | 348 | zone)) |
| 354 | subsec)) | ||
| 355 | 349 | ||
| 356 | (defun iso8601--encode-time (time) | 350 | (defun iso8601--encode-time (time) |
| 357 | "Like `encode-time', but fill in nil values in TIME." | 351 | "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 9af93b5b1ee..b0b277db77d 100644 --- a/lisp/calendar/parse-time.el +++ b/lisp/calendar/parse-time.el | |||
| @@ -148,7 +148,7 @@ letters, digits, plus or minus signs or colons." | |||
| 148 | 148 | ||
| 149 | ;;;###autoload | 149 | ;;;###autoload |
| 150 | (defun parse-time-string (string) | 150 | (defun parse-time-string (string) |
| 151 | "Parse the time in STRING into (SEC MIN HOUR DAY MON YEAR DOW DST TZ SUBSEC). | 151 | "Parse the time in STRING into (SEC MIN HOUR DAY MON YEAR DOW DST TZ). |
| 152 | STRING should be something resembling an RFC 822 (or later) date-time, e.g., | 152 | STRING should be something resembling an RFC 822 (or later) date-time, e.g., |
| 153 | \"Fri, 25 Mar 2016 16:24:56 +0100\", but this function is | 153 | \"Fri, 25 Mar 2016 16:24:56 +0100\", but this function is |
| 154 | somewhat liberal in what format it accepts, and will attempt to | 154 | somewhat liberal in what format it accepts, and will attempt to |
| @@ -156,7 +156,7 @@ return a \"likely\" value even for somewhat malformed strings. | |||
| 156 | The values returned are identical to those of `decode-time', but | 156 | The values returned are identical to those of `decode-time', but |
| 157 | any unknown values other than DST are returned as nil, and an | 157 | any unknown values other than DST are returned as nil, and an |
| 158 | unknown DST value is returned as -1." | 158 | unknown DST value is returned as -1." |
| 159 | (let ((time (list nil nil nil nil nil nil nil -1 nil nil)) | 159 | (let ((time (list nil nil nil nil nil nil nil -1 nil)) |
| 160 | (temp (parse-time-tokenize (downcase string)))) | 160 | (temp (parse-time-tokenize (downcase string)))) |
| 161 | (while temp | 161 | (while temp |
| 162 | (let ((parse-time-elt (pop temp)) | 162 | (let ((parse-time-elt (pop temp)) |
| @@ -193,10 +193,6 @@ unknown DST value is returned as -1." | |||
| 193 | (funcall this))) | 193 | (funcall this))) |
| 194 | parse-time-val))) | 194 | parse-time-val))) |
| 195 | (setf (nth (pop slots) time) new-val)))))))) | 195 | (setf (nth (pop slots) time) new-val)))))))) |
| 196 | ;; FIXME: Currently parse-time-string does not parse subseconds. | ||
| 197 | ;; So if seconds were found, set subseconds to zero. | ||
| 198 | (when (nth 0 time) | ||
| 199 | (setf (nth 9 time) 0)) | ||
| 200 | time)) | 196 | time)) |
| 201 | 197 | ||
| 202 | (defun parse-iso8601-time-string (date-string) | 198 | (defun parse-iso8601-time-string (date-string) |
diff --git a/lisp/calendar/time-date.el b/lisp/calendar/time-date.el index fa5e886869a..f3d252f03c6 100644 --- a/lisp/calendar/time-date.el +++ b/lisp/calendar/time-date.el | |||
| @@ -420,26 +420,13 @@ changes in daylight saving time are not taken into account." | |||
| 420 | 420 | ||
| 421 | ;; Do the time part, which is pretty simple (except for leap | 421 | ;; Do the time part, which is pretty simple (except for leap |
| 422 | ;; seconds, I guess). | 422 | ;; seconds, I guess). |
| 423 | (setq seconds (+ (* (or (decoded-time-hour delta) 0) 3600) | ||
| 424 | (* (or (decoded-time-minute delta) 0) 60) | ||
| 425 | (or (decoded-time-second delta) 0))) | ||
| 426 | (when (decoded-time-subsec delta) | ||
| 427 | (let* ((subsec (time-convert (time-add (decoded-time-subsec time) | ||
| 428 | (decoded-time-subsec delta)) | ||
| 429 | t)) | ||
| 430 | (s (time-convert subsec 'integer))) | ||
| 431 | (setq seconds (+ seconds s)) | ||
| 432 | (setf (decoded-time-subsec time) (time-subtract subsec s)))) | ||
| 433 | |||
| 434 | ;; Time zone adjustments are basically the same as time adjustments. | 423 | ;; Time zone adjustments are basically the same as time adjustments. |
| 435 | (setq seconds (+ seconds (or (decoded-time-zone delta) 0))) | 424 | (setq seconds (time-add (+ (* (or (decoded-time-hour delta) 0) 3600) |
| 436 | 425 | (* (or (decoded-time-minute delta) 0) 60) | |
| 437 | (cond | 426 | (or (decoded-time-zone delta) 0)) |
| 438 | ((> seconds 0) | 427 | (or (decoded-time-second delta) 0))) |
| 439 | (decoded-time--alter-second time seconds t)) | ||
| 440 | ((< seconds 0) | ||
| 441 | (decoded-time--alter-second time (abs seconds) nil))) | ||
| 442 | 428 | ||
| 429 | (decoded-time--alter-second time seconds) | ||
| 443 | time)) | 430 | time)) |
| 444 | 431 | ||
| 445 | (defun decoded-time--alter-month (time increase) | 432 | (defun decoded-time--alter-month (time increase) |
| @@ -472,38 +459,31 @@ changes in daylight saving time are not taken into account." | |||
| 472 | (date-days-in-month (decoded-time-year time) | 459 | (date-days-in-month (decoded-time-year time) |
| 473 | (decoded-time-month time)))))) | 460 | (decoded-time-month time)))))) |
| 474 | 461 | ||
| 475 | (defun decoded-time--alter-second (time seconds increase) | 462 | (defun decoded-time--alter-second (time seconds) |
| 476 | "Increase or decrease the time in TIME by SECONDS." | 463 | "Increase the time in TIME by SECONDS." |
| 477 | (let ((old (+ (* (or (decoded-time-hour time) 0) 3600) | 464 | (let* ((secsperday 86400) |
| 478 | (* (or (decoded-time-minute time) 0) 60) | 465 | (old (time-add (+ (* 3600 (or (decoded-time-hour time) 0)) |
| 479 | (or (decoded-time-second time) 0)))) | 466 | (* 60 (or (decoded-time-minute time) 0))) |
| 480 | 467 | (or (decoded-time-second time) 0))) | |
| 481 | (if increase | 468 | (new (time-add old seconds))) |
| 482 | (progn | 469 | ;; Hm... DST... |
| 483 | (setq old (+ old seconds)) | 470 | (while (time-less-p new 0) |
| 484 | (setf (decoded-time-second time) (% old 60) | 471 | (decoded-time--alter-day time nil) |
| 485 | (decoded-time-minute time) (% (/ old 60) 60) | 472 | (setq new (time-add new secsperday))) |
| 486 | (decoded-time-hour time) (% (/ old 3600) 24)) | 473 | (while (not (time-less-p new secsperday)) |
| 487 | ;; Hm... DST... | 474 | (decoded-time--alter-day time t) |
| 488 | (let ((days (/ old (* 60 60 24)))) | 475 | (setq new (time-subtract new secsperday))) |
| 489 | (while (> days 0) | 476 | (let ((sec (time-convert new 'integer))) |
| 490 | (decoded-time--alter-day time t) | 477 | (setf (decoded-time-second time) (time-add (% sec 60) |
| 491 | (cl-decf days)))) | 478 | (time-subtract new sec)) |
| 492 | (setq old (abs (- old seconds))) | 479 | (decoded-time-minute time) (% (/ sec 60) 60) |
| 493 | (setf (decoded-time-second time) (% old 60) | 480 | (decoded-time-hour time) (/ sec 3600))))) |
| 494 | (decoded-time-minute time) (% (/ old 60) 60) | ||
| 495 | (decoded-time-hour time) (% (/ old 3600) 24)) | ||
| 496 | ;; Hm... DST... | ||
| 497 | (let ((days (/ old (* 60 60 24)))) | ||
| 498 | (while (> days 0) | ||
| 499 | (decoded-time--alter-day time nil) | ||
| 500 | (cl-decf days)))))) | ||
| 501 | 481 | ||
| 502 | (cl-defun make-decoded-time (&key second minute hour | 482 | (cl-defun make-decoded-time (&key second minute hour |
| 503 | day month year | 483 | day month year |
| 504 | dst zone subsec) | 484 | dst zone) |
| 505 | "Return a `decoded-time' structure with only the keywords given filled out." | 485 | "Return a `decoded-time' structure with only the keywords given filled out." |
| 506 | (list second minute hour day month year nil dst zone subsec)) | 486 | (list second minute hour day month year nil dst zone)) |
| 507 | 487 | ||
| 508 | (defun decoded-time-set-defaults (time &optional default-zone) | 488 | (defun decoded-time-set-defaults (time &optional default-zone) |
| 509 | "Set any nil values in `decoded-time' TIME to default values. | 489 | "Set any nil values in `decoded-time' TIME to default values. |
| @@ -533,9 +513,6 @@ TIME is modified and returned." | |||
| 533 | (when (and (not (decoded-time-zone time)) | 513 | (when (and (not (decoded-time-zone time)) |
| 534 | default-zone) | 514 | default-zone) |
| 535 | (setf (decoded-time-zone time) 0)) | 515 | (setf (decoded-time-zone time) 0)) |
| 536 | |||
| 537 | (unless (decoded-time-subsec time) | ||
| 538 | (setf (decoded-time-subsec time) 0)) | ||
| 539 | time) | 516 | time) |
| 540 | 517 | ||
| 541 | (provide 'time-date) | 518 | (provide 'time-date) |
diff --git a/lisp/net/soap-client.el b/lisp/net/soap-client.el index eb08511171e..7ce7d79c742 100644 --- a/lisp/net/soap-client.el +++ b/lisp/net/soap-client.el | |||
| @@ -561,8 +561,8 @@ gMonthDay, gDay or gMonth. | |||
| 561 | Return a list in a format (SEC MINUTE HOUR DAY MONTH YEAR | 561 | Return a list in a format (SEC MINUTE HOUR DAY MONTH YEAR |
| 562 | SEC-FRACTION DATATYPE ZONE). This format is meant to be similar | 562 | SEC-FRACTION DATATYPE ZONE). This format is meant to be similar |
| 563 | to that returned by `decode-time' (and compatible with | 563 | to that returned by `decode-time' (and compatible with |
| 564 | `encode-time'). The differences are the SUBSEC (fractional | 564 | `encode-time'). The differences are the SEC (seconds) |
| 565 | seconds) field is omitted, the DOW (day-of-week) field | 565 | field is always an integer, the DOW (day-of-week) field |
| 566 | is replaced with SEC-FRACTION, a float representing the | 566 | is replaced with SEC-FRACTION, a float representing the |
| 567 | fractional seconds, and the DST (daylight savings time) field is | 567 | fractional seconds, and the DST (daylight savings time) field is |
| 568 | replaced with DATATYPE, a symbol representing the XSD primitive | 568 | replaced with DATATYPE, a symbol representing the XSD primitive |
diff --git a/lisp/org/org.el b/lisp/org/org.el index 336c413c8c7..ab29353ae89 100644 --- a/lisp/org/org.el +++ b/lisp/org/org.el | |||
| @@ -17775,12 +17775,14 @@ NODEFAULT, hour and minute fields will be nil if not given." | |||
| 17775 | (string-to-number (match-string 4 s)) | 17775 | (string-to-number (match-string 4 s)) |
| 17776 | (string-to-number (match-string 3 s)) | 17776 | (string-to-number (match-string 3 s)) |
| 17777 | (string-to-number (match-string 2 s)) | 17777 | (string-to-number (match-string 2 s)) |
| 17778 | nil nil nil 0)) | 17778 | nil nil nil)) |
| 17779 | ((string-match "^<[^>]+>$" s) | 17779 | ((string-match "^<[^>]+>$" s) |
| 17780 | ;; FIXME: `decode-time' needs to be called with ZONE as its | 17780 | ;; FIXME: `decode-time' needs to be called with ZONE as its |
| 17781 | ;; second argument. However, this requires at least Emacs | 17781 | ;; second argument. However, this requires at least Emacs |
| 17782 | ;; 25.1. We can do it when we switch to this version as our | 17782 | ;; 25.1. We can do it when we switch to this version as our |
| 17783 | ;; minimal requirement. | 17783 | ;; minimal requirement. |
| 17784 | ;; FIXME: decode-time needs to be called with t as its | ||
| 17785 | ;; third argument, but this requires at least Emacs 27. | ||
| 17784 | (decode-time (org-matcher-time s))) | 17786 | (decode-time (org-matcher-time s))) |
| 17785 | (t (error "Not a standard Org time string: %s" s)))) | 17787 | (t (error "Not a standard Org time string: %s" s)))) |
| 17786 | 17788 | ||
diff --git a/lisp/simple.el b/lisp/simple.el index cb938bb341d..fdf7d893cde 100644 --- a/lisp/simple.el +++ b/lisp/simple.el | |||
| @@ -9082,8 +9082,9 @@ to capitalize ARG words." | |||
| 9082 | (:copier nil) | 9082 | (:copier nil) |
| 9083 | (:type list)) | 9083 | (:type list)) |
| 9084 | (second nil :documentation "\ | 9084 | (second nil :documentation "\ |
| 9085 | This is an integer between 0 and 60 (inclusive). (60 is a leap | 9085 | This is an integer or a Lisp timestamp (TICKS . HZ) representing a nonnegative |
| 9086 | second, which only some operating systems support.)") | 9086 | number of seconds less than 61. (If not less than 60, it is a leap second, |
| 9087 | which only some operating systems support.)") | ||
| 9087 | (minute nil :documentation "This is an integer between 0 and 59 (inclusive).") | 9088 | (minute nil :documentation "This is an integer between 0 and 59 (inclusive).") |
| 9088 | (hour nil :documentation "This is an integer between 0 and 23 (inclusive).") | 9089 | (hour nil :documentation "This is an integer between 0 and 23 (inclusive).") |
| 9089 | (day nil :documentation "This is an integer between 1 and 31 (inclusive).") | 9090 | (day nil :documentation "This is an integer between 1 and 31 (inclusive).") |
| @@ -9099,9 +9100,6 @@ available.") | |||
| 9099 | (zone nil :documentation "\ | 9100 | (zone nil :documentation "\ |
| 9100 | This is an integer indicating the UTC offset in seconds, i.e., | 9101 | This is an integer indicating the UTC offset in seconds, i.e., |
| 9101 | the number of seconds east of Greenwich.") | 9102 | the number of seconds east of Greenwich.") |
| 9102 | (subsec nil :documentation "\ | ||
| 9103 | This is 0, or is an integer pair (TICKS . HZ) indicating TICKS/HZ seconds, | ||
| 9104 | where HZ is positive and TICKS is nonnegative and less than HZ.") | ||
| 9105 | ) | 9103 | ) |
| 9106 | 9104 | ||
| 9107 | 9105 | ||