Version 1.102 (other changes not in last commit)
[kmrcl.git] / datetime.lisp
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          datetime.lisp
6 ;;;; Purpose:       Date & Time functions for KMRCL package
7 ;;;; Programmer:    Kevin M. Rosenberg
8 ;;;; Date Started:  Apr 2000
9 ;;;;
10 ;;;; This file, part of KMRCL, is Copyright (c) 2002 by Kevin M. Rosenberg
11 ;;;;
12 ;;;; KMRCL users are granted the rights to distribute and use this software
13 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
14 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
15 ;;;; *************************************************************************
16
17 (in-package #:kmrcl)
18
19
20 ;;; Formatting functions
21
22 (defun pretty-date (year month day &optional (hour 12) (m 0) (s 0))
23   (multiple-value-bind (sec min hr dy mn yr wkday)
24     (decode-universal-time
25      (encode-universal-time s m hour day month year))
26     (values (elt '("Monday" "Tuesday" "Wednesday" "Thursday"
27                    "Friday" "Saturday" "Sunday")
28                  wkday)
29             (elt '("January" "February" "March" "April" "May" "June"
30                    "July" "August" "September" "October" "November"
31                    "December")
32                  (1- mn))
33             (format nil "~A" dy)
34             (format nil "~A" yr)
35             (format nil "~2,'0D:~2,'0D:~2,'0D" hr min sec))))
36
37 (defun pretty-date-ut (&optional (tm (get-universal-time)))
38   (multiple-value-bind (sec min hr dy mn yr) (decode-universal-time tm)
39     (pretty-date yr mn dy hr min sec)))
40
41 (defun date-string (&optional (ut (get-universal-time)))
42   (if (typep ut 'integer)
43       (multiple-value-bind (sec min hr day mon year dow daylight-p zone)
44           (decode-universal-time ut)
45         (declare (ignore daylight-p zone))
46         (format nil "~[Mon~;Tue~;Wed~;Thu~;Fri~;Sat~;Sun~] ~d ~[Jan~;Feb~;Mar~;Apr~;May~;Jun~;Jul~;Aug~;Sep~;Oct~;Nov~;Dec~] ~d ~2,'0d:~2,'0d:~2,'0d"
47                 dow
48                 day
49                 (1- mon)
50                 year
51                 hr min sec))))
52
53 (defun print-seconds (secs)
54   (print-float-units secs "sec"))
55
56 (defun print-float-units (val unit)
57   (cond
58     ((< val 1d-6)
59      (format t "~,2,9F nano~A" val unit))
60     ((< val 1d-3)
61      (format t "~,2,6F micro~A" val unit))
62     ((< val 1)
63      (format t "~,2,3F milli~A" val unit))
64     ((> val 1d9)
65      (format t "~,2,-9F giga~A" val unit))
66     ((> val 1d6)
67      (format t "~,2,-6F mega~A" val unit))
68     ((> val 1d3)
69      (format t "~,2,-3F kilo~A" val unit))
70     (t
71      (format t "~,2F ~A" val unit))))
72
73 (defconstant +posix-epoch+
74   (encode-universal-time 0 0 0 1 1 1970 0))
75
76 (defun posix-time-to-utime (time)
77   (+ time +posix-epoch+))
78
79 (defun utime-to-posix-time (utime)
80   (- utime +posix-epoch+))
81
82 ;; Monthnames taken from net-telent-date to support lml2
83
84 (defvar *monthnames*
85   '((1 . "January")
86     (2 . "February")
87     (3 . "March")
88     (4 . "April")
89     (5 . "May")
90     (6 . "June")
91     (7 . "July")
92     (8 . "August")
93     (9 . "September")
94     (10 . "October")
95     (11 . "November")
96     (12 . "December")))
97
98 (defun monthname (stream arg colon-p at-p &optional width (mincol 0) (colinc 1) (minpad 0) (padchar #\Space))
99   "Print the name of the month (1=January) corresponding to ARG on STREAM.  This is intended for embedding in a FORMAT directive: WIDTH governs the number of characters of text printed, MINCOL, COLINC, MINPAD, PADCHAR work as for ~A"
100   (declare (ignore colon-p))
101   (let ((monthstring (cdr (assoc arg *monthnames*))))
102     (if (not monthstring) (return-from monthname nil))
103     (let ((truncate (if width (min width (length monthstring)) nil)))
104       (format stream
105               (if at-p "~V,V,V,V@A" "~V,V,V,VA")
106               mincol colinc minpad padchar
107               (subseq monthstring 0 truncate)))))
108
109 (defconstant* +zellers-adj+ #(0 3 2 5 0 3 5 1 4 6 2 4))
110
111 (defun day-of-week (year month day)
112   "Day of week calculation using Zeller's Congruence.
113 Input: The year y, month m (1 ≤ m ≤ 12) and day d (1 ≤ d ≤ 31).
114 Output: n - the day of the week (Sunday = 0, Saturday = 6)."
115
116   (when (< month 3)
117     (decf year))
118   (mod
119    (+ year (floor year 4) (- (floor year 100)) (floor year 400)
120       (aref +zellers-adj+ (1- month)) day)
121    7))
122
123 ;;;; Daylight Saving Time calculations
124
125 ;; Daylight Saving Time begins for most of the United States at 2
126 ;; a.m. on the first Sunday of April. Time reverts to standard time at
127 ;; 2 a.m. on the last Sunday of October. In the U.S., each time zone
128 ;; switches at a different time.
129
130 ;; In the European Union, Summer Time begins and ends at 1 am
131 ;; Universal Time (Greenwich Mean Time). It starts the last Sunday in
132 ;; March, and ends the last Sunday in October. In the EU, all time
133 ;; zones change at the same moment.
134
135 ;; Spring forward, Fall back
136 ;; During DST, clocks are turned forward an hour, effectively moving
137 ;; an hour of daylight from the morning to the evening.
138
139 ;; United States                  European Union
140
141 ;; Year  DST Begins DST Ends     Summertime     Summertime
142 ;;       at 2 a.m.  at 2 a.m.    period begins  period ends
143 ;;                               at 1 a.m. UT   at 1 a.m. UT
144 ;; ----------------------------------------------------------
145 ;; 2000  April 2   October 29    March 26       October 29
146 ;; 2001  April 1   October 28    March 25       October 28
147 ;; 2002  April 7   October 27    March 31       October 27
148 ;; 2003  April 6   October 26    March 30       October 26
149 ;; 2004  April 4   October 31    March 28       October 31
150 ;; 2005  April 3   October 30    March 27       October 30
151 ;; 2006  April 2   October 29    March 26       October 29
152 ;; 2007  April 1   October 28    March 25       October 28
153 ;; 2008  April 6   October 26    March 30       October 26
154
155