srfi-19.scm 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537
  1. ;;; srfi-19.scm --- Time/Date Library
  2. ;; Copyright (C) 2001, 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
  3. ;;
  4. ;; This library is free software; you can redistribute it and/or
  5. ;; modify it under the terms of the GNU Lesser General Public
  6. ;; License as published by the Free Software Foundation; either
  7. ;; version 2.1 of the License, or (at your option) any later version.
  8. ;;
  9. ;; This library is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;; Lesser General Public License for more details.
  13. ;;
  14. ;; You should have received a copy of the GNU Lesser General Public
  15. ;; License along with this library; if not, write to the Free Software
  16. ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. ;;; Author: Rob Browning <rlb@cs.utexas.edu>
  18. ;;; Originally from SRFI reference implementation by Will Fitzgerald.
  19. ;;; Commentary:
  20. ;; This module is fully documented in the Guile Reference Manual.
  21. ;;; Code:
  22. ;; FIXME: I haven't checked a decent amount of this code for potential
  23. ;; performance improvements, but I suspect that there may be some
  24. ;; substantial ones to be realized, esp. in the later "parsing" half
  25. ;; of the file, by rewriting the code with use of more Guile native
  26. ;; functions that do more work in a "chunk".
  27. ;;
  28. ;; FIXME: mkoeppe: Time zones are treated a little simplistic in
  29. ;; SRFI-19; they are only a numeric offset. Thus, printing time zones
  30. ;; (PRIV:LOCALE-PRINT-TIME-ZONE) can't be implemented sensibly. The
  31. ;; functions taking an optional TZ-OFFSET should be extended to take a
  32. ;; symbolic time-zone (like "CET"); this string should be stored in
  33. ;; the DATE structure.
  34. (define-module (srfi srfi-19)
  35. :use-module (srfi srfi-6)
  36. :use-module (srfi srfi-8)
  37. :use-module (srfi srfi-9))
  38. (begin-deprecated
  39. ;; Prevent `export' from re-exporting core bindings. This behaviour
  40. ;; of `export' is deprecated and will disappear in one of the next
  41. ;; releases.
  42. (define current-time #f))
  43. (export ;; Constants
  44. time-duration
  45. time-monotonic
  46. time-process
  47. time-tai
  48. time-thread
  49. time-utc
  50. ;; Current time and clock resolution
  51. current-date
  52. current-julian-day
  53. current-modified-julian-day
  54. current-time
  55. time-resolution
  56. ;; Time object and accessors
  57. make-time
  58. time?
  59. time-type
  60. time-nanosecond
  61. time-second
  62. set-time-type!
  63. set-time-nanosecond!
  64. set-time-second!
  65. copy-time
  66. ;; Time comparison procedures
  67. time<=?
  68. time<?
  69. time=?
  70. time>=?
  71. time>?
  72. ;; Time arithmetic procedures
  73. time-difference
  74. time-difference!
  75. add-duration
  76. add-duration!
  77. subtract-duration
  78. subtract-duration!
  79. ;; Date object and accessors
  80. make-date
  81. date?
  82. date-nanosecond
  83. date-second
  84. date-minute
  85. date-hour
  86. date-day
  87. date-month
  88. date-year
  89. date-zone-offset
  90. date-year-day
  91. date-week-day
  92. date-week-number
  93. ;; Time/Date/Julian Day/Modified Julian Day converters
  94. date->julian-day
  95. date->modified-julian-day
  96. date->time-monotonic
  97. date->time-tai
  98. date->time-utc
  99. julian-day->date
  100. julian-day->time-monotonic
  101. julian-day->time-tai
  102. julian-day->time-utc
  103. modified-julian-day->date
  104. modified-julian-day->time-monotonic
  105. modified-julian-day->time-tai
  106. modified-julian-day->time-utc
  107. time-monotonic->date
  108. time-monotonic->time-tai
  109. time-monotonic->time-tai!
  110. time-monotonic->time-utc
  111. time-monotonic->time-utc!
  112. time-tai->date
  113. time-tai->julian-day
  114. time-tai->modified-julian-day
  115. time-tai->time-monotonic
  116. time-tai->time-monotonic!
  117. time-tai->time-utc
  118. time-tai->time-utc!
  119. time-utc->date
  120. time-utc->julian-day
  121. time-utc->modified-julian-day
  122. time-utc->time-monotonic
  123. time-utc->time-monotonic!
  124. time-utc->time-tai
  125. time-utc->time-tai!
  126. ;; Date to string/string to date converters.
  127. date->string
  128. string->date)
  129. (cond-expand-provide (current-module) '(srfi-19))
  130. (define time-tai 'time-tai)
  131. (define time-utc 'time-utc)
  132. (define time-monotonic 'time-monotonic)
  133. (define time-thread 'time-thread)
  134. (define time-process 'time-process)
  135. (define time-duration 'time-duration)
  136. ;; FIXME: do we want to add gc time?
  137. ;; (define time-gc 'time-gc)
  138. ;;-- LOCALE dependent constants
  139. (define priv:locale-number-separator ".")
  140. (define priv:locale-abbr-weekday-vector
  141. (vector "Sun" "Mon" "Tue" "Wed" "Thu" "Fri" "Sat"))
  142. (define priv:locale-long-weekday-vector
  143. (vector
  144. "Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday"))
  145. ;; note empty string in 0th place.
  146. (define priv:locale-abbr-month-vector
  147. (vector ""
  148. "Jan"
  149. "Feb"
  150. "Mar"
  151. "Apr"
  152. "May"
  153. "Jun"
  154. "Jul"
  155. "Aug"
  156. "Sep"
  157. "Oct"
  158. "Nov"
  159. "Dec"))
  160. (define priv:locale-long-month-vector
  161. (vector ""
  162. "January"
  163. "February"
  164. "March"
  165. "April"
  166. "May"
  167. "June"
  168. "July"
  169. "August"
  170. "September"
  171. "October"
  172. "November"
  173. "December"))
  174. (define priv:locale-pm "PM")
  175. (define priv:locale-am "AM")
  176. ;; See date->string
  177. (define priv:locale-date-time-format "~a ~b ~d ~H:~M:~S~z ~Y")
  178. (define priv:locale-short-date-format "~m/~d/~y")
  179. (define priv:locale-time-format "~H:~M:~S")
  180. (define priv:iso-8601-date-time-format "~Y-~m-~dT~H:~M:~S~z")
  181. ;;-- Miscellaneous Constants.
  182. ;;-- only the priv:tai-epoch-in-jd might need changing if
  183. ;; a different epoch is used.
  184. (define priv:nano 1000000000) ; nanoseconds in a second
  185. (define priv:sid 86400) ; seconds in a day
  186. (define priv:sihd 43200) ; seconds in a half day
  187. (define priv:tai-epoch-in-jd 4881175/2) ; julian day number for 'the epoch'
  188. ;; FIXME: should this be something other than misc-error?
  189. (define (priv:time-error caller type value)
  190. (if value
  191. (throw 'misc-error caller "TIME-ERROR type ~A: ~S" (list type value) #f)
  192. (throw 'misc-error caller "TIME-ERROR type ~A" (list type) #f)))
  193. ;; A table of leap seconds
  194. ;; See ftp://maia.usno.navy.mil/ser7/tai-utc.dat
  195. ;; and update as necessary.
  196. ;; this procedures reads the file in the abover
  197. ;; format and creates the leap second table
  198. ;; it also calls the almost standard, but not R5 procedures read-line
  199. ;; & open-input-string
  200. ;; ie (set! priv:leap-second-table (priv:read-tai-utc-date "tai-utc.dat"))
  201. (define (priv:read-tai-utc-data filename)
  202. (define (convert-jd jd)
  203. (* (- (inexact->exact jd) priv:tai-epoch-in-jd) priv:sid))
  204. (define (convert-sec sec)
  205. (inexact->exact sec))
  206. (let ((port (open-input-file filename))
  207. (table '()))
  208. (let loop ((line (read-line port)))
  209. (if (not (eof-object? line))
  210. (begin
  211. (let* ((data (read (open-input-string
  212. (string-append "(" line ")"))))
  213. (year (car data))
  214. (jd (cadddr (cdr data)))
  215. (secs (cadddr (cdddr data))))
  216. (if (>= year 1972)
  217. (set! table (cons
  218. (cons (convert-jd jd) (convert-sec secs))
  219. table)))
  220. (loop (read-line port))))))
  221. table))
  222. ;; each entry is (tai seconds since epoch . # seconds to subtract for utc)
  223. ;; note they go higher to lower, and end in 1972.
  224. (define priv:leap-second-table
  225. '((1136073600 . 33)
  226. (915148800 . 32)
  227. (867715200 . 31)
  228. (820454400 . 30)
  229. (773020800 . 29)
  230. (741484800 . 28)
  231. (709948800 . 27)
  232. (662688000 . 26)
  233. (631152000 . 25)
  234. (567993600 . 24)
  235. (489024000 . 23)
  236. (425865600 . 22)
  237. (394329600 . 21)
  238. (362793600 . 20)
  239. (315532800 . 19)
  240. (283996800 . 18)
  241. (252460800 . 17)
  242. (220924800 . 16)
  243. (189302400 . 15)
  244. (157766400 . 14)
  245. (126230400 . 13)
  246. (94694400 . 12)
  247. (78796800 . 11)
  248. (63072000 . 10)))
  249. (define (read-leap-second-table filename)
  250. (set! priv:leap-second-table (priv:read-tai-utc-data filename))
  251. (values))
  252. (define (priv:leap-second-delta utc-seconds)
  253. (letrec ((lsd (lambda (table)
  254. (cond ((>= utc-seconds (caar table))
  255. (cdar table))
  256. (else (lsd (cdr table)))))))
  257. (if (< utc-seconds (* (- 1972 1970) 365 priv:sid)) 0
  258. (lsd priv:leap-second-table))))
  259. ;;; the TIME structure; creates the accessors, too.
  260. (define-record-type time
  261. (make-time-unnormalized type nanosecond second)
  262. time?
  263. (type time-type set-time-type!)
  264. (nanosecond time-nanosecond set-time-nanosecond!)
  265. (second time-second set-time-second!))
  266. (define (copy-time time)
  267. (make-time (time-type time) (time-nanosecond time) (time-second time)))
  268. (define (priv:split-real r)
  269. (if (integer? r)
  270. (values (inexact->exact r) 0)
  271. (let ((l (truncate r)))
  272. (values (inexact->exact l) (- r l)))))
  273. (define (priv:time-normalize! t)
  274. (if (>= (abs (time-nanosecond t)) 1000000000)
  275. (receive (int frac)
  276. (priv:split-real (time-nanosecond t))
  277. (set-time-second! t (+ (time-second t)
  278. (quotient int 1000000000)))
  279. (set-time-nanosecond! t (+ (remainder int 1000000000)
  280. frac))))
  281. (if (and (positive? (time-second t))
  282. (negative? (time-nanosecond t)))
  283. (begin
  284. (set-time-second! t (- (time-second t) 1))
  285. (set-time-nanosecond! t (+ 1000000000 (time-nanosecond t))))
  286. (if (and (negative? (time-second t))
  287. (positive? (time-nanosecond t)))
  288. (begin
  289. (set-time-second! t (+ (time-second t) 1))
  290. (set-time-nanosecond! t (+ 1000000000 (time-nanosecond t))))))
  291. t)
  292. (define (make-time type nanosecond second)
  293. (priv:time-normalize! (make-time-unnormalized type nanosecond second)))
  294. ;; Helpers
  295. ;; FIXME: finish this and publish it?
  296. (define (date->broken-down-time date)
  297. (let ((result (mktime 0)))
  298. ;; FIXME: What should we do about leap-seconds which may overflow
  299. ;; set-tm:sec?
  300. (set-tm:sec result (date-second date))
  301. (set-tm:min result (date-minute date))
  302. (set-tm:hour result (date-hour date))
  303. ;; FIXME: SRFI day ranges from 0-31. (not compatible with set-tm:mday).
  304. (set-tm:mday result (date-day date))
  305. (set-tm:month result (- (date-month date) 1))
  306. ;; FIXME: need to signal error on range violation.
  307. (set-tm:year result (+ 1900 (date-year date)))
  308. (set-tm:isdst result -1)
  309. (set-tm:gmtoff result (- (date-zone-offset date)))
  310. result))
  311. ;;; current-time
  312. ;;; specific time getters.
  313. (define (priv:current-time-utc)
  314. ;; Resolution is microseconds.
  315. (let ((tod (gettimeofday)))
  316. (make-time time-utc (* (cdr tod) 1000) (car tod))))
  317. (define (priv:current-time-tai)
  318. ;; Resolution is microseconds.
  319. (let* ((tod (gettimeofday))
  320. (sec (car tod))
  321. (usec (cdr tod)))
  322. (make-time time-tai
  323. (* usec 1000)
  324. (+ (car tod) (priv:leap-second-delta sec)))))
  325. ;;(define (priv:current-time-ms-time time-type proc)
  326. ;; (let ((current-ms (proc)))
  327. ;; (make-time time-type
  328. ;; (quotient current-ms 10000)
  329. ;; (* (remainder current-ms 1000) 10000))))
  330. ;; -- we define it to be the same as TAI.
  331. ;; A different implemation of current-time-montonic
  332. ;; will require rewriting all of the time-monotonic converters,
  333. ;; of course.
  334. (define (priv:current-time-monotonic)
  335. ;; Resolution is microseconds.
  336. (priv:current-time-tai))
  337. (define (priv:current-time-thread)
  338. (priv:time-error 'current-time 'unsupported-clock-type 'time-thread))
  339. (define priv:ns-per-guile-tick (/ 1000000000 internal-time-units-per-second))
  340. (define (priv:current-time-process)
  341. (let ((run-time (get-internal-run-time)))
  342. (make-time
  343. time-process
  344. (* (remainder run-time internal-time-units-per-second)
  345. priv:ns-per-guile-tick)
  346. (quotient run-time internal-time-units-per-second))))
  347. ;;(define (priv:current-time-gc)
  348. ;; (priv:current-time-ms-time time-gc current-gc-milliseconds))
  349. (define (current-time . clock-type)
  350. (let ((clock-type (if (null? clock-type) time-utc (car clock-type))))
  351. (cond
  352. ((eq? clock-type time-tai) (priv:current-time-tai))
  353. ((eq? clock-type time-utc) (priv:current-time-utc))
  354. ((eq? clock-type time-monotonic) (priv:current-time-monotonic))
  355. ((eq? clock-type time-thread) (priv:current-time-thread))
  356. ((eq? clock-type time-process) (priv:current-time-process))
  357. ;; ((eq? clock-type time-gc) (priv:current-time-gc))
  358. (else (priv:time-error 'current-time 'invalid-clock-type clock-type)))))
  359. ;; -- Time Resolution
  360. ;; This is the resolution of the clock in nanoseconds.
  361. ;; This will be implementation specific.
  362. (define (time-resolution . clock-type)
  363. (let ((clock-type (if (null? clock-type) time-utc (car clock-type))))
  364. (case clock-type
  365. ((time-tai) 1000)
  366. ((time-utc) 1000)
  367. ((time-monotonic) 1000)
  368. ((time-process) priv:ns-per-guile-tick)
  369. ;; ((eq? clock-type time-thread) 1000)
  370. ;; ((eq? clock-type time-gc) 10000)
  371. (else (priv:time-error 'time-resolution 'invalid-clock-type clock-type)))))
  372. ;; -- Time comparisons
  373. (define (time=? t1 t2)
  374. ;; Arrange tests for speed and presume that t1 and t2 are actually times.
  375. ;; also presume it will be rare to check two times of different types.
  376. (and (= (time-second t1) (time-second t2))
  377. (= (time-nanosecond t1) (time-nanosecond t2))
  378. (eq? (time-type t1) (time-type t2))))
  379. (define (time>? t1 t2)
  380. (or (> (time-second t1) (time-second t2))
  381. (and (= (time-second t1) (time-second t2))
  382. (> (time-nanosecond t1) (time-nanosecond t2)))))
  383. (define (time<? t1 t2)
  384. (or (< (time-second t1) (time-second t2))
  385. (and (= (time-second t1) (time-second t2))
  386. (< (time-nanosecond t1) (time-nanosecond t2)))))
  387. (define (time>=? t1 t2)
  388. (or (> (time-second t1) (time-second t2))
  389. (and (= (time-second t1) (time-second t2))
  390. (>= (time-nanosecond t1) (time-nanosecond t2)))))
  391. (define (time<=? t1 t2)
  392. (or (< (time-second t1) (time-second t2))
  393. (and (= (time-second t1) (time-second t2))
  394. (<= (time-nanosecond t1) (time-nanosecond t2)))))
  395. ;; -- Time arithmetic
  396. (define (time-difference! time1 time2)
  397. (let ((sec-diff (- (time-second time1) (time-second time2)))
  398. (nsec-diff (- (time-nanosecond time1) (time-nanosecond time2))))
  399. (set-time-type! time1 time-duration)
  400. (set-time-second! time1 sec-diff)
  401. (set-time-nanosecond! time1 nsec-diff)
  402. (priv:time-normalize! time1)))
  403. (define (time-difference time1 time2)
  404. (let ((result (copy-time time1)))
  405. (time-difference! result time2)))
  406. (define (add-duration! t duration)
  407. (if (not (eq? (time-type duration) time-duration))
  408. (priv:time-error 'add-duration 'not-duration duration)
  409. (let ((sec-plus (+ (time-second t) (time-second duration)))
  410. (nsec-plus (+ (time-nanosecond t) (time-nanosecond duration))))
  411. (set-time-second! t sec-plus)
  412. (set-time-nanosecond! t nsec-plus)
  413. (priv:time-normalize! t))))
  414. (define (add-duration t duration)
  415. (let ((result (copy-time t)))
  416. (add-duration! result duration)))
  417. (define (subtract-duration! t duration)
  418. (if (not (eq? (time-type duration) time-duration))
  419. (priv:time-error 'add-duration 'not-duration duration)
  420. (let ((sec-minus (- (time-second t) (time-second duration)))
  421. (nsec-minus (- (time-nanosecond t) (time-nanosecond duration))))
  422. (set-time-second! t sec-minus)
  423. (set-time-nanosecond! t nsec-minus)
  424. (priv:time-normalize! t))))
  425. (define (subtract-duration time1 duration)
  426. (let ((result (copy-time time1)))
  427. (subtract-duration! result duration)))
  428. ;; -- Converters between types.
  429. (define (priv:time-tai->time-utc! time-in time-out caller)
  430. (if (not (eq? (time-type time-in) time-tai))
  431. (priv:time-error caller 'incompatible-time-types time-in))
  432. (set-time-type! time-out time-utc)
  433. (set-time-nanosecond! time-out (time-nanosecond time-in))
  434. (set-time-second! time-out (- (time-second time-in)
  435. (priv:leap-second-delta
  436. (time-second time-in))))
  437. time-out)
  438. (define (time-tai->time-utc time-in)
  439. (priv:time-tai->time-utc! time-in (make-time-unnormalized #f #f #f) 'time-tai->time-utc))
  440. (define (time-tai->time-utc! time-in)
  441. (priv:time-tai->time-utc! time-in time-in 'time-tai->time-utc!))
  442. (define (priv:time-utc->time-tai! time-in time-out caller)
  443. (if (not (eq? (time-type time-in) time-utc))
  444. (priv:time-error caller 'incompatible-time-types time-in))
  445. (set-time-type! time-out time-tai)
  446. (set-time-nanosecond! time-out (time-nanosecond time-in))
  447. (set-time-second! time-out (+ (time-second time-in)
  448. (priv:leap-second-delta
  449. (time-second time-in))))
  450. time-out)
  451. (define (time-utc->time-tai time-in)
  452. (priv:time-utc->time-tai! time-in (make-time-unnormalized #f #f #f) 'time-utc->time-tai))
  453. (define (time-utc->time-tai! time-in)
  454. (priv:time-utc->time-tai! time-in time-in 'time-utc->time-tai!))
  455. ;; -- these depend on time-monotonic having the same definition as time-tai!
  456. (define (time-monotonic->time-utc time-in)
  457. (if (not (eq? (time-type time-in) time-monotonic))
  458. (priv:time-error caller 'incompatible-time-types time-in))
  459. (let ((ntime (copy-time time-in)))
  460. (set-time-type! ntime time-tai)
  461. (priv:time-tai->time-utc! ntime ntime 'time-monotonic->time-utc)))
  462. (define (time-monotonic->time-utc! time-in)
  463. (if (not (eq? (time-type time-in) time-monotonic))
  464. (priv:time-error caller 'incompatible-time-types time-in))
  465. (set-time-type! time-in time-tai)
  466. (priv:time-tai->time-utc! ntime ntime 'time-monotonic->time-utc))
  467. (define (time-monotonic->time-tai time-in)
  468. (if (not (eq? (time-type time-in) time-monotonic))
  469. (priv:time-error caller 'incompatible-time-types time-in))
  470. (let ((ntime (copy-time time-in)))
  471. (set-time-type! ntime time-tai)
  472. ntime))
  473. (define (time-monotonic->time-tai! time-in)
  474. (if (not (eq? (time-type time-in) time-monotonic))
  475. (priv:time-error caller 'incompatible-time-types time-in))
  476. (set-time-type! time-in time-tai)
  477. time-in)
  478. (define (time-utc->time-monotonic time-in)
  479. (if (not (eq? (time-type time-in) time-utc))
  480. (priv:time-error caller 'incompatible-time-types time-in))
  481. (let ((ntime (priv:time-utc->time-tai! time-in (make-time-unnormalized #f #f #f)
  482. 'time-utc->time-monotonic)))
  483. (set-time-type! ntime time-monotonic)
  484. ntime))
  485. (define (time-utc->time-monotonic! time-in)
  486. (if (not (eq? (time-type time-in) time-utc))
  487. (priv:time-error caller 'incompatible-time-types time-in))
  488. (let ((ntime (priv:time-utc->time-tai! time-in time-in
  489. 'time-utc->time-monotonic!)))
  490. (set-time-type! ntime time-monotonic)
  491. ntime))
  492. (define (time-tai->time-monotonic time-in)
  493. (if (not (eq? (time-type time-in) time-tai))
  494. (priv:time-error caller 'incompatible-time-types time-in))
  495. (let ((ntime (copy-time time-in)))
  496. (set-time-type! ntime time-monotonic)
  497. ntime))
  498. (define (time-tai->time-monotonic! time-in)
  499. (if (not (eq? (time-type time-in) time-tai))
  500. (priv:time-error caller 'incompatible-time-types time-in))
  501. (set-time-type! time-in time-monotonic)
  502. time-in)
  503. ;; -- Date Structures
  504. ;; FIXME: to be really safe, perhaps we should normalize the
  505. ;; seconds/nanoseconds/minutes coming in to make-date...
  506. (define-record-type date
  507. (make-date nanosecond second minute
  508. hour day month
  509. year
  510. zone-offset)
  511. date?
  512. (nanosecond date-nanosecond set-date-nanosecond!)
  513. (second date-second set-date-second!)
  514. (minute date-minute set-date-minute!)
  515. (hour date-hour set-date-hour!)
  516. (day date-day set-date-day!)
  517. (month date-month set-date-month!)
  518. (year date-year set-date-year!)
  519. (zone-offset date-zone-offset set-date-zone-offset!))
  520. ;; gives the julian day which starts at noon.
  521. (define (priv:encode-julian-day-number day month year)
  522. (let* ((a (quotient (- 14 month) 12))
  523. (y (- (+ year 4800) a (if (negative? year) -1 0)))
  524. (m (- (+ month (* 12 a)) 3)))
  525. (+ day
  526. (quotient (+ (* 153 m) 2) 5)
  527. (* 365 y)
  528. (quotient y 4)
  529. (- (quotient y 100))
  530. (quotient y 400)
  531. -32045)))
  532. ;; gives the seconds/date/month/year
  533. (define (priv:decode-julian-day-number jdn)
  534. (let* ((days (inexact->exact (truncate jdn)))
  535. (a (+ days 32044))
  536. (b (quotient (+ (* 4 a) 3) 146097))
  537. (c (- a (quotient (* 146097 b) 4)))
  538. (d (quotient (+ (* 4 c) 3) 1461))
  539. (e (- c (quotient (* 1461 d) 4)))
  540. (m (quotient (+ (* 5 e) 2) 153))
  541. (y (+ (* 100 b) d -4800 (quotient m 10))))
  542. (values ; seconds date month year
  543. (* (- jdn days) priv:sid)
  544. (+ e (- (quotient (+ (* 153 m) 2) 5)) 1)
  545. (+ m 3 (* -12 (quotient m 10)))
  546. (if (>= 0 y) (- y 1) y))))
  547. ;; relies on the fact that we named our time zone accessor
  548. ;; differently from MzScheme's....
  549. ;; This should be written to be OS specific.
  550. (define (priv:local-tz-offset utc-time)
  551. ;; SRFI uses seconds West, but guile (and libc) use seconds East.
  552. (- (tm:gmtoff (localtime (time-second utc-time)))))
  553. ;; special thing -- ignores nanos
  554. (define (priv:time->julian-day-number seconds tz-offset)
  555. (+ (/ (+ seconds tz-offset priv:sihd)
  556. priv:sid)
  557. priv:tai-epoch-in-jd))
  558. (define (priv:leap-second? second)
  559. (and (assoc second priv:leap-second-table) #t))
  560. (define (time-utc->date time . tz-offset)
  561. (if (not (eq? (time-type time) time-utc))
  562. (priv:time-error 'time->date 'incompatible-time-types time))
  563. (let* ((offset (if (null? tz-offset)
  564. (priv:local-tz-offset time)
  565. (car tz-offset)))
  566. (leap-second? (priv:leap-second? (+ offset (time-second time))))
  567. (jdn (priv:time->julian-day-number (if leap-second?
  568. (- (time-second time) 1)
  569. (time-second time))
  570. offset)))
  571. (call-with-values (lambda () (priv:decode-julian-day-number jdn))
  572. (lambda (secs date month year)
  573. ;; secs is a real because jdn is a real in Guile;
  574. ;; but it is conceptionally an integer.
  575. (let* ((int-secs (inexact->exact (round secs)))
  576. (hours (quotient int-secs (* 60 60)))
  577. (rem (remainder int-secs (* 60 60)))
  578. (minutes (quotient rem 60))
  579. (seconds (remainder rem 60)))
  580. (make-date (time-nanosecond time)
  581. (if leap-second? (+ seconds 1) seconds)
  582. minutes
  583. hours
  584. date
  585. month
  586. year
  587. offset))))))
  588. (define (time-tai->date time . tz-offset)
  589. (if (not (eq? (time-type time) time-tai))
  590. (priv:time-error 'time->date 'incompatible-time-types time))
  591. (let* ((offset (if (null? tz-offset)
  592. (priv:local-tz-offset (time-tai->time-utc time))
  593. (car tz-offset)))
  594. (seconds (- (time-second time)
  595. (priv:leap-second-delta (time-second time))))
  596. (leap-second? (priv:leap-second? (+ offset seconds)))
  597. (jdn (priv:time->julian-day-number (if leap-second?
  598. (- seconds 1)
  599. seconds)
  600. offset)))
  601. (call-with-values (lambda () (priv:decode-julian-day-number jdn))
  602. (lambda (secs date month year)
  603. ;; secs is a real because jdn is a real in Guile;
  604. ;; but it is conceptionally an integer.
  605. ;; adjust for leap seconds if necessary ...
  606. (let* ((int-secs (inexact->exact (round secs)))
  607. (hours (quotient int-secs (* 60 60)))
  608. (rem (remainder int-secs (* 60 60)))
  609. (minutes (quotient rem 60))
  610. (seconds (remainder rem 60)))
  611. (make-date (time-nanosecond time)
  612. (if leap-second? (+ seconds 1) seconds)
  613. minutes
  614. hours
  615. date
  616. month
  617. year
  618. offset))))))
  619. ;; this is the same as time-tai->date.
  620. (define (time-monotonic->date time . tz-offset)
  621. (if (not (eq? (time-type time) time-monotonic))
  622. (priv:time-error 'time->date 'incompatible-time-types time))
  623. (let* ((offset (if (null? tz-offset)
  624. (priv:local-tz-offset (time-monotonic->time-utc time))
  625. (car tz-offset)))
  626. (seconds (- (time-second time)
  627. (priv:leap-second-delta (time-second time))))
  628. (leap-second? (priv:leap-second? (+ offset seconds)))
  629. (jdn (priv:time->julian-day-number (if leap-second?
  630. (- seconds 1)
  631. seconds)
  632. offset)))
  633. (call-with-values (lambda () (priv:decode-julian-day-number jdn))
  634. (lambda (secs date month year)
  635. ;; secs is a real because jdn is a real in Guile;
  636. ;; but it is conceptionally an integer.
  637. ;; adjust for leap seconds if necessary ...
  638. (let* ((int-secs (inexact->exact (round secs)))
  639. (hours (quotient int-secs (* 60 60)))
  640. (rem (remainder int-secs (* 60 60)))
  641. (minutes (quotient rem 60))
  642. (seconds (remainder rem 60)))
  643. (make-date (time-nanosecond time)
  644. (if leap-second? (+ seconds 1) seconds)
  645. minutes
  646. hours
  647. date
  648. month
  649. year
  650. offset))))))
  651. (define (date->time-utc date)
  652. (let* ((jdays (- (priv:encode-julian-day-number (date-day date)
  653. (date-month date)
  654. (date-year date))
  655. priv:tai-epoch-in-jd))
  656. ;; jdays is an integer plus 1/2,
  657. (jdays-1/2 (inexact->exact (- jdays 1/2))))
  658. (make-time
  659. time-utc
  660. (date-nanosecond date)
  661. (+ (* jdays-1/2 24 60 60)
  662. (* (date-hour date) 60 60)
  663. (* (date-minute date) 60)
  664. (date-second date)
  665. (- (date-zone-offset date))))))
  666. (define (date->time-tai date)
  667. (time-utc->time-tai! (date->time-utc date)))
  668. (define (date->time-monotonic date)
  669. (time-utc->time-monotonic! (date->time-utc date)))
  670. (define (priv:leap-year? year)
  671. (or (= (modulo year 400) 0)
  672. (and (= (modulo year 4) 0) (not (= (modulo year 100) 0)))))
  673. (define (leap-year? date)
  674. (priv:leap-year? (date-year date)))
  675. ;; Map 1-based month number M to number of days in the year before the
  676. ;; start of month M (in a non-leap year).
  677. (define priv:month-assoc '((1 . 0) (2 . 31) (3 . 59) (4 . 90)
  678. (5 . 120) (6 . 151) (7 . 181) (8 . 212)
  679. (9 . 243) (10 . 273) (11 . 304) (12 . 334)))
  680. (define (priv:year-day day month year)
  681. (let ((days-pr (assoc month priv:month-assoc)))
  682. (if (not days-pr)
  683. (priv:error 'date-year-day 'invalid-month-specification month))
  684. (if (and (priv:leap-year? year) (> month 2))
  685. (+ day (cdr days-pr) 1)
  686. (+ day (cdr days-pr)))))
  687. (define (date-year-day date)
  688. (priv:year-day (date-day date) (date-month date) (date-year date)))
  689. ;; from calendar faq
  690. (define (priv:week-day day month year)
  691. (let* ((a (quotient (- 14 month) 12))
  692. (y (- year a))
  693. (m (+ month (* 12 a) -2)))
  694. (modulo (+ day
  695. y
  696. (quotient y 4)
  697. (- (quotient y 100))
  698. (quotient y 400)
  699. (quotient (* 31 m) 12))
  700. 7)))
  701. (define (date-week-day date)
  702. (priv:week-day (date-day date) (date-month date) (date-year date)))
  703. (define (priv:days-before-first-week date day-of-week-starting-week)
  704. (let* ((first-day (make-date 0 0 0 0
  705. 1
  706. 1
  707. (date-year date)
  708. #f))
  709. (fdweek-day (date-week-day first-day)))
  710. (modulo (- day-of-week-starting-week fdweek-day)
  711. 7)))
  712. ;; The "-1" here is a fix for the reference implementation, to make a new
  713. ;; week start on the given day-of-week-starting-week. date-year-day returns
  714. ;; a day starting from 1 for 1st Jan.
  715. ;;
  716. (define (date-week-number date day-of-week-starting-week)
  717. (quotient (- (date-year-day date)
  718. 1
  719. (priv:days-before-first-week date day-of-week-starting-week))
  720. 7))
  721. (define (current-date . tz-offset)
  722. (let ((time (current-time time-utc)))
  723. (time-utc->date
  724. time
  725. (if (null? tz-offset)
  726. (priv:local-tz-offset time)
  727. (car tz-offset)))))
  728. ;; given a 'two digit' number, find the year within 50 years +/-
  729. (define (priv:natural-year n)
  730. (let* ((current-year (date-year (current-date)))
  731. (current-century (* (quotient current-year 100) 100)))
  732. (cond
  733. ((>= n 100) n)
  734. ((< n 0) n)
  735. ((<= (- (+ current-century n) current-year) 50) (+ current-century n))
  736. (else (+ (- current-century 100) n)))))
  737. (define (date->julian-day date)
  738. (let ((nanosecond (date-nanosecond date))
  739. (second (date-second date))
  740. (minute (date-minute date))
  741. (hour (date-hour date))
  742. (day (date-day date))
  743. (month (date-month date))
  744. (year (date-year date))
  745. (offset (date-zone-offset date)))
  746. (+ (priv:encode-julian-day-number day month year)
  747. (- 1/2)
  748. (+ (/ (+ (- offset)
  749. (* hour 60 60)
  750. (* minute 60)
  751. second
  752. (/ nanosecond priv:nano))
  753. priv:sid)))))
  754. (define (date->modified-julian-day date)
  755. (- (date->julian-day date)
  756. 4800001/2))
  757. (define (time-utc->julian-day time)
  758. (if (not (eq? (time-type time) time-utc))
  759. (priv:time-error 'time->date 'incompatible-time-types time))
  760. (+ (/ (+ (time-second time) (/ (time-nanosecond time) priv:nano))
  761. priv:sid)
  762. priv:tai-epoch-in-jd))
  763. (define (time-utc->modified-julian-day time)
  764. (- (time-utc->julian-day time)
  765. 4800001/2))
  766. (define (time-tai->julian-day time)
  767. (if (not (eq? (time-type time) time-tai))
  768. (priv:time-error 'time->date 'incompatible-time-types time))
  769. (+ (/ (+ (- (time-second time)
  770. (priv:leap-second-delta (time-second time)))
  771. (/ (time-nanosecond time) priv:nano))
  772. priv:sid)
  773. priv:tai-epoch-in-jd))
  774. (define (time-tai->modified-julian-day time)
  775. (- (time-tai->julian-day time)
  776. 4800001/2))
  777. ;; this is the same as time-tai->julian-day
  778. (define (time-monotonic->julian-day time)
  779. (if (not (eq? (time-type time) time-monotonic))
  780. (priv:time-error 'time->date 'incompatible-time-types time))
  781. (+ (/ (+ (- (time-second time)
  782. (priv:leap-second-delta (time-second time)))
  783. (/ (time-nanosecond time) priv:nano))
  784. priv:sid)
  785. priv:tai-epoch-in-jd))
  786. (define (time-monotonic->modified-julian-day time)
  787. (- (time-monotonic->julian-day time)
  788. 4800001/2))
  789. (define (julian-day->time-utc jdn)
  790. (let ((secs (* priv:sid (- jdn priv:tai-epoch-in-jd))))
  791. (receive (seconds parts)
  792. (priv:split-real secs)
  793. (make-time time-utc
  794. (* parts priv:nano)
  795. seconds))))
  796. (define (julian-day->time-tai jdn)
  797. (time-utc->time-tai! (julian-day->time-utc jdn)))
  798. (define (julian-day->time-monotonic jdn)
  799. (time-utc->time-monotonic! (julian-day->time-utc jdn)))
  800. (define (julian-day->date jdn . tz-offset)
  801. (let* ((time (julian-day->time-utc jdn))
  802. (offset (if (null? tz-offset)
  803. (priv:local-tz-offset time)
  804. (car tz-offset))))
  805. (time-utc->date time offset)))
  806. (define (modified-julian-day->date jdn . tz-offset)
  807. (apply julian-day->date (+ jdn 4800001/2)
  808. tz-offset))
  809. (define (modified-julian-day->time-utc jdn)
  810. (julian-day->time-utc (+ jdn 4800001/2)))
  811. (define (modified-julian-day->time-tai jdn)
  812. (julian-day->time-tai (+ jdn 4800001/2)))
  813. (define (modified-julian-day->time-monotonic jdn)
  814. (julian-day->time-monotonic (+ jdn 4800001/2)))
  815. (define (current-julian-day)
  816. (time-utc->julian-day (current-time time-utc)))
  817. (define (current-modified-julian-day)
  818. (time-utc->modified-julian-day (current-time time-utc)))
  819. ;; returns a string rep. of number N, of minimum LENGTH, padded with
  820. ;; character PAD-WITH. If PAD-WITH is #f, no padding is done, and it's
  821. ;; as if number->string was used. if string is longer than or equal
  822. ;; in length to LENGTH, it's as if number->string was used.
  823. (define (priv:padding n pad-with length)
  824. (let* ((str (number->string n))
  825. (str-len (string-length str)))
  826. (if (or (>= str-len length)
  827. (not pad-with))
  828. str
  829. (string-append (make-string (- length str-len) pad-with) str))))
  830. (define (priv:last-n-digits i n)
  831. (abs (remainder i (expt 10 n))))
  832. (define (priv:locale-abbr-weekday n)
  833. (vector-ref priv:locale-abbr-weekday-vector n))
  834. (define (priv:locale-long-weekday n)
  835. (vector-ref priv:locale-long-weekday-vector n))
  836. (define (priv:locale-abbr-month n)
  837. (vector-ref priv:locale-abbr-month-vector n))
  838. (define (priv:locale-long-month n)
  839. (vector-ref priv:locale-long-month-vector n))
  840. (define (priv:vector-find needle haystack comparator)
  841. (let ((len (vector-length haystack)))
  842. (define (priv:vector-find-int index)
  843. (cond
  844. ((>= index len) #f)
  845. ((comparator needle (vector-ref haystack index)) index)
  846. (else (priv:vector-find-int (+ index 1)))))
  847. (priv:vector-find-int 0)))
  848. (define (priv:locale-abbr-weekday->index string)
  849. (priv:vector-find string priv:locale-abbr-weekday-vector string=?))
  850. (define (priv:locale-long-weekday->index string)
  851. (priv:vector-find string priv:locale-long-weekday-vector string=?))
  852. (define (priv:locale-abbr-month->index string)
  853. (priv:vector-find string priv:locale-abbr-month-vector string=?))
  854. (define (priv:locale-long-month->index string)
  855. (priv:vector-find string priv:locale-long-month-vector string=?))
  856. ;; FIXME: mkoeppe: Put a symbolic time zone in the date structs.
  857. ;; Print it here instead of the numerical offset if available.
  858. (define (priv:locale-print-time-zone date port)
  859. (priv:tz-printer (date-zone-offset date) port))
  860. ;; FIXME: we should use strftime to determine this dynamically if possible.
  861. ;; Again, locale specific.
  862. (define (priv:locale-am/pm hr)
  863. (if (> hr 11) priv:locale-pm priv:locale-am))
  864. (define (priv:tz-printer offset port)
  865. (cond
  866. ((= offset 0) (display "Z" port))
  867. ((negative? offset) (display "-" port))
  868. (else (display "+" port)))
  869. (if (not (= offset 0))
  870. (let ((hours (abs (quotient offset (* 60 60))))
  871. (minutes (abs (quotient (remainder offset (* 60 60)) 60))))
  872. (display (priv:padding hours #\0 2) port)
  873. (display (priv:padding minutes #\0 2) port))))
  874. ;; A table of output formatting directives.
  875. ;; the first time is the format char.
  876. ;; the second is a procedure that takes the date, a padding character
  877. ;; (which might be #f), and the output port.
  878. ;;
  879. (define priv:directives
  880. (list
  881. (cons #\~ (lambda (date pad-with port)
  882. (display #\~ port)))
  883. (cons #\a (lambda (date pad-with port)
  884. (display (priv:locale-abbr-weekday (date-week-day date))
  885. port)))
  886. (cons #\A (lambda (date pad-with port)
  887. (display (priv:locale-long-weekday (date-week-day date))
  888. port)))
  889. (cons #\b (lambda (date pad-with port)
  890. (display (priv:locale-abbr-month (date-month date))
  891. port)))
  892. (cons #\B (lambda (date pad-with port)
  893. (display (priv:locale-long-month (date-month date))
  894. port)))
  895. (cons #\c (lambda (date pad-with port)
  896. (display (date->string date priv:locale-date-time-format) port)))
  897. (cons #\d (lambda (date pad-with port)
  898. (display (priv:padding (date-day date)
  899. #\0 2)
  900. port)))
  901. (cons #\D (lambda (date pad-with port)
  902. (display (date->string date "~m/~d/~y") port)))
  903. (cons #\e (lambda (date pad-with port)
  904. (display (priv:padding (date-day date)
  905. #\Space 2)
  906. port)))
  907. (cons #\f (lambda (date pad-with port)
  908. (if (> (date-nanosecond date)
  909. priv:nano)
  910. (display (priv:padding (+ (date-second date) 1)
  911. pad-with 2)
  912. port)
  913. (display (priv:padding (date-second date)
  914. pad-with 2)
  915. port))
  916. (receive (i f)
  917. (priv:split-real (/
  918. (date-nanosecond date)
  919. priv:nano 1.0))
  920. (let* ((ns (number->string f))
  921. (le (string-length ns)))
  922. (if (> le 2)
  923. (begin
  924. (display priv:locale-number-separator port)
  925. (display (substring ns 2 le) port)))))))
  926. (cons #\h (lambda (date pad-with port)
  927. (display (date->string date "~b") port)))
  928. (cons #\H (lambda (date pad-with port)
  929. (display (priv:padding (date-hour date)
  930. pad-with 2)
  931. port)))
  932. (cons #\I (lambda (date pad-with port)
  933. (let ((hr (date-hour date)))
  934. (if (> hr 12)
  935. (display (priv:padding (- hr 12)
  936. pad-with 2)
  937. port)
  938. (display (priv:padding hr
  939. pad-with 2)
  940. port)))))
  941. (cons #\j (lambda (date pad-with port)
  942. (display (priv:padding (date-year-day date)
  943. pad-with 3)
  944. port)))
  945. (cons #\k (lambda (date pad-with port)
  946. (display (priv:padding (date-hour date)
  947. #\Space 2)
  948. port)))
  949. (cons #\l (lambda (date pad-with port)
  950. (let ((hr (if (> (date-hour date) 12)
  951. (- (date-hour date) 12) (date-hour date))))
  952. (display (priv:padding hr #\Space 2)
  953. port))))
  954. (cons #\m (lambda (date pad-with port)
  955. (display (priv:padding (date-month date)
  956. pad-with 2)
  957. port)))
  958. (cons #\M (lambda (date pad-with port)
  959. (display (priv:padding (date-minute date)
  960. pad-with 2)
  961. port)))
  962. (cons #\n (lambda (date pad-with port)
  963. (newline port)))
  964. (cons #\N (lambda (date pad-with port)
  965. (display (priv:padding (date-nanosecond date)
  966. pad-with 7)
  967. port)))
  968. (cons #\p (lambda (date pad-with port)
  969. (display (priv:locale-am/pm (date-hour date)) port)))
  970. (cons #\r (lambda (date pad-with port)
  971. (display (date->string date "~I:~M:~S ~p") port)))
  972. (cons #\s (lambda (date pad-with port)
  973. (display (time-second (date->time-utc date)) port)))
  974. (cons #\S (lambda (date pad-with port)
  975. (if (> (date-nanosecond date)
  976. priv:nano)
  977. (display (priv:padding (+ (date-second date) 1)
  978. pad-with 2)
  979. port)
  980. (display (priv:padding (date-second date)
  981. pad-with 2)
  982. port))))
  983. (cons #\t (lambda (date pad-with port)
  984. (display #\Tab port)))
  985. (cons #\T (lambda (date pad-with port)
  986. (display (date->string date "~H:~M:~S") port)))
  987. (cons #\U (lambda (date pad-with port)
  988. (if (> (priv:days-before-first-week date 0) 0)
  989. (display (priv:padding (+ (date-week-number date 0) 1)
  990. #\0 2) port)
  991. (display (priv:padding (date-week-number date 0)
  992. #\0 2) port))))
  993. (cons #\V (lambda (date pad-with port)
  994. (display (priv:padding (date-week-number date 1)
  995. #\0 2) port)))
  996. (cons #\w (lambda (date pad-with port)
  997. (display (date-week-day date) port)))
  998. (cons #\x (lambda (date pad-with port)
  999. (display (date->string date priv:locale-short-date-format) port)))
  1000. (cons #\X (lambda (date pad-with port)
  1001. (display (date->string date priv:locale-time-format) port)))
  1002. (cons #\W (lambda (date pad-with port)
  1003. (if (> (priv:days-before-first-week date 1) 0)
  1004. (display (priv:padding (+ (date-week-number date 1) 1)
  1005. #\0 2) port)
  1006. (display (priv:padding (date-week-number date 1)
  1007. #\0 2) port))))
  1008. (cons #\y (lambda (date pad-with port)
  1009. (display (priv:padding (priv:last-n-digits
  1010. (date-year date) 2)
  1011. pad-with
  1012. 2)
  1013. port)))
  1014. (cons #\Y (lambda (date pad-with port)
  1015. (display (date-year date) port)))
  1016. (cons #\z (lambda (date pad-with port)
  1017. (priv:tz-printer (date-zone-offset date) port)))
  1018. (cons #\Z (lambda (date pad-with port)
  1019. (priv:locale-print-time-zone date port)))
  1020. (cons #\1 (lambda (date pad-with port)
  1021. (display (date->string date "~Y-~m-~d") port)))
  1022. (cons #\2 (lambda (date pad-with port)
  1023. (display (date->string date "~k:~M:~S~z") port)))
  1024. (cons #\3 (lambda (date pad-with port)
  1025. (display (date->string date "~k:~M:~S") port)))
  1026. (cons #\4 (lambda (date pad-with port)
  1027. (display (date->string date "~Y-~m-~dT~k:~M:~S~z") port)))
  1028. (cons #\5 (lambda (date pad-with port)
  1029. (display (date->string date "~Y-~m-~dT~k:~M:~S") port)))))
  1030. (define (priv:get-formatter char)
  1031. (let ((associated (assoc char priv:directives)))
  1032. (if associated (cdr associated) #f)))
  1033. (define (priv:date-printer date index format-string str-len port)
  1034. (if (>= index str-len)
  1035. (values)
  1036. (let ((current-char (string-ref format-string index)))
  1037. (if (not (char=? current-char #\~))
  1038. (begin
  1039. (display current-char port)
  1040. (priv:date-printer date (+ index 1) format-string str-len port))
  1041. (if (= (+ index 1) str-len) ; bad format string.
  1042. (priv:time-error 'priv:date-printer 'bad-date-format-string
  1043. format-string)
  1044. (let ((pad-char? (string-ref format-string (+ index 1))))
  1045. (cond
  1046. ((char=? pad-char? #\-)
  1047. (if (= (+ index 2) str-len) ; bad format string.
  1048. (priv:time-error 'priv:date-printer
  1049. 'bad-date-format-string
  1050. format-string)
  1051. (let ((formatter (priv:get-formatter
  1052. (string-ref format-string
  1053. (+ index 2)))))
  1054. (if (not formatter)
  1055. (priv:time-error 'priv:date-printer
  1056. 'bad-date-format-string
  1057. format-string)
  1058. (begin
  1059. (formatter date #f port)
  1060. (priv:date-printer date
  1061. (+ index 3)
  1062. format-string
  1063. str-len
  1064. port))))))
  1065. ((char=? pad-char? #\_)
  1066. (if (= (+ index 2) str-len) ; bad format string.
  1067. (priv:time-error 'priv:date-printer
  1068. 'bad-date-format-string
  1069. format-string)
  1070. (let ((formatter (priv:get-formatter
  1071. (string-ref format-string
  1072. (+ index 2)))))
  1073. (if (not formatter)
  1074. (priv:time-error 'priv:date-printer
  1075. 'bad-date-format-string
  1076. format-string)
  1077. (begin
  1078. (formatter date #\Space port)
  1079. (priv:date-printer date
  1080. (+ index 3)
  1081. format-string
  1082. str-len
  1083. port))))))
  1084. (else
  1085. (let ((formatter (priv:get-formatter
  1086. (string-ref format-string
  1087. (+ index 1)))))
  1088. (if (not formatter)
  1089. (priv:time-error 'priv:date-printer
  1090. 'bad-date-format-string
  1091. format-string)
  1092. (begin
  1093. (formatter date #\0 port)
  1094. (priv:date-printer date
  1095. (+ index 2)
  1096. format-string
  1097. str-len
  1098. port))))))))))))
  1099. (define (date->string date . format-string)
  1100. (let ((str-port (open-output-string))
  1101. (fmt-str (if (null? format-string) "~c" (car format-string))))
  1102. (priv:date-printer date 0 fmt-str (string-length fmt-str) str-port)
  1103. (get-output-string str-port)))
  1104. (define (priv:char->int ch)
  1105. (case ch
  1106. ((#\0) 0)
  1107. ((#\1) 1)
  1108. ((#\2) 2)
  1109. ((#\3) 3)
  1110. ((#\4) 4)
  1111. ((#\5) 5)
  1112. ((#\6) 6)
  1113. ((#\7) 7)
  1114. ((#\8) 8)
  1115. ((#\9) 9)
  1116. (else (priv:time-error 'bad-date-template-string
  1117. (list "Non-integer character" ch i)))))
  1118. ;; read an integer upto n characters long on port; upto -> #f is any length
  1119. (define (priv:integer-reader upto port)
  1120. (let loop ((accum 0) (nchars 0))
  1121. (let ((ch (peek-char port)))
  1122. (if (or (eof-object? ch)
  1123. (not (char-numeric? ch))
  1124. (and upto (>= nchars upto)))
  1125. accum
  1126. (loop (+ (* accum 10) (priv:char->int (read-char port)))
  1127. (+ nchars 1))))))
  1128. (define (priv:make-integer-reader upto)
  1129. (lambda (port)
  1130. (priv:integer-reader upto port)))
  1131. ;; read *exactly* n characters and convert to integer; could be padded
  1132. (define (priv:integer-reader-exact n port)
  1133. (let ((padding-ok #t))
  1134. (define (accum-int port accum nchars)
  1135. (let ((ch (peek-char port)))
  1136. (cond
  1137. ((>= nchars n) accum)
  1138. ((eof-object? ch)
  1139. (priv:time-error 'string->date 'bad-date-template-string
  1140. "Premature ending to integer read."))
  1141. ((char-numeric? ch)
  1142. (set! padding-ok #f)
  1143. (accum-int port
  1144. (+ (* accum 10) (priv:char->int (read-char port)))
  1145. (+ nchars 1)))
  1146. (padding-ok
  1147. (read-char port) ; consume padding
  1148. (accum-int port accum (+ nchars 1)))
  1149. (else ; padding where it shouldn't be
  1150. (priv:time-error 'string->date 'bad-date-template-string
  1151. "Non-numeric characters in integer read.")))))
  1152. (accum-int port 0 0)))
  1153. (define (priv:make-integer-exact-reader n)
  1154. (lambda (port)
  1155. (priv:integer-reader-exact n port)))
  1156. (define (priv:zone-reader port)
  1157. (let ((offset 0)
  1158. (positive? #f))
  1159. (let ((ch (read-char port)))
  1160. (if (eof-object? ch)
  1161. (priv:time-error 'string->date 'bad-date-template-string
  1162. (list "Invalid time zone +/-" ch)))
  1163. (if (or (char=? ch #\Z) (char=? ch #\z))
  1164. 0
  1165. (begin
  1166. (cond
  1167. ((char=? ch #\+) (set! positive? #t))
  1168. ((char=? ch #\-) (set! positive? #f))
  1169. (else
  1170. (priv:time-error 'string->date 'bad-date-template-string
  1171. (list "Invalid time zone +/-" ch))))
  1172. (let ((ch (read-char port)))
  1173. (if (eof-object? ch)
  1174. (priv:time-error 'string->date 'bad-date-template-string
  1175. (list "Invalid time zone number" ch)))
  1176. (set! offset (* (priv:char->int ch)
  1177. 10 60 60)))
  1178. (let ((ch (read-char port)))
  1179. (if (eof-object? ch)
  1180. (priv:time-error 'string->date 'bad-date-template-string
  1181. (list "Invalid time zone number" ch)))
  1182. (set! offset (+ offset (* (priv:char->int ch)
  1183. 60 60))))
  1184. (let ((ch (read-char port)))
  1185. (if (eof-object? ch)
  1186. (priv:time-error 'string->date 'bad-date-template-string
  1187. (list "Invalid time zone number" ch)))
  1188. (set! offset (+ offset (* (priv:char->int ch)
  1189. 10 60))))
  1190. (let ((ch (read-char port)))
  1191. (if (eof-object? ch)
  1192. (priv:time-error 'string->date 'bad-date-template-string
  1193. (list "Invalid time zone number" ch)))
  1194. (set! offset (+ offset (* (priv:char->int ch)
  1195. 60))))
  1196. (if positive? offset (- offset)))))))
  1197. ;; looking at a char, read the char string, run thru indexer, return index
  1198. (define (priv:locale-reader port indexer)
  1199. (define (read-char-string result)
  1200. (let ((ch (peek-char port)))
  1201. (if (char-alphabetic? ch)
  1202. (read-char-string (cons (read-char port) result))
  1203. (list->string (reverse! result)))))
  1204. (let* ((str (read-char-string '()))
  1205. (index (indexer str)))
  1206. (if index index (priv:time-error 'string->date
  1207. 'bad-date-template-string
  1208. (list "Invalid string for " indexer)))))
  1209. (define (priv:make-locale-reader indexer)
  1210. (lambda (port)
  1211. (priv:locale-reader port indexer)))
  1212. (define (priv:make-char-id-reader char)
  1213. (lambda (port)
  1214. (if (char=? char (read-char port))
  1215. char
  1216. (priv:time-error 'string->date
  1217. 'bad-date-template-string
  1218. "Invalid character match."))))
  1219. ;; A List of formatted read directives.
  1220. ;; Each entry is a list.
  1221. ;; 1. the character directive;
  1222. ;; a procedure, which takes a character as input & returns
  1223. ;; 2. #t as soon as a character on the input port is acceptable
  1224. ;; for input,
  1225. ;; 3. a port reader procedure that knows how to read the current port
  1226. ;; for a value. Its one parameter is the port.
  1227. ;; 4. a action procedure, that takes the value (from 3.) and some
  1228. ;; object (here, always the date) and (probably) side-effects it.
  1229. ;; In some cases (e.g., ~A) the action is to do nothing
  1230. (define priv:read-directives
  1231. (let ((ireader4 (priv:make-integer-reader 4))
  1232. (ireader2 (priv:make-integer-reader 2))
  1233. (ireaderf (priv:make-integer-reader #f))
  1234. (eireader2 (priv:make-integer-exact-reader 2))
  1235. (eireader4 (priv:make-integer-exact-reader 4))
  1236. (locale-reader-abbr-weekday (priv:make-locale-reader
  1237. priv:locale-abbr-weekday->index))
  1238. (locale-reader-long-weekday (priv:make-locale-reader
  1239. priv:locale-long-weekday->index))
  1240. (locale-reader-abbr-month (priv:make-locale-reader
  1241. priv:locale-abbr-month->index))
  1242. (locale-reader-long-month (priv:make-locale-reader
  1243. priv:locale-long-month->index))
  1244. (char-fail (lambda (ch) #t))
  1245. (do-nothing (lambda (val object) (values))))
  1246. (list
  1247. (list #\~ char-fail (priv:make-char-id-reader #\~) do-nothing)
  1248. (list #\a char-alphabetic? locale-reader-abbr-weekday do-nothing)
  1249. (list #\A char-alphabetic? locale-reader-long-weekday do-nothing)
  1250. (list #\b char-alphabetic? locale-reader-abbr-month
  1251. (lambda (val object)
  1252. (set-date-month! object val)))
  1253. (list #\B char-alphabetic? locale-reader-long-month
  1254. (lambda (val object)
  1255. (set-date-month! object val)))
  1256. (list #\d char-numeric? ireader2 (lambda (val object)
  1257. (set-date-day!
  1258. object val)))
  1259. (list #\e char-fail eireader2 (lambda (val object)
  1260. (set-date-day! object val)))
  1261. (list #\h char-alphabetic? locale-reader-abbr-month
  1262. (lambda (val object)
  1263. (set-date-month! object val)))
  1264. (list #\H char-numeric? ireader2 (lambda (val object)
  1265. (set-date-hour! object val)))
  1266. (list #\k char-fail eireader2 (lambda (val object)
  1267. (set-date-hour! object val)))
  1268. (list #\m char-numeric? ireader2 (lambda (val object)
  1269. (set-date-month! object val)))
  1270. (list #\M char-numeric? ireader2 (lambda (val object)
  1271. (set-date-minute!
  1272. object val)))
  1273. (list #\S char-numeric? ireader2 (lambda (val object)
  1274. (set-date-second! object val)))
  1275. (list #\y char-fail eireader2
  1276. (lambda (val object)
  1277. (set-date-year! object (priv:natural-year val))))
  1278. (list #\Y char-numeric? ireader4 (lambda (val object)
  1279. (set-date-year! object val)))
  1280. (list #\z (lambda (c)
  1281. (or (char=? c #\Z)
  1282. (char=? c #\z)
  1283. (char=? c #\+)
  1284. (char=? c #\-)))
  1285. priv:zone-reader (lambda (val object)
  1286. (set-date-zone-offset! object val))))))
  1287. (define (priv:string->date date index format-string str-len port template-string)
  1288. (define (skip-until port skipper)
  1289. (let ((ch (peek-char port)))
  1290. (if (eof-object? ch)
  1291. (priv:time-error 'string->date 'bad-date-format-string template-string)
  1292. (if (not (skipper ch))
  1293. (begin (read-char port) (skip-until port skipper))))))
  1294. (if (>= index str-len)
  1295. (begin
  1296. (values))
  1297. (let ((current-char (string-ref format-string index)))
  1298. (if (not (char=? current-char #\~))
  1299. (let ((port-char (read-char port)))
  1300. (if (or (eof-object? port-char)
  1301. (not (char=? current-char port-char)))
  1302. (priv:time-error 'string->date
  1303. 'bad-date-format-string template-string))
  1304. (priv:string->date date
  1305. (+ index 1)
  1306. format-string
  1307. str-len
  1308. port
  1309. template-string))
  1310. ;; otherwise, it's an escape, we hope
  1311. (if (> (+ index 1) str-len)
  1312. (priv:time-error 'string->date
  1313. 'bad-date-format-string template-string)
  1314. (let* ((format-char (string-ref format-string (+ index 1)))
  1315. (format-info (assoc format-char priv:read-directives)))
  1316. (if (not format-info)
  1317. (priv:time-error 'string->date
  1318. 'bad-date-format-string template-string)
  1319. (begin
  1320. (let ((skipper (cadr format-info))
  1321. (reader (caddr format-info))
  1322. (actor (cadddr format-info)))
  1323. (skip-until port skipper)
  1324. (let ((val (reader port)))
  1325. (if (eof-object? val)
  1326. (priv:time-error 'string->date
  1327. 'bad-date-format-string
  1328. template-string)
  1329. (actor val date)))
  1330. (priv:string->date date
  1331. (+ index 2)
  1332. format-string
  1333. str-len
  1334. port
  1335. template-string))))))))))
  1336. (define (string->date input-string template-string)
  1337. (define (priv:date-ok? date)
  1338. (and (date-nanosecond date)
  1339. (date-second date)
  1340. (date-minute date)
  1341. (date-hour date)
  1342. (date-day date)
  1343. (date-month date)
  1344. (date-year date)
  1345. (date-zone-offset date)))
  1346. (let ((newdate (make-date 0 0 0 0 #f #f #f #f)))
  1347. (priv:string->date newdate
  1348. 0
  1349. template-string
  1350. (string-length template-string)
  1351. (open-input-string input-string)
  1352. template-string)
  1353. (if (not (date-zone-offset newdate))
  1354. (begin
  1355. ;; this is necessary to get DST right -- as far as we can
  1356. ;; get it right (think of the double/missing hour in the
  1357. ;; night when we are switching between normal time and DST).
  1358. (set-date-zone-offset! newdate
  1359. (priv:local-tz-offset
  1360. (make-time time-utc 0 0)))
  1361. (set-date-zone-offset! newdate
  1362. (priv:local-tz-offset
  1363. (date->time-utc newdate)))))
  1364. (if (priv:date-ok? newdate)
  1365. newdate
  1366. (priv:time-error
  1367. 'string->date
  1368. 'bad-date-format-string
  1369. (list "Incomplete date read. " newdate template-string)))))
  1370. ;;; srfi-19.scm ends here