diff options
| author | Paul Eggert | 2019-10-05 21:23:15 -0700 |
|---|---|---|
| committer | Paul Eggert | 2019-10-05 21:24:09 -0700 |
| commit | 9d829b8be5b86668d5165b9d0c0cdc392b558dd3 (patch) | |
| tree | b2dc5d94b96bf540fdfde80a33f93bf4b4018e66 | |
| parent | aadf72167673c8702531ba3802e3d5f137f139ba (diff) | |
| download | emacs-9d829b8be5b86668d5165b9d0c0cdc392b558dd3.tar.gz emacs-9d829b8be5b86668d5165b9d0c0cdc392b558dd3.zip | |
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.
| -rw-r--r-- | lisp/calendar/iso8601.el | 49 | ||||
| -rw-r--r-- | test/lisp/calendar/iso8601-tests.el | 2 |
2 files changed, 18 insertions, 33 deletions
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 @@ | |||
| 62 | regexps "\\|")) | 62 | regexps "\\|")) |
| 63 | 63 | ||
| 64 | (defconst iso8601--year-match | 64 | (defconst iso8601--year-match |
| 65 | "\\([+-]\\)?\\([0-9][0-9][0-9][0-9]\\)") | 65 | "\\([+-]?[0-9][0-9][0-9][0-9]\\)") |
| 66 | (defconst iso8601--full-date-match | 66 | (defconst iso8601--full-date-match |
| 67 | "\\([+-]\\)?\\([0-9][0-9][0-9][0-9]\\)-?\\([0-9][0-9]\\)-?\\([0-9][0-9]\\)") | 67 | "\\([+-]?[0-9][0-9][0-9][0-9]\\)-?\\([0-9][0-9]\\)-?\\([0-9][0-9]\\)") |
| 68 | (defconst iso8601--without-day-match | 68 | (defconst iso8601--without-day-match |
| 69 | "\\([+-]\\)?\\([0-9][0-9][0-9][0-9]\\)-\\([0-9][0-9]\\)") | 69 | "\\([+-]?[0-9][0-9][0-9][0-9]\\)-\\([0-9][0-9]\\)") |
| 70 | (defconst iso8601--outdated-date-match | 70 | (defconst iso8601--outdated-date-match |
| 71 | "--\\([0-9][0-9]\\)-?\\([0-9][0-9]\\)") | 71 | "--\\([0-9][0-9]\\)-?\\([0-9][0-9]\\)") |
| 72 | (defconst iso8601--week-date-match | 72 | (defconst iso8601--week-date-match |
| 73 | "\\([+-]\\)?\\([0-9][0-9][0-9][0-9]\\)-?W\\([0-9][0-9]\\)-?\\([0-9]\\)?") | 73 | "\\([+-]?[0-9][0-9][0-9][0-9]\\)-?W\\([0-9][0-9]\\)-?\\([0-9]\\)?") |
| 74 | (defconst iso8601--ordinal-date-match | 74 | (defconst iso8601--ordinal-date-match |
| 75 | "\\([+-]\\)?\\([0-9][0-9][0-9][0-9]\\)-?\\([0-9][0-9][0-9]\\)") | 75 | "\\([+-]?[0-9][0-9][0-9][0-9]\\)-?\\([0-9][0-9][0-9]\\)") |
| 76 | (defconst iso8601--date-match | 76 | (defconst iso8601--date-match |
| 77 | (iso8601--concat-regexps | 77 | (iso8601--concat-regexps |
| 78 | (list iso8601--year-match | 78 | (list iso8601--year-match |
| @@ -145,21 +145,18 @@ See `decode-time' for the meaning of FORM." | |||
| 145 | ;; Just a year: [+-]YYYY. | 145 | ;; Just a year: [+-]YYYY. |
| 146 | ((iso8601--match iso8601--year-match string) | 146 | ((iso8601--match iso8601--year-match string) |
| 147 | (iso8601--decoded-time | 147 | (iso8601--decoded-time |
| 148 | :year (iso8601--adjust-year (match-string 1 string) | 148 | :year (string-to-number string))) |
| 149 | (match-string 2 string)))) | ||
| 150 | ;; Calendar dates: YYYY-MM-DD and variants. | 149 | ;; Calendar dates: YYYY-MM-DD and variants. |
| 151 | ((iso8601--match iso8601--full-date-match string) | 150 | ((iso8601--match iso8601--full-date-match string) |
| 152 | (iso8601--decoded-time | 151 | (iso8601--decoded-time |
| 153 | :year (iso8601--adjust-year (match-string 1 string) | 152 | :year (string-to-number (match-string 1 string)) |
| 154 | (match-string 2 string)) | 153 | :month (match-string 2 string) |
| 155 | :month (match-string 3 string) | 154 | :day (match-string 3 string))) |
| 156 | :day (match-string 4 string))) | ||
| 157 | ;; Calendar date without day: YYYY-MM. | 155 | ;; Calendar date without day: YYYY-MM. |
| 158 | ((iso8601--match iso8601--without-day-match string) | 156 | ((iso8601--match iso8601--without-day-match string) |
| 159 | (iso8601--decoded-time | 157 | (iso8601--decoded-time |
| 160 | :year (iso8601--adjust-year (match-string 1 string) | 158 | :year (string-to-number string) |
| 161 | (match-string 2 string)) | 159 | :month (match-string 2 string))) |
| 162 | :month (match-string 3 string))) | ||
| 163 | ;; Outdated date without year: --MM-DD | 160 | ;; Outdated date without year: --MM-DD |
| 164 | ((iso8601--match iso8601--outdated-date-match string) | 161 | ((iso8601--match iso8601--outdated-date-match string) |
| 165 | (iso8601--decoded-time | 162 | (iso8601--decoded-time |
| @@ -167,11 +164,10 @@ See `decode-time' for the meaning of FORM." | |||
| 167 | :day (match-string 2 string))) | 164 | :day (match-string 2 string))) |
| 168 | ;; Week dates: YYYY-Www-D | 165 | ;; Week dates: YYYY-Www-D |
| 169 | ((iso8601--match iso8601--week-date-match string) | 166 | ((iso8601--match iso8601--week-date-match string) |
| 170 | (let* ((year (iso8601--adjust-year (match-string 1 string) | 167 | (let* ((year (string-to-number string)) |
| 171 | (match-string 2 string))) | 168 | (week (string-to-number (match-string 2 string))) |
| 172 | (week (string-to-number (match-string 3 string))) | 169 | (day-of-week (and (match-string 3 string) |
| 173 | (day-of-week (and (match-string 4 string) | 170 | (string-to-number (match-string 3 string)))) |
| 174 | (string-to-number (match-string 4 string)))) | ||
| 175 | (jan-start (decoded-time-weekday | 171 | (jan-start (decoded-time-weekday |
| 176 | (decode-time | 172 | (decode-time |
| 177 | (iso8601--encode-time | 173 | (iso8601--encode-time |
| @@ -199,9 +195,8 @@ See `decode-time' for the meaning of FORM." | |||
| 199 | :day (decoded-time-day month-day))))) | 195 | :day (decoded-time-day month-day))))) |
| 200 | ;; Ordinal dates: YYYY-DDD | 196 | ;; Ordinal dates: YYYY-DDD |
| 201 | ((iso8601--match iso8601--ordinal-date-match string) | 197 | ((iso8601--match iso8601--ordinal-date-match string) |
| 202 | (let* ((year (iso8601--adjust-year (match-string 1 string) | 198 | (let* ((year (string-to-number (match-string 1 string))) |
| 203 | (match-string 2 string))) | 199 | (ordinal (string-to-number (match-string 2 string))) |
| 204 | (ordinal (string-to-number (match-string 3 string))) | ||
| 205 | (month-day (date-ordinal-to-time year ordinal))) | 200 | (month-day (date-ordinal-to-time year ordinal))) |
| 206 | (iso8601--decoded-time :year year | 201 | (iso8601--decoded-time :year year |
| 207 | :month (decoded-time-month month-day) | 202 | :month (decoded-time-month month-day) |
| @@ -209,16 +204,6 @@ See `decode-time' for the meaning of FORM." | |||
| 209 | (t | 204 | (t |
| 210 | (signal 'wrong-type-argument string)))) | 205 | (signal 'wrong-type-argument string)))) |
| 211 | 206 | ||
| 212 | (defun iso8601--adjust-year (sign year) | ||
| 213 | (save-match-data | ||
| 214 | (let ((year (if (stringp year) | ||
| 215 | (string-to-number year) | ||
| 216 | year))) | ||
| 217 | (if (string= sign "-") | ||
| 218 | ;; -0001 is 2 BCE. | ||
| 219 | (1- (- year)) | ||
| 220 | year)))) | ||
| 221 | |||
| 222 | (defun iso8601-parse-time (string &optional form) | 207 | (defun iso8601-parse-time (string &optional form) |
| 223 | "Parse STRING, which should be an ISO 8601 time string. | 208 | "Parse STRING, which should be an ISO 8601 time string. |
| 224 | The return value will be a `decode-time' structure with just the | 209 | 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 @@ | |||
| 26 | (should (equal (iso8601-parse-date "1985") | 26 | (should (equal (iso8601-parse-date "1985") |
| 27 | '(nil nil nil nil nil 1985 nil nil nil))) | 27 | '(nil nil nil nil nil 1985 nil nil nil))) |
| 28 | (should (equal (iso8601-parse-date "-0003") | 28 | (should (equal (iso8601-parse-date "-0003") |
| 29 | '(nil nil nil nil nil -4 nil nil nil))) | 29 | '(nil nil nil nil nil -3 nil nil nil))) |
| 30 | (should (equal (iso8601-parse-date "+1985") | 30 | (should (equal (iso8601-parse-date "+1985") |
| 31 | '(nil nil nil nil nil 1985 nil nil nil)))) | 31 | '(nil nil nil nil nil 1985 nil nil nil)))) |
| 32 | 32 | ||