summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Ingebrigtsen <larsi@gnus.org>2019-07-08 15:30:35 +0200
committerLars Ingebrigtsen <larsi@gnus.org>2019-07-08 15:30:35 +0200
commitf0da4f03be7ced01aa80102192e12ed5153214ee (patch)
treeaeb04dfa1aa76d550aca629f0c63ff5c855424a5
parentb00264de4e443d9a954eecb89a1fd1fa430bc639 (diff)
downloademacs-f0da4f03be7ced01aa80102192e12ed5153214ee.tar.gz
Be stricter in what we accept and add more tests
-rw-r--r--lisp/calendar/iso8601.el73
-rw-r--r--test/lisp/calendar/iso8601-tests.el7
2 files changed, 53 insertions, 27 deletions
diff --git a/lisp/calendar/iso8601.el b/lisp/calendar/iso8601.el
index 991926f6bb5..c8a15794f6b 100644
--- a/lisp/calendar/iso8601.el
+++ b/lisp/calendar/iso8601.el
@@ -26,6 +26,13 @@
(require 'time-date)
(require 'cl-lib)
+(defun iso8601--concat-regexps (regexps)
+ (mapconcat (lambda (regexp)
+ (concat "\\(?:"
+ (replace-regexp-in-string "(" "(?:" regexp)
+ "\\)"))
+ regexps "\\|"))
+
(defconst iso8601--year-match
"\\([-+]\\)?\\([0-9][0-9][0-9][0-9]\\)")
(defconst iso8601--full-date-match
@@ -39,17 +46,13 @@
(defconst iso8601--ordinal-date-match
"\\([0-9][0-9][0-9][0-9]\\)-?\\([0-9][0-9][0-9]\\)")
(defconst iso8601--date-match
- (mapconcat (lambda (regexp)
- (concat "\\(?:"
- (replace-regexp-in-string "(" "(?:" regexp)
- "\\)"))
- (list iso8601--year-match
+ (iso8601--concat-regexps
+ (list iso8601--year-match
iso8601--full-date-match
iso8601--without-day-match
iso8601--outdated-date-match
iso8601--week-date-match
- iso8601--ordinal-date-match)
- "\\|"))
+ iso8601--ordinal-date-match)))
(defconst iso8601--time-match
"\\([0-9][0-9]\\):?\\([0-9][0-9]\\)?:?\\([0-9][0-9]\\)?\\.?\\([0-9][0-9][0-9]\\)?")
@@ -57,6 +60,25 @@
(defconst iso8601--zone-match
"\\(Z\\|\\([-+]\\)?\\([0-9][0-9]\\):?\\([0-9][0-9]\\)?\\)")
+(defconst iso8601--combined-match
+ (concat "\\(" iso8601--date-match "\\)"
+ "\\(?:T\\("
+ (replace-regexp-in-string "(" "(?:" iso8601--time-match)
+ "\\)\\)?"
+ "\\(" iso8601--zone-match "\\)?"))
+
+(defconst iso8601--duration-full-match
+ "P\\([0-9]+Y\\)?\\([0-9]+M\\)?\\([0-9]+D\\)?\\(T\\([0-9]+H\\)?\\([0-9]+M\\)?\\([0-9]+S\\)?\\)?")
+(defconst iso8601--duration-week-match
+ "P\\([0-9]+\\)W")
+(defconst iso8601--duration-combined-match
+ (concat "P" iso8601--combined-match))
+(defconst iso8601--duration-match
+ (iso8601--concat-regexps
+ (list iso8601--duration-full-match
+ iso8601--duration-week-match
+ iso8601--duration-combined-match)))
+
(defun iso8601-parse (string)
"Parse an ISO 8601 date/time string and return a `decoded-time' structure.
@@ -189,19 +211,14 @@ Return the number of minutes."
(defun iso8601-valid-p (string)
"Say whether STRING is a valid ISO 8601 representation."
- (iso8601--match (concat "\\(" iso8601--date-match "\\)"
- "\\(?:T\\("
- (replace-regexp-in-string
- "(" "(?:" iso8601--time-match)
- "\\)\\)?"
- "\\(" iso8601--zone-match "\\)?")
- string))
+ (iso8601--match iso8601--combined-match string))
(defun iso8601-parse-duration (string)
"Parse ISO 8601 durations on the form P3Y6M4DT12H30M5S."
(cond
- ((and (string-match "\\`P\\([0-9]+Y\\)?\\([0-9]+M\\)?\\([0-9]+D\\)?\\(T\\([0-9]+H\\)?\\([0-9]+M\\)?\\([0-9]+S\\)?\\)?\\'"
- string)
+ ((and (iso8601--match iso8601--duration-full-match string)
+ ;; Just a "P" isn't valid; there has to be at least one
+ ;; element, like P1M.
(> (length (match-string 0 string)) 2))
(iso8601--decoded-time :year (or (match-string 1 string) 0)
:month (or (match-string 2 string) 0)
@@ -210,13 +227,12 @@ Return the number of minutes."
:minute (or (match-string 6 string) 0)
:second (or (match-string 7 string) 0)))
;; PnW: Weeks.
- ((string-match "\\`P\\([0-9]+\\)W\\'" string)
+ ((iso8601--match iso8601--duration-week-match string)
(let ((weeks (string-to-number (match-string 1 string))))
;; Does this make sense? Hm...
(iso8601--decoded-time :day (* weeks 7))))
;; P<date>T<time>
- ((and (string-match "\\`P" string)
- (iso8601-valid-p (substring string 1)))
+ ((iso8601--match iso8601--duration-combined-match string)
(iso8601-parse (substring string 1)))
(t
(signal 'wrong-type-argument string))))
@@ -228,17 +244,20 @@ Return the number of minutes."
(if (not (= (length bits) 2))
(signal 'wrong-type-argument string)
(cond
- ((string-match "\\`P" (car bits))
+ ((and (string-match "\\`P" (car bits))
+ (iso8601-valid-p (cadr bits)))
(setq duration (iso8601-parse-duration (car bits))
- end (encode-time (iso8601-parse (cadr bits)))
- start (time-subtract end (encode-time duration))))
- ((string-match "\\`P" (cadr bits))
+ end (iso8601-parse (cadr bits))))
+ ((and (string-match "\\`P" (cadr bits))
+ (iso8601-valid-p (car bits)))
(setq duration (iso8601-parse-duration (cadr bits))
- start (encode-time (iso8601-parse (car bits)))
- end (time-add start (encode-time duration))))
- (t
+ start (iso8601-parse (car bits))))
+ ((and (iso8601-valid-p (car bits))
+ (iso8601-valid-p (cadr bits)))
(setq start (encode-time (iso8601-parse (car bits)))
- end (encode-time (iso8601-parse (cadr bits)))))))
+ end (encode-time (iso8601-parse (cadr bits)))))
+ (t
+ (signal 'wrong-type-argument string))))
(list start end)))
(defun iso8601--match (regexp string)
diff --git a/test/lisp/calendar/iso8601-tests.el b/test/lisp/calendar/iso8601-tests.el
index a8f00e74790..f9ea6e886b1 100644
--- a/test/lisp/calendar/iso8601-tests.el
+++ b/test/lisp/calendar/iso8601-tests.el
@@ -84,4 +84,11 @@
(should (equal (iso8601-parse-duration "P0003-06-04T12:30:05")
'(5 30 12 4 6 3 nil nil nil))))
+(ert-deftest test-iso8601-invalid ()
+ (should-not (iso8601-valid-p " 2008-03-02T13:47:30-01"))
+ (should-not (iso8601-valid-p "2008-03-02T13:47:30-01:200"))
+ (should-not (iso8601-valid-p "2008-03-02T13:47:30-01 "))
+ (should-not (iso8601-valid-p "2008-03-02 T 13:47:30-01 "))
+ (should-not (iso8601-valid-p "20008-03-02T13:47:30-01")))
+
;;; iso8601-tests.el ends here