r10839: 15 Nov 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   (if *print-escape*
71       (format stream "#<WALL-TIME: ~a>" (format-time nil time))
72       (format-time stream time :format :pretty)))
73
74 (defstruct (duration (:constructor %make-duration)
75                      (:print-function %print-duration))
76   (year 0 :type fixnum)
77   (month 0 :type fixnum)
78   (day 0 :type fixnum)
79   (hour 0 :type fixnum)
80   (second 0 :type fixnum)
81   (minute 0 :type fixnum)
82   (usec 0 :type fixnum))
83
84 (defun %print-duration (duration stream depth)
85   (declare (ignore depth))
86   (if *print-escape*
87       (format stream "#<DURATION: ~a>"
88               (format-duration nil duration :precision :second))
89       (format-duration stream duration :precision :second)))
90
91 (defstruct (date (:constructor %make-date)
92                  (:print-function %print-date))
93   (mjd 0 :type fixnum))
94
95 (defun %print-date (date stream depth)
96   (declare (ignore depth))
97   (if *print-escape*
98       (format stream "#<DATE: ~a>" (format-date nil date))
99       (format-date stream date :format :pretty)))
100
101 );eval-when
102
103 (defun duration-timestring (duration)
104   (let ((second (duration-second duration))
105         (minute (duration-minute duration))
106         (hour (duration-hour duration))
107         (day (duration-day duration)))
108     (format nil "P~dD~dH~dM~dS" 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) "TIME" "DATE"))))
408     `(defun ,date-func (number &rest more-numbers)
409       (let ((result (apply #',time-func (mapcar #'date->time (cons number more-numbers)))))
410         ,(if result-func
411              `(funcall #',result-func result)
412              'result)))))
413
414 (wrap-time-for-date time=)
415 (wrap-time-for-date time/=)
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-max :result-func time->date)
421 (wrap-time-for-date time-min :result-func time->date)
422
423 (defun date-compare (date-a date-b)
424   (time-compare (date->time date-a) (date->time date-b)))
425
426 ;; ------------------------------------------------------------
427 ;; Formatting and output
428
429 (defvar +decimal-printer+ #(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
430
431 (defun db-timestring (time)
432   "return the string to store the given time in the database"
433   (declare (optimize (speed 3)))
434   (let ((output (copy-seq "'XXXX-XX-XX XX:XX:XX.")))
435     (flet ((inscribe-base-10 (output offset size decimal)
436              (declare (type fixnum offset size decimal)
437                       (type (simple-vector 10) +decimal-printer+))
438              (dotimes (x size)
439                (declare (type fixnum x)
440                         (optimize (safety 0)))
441                (multiple-value-bind (next this)
442                    (floor decimal 10)
443                  (setf (aref output (+ (- size x 1) offset))
444                        (aref +decimal-printer+ this))
445                  (setf decimal next)))))
446       (multiple-value-bind (usec second minute hour day month year)
447           (decode-time time)
448         (inscribe-base-10 output 1 4 year)
449         (inscribe-base-10 output 6 2 month)
450         (inscribe-base-10 output 9 2 day)
451         (inscribe-base-10 output 12 2 hour)
452         (inscribe-base-10 output 15 2 minute)
453         (inscribe-base-10 output 18 2 second)
454         (format nil "~a~d'" output usec)))))
455
456 (defun iso-timestring (time)
457   "return the string to store the given time in the database"
458   (declare (optimize (speed 3)))
459   (let ((output (copy-seq "XXXX-XX-XX XX:XX:XX,")))
460     (flet ((inscribe-base-10 (output offset size decimal)
461              (declare (type fixnum offset size decimal)
462                       (type (simple-vector 10) +decimal-printer+))
463              (dotimes (x size)
464                (declare (type fixnum x)
465                         (optimize (safety 0)))
466                (multiple-value-bind (next this)
467                    (floor decimal 10)
468                  (setf (aref output (+ (- size x 1) offset))
469                        (aref +decimal-printer+ this))
470                  (setf decimal next)))))
471       (multiple-value-bind (usec second minute hour day month year)
472           (decode-time time)
473         (inscribe-base-10 output 0 4 year)
474         (inscribe-base-10 output 5 2 month)
475         (inscribe-base-10 output 8 2 day)
476         (inscribe-base-10 output 11 2 hour)
477         (inscribe-base-10 output 14 2 minute)
478         (inscribe-base-10 output 17 2 second)
479         (format nil "~a,~d" output usec)))))
480
481 (defun db-datestring (date)
482   (db-timestring (date->time date)))
483 (defun iso-datestring (date)
484   (iso-timestring (date->time date)))
485
486
487 ;; ------------------------------------------------------------
488 ;; Intervals
489
490 (defstruct interval
491   (start nil)
492   (end nil)
493   (name nil) 
494   (contained nil)
495   (type nil)
496   (data nil))
497
498 ;; fix : should also return :contains / :contained
499
500 (defun interval-relation (x y)
501   "Compare the relationship of node x to node y. Returns either
502 :contained :contains :follows :overlaps or :precedes."
503   (let ((xst  (interval-start x))
504         (xend (interval-end x))
505         (yst  (interval-start y))
506         (yend (interval-end y)))
507     (case (time-compare xst yst)
508       (:equal
509        (case (time-compare xend yend)
510          (:less-than
511           :contained)
512          ((:equal :greater-than)
513           :contains)))
514       (:greater-than
515        (case (time-compare xst yend)
516          ((:equal :greater-than)
517           :follows)
518          (:less-than
519           (case (time-compare xend yend)
520             ((:less-than :equal)
521              :contained)
522             ((:greater-than)
523              :overlaps)))))
524       (:less-than
525        (case (time-compare xend yst)
526          ((:equal :less-than)
527           :precedes)
528          (:greater-than
529           (case (time-compare xend yend)
530             (:less-than
531              :overlaps)
532             ((:equal :greater-than)
533              :contains))))))))
534
535 ;; ------------------------------------------------------------
536 ;; interval lists
537
538 (defun sort-interval-list (list)
539   (sort list (lambda (x y)
540                (case (interval-relation x y)
541                  ((:precedes :contains) t)
542                  ((:follows :overlaps :contained) nil)))))
543
544 ;; interval push will return its list of intervals in strict order.
545 (defun interval-push (interval-list interval &optional container-rule)
546   (declare (ignore container-rule))
547   (let ((sorted-list (sort-interval-list interval-list)))
548     (dotimes (x (length sorted-list))
549       (let ((elt (nth x sorted-list)))
550         (case (interval-relation elt interval)
551           (:follows
552            (return-from interval-push (insert-at-index x sorted-list interval)))
553           (:contains
554            (return-from interval-push
555              (replace-at-index x sorted-list
556                                (make-interval :start (interval-start elt)
557                                               :end (interval-end elt)
558                                               :type (interval-type elt)
559                                               :contained (interval-push (interval-contained elt) interval)
560                                               :data (interval-data elt)))))
561           ((:overlaps :contained)
562            (error "Overlap")))))
563     (append sorted-list (list interval))))
564
565 ;; interval lists
566                   
567 (defun interval-match (list time)
568   "Return the index of the first interval in list containing time"
569   ;; this depends on ordering of intervals!
570   (let ((list (sort-interval-list list))) 
571     (dotimes (x (length list))
572       (let ((elt (nth x list)))
573         (when (and (time<= (interval-start elt) time)
574                    (time< time (interval-end elt)))
575           (return-from interval-match x))))))
576   
577 (defun interval-clear (list time)
578   (dotimes (x (length list))
579     (let ((elt (nth x list)))
580       (when (and (time<= (interval-start elt) time)
581                  (time< time (interval-end elt)))
582         (if (interval-match (interval-contained elt) time)
583             (return-from interval-clear
584               (replace-at-index x list
585                                 (make-interval :start (interval-start elt)
586                                                :end (interval-end elt)
587                                                :type (interval-type elt)
588                                                :contained (interval-clear (interval-contained elt) time)
589                                                :data (interval-data elt))))
590             (return-from interval-clear
591               (delete-at-index x list)))))))
592
593 (defun interval-edit (list time start end &optional tag)
594   "Attempts to modify the most deeply nested interval in list which
595 begins at time.  If no changes are made, returns nil."
596   ;; function required sorted interval list
597   (let ((list (sort-interval-list list))) 
598     (if (null list) nil
599       (dotimes (x (length list))
600         (let ((elt (nth x list)))
601           (when (and (time<= (interval-start elt) time)
602                      (time< time (interval-end elt)))
603             (or (interval-edit (interval-contained elt) time start end tag)
604                 (cond ((and (< 0 x)
605                             (time< start (interval-end (nth (1- x) list))))
606                        (error "Overlap of previous interval"))
607                       ((and (< x (1- (length list)))
608                             (time< (interval-start (nth (1+ x) list)) end))
609                        (error "~S ~S ~S ~S Overlap of next interval" x (length list) (interval-start (nth (1+ x) list)) end ))
610                       ((time= (interval-start elt) time)
611                        (return-from interval-edit
612                          (replace-at-index x list
613                                            (make-interval :start start
614                                                           :end end
615                                                           :type (interval-type elt)
616                                                           :contained (restrict-intervals (interval-contained elt) start end)
617                                                           :data (or tag (interval-data elt))))))))))))))
618
619 (defun restrict-intervals (list start end &aux newlist)
620   (let ((test-interval (make-interval :start start :end end)))
621     (dolist (elt list)
622       (when (equal :contained
623                    (interval-relation elt test-interval))
624         (push elt newlist)))
625     (nreverse newlist)))
626
627 ;;; utils from odcl/list.lisp
628
629 (defun replace-at-index (idx list elt)
630   (cond ((= idx 0)
631          (cons elt (cdr list)))
632         ((= idx (1- (length list)))
633          (append (butlast list) (list elt)))
634         (t
635          (append (subseq list 0 idx)
636                  (list elt)
637                  (subseq list (1+ idx))))))
638
639 (defun insert-at-index (idx list elt)
640   (cond ((= idx 0)
641          (cons elt list))
642         ((= idx (1- (length list)))
643          (append list (list elt)))
644         (t
645          (append (subseq list 0 idx)
646                  (list elt)
647                  (subseq list idx)))))
648
649 (defun delete-at-index (idx list)
650   (cond ((= idx 0)
651          (cdr list))
652         ((= idx (1- (length list)))
653          (butlast list))
654         (t
655          (append (subseq list 0 idx)
656                  (subseq list (1+ idx))))))
657
658
659 ;; ------------------------------------------------------------
660 ;; return MJD for Gregorian date
661
662 (defun gregorian-to-mjd (month day year)
663   (let ((b 0)
664         (month-adj month)
665         (year-adj (if (< year 0)
666                       (+ year 1)
667                       year))
668         d
669         c)
670     (when (< month 3)
671       (incf month-adj 12)
672       (decf year-adj))
673     (unless (or (< year 1582)
674                 (and (= year 1582)
675                      (or (< month 10)
676                          (and (= month 10)
677                               (< day 15)))))
678       (let ((a (floor (/ year-adj 100))))
679         (setf b (+ (- 2 a) (floor (/ a 4))))))
680     (if (< year-adj 0)
681         (setf c (floor (- (* 365.25d0 year-adj) 679006.75d0)))
682         (setf c (floor (- (* 365.25d0 year-adj) 679006d0))))
683     (setf d (floor (* 30.6001 (+ 1 month-adj))))
684     ;; (cmsg "b ~s c ~s d ~s day ~s" b c d day)
685     (+ b c d day)))
686
687 ;; convert MJD to Gregorian date
688
689 (defun mjd-to-gregorian (mjd)
690   (let (z r g a b c year month day)
691     (setf z (floor (+ mjd 678882)))
692     (setf r (- (+ mjd 678882) z))
693     (setf g (- z .25))
694     (setf a (floor (/ g 36524.25)))
695     (setf b (- a (floor (/ a 4))))
696     (setf year (floor (/ (+ b g) 365.25)))
697     (setf c (- (+ b z) (floor (* 365.25 year))))
698     (setf month (truncate (/ (+ (* 5 c) 456) 153)))
699     (setf day (+ (- c (truncate (/ (- (* 153 month) 457) 5))) r))
700     (when (> month 12)
701       (incf year)
702       (decf month 12))
703     (list month day year)))
704
705 (defun duration+ (time &rest durations)
706   "Add each DURATION to TIME, returning a new wall-time value."
707   (let ((year   (duration-year time))
708         (month  (duration-month time))
709         (day    (duration-day time))
710         (hour   (duration-hour time))
711         (minute (duration-minute time))
712         (second (duration-second time))
713         (usec   (duration-usec time)))
714     (dolist (duration durations)
715       (incf year    (duration-year duration))
716       (incf month   (duration-month duration))
717       (incf day     (duration-day duration))
718       (incf hour    (duration-hour duration))
719       (incf minute  (duration-minute duration))
720       (incf second  (duration-second duration))
721       (incf usec    (duration-usec duration)))
722     (make-duration :year year :month month :day day :hour hour :minute minute
723                    :second second :usec usec)))
724
725 (defun duration- (duration &rest durations)
726     "Subtract each DURATION from TIME, returning a new duration value."
727   (let ((year   (duration-year duration))
728         (month  (duration-month duration))
729         (day    (duration-day duration))
730         (hour   (duration-hour duration))
731         (minute (duration-minute duration))
732         (second (duration-second duration))
733         (usec   (duration-usec duration)))
734     (dolist (duration durations)
735       (decf year    (duration-year duration))
736       (decf month   (duration-month duration))
737       (decf day     (duration-day duration))
738       (decf hour    (duration-hour duration))
739       (decf minute  (duration-minute duration))
740       (decf second  (duration-second duration))
741       (decf usec    (duration-usec duration)))
742     (make-duration :year year :month month :day day :hour hour :minute minute
743                    :second second :usec usec)))
744
745 ;; Date + Duration
746
747 (defun time+ (time &rest durations)
748   "Add each DURATION to TIME, returning a new wall-time value."
749   (let ((new-time (copy-time time)))
750     (dolist (duration durations)
751       (roll new-time
752             :year (duration-year duration)
753             :month (duration-month duration)
754             :day (duration-day duration)
755             :hour (duration-hour duration)
756             :minute (duration-minute duration)
757             :second (duration-second duration)
758             :usec (duration-usec duration)
759             :destructive t))
760     new-time))
761
762 (defun date+ (date &rest durations)
763   "Add each DURATION to DATE, returning a new date value.
764 Note that (barring daylight saving time) 12h + 12h will result in a new day, but doing
765 it as separate calculations will not, as the time is chopped to a date before being returned."
766   (time->date (apply #'time+ (cons (date->time date) durations))))
767
768 (defun time- (time &rest durations)
769   "Subtract each DURATION from TIME, returning a new wall-time value."
770   (let ((new-time (copy-time time)))
771     (dolist (duration durations)
772       (roll new-time
773             :year (- (duration-year duration))
774             :month (- (duration-month duration))
775             :day (- (duration-day duration))
776             :hour (- (duration-hour duration))
777             :minute (- (duration-minute duration))
778             :second (- (duration-second duration))
779             :usec (- (duration-usec duration))
780             :destructive t))
781     new-time))
782
783 (defun date- (date &rest durations)
784   "Subtract each DURATION to DATE, returning a new date value.
785 Note that (barring daylight saving time) 12h + 12h will result in a new day, but doing
786 it as separate calculations will not, as the time is chopped to a date before being returned."
787   (time->date (apply #'time- (cons (date->time date) durations))))
788
789 (defun time-difference (time1 time2)
790   "Returns a DURATION representing the difference between TIME1 and
791 TIME2."
792   (flet ((do-diff (time1 time2)
793            
794   (let (day-diff sec-diff)
795     (setf day-diff (- (time-mjd time2)
796                       (time-mjd time1)))
797     (if (> day-diff 0)
798         (progn (decf day-diff)
799                (setf sec-diff (+ (time-second time2)
800                                  (- (* 60 60 24)
801                                     (time-second time1)))))
802       (setf sec-diff (- (time-second time2)
803                         (time-second time1))))
804      (make-duration :day day-diff
805                    :second sec-diff))))
806     (if (time< time1 time2)
807         (do-diff time1 time2)
808       (do-diff time2 time1))))
809
810 (defun date-difference (date1 date2)
811   "Returns a DURATION representing the difference between TIME1 and
812 TIME2."
813   (time-difference (date->time date1) (date->time date2)))
814
815 (defun format-date (stream date &key format
816                     (date-separator "-")
817                     (internal-separator " "))
818   "produces on stream the datestring corresponding to the date
819 with the given options"
820   (format-time stream (date->time date)
821                :format format
822                :date-separator date-separator
823                :internal-separator internal-separator))
824
825 (defun format-time (stream time &key format
826                     (date-separator "-")
827                     (time-separator ":")
828                     (internal-separator " "))
829   "produces on stream the timestring corresponding to the wall-time
830 with the given options"
831   (let ((*print-circle* nil))
832     (multiple-value-bind (usec second minute hour day month year dow)
833         (decode-time time)
834       (case format
835         (:pretty
836          (format stream "~A ~A, ~A ~D, ~D"
837                  (pretty-time hour minute)
838                  (day-name dow)
839                  (month-name month)
840                  day
841                  year))
842         (:short-pretty
843          (format stream "~A, ~D/~D/~D"
844                  (pretty-time hour minute)
845                  month day year))
846         (:iso
847          (let ((string (iso-timestring time)))
848            (if stream
849                (write-string string stream)
850              string)))
851         (t
852          (format stream "~2,'0D~A~2,'0D~A~2,'0D~A~2,'0D~A~2,'0D~A~2,'0D.~6,'0D"
853                  year date-separator month date-separator day
854                  internal-separator hour time-separator minute time-separator
855                  second usec))))))
856   
857 (defun pretty-time (hour minute)
858   (cond
859    ((eq hour 0)
860     (format nil "12:~2,'0D AM" minute))
861    ((eq hour 12)
862     (format nil "12:~2,'0D PM" minute))
863    ((< hour 12)
864     (format nil "~D:~2,'0D AM" hour minute))
865    ((and (> hour 12) (< hour 24))
866     (format nil "~D:~2,'0D PM" (- hour 12) minute))
867    (t
868     (error "pretty-time got bad hour"))))
869
870 (defun leap-days-in-days (days)
871   ;; return the number of leap days between Mar 1 2000 and
872   ;; (Mar 1 2000) + days, where days can be negative
873   (if (< days 0)
874       (ceiling (/ (- days) (* 365 4)))
875       (floor (/ days (* 365 4)))))
876
877 (defun current-year ()
878   (third (mjd-to-gregorian (time-mjd (get-time)))))
879
880 (defun current-month ()
881   (second (mjd-to-gregorian (time-mjd (get-time)))))
882
883 (defun current-day ()
884   (first (mjd-to-gregorian (time-mjd (get-time)))))
885
886 (defun parse-date-time (string)
887   "parses date like 08/08/01, 8.8.2001, eg"
888   (when (> (length string) 1)
889     (let ((m (current-month))
890           (d (current-day))
891           (y (current-year)))
892       (let ((integers (mapcar #'parse-integer (hork-integers string))))
893         (case (length integers)
894           (1
895            (setf y (car integers)))
896           (2
897            (setf m (car integers))
898            (setf y (cadr integers)))
899           (3
900            (setf m (car integers))
901            (setf d (cadr integers))
902            (setf y (caddr integers)))
903           (t
904            (return-from parse-date-time))))
905       (when (< y 100)
906         (incf y 2000))
907       (make-time :year y :month m :day d))))
908
909 (defun hork-integers (input)
910   (let ((output '())
911         (start 0))
912     (dotimes (x (length input))
913       (unless (<= 48 (char-code (aref input x)) 57)
914         (push (subseq input start x) output)
915         (setf start (1+ x))))
916     (nreverse (push (subseq input start) output))))
917     
918 (defun merged-time (day time-of-day)
919   (%make-wall-time :mjd (time-mjd day)
920                    :second (time-second time-of-day)))
921
922 (defun time-meridian (hours)
923   (cond ((= hours 0)
924          (values 12 "AM"))
925         ((= hours 12)
926          (values 12 "PM"))
927         ((< 12 hours)
928          (values (- hours 12) "PM"))
929         (t
930          (values hours "AM"))))
931
932 (defgeneric to-string (val &rest keys)
933   )
934
935 (defmethod to-string ((time wall-time) &rest keys)
936   (destructuring-bind (&key (style :daytime) &allow-other-keys)
937       keys
938     (print-date time style)))
939
940 (defun print-date (time &optional (style :daytime))
941   (multiple-value-bind (second minute hour day month year dow)
942       (decode-time time)
943     (declare (ignore second))
944     (multiple-value-bind (hours meridian)
945         (time-meridian hour)
946       (ecase style
947         (:time-of-day
948          ;; 2:00 PM
949          (format nil "~d:~2,'0d ~a" hours minute meridian))
950         (:long-day
951          ;; October 11th, 2000
952          (format nil "~a ~d, ~d" (month-name month) day year))
953         (:month
954          ;; October
955          (month-name month))
956         (:month-year
957          ;; October 2000
958          (format nil "~a ~d" (month-name month) year))
959         (:full
960          ;; 11:08 AM, November 22, 2002
961          (format nil "~d:~2,'0d ~a, ~a ~d, ~d"
962                  hours minute meridian (month-name month) day year))
963         (:full+weekday
964          ;; 11:09 AM Friday, November 22, 2002
965          (format nil "~d:~2,'0d ~a ~a, ~a ~d, ~d"
966                  hours minute meridian (nth dow *day-names*)
967                  (month-name month) day year))
968         (:daytime
969          ;; 11:09 AM, 11/22/2002
970          (format-time nil time :format :short-pretty))
971         (:day
972          ;; 11/22/2002
973          (format nil "~d/~d/~d" month day year))))))
974
975 (defun time-element (time element)
976   (multiple-value-bind (second minute hour day month year dow)
977       (decode-time time)
978     (ecase element
979       (:seconds
980        second)
981       (:minutes
982        minute)
983       (:hours
984        hour)
985       (:day-of-month
986        day)
987       (:integer-day-of-week
988        dow)
989       (:day-of-week
990        (nth dow *day-keywords*))
991       (:month
992        month)
993       (:year
994        year))))
995
996 (defun date-element (date element)
997   (time-element (date->time date) element))
998
999 (defun format-duration (stream duration &key (precision :minute))
1000   (let ((second (duration-second duration))
1001         (minute (duration-minute duration))
1002         (hour (duration-hour duration))
1003         (day (duration-day duration))
1004         (return (null stream))
1005         (stream (or stream (make-string-output-stream))))
1006     (ecase precision
1007       (:day
1008        (setf hour 0 second 0 minute 0))
1009       (:hour
1010        (setf second 0 minute 0))
1011       (:minute
1012        (setf second 0))
1013       (:second
1014        t))
1015     (if (= 0 day hour minute)
1016         (format stream "0 minutes")
1017         (let ((sent? nil))
1018           (when (< 0 day)
1019             (format stream "~d day~p" day day)
1020             (setf sent? t))
1021           (when (< 0 hour)
1022             (when sent?
1023               (write-char #\Space stream))
1024             (format stream "~d hour~p" hour hour)
1025             (setf sent? t))
1026           (when (< 0 minute)
1027             (when sent?
1028               (write-char #\Space stream))
1029             (format stream "~d min~p" minute minute)
1030             (setf sent? t))
1031           (when (< 0 second)
1032             (when sent?
1033               (write-char #\Space stream))
1034             (format stream "~d sec~p" second second))))
1035     (when return
1036       (get-output-stream-string stream))))
1037
1038 (defgeneric midnight (self))
1039 (defmethod midnight ((self wall-time))
1040   "truncate hours, minutes and seconds"
1041   (%make-wall-time :mjd (time-mjd self)))
1042
1043 (defun roll (date &key (year 0) (month 0) (day 0) (second 0) (hour 0)
1044              (minute 0) (usec 0) (destructive nil))
1045   (unless (= 0 year month)
1046     (multiple-value-bind (year-orig month-orig day-orig)
1047         (time-ymd date)
1048       (setf date (make-time :year (+ year year-orig)
1049                             :month (+ month month-orig)
1050                             :day day-orig
1051                             :second (time-second date)
1052                             :usec usec))))
1053   (let ((mjd (time-mjd date))
1054         (sec (time-second date))
1055         (usec (time-usec date)))
1056     (multiple-value-bind (sec-new usec-new)
1057         (floor (+ usec
1058                   (* 1000000
1059                      (+ sec second
1060                         (* 60 minute)
1061                         (* 60 60 hour))))
1062                1000000)
1063       (multiple-value-bind (mjd-new sec-new)
1064           (floor sec-new (* 60 60 24))
1065         (if destructive
1066             (progn
1067               (setf (time-mjd date) (+ mjd mjd-new day)
1068                     (time-second date) sec-new
1069                     (time-usec date) usec-new)
1070               date)
1071             (%make-wall-time :mjd (+ mjd mjd-new day)
1072                              :second sec-new
1073                              :usec usec-new))))))
1074
1075 (defun roll-to (date size position)
1076   (ecase size
1077     (:month
1078      (ecase position
1079        (:beginning
1080         (roll date :day (+ 1
1081                            (- (time-element date :day-of-month)))))
1082        (:end
1083         (roll date :day (+ (days-in-month (time-element date :month)
1084                                           (time-element date :year))
1085                            (- (time-element date :day-of-month)))))))))
1086
1087 (defun week-containing (time)
1088   (let* ((midn (midnight time))
1089          (dow (time-element midn :integer-day-of-week)))
1090     (list (roll midn :day (- dow))
1091           (roll midn :day (- 7 dow)))))
1092
1093 (defun leap-year? (year)
1094   "t if YEAR is a leap yeap in the Gregorian calendar"
1095   (and (= 0 (mod year 4))
1096        (or (not (= 0 (mod year 100)))
1097            (= 0 (mod year 400)))))
1098
1099 (defun valid-month-p (month)
1100   "t if MONTH exists in the Gregorian calendar"
1101   (<= 1 month 12))
1102
1103 (defun valid-gregorian-date-p (date)
1104   "t if DATE (year month day) exists in the Gregorian calendar"
1105   (let ((max-day (days-in-month (nth 1 date) (nth 0 date))))
1106     (<= 1 (nth 2 date) max-day)))
1107
1108 (defun days-in-month (month year &key (careful t))
1109   "the number of days in MONTH of YEAR, observing Gregorian leap year
1110 rules"
1111   (declare (type fixnum month year))
1112   (when careful
1113     (check-type month (satisfies valid-month-p)
1114                 "between 1 (January) and 12 (December)"))
1115   (if (eql month 2)                     ; feb
1116       (if (leap-year? year)
1117           29 28)
1118       (let ((even (mod (1- month) 2)))
1119         (if (< month 8)                 ; aug
1120             (- 31 even)
1121             (+ 30 even)))))
1122
1123 (defun day-of-year (year month day &key (careful t))
1124   "the day number within the year of the date DATE.  For example,
1125 1987 1 1 returns 1"
1126   (declare (type fixnum year month day))
1127   (when careful
1128     (let ((date (list year month day)))
1129     (check-type date (satisfies valid-gregorian-date-p)
1130                 "a valid Gregorian date")))
1131   (let ((doy (+ day (* 31 (1- month)))))
1132     (declare (type fixnum doy))
1133     (when (< 2 month)
1134       (setf doy (- doy (floor (+ 23 (* 4 month)) 10)))
1135       (when (leap-year? year)
1136         (incf doy)))
1137     doy))
1138
1139 (defun parse-yearstring (string)
1140   (let ((year (or (parse-integer-insensitively string) 
1141                   (extract-roman string))))
1142     (when (and year (< 1500 year 2500))
1143       (make-time :year year))))
1144
1145 (defun parse-integer-insensitively (string)
1146   (let ((start (position-if #'digit-char-p string))
1147         (end   (position-if #'digit-char-p string :from-end t)))
1148     (when (and start end)
1149       (parse-integer (subseq string start (1+ end)) :junk-allowed t))))
1150
1151 (defvar *roman-digits*
1152   '((#\M . 1000)
1153     (#\D . 500)
1154     (#\C . 100)
1155     (#\L . 50)
1156     (#\X . 10)
1157     (#\V . 5)
1158     (#\I . 1)))
1159
1160 (defun extract-roman (string &aux parse)
1161   (dotimes (x (length string))
1162     (let ((val (cdr (assoc (aref string x) *roman-digits*))))
1163       (when (and val parse (< (car parse) val))
1164         (push (- (pop parse)) parse))
1165       (push val parse)))
1166   (apply #'+ parse))
1167
1168
1169 ;; ------------------------------------------------------------
1170 ;; Parsing iso-8601 timestrings 
1171
1172 (define-condition iso-8601-syntax-error (sql-user-error)
1173   ((bad-component;; year, month whatever
1174     :initarg :bad-component
1175     :reader bad-component))
1176   (:report (lambda (c stream)
1177              (format stream "Bad component: ~A " (bad-component c)))))
1178
1179 (defun parse-timestring (timestring &key (start 0) end junk-allowed)
1180   "parse a timestring and return the corresponding wall-time.  If the
1181 timestring starts with P, read a duration; otherwise read an ISO 8601
1182 formatted date string."
1183   (declare (ignore junk-allowed))  
1184   (let ((string (subseq timestring start end)))
1185     (if (char= (aref string 0) #\P)
1186         (parse-iso-8601-duration string)
1187       (parse-iso-8601-time string))))
1188
1189 (defun parse-datestring (datestring &key (start 0) end junk-allowed)
1190   "parse a ISO 8601 timestring and return the corresponding date.
1191 Will throw a hissy fit if the date string is a duration. Will ignore any precision beyond day (hour/min/sec/usec)."
1192   (let ((parsed-value (parse-timestring datestring :start start :end end :junk-allowed junk-allowed)))
1193     (ecase (type-of parsed-value)
1194       (wall-time (%make-date :mjd (time-mjd parsed-value))))))
1195
1196
1197 (defvar *iso-8601-duration-delimiters*
1198   '((#\D . :days)
1199     (#\H . :hours)
1200     (#\M . :minutes)
1201     (#\S . :seconds)))
1202
1203 (defun iso-8601-delimiter (elt)
1204   (cdr (assoc elt *iso-8601-duration-delimiters*)))
1205
1206 (defun iso-8601-duration-subseq (string start)
1207   (let* ((pos (position-if #'iso-8601-delimiter string :start start))
1208          (number (when pos (parse-integer (subseq string start pos)
1209                                           :junk-allowed t))))
1210     (when number
1211       (values number
1212               (1+ pos)
1213               (iso-8601-delimiter (aref string pos))))))
1214
1215 (defun parse-iso-8601-duration (string)
1216   "return a wall-time from a duration string"
1217   (block parse
1218     (let ((days 0) (secs 0) (hours 0) (minutes 0) (index 1))
1219       (loop
1220        (multiple-value-bind (duration next-index duration-type)
1221            (iso-8601-duration-subseq string index)
1222          (case duration-type
1223            (:hours
1224             (incf hours duration))
1225            (:minutes
1226             (incf minutes duration))
1227            (:seconds
1228             (incf secs duration))
1229            (:days
1230             (incf days duration))
1231            (t
1232             (return-from parse (make-duration :day days :hour hours
1233                                               :minute minutes :second secs))))
1234          (setf index next-index))))))
1235
1236 ;; e.g. 2000-11-11 00:00:00-06
1237
1238 (defun parse-iso-8601-time (string)
1239   "return the wall-time corresponding to the given ISO 8601 datestring"
1240   (multiple-value-bind (year month day hour minute second usec offset)
1241       (syntax-parse-iso-8601 string)
1242     (make-time :year year
1243                :month month
1244                :day day
1245                :hour hour
1246                :minute minute
1247                :second second
1248                :usec usec
1249                :offset offset)))
1250
1251
1252 (defun syntax-parse-iso-8601 (string)
1253   ;; use strlen to determine if fractional seconds are present in timestamp
1254   (let ((strlen (length string))
1255         year month day hour minute second usec gmt-sec-offset)
1256     (handler-case
1257         (progn
1258           (setf year           (parse-integer string :start 0 :end 4)
1259                 month          (parse-integer string :start 5 :end 7)
1260                 day            (parse-integer string :start 8 :end 10)
1261                 hour           (if (<= 13 strlen)
1262                                    (parse-integer string :start 11 :end 13)
1263                                    0)
1264                 minute         (if (<= 16 strlen)
1265                                    (parse-integer string :start 14 :end 16)
1266                                    0)
1267                 second         (if (<= 19 strlen)
1268                                    (parse-integer string :start 17 :end 19)
1269                                    0))
1270           (cond
1271             ((and (> strlen 19)
1272                   (or (char= #\, (char string 19))
1273                       (char= #\. (char string 19))))
1274              (multiple-value-bind (parsed-usec usec-end)
1275                  (parse-integer string :start 20 :junk-allowed t)
1276                (setf usec          parsed-usec
1277                      gmt-sec-offset (if (<= (+ 3 usec-end)  strlen)
1278                                         (let ((skip-to (or (position #\+ string :start 19)
1279                                                            (position #\- string :start 19))))
1280                                           (if skip-to
1281                                               (* 60 60
1282                                                  (parse-integer string :start skip-to
1283                                                                 :end (+ skip-to 3)))
1284                                               0))
1285                                         0))))
1286             (t
1287              (setf usec           0
1288                    gmt-sec-offset (if (<= 22  strlen)
1289                                       (let ((skip-to (or (position #\+ string :start 19)
1290                                                          (position #\- string :start 19))))
1291                                         (if skip-to
1292                                             (* 60 60
1293                                                (parse-integer string :start skip-to
1294                                                               :end (+ skip-to 3)))
1295                                             0))
1296                                       0))))
1297           (unless (< 0 year)
1298             (error 'iso-8601-syntax-error
1299                    :bad-component '(year . 0)))
1300           (unless (< 0 month)
1301             (error 'iso-8601-syntax-error
1302                    :bad-component '(month . 0)))
1303           (unless (< 0 day)
1304             (error 'iso-8601-syntax-error
1305                    :bad-component '(month . 0)))
1306           (values year month day hour minute second usec gmt-sec-offset))
1307       (simple-error ()
1308         (error 'iso-8601-syntax-error
1309                :bad-component
1310                (car (find-if (lambda (pair) (null (cdr pair)))
1311                              `((year . ,year) (month . ,month)
1312                                (day . ,day) (hour . ,hour)
1313                                (minute . ,minute) (second . ,second)
1314                                (usec . ,usec)
1315                                (timezone . ,gmt-sec-offset)))))))))