Fix for time.lisp's roll function
[clsql.git] / sql / time.lisp
1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;;
4 ;;;; A variety of structures and function for creating and
5 ;;;; manipulating dates, times, durations and intervals for
6 ;;;; CLSQL.
7 ;;;;
8 ;;;; This file was originally part of ODCL and is Copyright (c) 2002 -
9 ;;;; 2003 onShore Development, Inc.
10 ;;;;
11 ;;;; CLSQL users are granted the rights to distribute and use this software
12 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
13 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
14 ;;;; *************************************************************************
15
16 (in-package #:clsql-sys)
17
18 ;; ------------------------------------------------------------
19 ;; Months
20
21 (defvar *month-keywords*
22   '(:january :february :march :april :may :june :july :august :september
23     :october :november :december))
24
25 (defvar *month-names*
26   '("" "January" "February" "March" "April" "May" "June" "July" "August"
27     "September" "October" "November" "December"))
28
29 (defun month-name (month-index)
30   (nth month-index *month-names*))
31
32 (defun ordinal-month (month-keyword)
33   "Return the zero-based month number for the given MONTH keyword."
34   (position month-keyword *month-keywords*))
35
36
37 ;; ------------------------------------------------------------
38 ;; Days
39
40 (defvar *day-keywords*
41   '(:sunday :monday :tuesday :wednesday :thursday :friday :saturday))
42
43 (defvar *day-names*
44   '("Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday"))
45
46 (defun day-name (day-index)
47   (nth day-index *day-names*))
48
49 (defun ordinal-day (day-keyword)
50   "Return the zero-based day number for the given DAY keyword."
51   (position day-keyword *day-keywords*))
52
53
54 ;; ------------------------------------------------------------
55 ;; time classes: wall-time, duration
56
57 (eval-when (:compile-toplevel :load-toplevel)
58
59 (defstruct (wall-time (:conc-name time-)
60                       (:constructor %make-wall-time)
61                       (:print-function %print-wall-time))
62   (mjd 0 :type fixnum)
63   (second 0 :type fixnum)
64   (usec 0 :type fixnum))
65
66 (defun %print-wall-time (time stream depth)
67   (declare (ignore depth))
68   (if *print-escape*
69       (format stream "#<WALL-TIME: ~a>" (format-time nil time))
70       (format-time stream time :format :pretty)))
71
72 (defstruct (duration (:constructor %make-duration)
73                      (:print-function %print-duration))
74   (year 0 :type fixnum)
75   (month 0 :type fixnum)
76   (day 0 :type fixnum)
77   (hour 0 :type fixnum)
78   (second 0 :type fixnum)
79   (minute 0 :type fixnum)
80   (usec 0 :type fixnum))
81
82 (defun %print-duration (duration stream depth)
83   (declare (ignore depth))
84   (if *print-escape*
85       (format stream "#<DURATION: ~a>"
86               (format-duration nil duration :precision :second))
87       (format-duration stream duration :precision :second)))
88
89 (defstruct (date (:constructor %make-date)
90                  (:print-function %print-date))
91   (mjd 0 :type fixnum))
92
93 (defun %print-date (date stream depth)
94   (declare (ignore depth))
95   (if *print-escape*
96       (format stream "#<DATE: ~a>" (format-date nil date))
97       (format-date stream date :format :pretty)))
98
99 );eval-when
100
101 (defun duration-timestring (duration)
102   (let ((second (duration-second duration))
103         (minute (duration-minute duration))
104         (hour (duration-hour duration))
105         (day (duration-day duration))
106         (month (duration-month duration))
107         (year (duration-year duration)))
108     (format nil "P~dY~dM~dD~dH~dM~dS" year month day hour minute second)))
109
110
111 ;; ------------------------------------------------------------
112 ;; Constructors
113
114 (defun make-time (&key (year 0) (month 1) (day 1) (hour 0) (minute 0)
115                        (second 0) (usec 0) (offset 0))
116   (let ((mjd (gregorian-to-mjd month day year))
117         (sec (+ (* hour 60 60)
118                 (* minute 60)
119                 second (- offset))))
120     (multiple-value-bind (day-add raw-sec)
121         (floor sec (* 60 60 24))
122       (%make-wall-time :mjd (+ mjd day-add) :second raw-sec :usec usec))))
123
124 (defun make-date (&key (year 0) (month 1) (day 1) (hour 0) (minute 0)
125                        (second 0) (usec 0) (offset 0))
126   (time->date (make-time :year year :month month :day day :hour hour
127                          :minute minute :second second :usec usec :offset offset)))
128
129 (defun copy-time (time)
130   (%make-wall-time :mjd (time-mjd time)
131                    :second (time-second time)))
132
133 (defun utime->time (utime)
134   "Return a pair: (GREGORIAN DAY . TIME-OF-DAY)"
135   (multiple-value-bind (second minute hour day mon year)
136       (decode-universal-time utime)
137     (make-time :year year :month mon :day day :hour hour :minute minute
138                :second second)))
139
140 (defun date->time (date)
141   "Returns a walltime for the given date"
142   (%make-wall-time :mjd (date-mjd date)))
143
144 (defun time->date (time)
145   "Returns a date for the given wall time (obvious loss in resolution)"
146   (%make-date :mjd (time-mjd time)))
147
148 (defun get-time ()
149   "Return a pair: (GREGORIAN DAY . TIME-OF-DAY)"
150   (utime->time (get-universal-time)))
151
152 (defun get-date ()
153   "Returns a date for today"
154   (time->date (get-time)))
155
156 (defun make-duration (&key (year 0) (month 0) (day 0) (hour 0) (minute 0)
157                       (second 0) (usec 0))
158   (multiple-value-bind (second-add usec-1000000)
159       (floor usec 1000000)
160     (multiple-value-bind (minute-add second-60)
161         (floor (+ second second-add) 60)
162       (multiple-value-bind (hour-add minute-60)
163           (floor (+ minute minute-add) 60)
164         (multiple-value-bind (day-add hour-24)
165             (floor (+ hour hour-add) 24)
166           (%make-duration :year year :month month :day (+ day day-add)
167                           :hour hour-24
168                           :minute minute-60
169                           :second second-60
170                           :usec usec-1000000))))))
171
172
173 ;; ------------------------------------------------------------
174 ;; Accessors
175
176 (defun time-hms (time)
177   (multiple-value-bind (hourminute second)
178       (floor (time-second time) 60)
179     (multiple-value-bind (hour minute)
180         (floor hourminute 60)
181       (values hour minute second))))
182
183 (defun time-ymd (time)
184   (destructuring-bind (month day year)
185       (mjd-to-gregorian (time-mjd time))
186     (values year month day)))
187
188 (defun time-dow (time)
189   "Return the 0 indexed Day of the week starting with Sunday"
190   (mod (+ 3 (time-mjd time)) 7))
191
192 (defun decode-time (time)
193   "returns the decoded time as multiple values: usec, second, minute, hour,
194   day, month, year, integer day-of-week"
195   (multiple-value-bind (year month day)
196       (time-ymd time)
197     (multiple-value-bind (hour minute second)
198         (time-hms time)
199       (values (time-usec time) second minute hour day month year (mod (+ (time-mjd time) 3) 7)))))
200
201 (defun date-ymd (date)
202   (time-ymd (date->time date)))
203
204 (defun date-dow (date)
205   (time-dow (date->time date)))
206
207 (defun decode-date (date)
208   "returns the decoded date as multiple values: day month year integer day-of-week"
209   (multiple-value-bind (year month day)
210       (time-ymd (date->time date))
211     (values day month year (date-dow date))))
212
213 ;; duration specific
214 (defun duration-reduce (duration precision &optional round)
215   (ecase precision
216     (:usec
217      (+ (duration-usec duration)
218         (* (duration-reduce duration :second) 1000000)))
219     (:second
220      (+ (if round
221             (floor (duration-usec duration) 500000)
222             0)
223         (duration-second duration)
224         (* (duration-reduce duration :minute) 60)))
225     (:minute
226      (+ (if round
227             (floor (duration-second duration) 30)
228             0)
229         (duration-minute duration)
230         (* (duration-reduce duration :hour) 60)))
231     (:hour
232      (+ (if round
233             (floor (duration-minute duration) 30)
234             0)
235         (duration-hour duration)
236         (* (duration-reduce duration :day) 24)))
237     (:day
238      (+ (if round
239             (floor (duration-hour duration) 12)
240             0)
241         (duration-day duration)))))
242
243
244 ;; ------------------------------------------------------------
245 ;; Arithemetic and comparators
246
247 (defun duration= (duration-a duration-b)
248   (= (duration-reduce duration-a :usec)
249      (duration-reduce duration-b :usec)))
250
251 (defun duration< (duration-a duration-b)
252   (< (duration-reduce duration-a :usec)
253      (duration-reduce duration-b :usec)))
254
255 (defun duration<= (duration-a duration-b)
256   (<= (duration-reduce duration-a :usec)
257       (duration-reduce duration-b :usec)))
258
259 (defun duration>= (x y)
260   (duration<= y x))
261
262 (defun duration> (x y)
263   (duration< y x))
264
265 (defun %time< (x y)
266   (let ((mjd-x (time-mjd x))
267         (mjd-y (time-mjd y)))
268     (if (/= mjd-x mjd-y)
269         (< mjd-x mjd-y)
270         (if (/= (time-second x) (time-second y))
271             (< (time-second x) (time-second y))
272             (< (time-usec x) (time-usec y))))))
273
274 (defun %time>= (x y)
275   (if (/= (time-mjd x) (time-mjd y))
276       (>= (time-mjd x) (time-mjd y))
277       (if (/= (time-second x) (time-second y))
278           (>= (time-second x) (time-second y))
279           (>= (time-usec x) (time-usec y)))))
280
281 (defun %time<= (x y)
282   (if (/= (time-mjd x) (time-mjd y))
283       (<= (time-mjd x) (time-mjd y))
284       (if (/= (time-second x) (time-second y))
285           (<= (time-second x) (time-second y))
286           (<= (time-usec x) (time-usec y)))))
287
288 (defun %time> (x y)
289   (if (/= (time-mjd x) (time-mjd y))
290       (> (time-mjd x) (time-mjd y))
291       (if (/= (time-second x) (time-second y))
292           (> (time-second x) (time-second y))
293           (> (time-usec x) (time-usec y)))))
294
295 (defun %time= (x y)
296   (and (= (time-mjd x) (time-mjd y))
297        (= (time-second x) (time-second y))
298        (= (time-usec x) (time-usec y))))
299
300 (defun time= (number &rest more-numbers)
301   "Returns T if all of its arguments are numerically equal, NIL otherwise."
302   (do ((nlist more-numbers (cdr nlist)))
303       ((atom nlist) t)
304      (declare (list nlist))
305      (if (not (%time= (car nlist) number)) (return nil))))
306
307 (defun time/= (number &rest more-numbers)
308   "Returns T if no two of its arguments are numerically equal, NIL otherwise."
309   (do* ((head number (car nlist))
310         (nlist more-numbers (cdr nlist)))
311        ((atom nlist) t)
312      (declare (list nlist))
313      (unless (do* ((nl nlist (cdr nl)))
314                   ((atom nl) t)
315                (declare (list nl))
316                (if (%time= head (car nl)) (return nil)))
317        (return nil))))
318
319 (defun time< (number &rest more-numbers)
320   "Returns T if its arguments are in strictly increasing order, NIL otherwise."
321   (do* ((n number (car nlist))
322         (nlist more-numbers (cdr nlist)))
323        ((atom nlist) t)
324      (declare (list nlist))
325      (if (not (%time< n (car nlist))) (return nil))))
326
327 (defun time> (number &rest more-numbers)
328   "Returns T if its arguments are in strictly decreasing order, NIL otherwise."
329   (do* ((n number (car nlist))
330         (nlist more-numbers (cdr nlist)))
331        ((atom nlist) t)
332      (declare (list nlist))
333      (if (not (%time> n (car nlist))) (return nil))))
334
335 (defun time<= (number &rest more-numbers)
336   "Returns T if arguments are in strictly non-decreasing order, NIL otherwise."
337   (do* ((n number (car nlist))
338         (nlist more-numbers (cdr nlist)))
339        ((atom nlist) t)
340      (declare (list nlist))
341      (if (not (%time<= n (car nlist))) (return nil))))
342
343 (defun time>= (number &rest more-numbers)
344   "Returns T if arguments are in strictly non-increasing order, NIL otherwise."
345   (do* ((n number (car nlist))
346         (nlist more-numbers (cdr nlist)))
347        ((atom nlist) t)
348      (declare (list nlist))
349      (if (not (%time>= n (car nlist))) (return nil))))
350
351 (defun time-max (number &rest more-numbers)
352   "Returns the greatest of its arguments."
353   (do ((nlist more-numbers (cdr nlist))
354        (result number))
355       ((null nlist) (return result))
356      (declare (list nlist))
357      (if (%time> (car nlist) result) (setf result (car nlist)))))
358
359 (defun time-min (number &rest more-numbers)
360   "Returns the least of its arguments."
361   (do ((nlist more-numbers (cdr nlist))
362        (result number))
363       ((null nlist) (return result))
364      (declare (list nlist))
365      (if (%time< (car nlist) result) (setf result (car nlist)))))
366
367 (defun time-compare (time-a time-b)
368   (let ((mjd-a (time-mjd time-a))
369         (mjd-b (time-mjd time-b))
370         (sec-a (time-second time-a))
371         (sec-b (time-second time-b))
372         (usec-a (time-usec time-a))
373         (usec-b (time-usec time-b)))
374     (if (= mjd-a mjd-b)
375         (if (= sec-a sec-b)
376             (if (= usec-a usec-b)
377                 :equal
378                 (if (< usec-a usec-b)
379                     :less-than
380                     :greater-than))
381             (if (< sec-a sec-b)
382                 :less-than
383                 :greater-than))
384         (if (< mjd-a mjd-b)
385             :less-than
386             :greater-than))))
387
388 ; now the same for dates
389 (eval-when (:compile-toplevel :load-toplevel)
390 (defun replace-string (string1 search-string replace-string &key (test #'string=))
391   "Search within string1 for search-string, replace with replace-string, non-destructively."
392   (let ((replace-string-length (length replace-string))
393         (search-string-length  (length search-string)))
394     (labels ((sub-replace-string (current-string position)
395                (let ((found-position (search search-string current-string :test test :start2 position)))
396                  (if (null found-position)
397                      current-string
398                      (sub-replace-string (concatenate 'string
399                                                       (subseq current-string 0 found-position)
400                                                       replace-string
401                                                       (subseq current-string (+ found-position search-string-length)))
402                                          (+ position replace-string-length))))))
403       (sub-replace-string string1 0))))
404 );eval-when
405
406 (defmacro wrap-time-for-date (time-func &key (result-func))
407   (let ((date-func (intern (replace-string (symbol-name time-func)
408                                            (symbol-name-default-case "TIME")
409                                            (symbol-name-default-case "DATE")))))
410     `(defun ,date-func (number &rest more-numbers)
411       (let ((result (apply #',time-func (mapcar #'date->time (cons number more-numbers)))))
412         ,(if result-func
413              `(funcall #',result-func result)
414              'result)))))
415
416 (wrap-time-for-date time=)
417 (wrap-time-for-date time/=)
418 (wrap-time-for-date time<)
419 (wrap-time-for-date time>)
420 (wrap-time-for-date time<=)
421 (wrap-time-for-date time>=)
422 (wrap-time-for-date time-max :result-func time->date)
423 (wrap-time-for-date time-min :result-func time->date)
424
425 (defun date-compare (date-a date-b)
426   (time-compare (date->time date-a) (date->time date-b)))
427
428 ;; ------------------------------------------------------------
429 ;; Formatting and output
430
431 (defvar +decimal-printer+ #(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
432
433 (defun db-timestring (time)
434   "return the string to store the given time in the database"
435   (declare (optimize (speed 3)))
436   (let ((output (copy-seq "'XXXX-XX-XX XX:XX:XX.")))
437     (flet ((inscribe-base-10 (output offset size decimal)
438              (declare (type fixnum offset size decimal)
439                       (type (simple-vector 10) +decimal-printer+))
440              (dotimes (x size)
441                (declare (type fixnum x)
442                         (optimize (safety 0)))
443                (multiple-value-bind (next this)
444                    (floor decimal 10)
445                  (setf (aref output (+ (- size x 1) offset))
446                        (aref +decimal-printer+ this))
447                  (setf decimal next)))))
448       (multiple-value-bind (usec second minute hour day month year)
449           (decode-time time)
450         (inscribe-base-10 output 1 4 year)
451         (inscribe-base-10 output 6 2 month)
452         (inscribe-base-10 output 9 2 day)
453         (inscribe-base-10 output 12 2 hour)
454         (inscribe-base-10 output 15 2 minute)
455         (inscribe-base-10 output 18 2 second)
456         (format nil "~a~d'" output usec)))))
457
458 (defun iso-timestring (time)
459   "return the string to store the given time in the database"
460   (declare (optimize (speed 3)))
461   (let ((output (copy-seq "XXXX-XX-XX XX:XX:XX,")))
462     (flet ((inscribe-base-10 (output offset size decimal)
463              (declare (type fixnum offset size decimal)
464                       (type (simple-vector 10) +decimal-printer+))
465              (dotimes (x size)
466                (declare (type fixnum x)
467                         (optimize (safety 0)))
468                (multiple-value-bind (next this)
469                    (floor decimal 10)
470                  (setf (aref output (+ (- size x 1) offset))
471                        (aref +decimal-printer+ this))
472                  (setf decimal next)))))
473       (multiple-value-bind (usec second minute hour day month year)
474           (decode-time time)
475         (inscribe-base-10 output 0 4 year)
476         (inscribe-base-10 output 5 2 month)
477         (inscribe-base-10 output 8 2 day)
478         (inscribe-base-10 output 11 2 hour)
479         (inscribe-base-10 output 14 2 minute)
480         (inscribe-base-10 output 17 2 second)
481         (format nil "~a,~d" output usec)))))
482
483 (defun db-datestring (date)
484   (db-timestring (date->time date)))
485 (defun iso-datestring (date)
486   (iso-timestring (date->time date)))
487
488
489 ;; ------------------------------------------------------------
490 ;; Intervals
491
492 (defstruct interval
493   (start nil)
494   (end nil)
495   (name nil)
496   (contained nil)
497   (type nil)
498   (data nil))
499
500 ;; fix : should also return :contains / :contained
501
502 (defun interval-relation (x y)
503   "Compare the relationship of node x to node y. Returns either
504 :contained :contains :follows :overlaps or :precedes."
505   (let ((xst  (interval-start x))
506         (xend (interval-end x))
507         (yst  (interval-start y))
508         (yend (interval-end y)))
509     (case (time-compare xst yst)
510       (:equal
511        (case (time-compare xend yend)
512          (:less-than
513           :contained)
514          ((:equal :greater-than)
515           :contains)))
516       (:greater-than
517        (case (time-compare xst yend)
518          ((:equal :greater-than)
519           :follows)
520          (:less-than
521           (case (time-compare xend yend)
522             ((:less-than :equal)
523              :contained)
524             ((:greater-than)
525              :overlaps)))))
526       (:less-than
527        (case (time-compare xend yst)
528          ((:equal :less-than)
529           :precedes)
530          (:greater-than
531           (case (time-compare xend yend)
532             (:less-than
533              :overlaps)
534             ((:equal :greater-than)
535              :contains))))))))
536
537 ;; ------------------------------------------------------------
538 ;; interval lists
539
540 (defun sort-interval-list (list)
541   (sort list (lambda (x y)
542                (case (interval-relation x y)
543                  ((:precedes :contains) t)
544                  ((:follows :overlaps :contained) nil)))))
545
546 ;; interval push will return its list of intervals in strict order.
547 (defun interval-push (interval-list interval &optional container-rule)
548   (declare (ignore container-rule))
549   (let ((sorted-list (sort-interval-list interval-list)))
550     (dotimes (x (length sorted-list))
551       (let ((elt (nth x sorted-list)))
552         (case (interval-relation elt interval)
553           (:follows
554            (return-from interval-push (insert-at-index x sorted-list interval)))
555           (:contains
556            (return-from interval-push
557              (replace-at-index x sorted-list
558                                (make-interval :start (interval-start elt)
559                                               :end (interval-end elt)
560                                               :type (interval-type elt)
561                                               :contained (interval-push (interval-contained elt) interval)
562                                               :data (interval-data elt)))))
563           ((:overlaps :contained)
564            (error "Overlap")))))
565     (append sorted-list (list interval))))
566
567 ;; interval lists
568
569 (defun interval-match (list time)
570   "Return the index of the first interval in list containing time"
571   ;; this depends on ordering of intervals!
572   (let ((list (sort-interval-list list)))
573     (dotimes (x (length list))
574       (let ((elt (nth x list)))
575         (when (and (time<= (interval-start elt) time)
576                    (time< time (interval-end elt)))
577           (return-from interval-match x))))))
578
579 (defun interval-clear (list time)
580   (dotimes (x (length list))
581     (let ((elt (nth x list)))
582       (when (and (time<= (interval-start elt) time)
583                  (time< time (interval-end elt)))
584         (if (interval-match (interval-contained elt) time)
585             (return-from interval-clear
586               (replace-at-index x list
587                                 (make-interval :start (interval-start elt)
588                                                :end (interval-end elt)
589                                                :type (interval-type elt)
590                                                :contained (interval-clear (interval-contained elt) time)
591                                                :data (interval-data elt))))
592             (return-from interval-clear
593               (delete-at-index x list)))))))
594
595 (defun interval-edit (list time start end &optional tag)
596   "Attempts to modify the most deeply nested interval in list which
597 begins at time.  If no changes are made, returns nil."
598   ;; function required sorted interval list
599   (let ((list (sort-interval-list list)))
600     (if (null list) nil
601       (dotimes (x (length list))
602         (let ((elt (nth x list)))
603           (when (and (time<= (interval-start elt) time)
604                      (time< time (interval-end elt)))
605             (or (interval-edit (interval-contained elt) time start end tag)
606                 (cond ((and (< 0 x)
607                             (time< start (interval-end (nth (1- x) list))))
608                        (error "Overlap of previous interval"))
609                       ((and (< x (1- (length list)))
610                             (time< (interval-start (nth (1+ x) list)) end))
611                        (error "~S ~S ~S ~S Overlap of next interval" x (length list) (interval-start (nth (1+ x) list)) end ))
612                       ((time= (interval-start elt) time)
613                        (return-from interval-edit
614                          (replace-at-index x list
615                                            (make-interval :start start
616                                                           :end end
617                                                           :type (interval-type elt)
618                                                           :contained (restrict-intervals (interval-contained elt) start end)
619                                                           :data (or tag (interval-data elt))))))))))))))
620
621 (defun restrict-intervals (list start end &aux newlist)
622   (let ((test-interval (make-interval :start start :end end)))
623     (dolist (elt list)
624       (when (equal :contained
625                    (interval-relation elt test-interval))
626         (push elt newlist)))
627     (nreverse newlist)))
628
629 ;;; utils from odcl/list.lisp
630
631 (defun replace-at-index (idx list elt)
632   (cond ((= idx 0)
633          (cons elt (cdr list)))
634         ((= idx (1- (length list)))
635          (append (butlast list) (list elt)))
636         (t
637          (append (subseq list 0 idx)
638                  (list elt)
639                  (subseq list (1+ idx))))))
640
641 (defun insert-at-index (idx list elt)
642   (cond ((= idx 0)
643          (cons elt list))
644         ((= idx (1- (length list)))
645          (append list (list elt)))
646         (t
647          (append (subseq list 0 idx)
648                  (list elt)
649                  (subseq list idx)))))
650
651 (defun delete-at-index (idx list)
652   (cond ((= idx 0)
653          (cdr list))
654         ((= idx (1- (length list)))
655          (butlast list))
656         (t
657          (append (subseq list 0 idx)
658                  (subseq list (1+ idx))))))
659
660
661 ;; ------------------------------------------------------------
662 ;; return MJD for Gregorian date
663
664 (defun gregorian-to-mjd (month day year)
665   (let ((b 0)
666         (month-adj month)
667         (year-adj (if (< year 0)
668                       (+ year 1)
669                       year))
670         d
671         c)
672     (when (< month 3)
673       (incf month-adj 12)
674       (decf year-adj))
675     (unless (or (< year 1582)
676                 (and (= year 1582)
677                      (or (< month 10)
678                          (and (= month 10)
679                               (< day 15)))))
680       (let ((a (floor (/ year-adj 100))))
681         (setf b (+ (- 2 a) (floor (/ a 4))))))
682     (if (< year-adj 0)
683         (setf c (floor (- (* 365.25d0 year-adj) 679006.75d0)))
684         (setf c (floor (- (* 365.25d0 year-adj) 679006d0))))
685     (setf d (floor (* 30.6001 (+ 1 month-adj))))
686     ;; (cmsg "b ~s c ~s d ~s day ~s" b c d day)
687     (+ b c d day)))
688
689 ;; convert MJD to Gregorian date
690
691 (defun mjd-to-gregorian (mjd)
692   (let (z r g a b c year month day)
693     (setf z (floor (+ mjd 678882)))
694     (setf r (- (+ mjd 678882) z))
695     (setf g (- z .25))
696     (setf a (floor (/ g 36524.25)))
697     (setf b (- a (floor (/ a 4))))
698     (setf year (floor (/ (+ b g) 365.25)))
699     (setf c (- (+ b z) (floor (* 365.25 year))))
700     (setf month (truncate (/ (+ (* 5 c) 456) 153)))
701     (setf day (+ (- c (truncate (/ (- (* 153 month) 457) 5))) r))
702     (when (> month 12)
703       (incf year)
704       (decf month 12))
705     (list month day year)))
706
707 (defun duration+ (time &rest durations)
708   "Add each DURATION to TIME, returning a new wall-time value."
709   (let ((year   (duration-year time))
710         (month  (duration-month time))
711         (day    (duration-day time))
712         (hour   (duration-hour time))
713         (minute (duration-minute time))
714         (second (duration-second time))
715         (usec   (duration-usec time)))
716     (dolist (duration durations)
717       (incf year    (duration-year duration))
718       (incf month   (duration-month duration))
719       (incf day     (duration-day duration))
720       (incf hour    (duration-hour duration))
721       (incf minute  (duration-minute duration))
722       (incf second  (duration-second duration))
723       (incf usec    (duration-usec duration)))
724     (make-duration :year year :month month :day day :hour hour :minute minute
725                    :second second :usec usec)))
726
727 (defun duration- (duration &rest durations)
728     "Subtract each DURATION from TIME, returning a new duration value."
729   (let ((year   (duration-year duration))
730         (month  (duration-month duration))
731         (day    (duration-day duration))
732         (hour   (duration-hour duration))
733         (minute (duration-minute duration))
734         (second (duration-second duration))
735         (usec   (duration-usec duration)))
736     (dolist (duration durations)
737       (decf year    (duration-year duration))
738       (decf month   (duration-month duration))
739       (decf day     (duration-day duration))
740       (decf hour    (duration-hour duration))
741       (decf minute  (duration-minute duration))
742       (decf second  (duration-second duration))
743       (decf usec    (duration-usec duration)))
744     (make-duration :year year :month month :day day :hour hour :minute minute
745                    :second second :usec usec)))
746
747 ;; Date + Duration
748
749 (defun time+ (time &rest durations)
750   "Add each DURATION to TIME, returning a new wall-time value."
751   (let ((new-time (copy-time time)))
752     (dolist (duration durations)
753       (roll new-time
754             :year (duration-year duration)
755             :month (duration-month duration)
756             :day (duration-day duration)
757             :hour (duration-hour duration)
758             :minute (duration-minute duration)
759             :second (duration-second duration)
760             :usec (duration-usec duration)
761             :destructive t))
762     new-time))
763
764 (defun date+ (date &rest durations)
765   "Add each DURATION to DATE, returning a new date value.
766 Note that (barring daylight saving time) 12h + 12h will result in a new day, but doing
767 it as separate calculations will not, as the time is chopped to a date before being returned."
768   (time->date (apply #'time+ (cons (date->time date) durations))))
769
770 (defun time- (time &rest durations)
771   "Subtract each DURATION from TIME, returning a new wall-time value."
772   (let ((new-time (copy-time time)))
773     (dolist (duration durations)
774       (roll new-time
775             :year (- (duration-year duration))
776             :month (- (duration-month duration))
777             :day (- (duration-day duration))
778             :hour (- (duration-hour duration))
779             :minute (- (duration-minute duration))
780             :second (- (duration-second duration))
781             :usec (- (duration-usec duration))
782             :destructive t))
783     new-time))
784
785 (defun date- (date &rest durations)
786   "Subtract each DURATION to DATE, returning a new date value.
787 Note that (barring daylight saving time) 12h + 12h will result in a new day, but doing
788 it as separate calculations will not, as the time is chopped to a date before being returned."
789   (time->date (apply #'time- (cons (date->time date) durations))))
790
791 (defun time-difference (time1 time2)
792   "Returns a DURATION representing the difference between TIME1 and
793 TIME2."
794   (flet ((do-diff (time1 time2)
795
796   (let (day-diff sec-diff)
797     (setf day-diff (- (time-mjd time2)
798                       (time-mjd time1)))
799     (if (> day-diff 0)
800         (progn (decf day-diff)
801                (setf sec-diff (+ (time-second time2)
802                                  (- (* 60 60 24)
803                                     (time-second time1)))))
804       (setf sec-diff (- (time-second time2)
805                         (time-second time1))))
806      (make-duration :day day-diff
807                    :second sec-diff))))
808     (if (time< time1 time2)
809         (do-diff time1 time2)
810       (do-diff time2 time1))))
811
812 (defun date-difference (date1 date2)
813   "Returns a DURATION representing the difference between TIME1 and
814 TIME2."
815   (time-difference (date->time date1) (date->time date2)))
816
817 (defun format-date (stream date &key format
818                     (date-separator "-")
819                     (internal-separator " "))
820   "produces on stream the datestring corresponding to the date
821 with the given options"
822   (format-time stream (date->time date)
823                :format format
824                :date-separator date-separator
825                :internal-separator internal-separator))
826
827 (defun format-time (stream time &key format
828                     (date-separator "-")
829                     (time-separator ":")
830                     (internal-separator " "))
831   "produces on stream the timestring corresponding to the wall-time
832 with the given options"
833   (let ((*print-circle* nil))
834     (multiple-value-bind (usec second minute hour day month year dow)
835         (decode-time time)
836       (case format
837         (:pretty
838          (format stream "~A ~A, ~A ~D, ~D"
839                  (pretty-time hour minute)
840                  (day-name dow)
841                  (month-name month)
842                  day
843                  year))
844         (:short-pretty
845          (format stream "~A, ~D/~D/~D"
846                  (pretty-time hour minute)
847                  month day year))
848         (:iso
849          (let ((string (iso-timestring time)))
850            (if stream
851                (write-string string stream)
852              string)))
853         (t
854          (format stream "~2,'0D~A~2,'0D~A~2,'0D~A~2,'0D~A~2,'0D~A~2,'0D.~6,'0D"
855                  year date-separator month date-separator day
856                  internal-separator hour time-separator minute time-separator
857                  second usec))))))
858
859 (defun pretty-time (hour minute)
860   (cond
861    ((eq hour 0)
862     (format nil "12:~2,'0D AM" minute))
863    ((eq hour 12)
864     (format nil "12:~2,'0D PM" minute))
865    ((< hour 12)
866     (format nil "~D:~2,'0D AM" hour minute))
867    ((and (> hour 12) (< hour 24))
868     (format nil "~D:~2,'0D PM" (- hour 12) minute))
869    (t
870     (error "pretty-time got bad hour"))))
871
872 (defun leap-days-in-days (days)
873   ;; return the number of leap days between Mar 1 2000 and
874   ;; (Mar 1 2000) + days, where days can be negative
875   (if (< days 0)
876       (ceiling (/ (- days) (* 365 4)))
877       (floor (/ days (* 365 4)))))
878
879 (defun current-year ()
880   (third (mjd-to-gregorian (time-mjd (get-time)))))
881
882 (defun current-month ()
883   (first (mjd-to-gregorian (time-mjd (get-time)))))
884
885 (defun current-day ()
886   (second (mjd-to-gregorian (time-mjd (get-time)))))
887
888 (defun parse-date-time (string)
889   "parses date like 08/08/01, 8.8.2001, eg"
890   (when (> (length string) 1)
891     (let ((m (current-month))
892           (d (current-day))
893           (y (current-year)))
894       (let ((integers (mapcar #'parse-integer (hork-integers string))))
895         (case (length integers)
896           (1
897            (setf y (car integers)))
898           (2
899            (setf m (car integers))
900            (setf y (cadr integers)))
901           (3
902            (setf m (car integers))
903            (setf d (cadr integers))
904            (setf y (caddr integers)))
905           (t
906            (return-from parse-date-time))))
907       (when (< y 100)
908         (incf y 2000))
909       (make-time :year y :month m :day d))))
910
911 (defun hork-integers (input)
912   (let ((output '())
913         (start 0))
914     (dotimes (x (length input))
915       (unless (<= 48 (char-code (aref input x)) 57)
916         (push (subseq input start x) output)
917         (setf start (1+ x))))
918     (nreverse (push (subseq input start) output))))
919
920 (defun merged-time (day time-of-day)
921   (%make-wall-time :mjd (time-mjd day)
922                    :second (time-second time-of-day)))
923
924 (defun time-meridian (hours)
925   (cond ((= hours 0)
926          (values 12 "AM"))
927         ((= hours 12)
928          (values 12 "PM"))
929         ((< 12 hours)
930          (values (- hours 12) "PM"))
931         (t
932          (values hours "AM"))))
933
934 (defgeneric to-string (val &rest keys)
935   )
936
937 (defmethod to-string ((time wall-time) &rest keys)
938   (destructuring-bind (&key (style :daytime) &allow-other-keys)
939       keys
940     (print-date time style)))
941
942 (defun print-date (time &optional (style :daytime))
943   (multiple-value-bind (usec second minute hour day month year dow)
944       (decode-time time)
945     (declare (ignore usec second))
946     (multiple-value-bind (hours meridian)
947         (time-meridian hour)
948       (ecase style
949         (:time-of-day
950          ;; 2:00 PM
951          (format nil "~d:~2,'0d ~a" hours minute meridian))
952         (:long-day
953          ;; October 11th, 2000
954          (format nil "~a ~d, ~d" (month-name month) day year))
955         (:month
956          ;; October
957          (month-name month))
958         (:month-year
959          ;; October 2000
960          (format nil "~a ~d" (month-name month) year))
961         (:full
962          ;; 11:08 AM, November 22, 2002
963          (format nil "~d:~2,'0d ~a, ~a ~d, ~d"
964                  hours minute meridian (month-name month) day year))
965         (:full+weekday
966          ;; 11:09 AM Friday, November 22, 2002
967          (format nil "~d:~2,'0d ~a ~a, ~a ~d, ~d"
968                  hours minute meridian (nth dow *day-names*)
969                  (month-name month) day year))
970         (:daytime
971          ;; 11:09 AM, 11/22/2002
972          (format-time nil time :format :short-pretty))
973         (:day
974          ;; 11/22/2002
975          (format nil "~d/~d/~d" month day year))))))
976
977 (defun time-element (time element)
978   (multiple-value-bind (usec second minute hour day month year dow)
979       (decode-time time)
980     (declare (ignore usec))
981     (ecase element
982       (:seconds
983        second)
984       (:minutes
985        minute)
986       (:hours
987        hour)
988       (:day-of-month
989        day)
990       (:integer-day-of-week
991        dow)
992       (:day-of-week
993        (nth dow *day-keywords*))
994       (:month
995        month)
996       (:year
997        year))))
998
999 (defun date-element (date element)
1000   (time-element (date->time date) element))
1001
1002 (defun format-duration (stream duration &key (precision :minute))
1003   (let ((second (duration-second duration))
1004         (minute (duration-minute duration))
1005         (hour (duration-hour duration))
1006         (day (duration-day duration))
1007         (month (duration-month duration))
1008         (year (duration-year duration))
1009         (return (null stream))
1010         (stream (or stream (make-string-output-stream))))
1011     (ecase precision
1012       (:day
1013        (setf hour 0 second 0 minute 0))
1014       (:hour
1015        (setf second 0 minute 0))
1016       (:minute
1017        (setf second 0))
1018       (:second
1019        t))
1020     (if (= 0 year month day hour minute)
1021         (format stream "0 minutes")
1022         (let ((sent? nil))
1023           (when (< 0 year)
1024             (format stream "~d year~p" year year)
1025             (setf sent? t))
1026           (when (< 0 month)
1027             (when sent?
1028               (write-char #\Space stream))
1029             (format stream "~d month~p" month month)
1030             (setf sent? t))
1031           (when (< 0 day)
1032             (when sent?
1033               (write-char #\Space stream))
1034             (format stream "~d day~p" day day)
1035             (setf sent? t))
1036           (when (< 0 hour)
1037             (when sent?
1038               (write-char #\Space stream))
1039             (format stream "~d hour~p" hour hour)
1040             (setf sent? t))
1041           (when (< 0 minute)
1042             (when sent?
1043               (write-char #\Space stream))
1044             (format stream "~d min~p" minute minute)
1045             (setf sent? t))
1046           (when (< 0 second)
1047             (when sent?
1048               (write-char #\Space stream))
1049             (format stream "~d sec~p" second second))))
1050     (when return
1051       (get-output-stream-string stream))))
1052
1053 (defgeneric midnight (self))
1054 (defmethod midnight ((self wall-time))
1055   "truncate hours, minutes and seconds"
1056   (%make-wall-time :mjd (time-mjd self)))
1057
1058 (defun roll (date &key (year 0) (month 0) (day 0) (second 0) (hour 0)
1059              (minute 0) (usec 0) (destructive nil))
1060   (unless (= 0 year month)
1061     (multiple-value-bind (year-orig month-orig day-orig)
1062         (time-ymd date)
1063       (multiple-value-bind (new-year new-month)
1064          (floor (+ month month-orig (* 12 (+ year year-orig))) 12)
1065        (let ((new-date (make-time :year new-year
1066                                   :month new-month
1067                                   :day day-orig
1068                                   :second (time-second date)
1069                                   :usec usec)))
1070          (if destructive
1071              (setf (time-mjd date) (time-mjd new-date))
1072              (setq date new-date))))))
1073   (let ((mjd (time-mjd date))
1074         (sec (time-second date)))
1075     (multiple-value-bind (sec-new usec-new)
1076         (floor (+ usec (time-usec date)
1077                   (* 1000000
1078                      (+ sec second
1079                         (* 60 minute)
1080                         (* 60 60 hour))))
1081                1000000)
1082       (multiple-value-bind (mjd-new sec-new)
1083           (floor sec-new (* 60 60 24))
1084         (if destructive
1085             (progn
1086               (setf (time-mjd date) (+ mjd mjd-new day)
1087                     (time-second date) sec-new
1088                     (time-usec date) usec-new)
1089               date)
1090             (%make-wall-time :mjd (+ mjd mjd-new day)
1091                              :second sec-new
1092                              :usec usec-new))))))
1093
1094 (defun roll-to (date size position)
1095   (ecase size
1096     (:month
1097      (ecase position
1098        (:beginning
1099         (roll date :day (+ 1
1100                            (- (time-element date :day-of-month)))))
1101        (:end
1102         (roll date :day (+ (days-in-month (time-element date :month)
1103                                           (time-element date :year))
1104                            (- (time-element date :day-of-month)))))))))
1105
1106 (defun week-containing (time)
1107   (let* ((midn (midnight time))
1108          (dow (time-element midn :integer-day-of-week)))
1109     (list (roll midn :day (- dow))
1110           (roll midn :day (- 7 dow)))))
1111
1112 (defun leap-year? (year)
1113   "t if YEAR is a leap yeap in the Gregorian calendar"
1114   (and (= 0 (mod year 4))
1115        (or (not (= 0 (mod year 100)))
1116            (= 0 (mod year 400)))))
1117
1118 (defun valid-month-p (month)
1119   "t if MONTH exists in the Gregorian calendar"
1120   (<= 1 month 12))
1121
1122 (defun valid-gregorian-date-p (date)
1123   "t if DATE (year month day) exists in the Gregorian calendar"
1124   (let ((max-day (days-in-month (nth 1 date) (nth 0 date))))
1125     (<= 1 (nth 2 date) max-day)))
1126
1127 (defun days-in-month (month year &key (careful t))
1128   "the number of days in MONTH of YEAR, observing Gregorian leap year
1129 rules"
1130   (declare (type fixnum month year))
1131   (when careful
1132     (check-type month (satisfies valid-month-p)
1133                 "between 1 (January) and 12 (December)"))
1134   (if (eql month 2)                     ; feb
1135       (if (leap-year? year)
1136           29 28)
1137       (let ((even (mod (1- month) 2)))
1138         (if (< month 8)                 ; aug
1139             (- 31 even)
1140             (+ 30 even)))))
1141
1142 (defun day-of-year (year month day &key (careful t))
1143   "the day number within the year of the date DATE.  For example,
1144 1987 1 1 returns 1"
1145   (declare (type fixnum year month day))
1146   (when careful
1147     (let ((date (list year month day)))
1148     (check-type date (satisfies valid-gregorian-date-p)
1149                 "a valid Gregorian date")))
1150   (let ((doy (+ day (* 31 (1- month)))))
1151     (declare (type fixnum doy))
1152     (when (< 2 month)
1153       (setf doy (- doy (floor (+ 23 (* 4 month)) 10)))
1154       (when (leap-year? year)
1155         (incf doy)))
1156     doy))
1157
1158 (defun parse-yearstring (string)
1159   (let ((year (or (parse-integer-insensitively string)
1160                   (extract-roman string))))
1161     (when (and year (< 1500 year 2500))
1162       (make-time :year year))))
1163
1164 (defun parse-integer-insensitively (string)
1165   (let ((start (position-if #'digit-char-p string))
1166         (end   (position-if #'digit-char-p string :from-end t)))
1167     (when (and start end)
1168       (parse-integer (subseq string start (1+ end)) :junk-allowed t))))
1169
1170 (defvar *roman-digits*
1171   '((#\M . 1000)
1172     (#\D . 500)
1173     (#\C . 100)
1174     (#\L . 50)
1175     (#\X . 10)
1176     (#\V . 5)
1177     (#\I . 1)))
1178
1179 (defun extract-roman (string &aux parse)
1180   (dotimes (x (length string))
1181     (let ((val (cdr (assoc (aref string x) *roman-digits*))))
1182       (when (and val parse (< (car parse) val))
1183         (push (- (pop parse)) parse))
1184       (push val parse)))
1185   (apply #'+ parse))
1186
1187
1188 ;; ------------------------------------------------------------
1189 ;; Parsing iso-8601 timestrings
1190
1191 (define-condition iso-8601-syntax-error (sql-user-error)
1192   ((bad-component;; year, month whatever
1193     :initarg :bad-component
1194     :reader bad-component))
1195   (:report (lambda (c stream)
1196              (format stream "Bad component: ~A " (bad-component c)))))
1197
1198 (defun parse-timestring (timestring &key (start 0) end junk-allowed)
1199   "parse a timestring and return the corresponding wall-time.  If the
1200 timestring starts with P, read a duration; otherwise read an ISO 8601
1201 formatted date string."
1202   (declare (ignore junk-allowed))
1203   (let ((string (subseq timestring start end)))
1204     (if (char= (aref string 0) #\P)
1205         (parse-iso-8601-duration string)
1206       (parse-iso-8601-time string))))
1207
1208 (defun parse-datestring (datestring &key (start 0) end junk-allowed)
1209   "parse a ISO 8601 timestring and return the corresponding date.
1210 Will throw a hissy fit if the date string is a duration. Will ignore any precision beyond day (hour/min/sec/usec)."
1211   (let ((parsed-value (parse-timestring datestring :start start :end end :junk-allowed junk-allowed)))
1212     (ecase (type-of parsed-value)
1213       (wall-time (%make-date :mjd (time-mjd parsed-value))))))
1214
1215
1216 (defvar *iso-8601-duration-delimiters*
1217   '((#\Y . :years)
1218     (#\D . :days)
1219     (#\H . :hours)
1220     (#\M . :months/minutes)
1221     (#\S . :seconds)))
1222
1223 (defun iso-8601-delimiter (elt)
1224   (cdr (assoc elt *iso-8601-duration-delimiters*)))
1225
1226 (defun iso-8601-duration-subseq (string end)
1227   (let* ((pos (position-if #'iso-8601-delimiter string :end end :from-end t))
1228          (pos2 (when pos
1229                  (position-if-not #'digit-char-p string :end pos :from-end t)))
1230          (number (when pos2
1231                    (parse-integer
1232                     (subseq string (1+ pos2) pos) :junk-allowed t))))
1233     (when number
1234       (values number
1235               (1+ pos)
1236               (1+ pos2)
1237               (iso-8601-delimiter (aref string pos))))))
1238
1239 (defun parse-iso-8601-duration (string)
1240   "return a wall-time from a duration string"
1241   (block parse
1242     (let ((years 0)
1243           (months 0)
1244           (days 0)
1245           (secs 0)
1246           (hours 0)
1247           (minutes 0)
1248           (index (length string))
1249           (months/minutes nil))
1250       (loop
1251        (multiple-value-bind (duration end next-index duration-type)
1252            (iso-8601-duration-subseq string index)
1253          (declare (ignore end))
1254          (case duration-type
1255            (:years
1256             (incf years duration))
1257            (:months/minutes
1258             (if months/minutes
1259                 (incf months duration)
1260                 (progn
1261                   (setq months/minutes t)
1262                   (incf minutes duration))))
1263            (:days
1264             (setq months/minutes t)
1265             (incf days duration))
1266            (:hours
1267             (setq months/minutes t)
1268             (incf hours duration))
1269            (:seconds
1270             (incf secs duration))
1271            (t
1272             (return-from parse
1273               (make-duration
1274                :year years :month months :day days :hour hours
1275                :minute minutes :second secs))))
1276          (setf index next-index))))))
1277
1278 ;; e.g. 2000-11-11 00:00:00-06
1279
1280 (defun parse-iso-8601-time (string)
1281   "return the wall-time corresponding to the given ISO 8601 datestring"
1282   (multiple-value-bind (year month day hour minute second usec offset)
1283       (syntax-parse-iso-8601 string)
1284     (make-time :year year
1285                :month month
1286                :day day
1287                :hour hour
1288                :minute minute
1289                :second second
1290                :usec usec
1291                :offset offset)))
1292
1293
1294 (defun syntax-parse-iso-8601 (string)
1295   ;; use strlen to determine if fractional seconds are present in timestamp
1296   (let ((strlen (length string))
1297         year month day hour minute second usec gmt-sec-offset)
1298     (handler-case
1299         (progn
1300           (setf year           (parse-integer string :start 0 :end 4)
1301                 month          (parse-integer string :start 5 :end 7)
1302                 day            (parse-integer string :start 8 :end 10)
1303                 hour           (if (<= 13 strlen)
1304                                    (parse-integer string :start 11 :end 13)
1305                                    0)
1306                 minute         (if (<= 16 strlen)
1307                                    (parse-integer string :start 14 :end 16)
1308                                    0)
1309                 second         (if (<= 19 strlen)
1310                                    (parse-integer string :start 17 :end 19)
1311                                    0))
1312           (cond
1313             ((and (> strlen 19)
1314                   (or (char= #\, (char string 19))
1315                       (char= #\. (char string 19))))
1316              (multiple-value-bind (parsed-usec usec-end)
1317                  (parse-integer string :start 20 :junk-allowed t)
1318                (setf usec          (or parsed-usec 0)
1319                      gmt-sec-offset (if (<= (+ 3 usec-end)  strlen)
1320                                         (let ((skip-to (or (position #\+ string :start 19)
1321                                                            (position #\- string :start 19))))
1322                                           (if skip-to
1323                                               (* 60 60
1324                                                  (parse-integer string :start skip-to
1325                                                                 :end (+ skip-to 3)))
1326                                               0))
1327                                         0))))
1328             (t
1329              (setf usec           0
1330                    gmt-sec-offset (if (<= 22  strlen)
1331                                       (let ((skip-to (or (position #\+ string :start 19)
1332                                                          (position #\- string :start 19))))
1333                                         (if skip-to
1334                                             (* 60 60
1335                                                (parse-integer string :start skip-to
1336                                                               :end (+ skip-to 3)))
1337                                             0))
1338                                       0))))
1339           (unless (< 0 year)
1340             (error 'iso-8601-syntax-error
1341                    :bad-component '(year . 0)))
1342           (unless (< 0 month)
1343             (error 'iso-8601-syntax-error
1344                    :bad-component '(month . 0)))
1345           (unless (< 0 day)
1346             (error 'iso-8601-syntax-error
1347                    :bad-component '(month . 0)))
1348           (values year month day hour minute second usec gmt-sec-offset))
1349       (simple-error ()
1350         (error 'iso-8601-syntax-error
1351                :bad-component
1352                (car (find-if (lambda (pair) (null (cdr pair)))
1353                              `((year . ,year) (month . ,month)
1354                                (day . ,day) (hour . ,hour)
1355                                (minute . ,minute) (second . ,second)
1356                                (usec . ,usec)
1357                                (timezone . ,gmt-sec-offset)))))))))