isodate.lisp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. ;;;; isodate.lisp
  2. (defpackage #:iso-date
  3. (:use :cl
  4. :esrap)
  5. (:export #:parse-date
  6. #:print-date
  7. #:date-stamp
  8. #:parse-mini-date
  9. #:print-mini-date
  10. #:mini-date-stamp
  11. #:leap-year-p))
  12. (in-package #:iso-date)
  13. ;;; "isodate" goes here. Hacks and glory await!
  14. (define-condition month-error (error)
  15. ((month :initarg :month)))
  16. (define-condition date-error (error)
  17. ((month :initarg :month)
  18. (date :initarg :date)))
  19. (define-condition hour-error (error)
  20. ((hour :initarg :hour)))
  21. (define-condition minute-error (error)
  22. ((minute :initarg :minute)))
  23. (define-condition second-error (error)
  24. ((second :initarg :second)))
  25. (defrule year
  26. (and (or "1" "2" "3" "4" "5" "6" "7" "8" "9" "0")
  27. (or "1" "2" "3" "4" "5" "6" "7" "8" "9" "0")
  28. (or "1" "2" "3" "4" "5" "6" "7" "8" "9" "0")
  29. (or "1" "2" "3" "4" "5" "6" "7" "8" "9" "0"))
  30. (:text t)
  31. (:lambda (text)
  32. (parse-integer text)))
  33. (defrule month
  34. (and (or "0" "1")
  35. (or "1" "2" "3" "4" "5" "6" "7" "8" "9" "0"))
  36. (:text t)
  37. (:lambda (text)
  38. (parse-integer text)))
  39. (defrule day
  40. (and (or "0" "1" "2" "3")
  41. (or "1" "2" "3" "4" "5" "6" "7" "8" "9" "0"))
  42. (:text t)
  43. (:lambda (text)
  44. (parse-integer text)))
  45. (defrule hour
  46. (and (or "0" "1" "2")
  47. (or "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"))
  48. (:text t)
  49. (:lambda (text)
  50. (parse-integer text)))
  51. (defrule minute
  52. (and (or "0" "1" "2" "3" "4" "5")
  53. (or "1" "2" "3" "4" "5" "6" "7" "8" "9" "0"))
  54. (:text t)
  55. (:lambda (text)
  56. (parse-integer text)))
  57. (defrule second
  58. (and (or "0" "1" "2" "3" "4" "5")
  59. (or "1" "2" "3" "4" "5" "6" "7" "8" "9" "0"))
  60. (:text t)
  61. (:lambda (text)
  62. (parse-integer text)))
  63. (defun leap-year-p (year)
  64. (cond
  65. ((= 0 (mod year 4)) t)
  66. ((= 0 (mod year 100)) nil)
  67. ((= 0 (mod year 400)) t)
  68. (t nil)))
  69. (defrule whole-date
  70. (and year (? (and "-" month (? (and "-" day)))))
  71. (:destructure (year (d month (dd day)))
  72. (declare (ignore d dd)
  73. (ignorable year month day))
  74. (assert (or (null month)
  75. (<= month 12))
  76. (month)
  77. 'month-error :month month)
  78. (assert (or (null day)
  79. (and (leap-year-p year)
  80. (= month 2)
  81. (= day 29))
  82. (and (= month 1)
  83. (<= day 31))
  84. (and (= month 2)
  85. (<= day 28))
  86. (and (= month 3)
  87. (<= day 31))
  88. (and (= month 4)
  89. (<= day 30))
  90. (and (= month 5)
  91. (<= day 31))
  92. (and (= month 6)
  93. (<= day 30))
  94. (and (= month 7)
  95. (<= day 31))
  96. (and (= month 8)
  97. (<= day 31))
  98. (and (= month 9)
  99. (<= day 30))
  100. (and (= month 10)
  101. (<= day 31))
  102. (and (= month 11)
  103. (<= day 30))
  104. (and (= month 12)
  105. (<= day 31)))
  106. (day year month)
  107. 'date-error :year year :month month :day day)
  108. (let ((month (if (null month) 0 month))
  109. (day (if (null day) 0 day)))
  110. (list year month day))))
  111. (defrule whole-time
  112. (and hour ":" minute ":" second)
  113. (:destructure (hour c minute cc second)
  114. (declare (ignore c cc))
  115. (assert (<= hour 24)
  116. (hour)
  117. 'hour-error :hour hour)
  118. (assert (<= minute 59)
  119. (minute)
  120. 'minute-error :minute minute)
  121. (assert (<= second 59)
  122. (second)
  123. 'second-error :second second)
  124. (list hour minute second)))
  125. (defrule whole
  126. (and whole-date (? (and "T" whole-time)))
  127. (:lambda (list)
  128. (let ((year (first (first list)))
  129. (month (second (first list)))
  130. (day (third (first list)))
  131. (hour (first (second (second list))))
  132. (minute (second (second (second list))))
  133. (second (third (second (second list)))))
  134. (let ((hour (if (null hour) 0 hour))
  135. (minute (if (null minute) 0 minute))
  136. (second (if (null second) 0 second)))
  137. (encode-universal-time second minute hour day month year)))))
  138. (defun parse-date (date)
  139. (declare (string date))
  140. (let ((date (parse 'whole date)))
  141. (multiple-value-bind (second minute hour day month year day-of-year daylight-p zone) (decode-universal-time date)
  142. (declare (ignorable day-of-year daylight-p zone))
  143. (values date
  144. (list second minute hour day month year)))))
  145. (defun print-date (date)
  146. (declare (integer date))
  147. (multiple-value-bind (second minute hour day-of-month month year day daylight-p zone)
  148. (decode-universal-time date)
  149. (declare (ignorable day daylight-p zone))
  150. (values (format nil "~4,'0d-~2,'0d-~2,'0dT~2,'0d:~2,'0d:~2,'0d"
  151. year month day-of-month
  152. hour minute second)
  153. date)))
  154. (defun date-stamp ()
  155. (values (print-date (get-universal-time))
  156. (get-universal-time)))
  157. (defrule minidate
  158. (and year month day (? (and hour minute second)))
  159. (:destructure (year month day (&optional hour minute second))
  160. (let ((hour (if (null hour) 0 hour))
  161. (minute (if (null minute) 0 minute))
  162. (second (if (null second) 0 second)))
  163. (encode-universal-time second minute hour day month year))))
  164. (defun parse-mini-date (date)
  165. (declare (string date))
  166. (values (parse 'minidate date)
  167. date))
  168. (defun print-mini-date (date)
  169. (multiple-value-bind (second minute hour day-of-month month year day daylight-p zone)
  170. (decode-universal-time date)
  171. (declare (ignorable day daylight-p zone))
  172. (format nil "~4,'0d~2,'0d~2,'0d~2,'0d~2,'0d~2,'0d"
  173. year month day-of-month
  174. hour minute second)))
  175. (defun mini-date-stamp ()
  176. (print-mini-date (get-universal-time)))