From: Paul Eggert Date: Sun, 6 Oct 2019 04:23:15 +0000 (-0700) Subject: Fix off-by-one bug in ISO 8601 BC years X-Git-Tag: emacs-27.0.90~1257 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=9d829b8be5b86668d5165b9d0c0cdc392b558dd3;p=emacs.git Fix off-by-one bug in ISO 8601 BC years * 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. --- diff --git a/lisp/calendar/iso8601.el b/lisp/calendar/iso8601.el index 78a94d47be2..72929bdd7ac 100644 --- a/lisp/calendar/iso8601.el +++ b/lisp/calendar/iso8601.el @@ -62,17 +62,17 @@ 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 diff --git a/test/lisp/calendar/iso8601-tests.el b/test/lisp/calendar/iso8601-tests.el index b5a3c9538d1..cc18d0702d8 100644 --- a/test/lisp/calendar/iso8601-tests.el +++ b/test/lisp/calendar/iso8601-tests.el @@ -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))))