[go: up one dir, main page]

File: time-funcs.lisp

package info (click to toggle)
cl-local-time 1.1.6-2
  • links: PTS
  • area: main
  • in suites: woody
  • size: 188 kB
  • ctags: 203
  • sloc: lisp: 1,374; sh: 89; makefile: 75
file content (107 lines) | stat: -rw-r--r-- 3,768 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
;;; -*- Mode: Lisp -*-
;;; $Id: time-funcs.lisp,v 1.6 2001/11/12 19:48:20 jesse Exp $
;;;
;;; Copyright (c) 2000, 2001 onShore Development, Inc.
;;;
;;; Widely used time functions.

(in-package :local-time)

(defvar *days-per-lt-cycle*   146097)

(defun leap-year? (year)
  "t if YEAR is a leap yeap in the Gregorian calendar"
  (and (= 0 (mod year 4))
       (or (not (= 0 (mod year 100)))
           (= 0 (mod year 400)))))

(defun valid-month-p (month)
  "t if MONTH exists in the Gregorian calendar"
  (<= 1 month 12))

(defun valid-gregorian-date-p (date)
  "t if DATE (year month day) exists in the Gregorian calendar"
  (let ((max-day (days-in-month (nth 1 date) (nth 0 date))))
    (<= 1 (nth 2 date) max-day)))
  
(defun days-in-month (month year &key (careful t))
  "the number of days in MONTH of YEAR, observing Gregorian leap year
rules"
  (declare (type fixnum month year))
  (when careful
    (check-type month (satisfies valid-month-p)
                "between 1 (January) and 12 (December)"))
  (if (eql month 2)                     ; feb
      (if (leap-year? year)
          29 28)
      (let ((even (mod (1- month) 2)))
        (if (< month 8)                 ; aug
            (- 31 even)
            (+ 30 even)))))

(defun day-of-year (year month day &key (careful t))
  "the day number within the year of the date DATE.  For example,
1987 1 1 returns 1"
  (declare (type fixnum year month day))
  (when careful
    (let ((date (list year month day)))
    (check-type date (satisfies valid-gregorian-date-p)
                "a valid Gregorian date")))
  (let ((doy (+ day (* 31 (1- month)))))
    (declare (type fixnum doy))
    (when (< 2 month)
      (setq doy (- doy (floor (+ 23 (* 4 month)) 10)))
      (when (leap-year? year)
        (incf doy)))
    doy))

;; Inspired by: http://www.interlog.com/~r937/doomsday.html

(defun day-of-week (year month day &key (careful t))
  "Given a Gregorian year, month and day, return the day of the week,
zero-based on Sunday."
  (when careful
    (let ((date (list year month day)))
      (check-type date (satisfies valid-gregorian-date-p)
                  "a valid Gregorian date")))
  (flet ((month-value (month leap-year)
           (let ((val (nth month '(0 3 0 0 4 9 6 11 8 5 10 7 12))))
             (if (and leap-year (< month 3))
                 (1+ val)
                 val)))
         (century-value (century)
           (nth (mod century 4) '(2 0 5 3))))
    (multiple-value-bind (century year-in-century)
        (floor year 100)
      (multiple-value-bind (p q)
          (floor year-in-century 12)
        (let ((doomsday (mod (+ p q (floor q 4) (century-value century)) 7)))
          (mod (+ (- day (month-value month (leap-year? year))) doomsday 14) 7))))))

(defun day-of-gregorian-epoch (year month day)
  "the number of days after 1 AD"
  (declare (type fixnum year month day))
  (let ((prior-years (1- year)))
    (declare (type fixnum prior-years))
    (+ (day-of-year year month day :careful nil)
       (* 365 prior-years)              ; days in prior years
       (floor prior-years 4)            ; Julian leap years
       (- (floor prior-years 100))      ; century years
       (floor prior-years 400))))       ; Gregorian leap years

(defun %lt-date (year month day)
  "local-time epoch day number corresponding to Gregorian date"
  (- (day-of-gregorian-epoch year month day)
     (day-of-gregorian-epoch 2000 3 1)))

(defun %gregorian-date (lt)
  (multiple-value-bind (cycle day)
      (floor lt *days-per-lt-cycle*)
    (multiple-value-bind (y m d wd)
        (lt-entry-to-ymdw (lt-entry day))
      (values (+ 2000 y (* cycle 400)) m d wd))))

(defun unix-to-lt ()
  (* 24 60 60
     (- (day-of-gregorian-epoch 2000 3 1)
        (day-of-gregorian-epoch 1970 1 1))))