aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2019-10-05 21:23:15 -0700
committerPaul Eggert2019-10-05 21:24:09 -0700
commit9d829b8be5b86668d5165b9d0c0cdc392b558dd3 (patch)
treeb2dc5d94b96bf540fdfde80a33f93bf4b4018e66
parentaadf72167673c8702531ba3802e3d5f137f139ba (diff)
downloademacs-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.el49
-rw-r--r--test/lisp/calendar/iso8601-tests.el2
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.
224The return value will be a `decode-time' structure with just the 209The 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