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