]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix off-by-one bug in ISO 8601 BC years
authorPaul Eggert <eggert@cs.ucla.edu>
Sun, 6 Oct 2019 04:23:15 +0000 (21:23 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Sun, 6 Oct 2019 04:24:09 +0000 (21:24 -0700)
* lisp/calendar/iso8601.el (iso8601--year-match)
(iso8601--full-date-match, iso8601--without-day-match)
(iso8601--week-date-match, iso8601--ordinal-date-match)
(iso8601-parse-date):
Don’t bother to separate the year’s sign from the year,
as that distinction is not needed: ISO 8601 uses
astronomical year numbering with a year zero, which
is what the Emacs time functions use, so there’s no
need to treat nonpositive years specially.
(iso8601--adjust-year): Remove; no longer needed
since callers can just use string-to-number.
* test/lisp/calendar/iso8601-tests.el (test-iso8601-date-years):
Adjust test case to match fixed behavior.

lisp/calendar/iso8601.el
test/lisp/calendar/iso8601-tests.el

index 78a94d47be2a15da3c23704903481fb236a5896e..72929bdd7ac5c40a86207db8336a1068aa90abb4 100644 (file)
              regexps "\\|"))
 
 (defconst iso8601--year-match
-  "\\([+-]\\)?\\([0-9][0-9][0-9][0-9]\\)")
+  "\\([+-]?[0-9][0-9][0-9][0-9]\\)")
 (defconst iso8601--full-date-match
-  "\\([+-]\\)?\\([0-9][0-9][0-9][0-9]\\)-?\\([0-9][0-9]\\)-?\\([0-9][0-9]\\)")
+  "\\([+-]?[0-9][0-9][0-9][0-9]\\)-?\\([0-9][0-9]\\)-?\\([0-9][0-9]\\)")
 (defconst iso8601--without-day-match
-  "\\([+-]\\)?\\([0-9][0-9][0-9][0-9]\\)-\\([0-9][0-9]\\)")
+  "\\([+-]?[0-9][0-9][0-9][0-9]\\)-\\([0-9][0-9]\\)")
 (defconst iso8601--outdated-date-match
   "--\\([0-9][0-9]\\)-?\\([0-9][0-9]\\)")
 (defconst iso8601--week-date-match
-  "\\([+-]\\)?\\([0-9][0-9][0-9][0-9]\\)-?W\\([0-9][0-9]\\)-?\\([0-9]\\)?")
+  "\\([+-]?[0-9][0-9][0-9][0-9]\\)-?W\\([0-9][0-9]\\)-?\\([0-9]\\)?")
 (defconst iso8601--ordinal-date-match
-  "\\([+-]\\)?\\([0-9][0-9][0-9][0-9]\\)-?\\([0-9][0-9][0-9]\\)")
+  "\\([+-]?[0-9][0-9][0-9][0-9]\\)-?\\([0-9][0-9][0-9]\\)")
 (defconst iso8601--date-match
   (iso8601--concat-regexps
    (list iso8601--year-match
@@ -145,21 +145,18 @@ See `decode-time' for the meaning of FORM."
    ;; Just a year: [+-]YYYY.
    ((iso8601--match iso8601--year-match string)
     (iso8601--decoded-time
-     :year (iso8601--adjust-year (match-string 1 string)
-                                 (match-string 2 string))))
+     :year (string-to-number string)))
    ;; Calendar dates: YYYY-MM-DD and variants.
    ((iso8601--match iso8601--full-date-match string)
     (iso8601--decoded-time
-     :year (iso8601--adjust-year (match-string 1 string)
-                                 (match-string 2 string))
-     :month (match-string 3 string)
-     :day (match-string 4 string)))
+     :year (string-to-number (match-string 1 string))
+     :month (match-string 2 string)
+     :day (match-string 3 string)))
    ;; Calendar date without day: YYYY-MM.
    ((iso8601--match iso8601--without-day-match string)
     (iso8601--decoded-time
-     :year (iso8601--adjust-year (match-string 1 string)
-                                 (match-string 2 string))
-     :month (match-string 3 string)))
+     :year (string-to-number string)
+     :month (match-string 2 string)))
    ;; Outdated date without year: --MM-DD
    ((iso8601--match iso8601--outdated-date-match string)
     (iso8601--decoded-time
@@ -167,11 +164,10 @@ See `decode-time' for the meaning of FORM."
      :day (match-string 2 string)))
    ;; Week dates: YYYY-Www-D
    ((iso8601--match iso8601--week-date-match string)
-    (let* ((year (iso8601--adjust-year (match-string 1 string)
-                                       (match-string 2 string)))
-           (week (string-to-number (match-string 3 string)))
-           (day-of-week (and (match-string 4 string)
-                             (string-to-number (match-string 4 string))))
+    (let* ((year (string-to-number string))
+           (week (string-to-number (match-string 2 string)))
+           (day-of-week (and (match-string 3 string)
+                             (string-to-number (match-string 3 string))))
            (jan-start (decoded-time-weekday
                        (decode-time
                         (iso8601--encode-time
@@ -199,9 +195,8 @@ See `decode-time' for the meaning of FORM."
                                :day (decoded-time-day month-day)))))
    ;; Ordinal dates: YYYY-DDD
    ((iso8601--match iso8601--ordinal-date-match string)
-    (let* ((year (iso8601--adjust-year (match-string 1 string)
-                                       (match-string 2 string)))
-           (ordinal (string-to-number (match-string 3 string)))
+    (let* ((year (string-to-number (match-string 1 string)))
+           (ordinal (string-to-number (match-string 2 string)))
            (month-day (date-ordinal-to-time year ordinal)))
       (iso8601--decoded-time :year year
                              :month (decoded-time-month month-day)
@@ -209,16 +204,6 @@ See `decode-time' for the meaning of FORM."
    (t
     (signal 'wrong-type-argument string))))
 
-(defun iso8601--adjust-year (sign year)
-  (save-match-data
-    (let ((year (if (stringp year)
-                    (string-to-number year)
-                  year)))
-      (if (string= sign "-")
-          ;; -0001 is 2 BCE.
-          (1- (- year))
-        year))))
-
 (defun iso8601-parse-time (string &optional form)
   "Parse STRING, which should be an ISO 8601 time string.
 The return value will be a `decode-time' structure with just the
index b5a3c9538d1bce41fd8a5dd8a80e983167535ae9..cc18d0702d8c3918b2fa934e2008005b4ba6e4c4 100644 (file)
@@ -26,7 +26,7 @@
   (should (equal (iso8601-parse-date "1985")
                  '(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 -3 nil nil nil)))
   (should (equal (iso8601-parse-date "+1985")
                  '(nil nil nil nil nil 1985 nil nil nil))))