diff options
| author | Lars Ingebrigtsen | 2019-09-29 21:22:29 +0200 |
|---|---|---|
| committer | Lars Ingebrigtsen | 2019-09-29 21:22:36 +0200 |
| commit | 0df01e3aa5f8372995bdc39be36c444c54a52f7e (patch) | |
| tree | 7b26486c46b31fae0cbbc19dbca78a476235c5ed | |
| parent | 53ebec24c871c5e6adb6aed088199d1b15e0198a (diff) | |
| download | emacs-0df01e3aa5f8372995bdc39be36c444c54a52f7e.tar.gz emacs-0df01e3aa5f8372995bdc39be36c444c54a52f7e.zip | |
Add support for sub-second ISO8601 strings
* lisp/calendar/iso8601.el (iso8601--decimalize): New function.
(iso8601-parse-time): Support sub-second ISO8601 times.
| -rw-r--r-- | lisp/calendar/iso8601.el | 35 | ||||
| -rw-r--r-- | test/lisp/calendar/iso8601-tests.el | 38 |
2 files changed, 48 insertions, 25 deletions
diff --git a/lisp/calendar/iso8601.el b/lisp/calendar/iso8601.el index f8949914f78..66446dafb96 100644 --- a/lisp/calendar/iso8601.el +++ b/lisp/calendar/iso8601.el | |||
| @@ -112,12 +112,16 @@ | |||
| 112 | iso8601--duration-combined-match))) | 112 | iso8601--duration-combined-match))) |
| 113 | 113 | ||
| 114 | (defun iso8601-parse (string) | 114 | (defun iso8601-parse (string) |
| 115 | "Parse an ISO 8601 date/time string and return a `decoded-time' structure. | 115 | "Parse an ISO 8601 date/time string and return a `decode-time' structure. |
| 116 | 116 | ||
| 117 | The ISO 8601 date/time strings look like \"2008-03-02T13:47:30\", | 117 | The ISO 8601 date/time strings look like \"2008-03-02T13:47:30\", |
| 118 | but shorter, incomplete strings like \"2008-03-02\" are valid, as | 118 | but shorter, incomplete strings like \"2008-03-02\" are valid, as |
| 119 | well as variants like \"2008W32\" (week number) and | 119 | well as variants like \"2008W32\" (week number) and |
| 120 | \"2008-234\" (ordinal day number)." | 120 | \"2008-234\" (ordinal day number). |
| 121 | |||
| 122 | The `decode-time' value returned will have the same precision as | ||
| 123 | STRING, so if a sub-second STRING is passed in, the `decode-time' | ||
| 124 | seconds field will be on the (SECONDS . HZ) format." | ||
| 121 | (if (not (iso8601-valid-p string)) | 125 | (if (not (iso8601-valid-p string)) |
| 122 | (signal 'wrong-type-argument string) | 126 | (signal 'wrong-type-argument string) |
| 123 | (let* ((date-string (match-string 1 string)) | 127 | (let* ((date-string (match-string 1 string)) |
| @@ -138,7 +142,7 @@ well as variants like \"2008W32\" (week number) and | |||
| 138 | date))) | 142 | date))) |
| 139 | 143 | ||
| 140 | (defun iso8601-parse-date (string) | 144 | (defun iso8601-parse-date (string) |
| 141 | "Parse STRING (in ISO 8601 format) and return a decoded time value." | 145 | "Parse STRING (in ISO 8601 format) and return a `decode-time' value." |
| 142 | (cond | 146 | (cond |
| 143 | ;; Just a year: [-+]YYYY. | 147 | ;; Just a year: [-+]YYYY. |
| 144 | ((iso8601--match iso8601--year-match string) | 148 | ((iso8601--match iso8601--year-match string) |
| @@ -218,7 +222,9 @@ well as variants like \"2008W32\" (week number) and | |||
| 218 | year)))) | 222 | year)))) |
| 219 | 223 | ||
| 220 | (defun iso8601-parse-time (string) | 224 | (defun iso8601-parse-time (string) |
| 221 | "Parse STRING, which should be an ISO 8601 time string, and return a time value." | 225 | "Parse STRING, which should be an ISO 8601 time string. |
| 226 | The return value will be a `decode-time' structure with just the | ||
| 227 | hour/minute/seconds/zone fields filled in." | ||
| 222 | (if (not (iso8601--match iso8601--full-time-match string)) | 228 | (if (not (iso8601--match iso8601--full-time-match string)) |
| 223 | (signal 'wrong-type-argument string) | 229 | (signal 'wrong-type-argument string) |
| 224 | (let ((time (match-string 1 string)) | 230 | (let ((time (match-string 1 string)) |
| @@ -230,9 +236,22 @@ well as variants like \"2008W32\" (week number) and | |||
| 230 | (string-to-number (match-string 2 time)))) | 236 | (string-to-number (match-string 2 time)))) |
| 231 | (second (and (match-string 3 time) | 237 | (second (and (match-string 3 time) |
| 232 | (string-to-number (match-string 3 time)))) | 238 | (string-to-number (match-string 3 time)))) |
| 233 | ;; Hm... | 239 | (fraction (and (match-string 4 time) |
| 234 | (_millisecond (and (match-string 4 time) | ||
| 235 | (string-to-number (match-string 4 time))))) | 240 | (string-to-number (match-string 4 time))))) |
| 241 | (when fraction | ||
| 242 | (cond | ||
| 243 | ;; Sub-second time. | ||
| 244 | (second | ||
| 245 | (let ((digits (1+ (truncate (log fraction 10))))) | ||
| 246 | (setq second (cons (+ (* second (expt 10 digits)) | ||
| 247 | fraction) | ||
| 248 | (expt 10 digits))))) | ||
| 249 | ;; Fractional minute. | ||
| 250 | (minute | ||
| 251 | (setq second (iso8601--decimalize fraction 60))) | ||
| 252 | (hour | ||
| 253 | ;; Fractional hour. | ||
| 254 | (setq minute (iso8601--decimalize fraction 60))))) | ||
| 236 | (iso8601--decoded-time :hour hour | 255 | (iso8601--decoded-time :hour hour |
| 237 | :minute (or minute 0) | 256 | :minute (or minute 0) |
| 238 | :second (or second 0) | 257 | :second (or second 0) |
| @@ -240,6 +259,10 @@ well as variants like \"2008W32\" (week number) and | |||
| 240 | (* 60 (iso8601-parse-zone | 259 | (* 60 (iso8601-parse-zone |
| 241 | zone))))))))) | 260 | zone))))))))) |
| 242 | 261 | ||
| 262 | (defun iso8601--decimalize (fraction base) | ||
| 263 | (round (* base (/ (float fraction) | ||
| 264 | (expt 10 (1+ (truncate (log fraction 10)))))))) | ||
| 265 | |||
| 243 | (defun iso8601-parse-zone (string) | 266 | (defun iso8601-parse-zone (string) |
| 244 | "Parse STRING, which should be an ISO 8601 time zone. | 267 | "Parse STRING, which should be an ISO 8601 time zone. |
| 245 | Return the number of minutes." | 268 | Return the number of minutes." |
diff --git a/test/lisp/calendar/iso8601-tests.el b/test/lisp/calendar/iso8601-tests.el index 8d2aec3de5a..1d44e947a77 100644 --- a/test/lisp/calendar/iso8601-tests.el +++ b/test/lisp/calendar/iso8601-tests.el | |||
| @@ -153,25 +153,25 @@ | |||
| 153 | (should (equal (iso8601-parse-time "15") | 153 | (should (equal (iso8601-parse-time "15") |
| 154 | '(0 0 15 nil nil nil nil nil nil)))) | 154 | '(0 0 15 nil nil nil nil nil nil)))) |
| 155 | 155 | ||
| 156 | ;; Not implemented yet. | 156 | (ert-deftest standard-test-time-of-day-fractions () |
| 157 | 157 | (should (equal (iso8601-parse-time "152735,5") | |
| 158 | ;; (ert-deftest standard-test-time-of-day-fractions () | 158 | '((355 . 10) 27 15 nil nil nil nil nil nil))) |
| 159 | ;; (should (equal (iso8601-parse-time "152735,5") | 159 | (should (equal (iso8601-parse-time "15:27:35,5") |
| 160 | ;; '(46 27 15 nil nil nil nil nil nil))) | 160 | '((355 . 10) 27 15 nil nil nil nil nil nil))) |
| 161 | ;; (should (equal (iso8601-parse-time "15:27:35,5") | 161 | |
| 162 | ;; '(46 27 15 nil nil nil nil nil nil))) | 162 | (should (equal (iso8601-parse-time "2320,5") |
| 163 | 163 | '(30 20 23 nil nil nil nil nil nil))) | |
| 164 | ;; (should (equal (iso8601-parse-time "2320,8") | 164 | (should (equal (iso8601-parse-time "23:20,8") |
| 165 | ;; '(46 27 15 nil nil nil nil nil nil))) | 165 | '(48 20 23 nil nil nil nil nil nil))) |
| 166 | ;; (should (equal (iso8601-parse-time "23:20,8") | 166 | |
| 167 | ;; '(46 27 15 nil nil nil nil nil nil))) | 167 | (should (equal (iso8601-parse-time "23,3") |
| 168 | 168 | '(0 18 23 nil nil nil nil nil nil)))) | |
| 169 | ;; (should (equal (iso8601-parse-time "23,3") | 169 | |
| 170 | ;; '(46 27 15 nil nil nil nil nil nil)))) | 170 | (ert-deftest nonstandard-test-time-of-day-decimals () |
| 171 | 171 | (should (equal (iso8601-parse-time "15:27:35.123") | |
| 172 | ;; (ert-deftest nonstandard-test-time-of-day-decimals () | 172 | '((35123 . 1000) 27 15 nil nil nil nil nil nil))) |
| 173 | ;; (should (equal (iso8601-parse-time "15:27:35.123") | 173 | (should (equal (iso8601-parse-time "15:27:35.123456789") |
| 174 | ;; '(46 27 15 nil nil nil nil nil nil)))) | 174 | '((35123456789 . 1000000000) 27 15 nil nil nil nil nil nil)))) |
| 175 | 175 | ||
| 176 | (ert-deftest standard-test-time-of-day-beginning-of-day () | 176 | (ert-deftest standard-test-time-of-day-beginning-of-day () |
| 177 | (should (equal (iso8601-parse-time "000000") | 177 | (should (equal (iso8601-parse-time "000000") |