;;; parse-date-tests.el --- Test suite for parse-date.el -*- lexical-binding:t -*- ;; Copyright (C) 2016-2021 Free Software Foundation, Inc. ;; Author: Lars Ingebrigtsen ;; This file is part of GNU Emacs. ;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs. If not, see . ;;; Commentary: ;;; Code: (require 'ert) (require 'parse-date) (ert-deftest parse-date-tests () "Test basic parse-date functionality." ;; Test tokenization. (should (equal (parse-date--tokenize-string " ") '())) (should (equal (parse-date--tokenize-string " a b") '("a" "b"))) (should (equal (parse-date--tokenize-string "a bbc dde") '("a" "bbc" "dde"))) (should (equal (parse-date--tokenize-string " , a 27 b,, c 14:32 ") '("a" 27 "b" "c" "14:32"))) ;; Some folding whitespace tests. (should (equal (parse-date--tokenize-string " a b (end) c" 'first) '("a" "b"))) (should (equal (parse-date--tokenize-string "(quux)a (foo (bar)) b(baz)" t) '("a" "b"))) (should (equal (parse-date--tokenize-string "a b\\cde" 'all) ;; Strictly incorrect, but strictly unnecessary syntax. '("a" "b\\cde"))) (should (equal (parse-date--tokenize-string "a b\\ de" 'all) '("a" "b\\ de"))) (should (equal (parse-date--tokenize-string "a \\de \\(f" 'all) '("a" "\\de" "\\(f"))) ;; Start with some compatible RFC822 dates. (dolist (format '(nil rfc822 rfc2822)) (should (equal (parse-date "Mon, 22 Feb 2016 19:35:42 +0100" format) '(42 35 19 22 2 2016 1 -1 3600))) (should (equal (parse-date "22 Feb 2016 19:35:42 +0100" format) '(42 35 19 22 2 2016 nil -1 3600))) (should (equal (parse-date "Mon, 22 February 2016 19:35:42 +0100" format) '(42 35 19 22 2 2016 1 -1 3600))) (should (equal (parse-date "Mon, 22 feb 2016 19:35:42 +0100" format) '(42 35 19 22 2 2016 1 -1 3600))) (should (equal (parse-date "Monday, 22 february 2016 19:35:42 +0100" format) '(42 35 19 22 2 2016 1 -1 3600))) (should (equal (parse-date "Monday, 22 february 2016 19:35:42 PST" format) '(42 35 19 22 2 2016 1 nil -28800))) (should (equal (parse-date "Friday, 21 Sep 2018 13:47:58 PDT" format) '(58 47 13 21 9 2018 5 t -25200))) (should (equal (parse-date "Friday, 21 Sep 2018 13:47:58" format) '(58 47 13 21 9 2018 5 -1 nil)))) ;; These are not allowed by the default format. (should (equal (parse-date "22 Feb 16 19:35:42 +0100" 'rfc822) '(42 35 19 22 2 2016 nil -1 3600))) (should (equal (parse-date "22 Feb 96 19:35:42 +0100" 'rfc822) '(42 35 19 22 2 1996 nil -1 3600))) ;; Try them again with comments. (should (equal (parse-date "22 Feb (today) 16 19:35:42 +0100" 'rfc822) '(42 35 19 22 2 2016 nil -1 3600))) (should (equal (parse-date "22 Feb 96 (long ago) 19:35:42 +0100" 'rfc822) '(42 35 19 22 2 1996 nil -1 3600))) (should (equal (parse-date "Friday, 21 Sep(comment \\) with \\( parens)18 19:35:42" 'rfc822) '(42 35 19 21 9 2018 5 -1 nil))) (should (equal (parse-date "Friday, 21 Sep 18 19:35:42 (unterminated comment" 'rfc822) '(42 35 19 21 9 2018 5 -1 nil))) ;; Test some RFC822 error cases (dolist (test '(("33 1 2022" ("Slot out of range" day 33 1 31)) ("0 1 2022" ("Slot out of range" day 0 1 31)) ("1 1 2020 2021" ("Expected an alphabetic month" 1)) ("1 Jan 2020 2021" ("Expected a time" 2021)) ("1 Jan 2020 20:21 2000" ("Expected a timezone" 2000)) ("1 Jan 2020 20:21 +0200 33" ("Extra token(s)" 33)))) (should (equal (condition-case err (parse-date (car test) 'rfc822) (date-parse-error (cdr err))) (cadr test)))) ;; And these are not allowed by rfc822 because of missing time. (should (equal (parse-date "Friday, 21 Sep 2018" nil) '(nil nil nil 21 9 2018 5 -1 nil))) (should (equal (parse-date "22 Feb 2016 +0100" nil) '(nil nil nil 22 2 2016 nil -1 3600))) ;; Test the default format with both hyphens and slashes in dates. (dolist (case '(;; Month can be numeric if date uses hyphens/slashes. ("Friday, 2018-09-21" (nil nil nil 21 9 2018 5 -1 nil)) ;; Year can come last if four digits. ("Friday, 9-21-2018" (nil nil nil 21 9 2018 5 -1 nil)) ;; Day of week is optional ("2018-09-21" (nil nil nil 21 9 2018 nil -1 nil)) ;; The order of date, time, etc., does not matter. ("13:47:58, +0100, 2018-09-21, Friday" (58 47 13 21 9 2018 5 -1 3600)) ;; Month, day, or both, can be a single digit. ("Friday, 2018-9-08" (nil nil nil 8 9 2018 5 -1 nil)) ("Friday, 2018-09-8" (nil nil nil 8 9 2018 5 -1 nil)) ("Friday, 2018-9-8" (nil nil nil 8 9 2018 5 -1 nil)))) (let ((string (car case)) (expected (cadr case))) ;; Test with hyphens. (should (equal (parse-date string nil) expected)) (while (string-match "-" string) (setq string (replace-match "/" t t string))) ;; Test with slashes. (should (equal (parse-date string nil) expected)))) ;; Time by itself is recognized as such. (should (equal (parse-date "03:47:58" nil) '(58 47 3 nil nil nil nil -1 nil))) ;; A leading zero for hours is optional. (should (equal (parse-date "3:47:58" nil) '(58 47 3 nil nil nil nil -1 nil))) ;; Missing seconds are assumed to be zero. (should (equal (parse-date "3:47" nil) '(0 47 3 nil nil nil nil -1 nil))) ;; AM/PM are understood (in any case combination). (dolist (am '(am AM Am)) (should (equal (parse-date (format "3:47 %s" am) nil) '(0 47 3 nil nil nil nil -1 nil)))) (dolist (pm '(pm PM Pm)) (should (equal (parse-date (format "3:47 %s" pm) nil) '(0 47 15 nil nil nil nil -1 nil)))) ;; Ensure some cases fail. (should-error (parse-date "22 Feb 196" 'us-date)) (should-error (parse-date "22 Feb 16 19:35:42" nil)) (should-error (parse-date "22 Feb 96 19:35:42" nil)) ;; two-digit year (should-error (parse-date "2 Feb 2021 1996" nil)) ;; duplicate year (dolist (test '(("22 Feb 196" 'us-date ;; bad year ("Unrecognized token" 196)) ("22 Feb 16 19:35:42" nil ;; two-digit year ("Unrecognized token" 16)) ("22 Feb 96 19:35:42" nil ;; two-digit year ("Unrecognized token" 96)) ("2 Feb 2021 1996" nil ("Duplicate slot value" year 1996)) ("2020-1-1 2021" nil ("Duplicate slot value" year 2021)) ("22 Feb 196" 'us-date ("Unrecognized token" 196)) ("22 Feb 16 19:35:42" nil ("Unrecognized token" 16)) ("22 Feb 96 19:35:42" nil ("Unrecognized token" 96)) ("2 Feb 2021 1996" nil ("Duplicate slot value" year 1996)) ("2020-1-1 30" nil ("Unrecognized token" 30)) ("2020-1-1 12" nil ("Unrecognized token" 12)) ("15:47 15:15" nil ("Duplicate slot value" hour "15:15")) ("2020-1-1 +0800 -0800" t ("Duplicate slot value" zone -28800)) ("15:47 PM" nil ("Time already past noon" "pm")) ("15:47 AM" nil ("Time already past noon" "am")) ("2020-1-1 PM" nil ("Missing time" "pm")) ;; Range tests ("2021-12-32" nil ("Slot out of range" day "2021-12-32" 1 31)) ("2021-12-0" nil ("Slot out of range" day "2021-12-0" 1 31)) ("2021-13-3" nil ("Slot out of range" month "2021-13-3" 1 12)) ("0000-12-3" nil ("Slot out of range" year "0000-12-3" 1 9999)) ("20021 Dec 3" nil ("Slot out of range" year 20021 1 9999)) ("24:21:14" nil ("Slot out of range" hour "24:21:14" 0 23)) ("14:60:21" nil ("Slot out of range" minute "14:60:21" 0 59)) ("14:21:61" nil ("Slot out of range" second "14:21:61" 0 60)))) (should (equal (condition-case err (parse-date (car test) (cadr test)) (date-parse-error (cdr err))) (caddr test)))) (should (equal (parse-date "14:21:60" nil) ;; a leap second! '(60 21 14 nil nil nil nil -1 nil))) ;; Test ISO-8601 dates. (dolist (format '(t iso-8601)) (should (equal (parse-date "1998-09-12T12:21:54-0200" format) '(54 21 12 12 9 1998 nil nil -7200))) (should (equal (format-time-string "%Y-%m-%d %H:%M:%S" (encode-time (parse-date "1998-09-12T12:21:54-0230" format)) t) "1998-09-12 14:51:54")) (should (equal (format-time-string "%Y-%m-%d %H:%M:%S" (encode-time (parse-date "1998-09-12T12:21:54-02:00" format)) t) "1998-09-12 14:21:54")) (should (equal (format-time-string "%Y-%m-%d %H:%M:%S" (encode-time (parse-date "1998-09-12T12:21:54-02" format)) t) "1998-09-12 14:21:54")) (should (equal (format-time-string "%Y-%m-%d %H:%M:%S" (encode-time (parse-date "1998-09-12T12:21:54+0230" format)) t) "1998-09-12 09:51:54")) (should (equal (format-time-string "%Y-%m-%d %H:%M:%S" (encode-time (parse-date "1998-09-12T12:21:54+02" format)) t) "1998-09-12 10:21:54")) (should (equal (parse-date "1998-09-12T12:21:54Z" t) '(54 21 12 12 9 1998 nil nil 0))) (should (equal (parse-date "1998-09-12T12:21:54" format) '(54 21 12 12 9 1998 nil -1 nil))))) (provide 'parse-date-tests) ;;; parse-date-tests.el ends here