home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / share / guile / 1.6 / srfi / srfi-19.scm < prev    next >
Encoding:
Text File  |  2006-06-19  |  59.4 KB  |  1,568 lines

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