3. Dates and Times


;; Use the builtin POSIX time functions

;; get the current time
(current-time)   ; number of seconds since the epoch
(gettimeofday)   ; pair of seconds and microseconds since the epoch

;; create a time object from an integer (e.g. returned by current-time)
(localtime time) ; in localtime
(gmtime time)    ; in UTC

;; get/set broken down components of a time object

(tm:sec time)    (set-tm:sec time secs)    ; seconds (0-59)
(tm:min time)    (set-tm:min time mins)    ; minutes (0-59)
(tm:hour time)   (set-tm:hour time hours)  ; hours (0-23)
(tm:mday time)   (set-tm:mday time mday)   ; day of the month (1-31)
(tm:mon time)    (set-tm:mon time month)   ; month (0-11)
(tm:year time)   (set-tm:year time year)   ; year minus 1900 (70-)
(tm:wday time)   (set-tm:wday time wday)   ; day of the week (0-6)
                                           ; where Sunday is 0
(tm:yday time)   (set-tm:yday time yday)   ; day of year (0-365)
(tm:isdst time)  (set-tm:isdst time isdst) ; daylight saving indicator
                                           ; 0 for "no", > 0 for "yes",
                                           ; < 0 for "unknown"
(tm:gmtoff time) (set-tm:gmtoff time off)  ; time zone offset in seconds
                                           ; west of UTC (-46800 to 43200)
(tm:zone time)   (set-tm:zone time zone)   ; Time zone label (a string),
                                           ; not necessarily unique.

(format #t "Today is day ~A of the current year.\n"
        (tm:yday (localtime (current-time))))

;; Or use SRFI-19 - Time and Date Procedures
(use-modules (srfi srfi-19))

(define now (current-date))  ; immutable once created

(date-nanosecond now)        ; 0-9,999,999
(date-second now)            ; 0-60 (60 represents a leap second)
(date-minute now)            ; 0-59
(date-hour now)              ; 0-23
(date-day now)               ; 0-31
(date-month now)             ; 1-12
(date-year now)              ; integer representing the year
(date-year-day now)          ; day of year (Jan 1 is 1, etc.)
(date-week-day now)          ; day of week (Sunday is 0, etc.)
(date-week-number now start) ; week of year, ignoring a first partial week
                             ; start is the first day of week as above
(date-zone-offset now)       ; integer number of seconds east of GMT

(format #t "Today is day ~A of the current year.\n"
        (date-year-day (current-date)))

Finding Today's Date

;; using format and POSIX time components
(use-modules (ice-9 format))
(let ((now (localtime (current-time))))
  (format #t "The current date is ~4'0D ~2'0D ~2'0D\n"
          (+ 1900 (tm:year now)) (tm:mon now) (tm:mday now)))

;; using format and SRFI-19 time components
(use-modules (srfi srfi-19) (ice-9 format))
(let ((now (current-date)))
  (format #t "The current date is ~4'0d-~2'0D-~2'0D\n"
          (date-year now) (date-month now) (date-day now)))

;; using POSIX strftime with a libc time format string
(display (strftime "%Y-%m-%d\n" (localtime (current-time))))

Converting DMYHMS to Epoch Seconds

;; set the individual components of a time struct and use mktime
(define time (localtime (current-time)))
(set-tm:mday time mday)
(set-tm:mon time mon)
(set-tm:year time year)
(car (mktime time))  ; mktime returns a (epoch-seconds . time) pair

;; or use SRFI-19's make-date and date->time-monotonic
(use-modules (srfi srfi-19))
 (make-date nanosecond second minute hour day month year zone-offset))

Converting Epoch Seconds to DMYHMS

;; use localtime or gmtime with the accessors mentioned in the
;; introduction to this chapter
(let ((time (localtime seconds)))  ; or gmtime
  (format #t "Dateline: ~2'0d:~2'0d:~2'0d-~4'0d/~2'0d/~2'0d\n"
          (tm:hour time) (tm:min time) (tm:sec time)
          (+ 1900 (tm:year time)) (1+ (tm:mon time)) (tm:mday time)))

;; or use SRFI-19
(use-modules (srfi srfi-19))
(let* ((time (make-time time-monotonic nanosecond second)))
  (display (date->string (time-monotonic->date time) "~T-~1\n")))

Adding to or Subtracting from a Date

;; just add or subtract epoch seconds
(define when (+ now difference))
(define then (- now difference))

;; if you have DMYHMS values, you can convert them to times or add
;; them as seconds:
(define birthtime 96176750)
(define interval (+ 5                  ; 5 seconds
                    (* 17 60)          ; 17 minutes
                    (* 2 60 60)        ; 2 hours
                    (* 55 60 60 24)))  ; and 55 days
(define then (+ birthtime interval))
(format #t "Then is ~A\n" (strftime "%a %b %d %T %Y" (localtime then)))

Difference of Two Dates

;; subtract the epoch seconds:
(define bree 361535725)
(define nat 96201950)
(define difference (- bree nat))
(format #t "There were ~A seconds between Nat and Bree\n" difference)

;; or use SRFI-19's time arithmetic procedures:
(use-modules (srfi srfi-19))
(define time1 (make-time time-monotonic nano1 sec1))
(define time2 (make-time time-monotonic nano2 sec2))
(define duration (time-difference time1 time2))
(time=? (subtract-duration time1 duration) time2) ; #t
(time=? (add-duration time2 duration) time1)      ; #t

Day in a Week/Month/Year or Week Number

;; convert to a SRFI-19 date and use the accessors
(use-modules (srfi srfi-19))
(date-day date)
(date-year-day date)
(date-week-day date)
(date-week-number date start-day-of-week)

Parsing Dates and Times from Strings

;; use the strptime function:
(define time-pair (strptime "%Y-%m-%d" "1998-06-03"))
(format #t "Time is ~A\n." (strftime "%b %d, %Y" (car time-pair)))

;; or use SRFI-19's string->date:
(use-modules (srfi srfi-19))
(define date (string->date "1998-06-03" "~Y-~m-~d"))
(format #t "Time is ~A.\n" (date->string date))

Printing a Date

;; use the already seen strftime:
(format #t "strftime gives: ~A\n"
        (strftime "%A %D" (localtime (current-time))))

;; or SRFI-19's date->string:
(use-modules (srfi srfi-19))
(format #t "default date->string gives: ~A\n" (date->string (current-date)))
(format #t "date->string gives: ~A\n"
        (date->string (current-date) "~a ~b ~e ~H:~M:~S ~z ~Y"))

High-Resolution Timers

;; gettimeofday will return seconds and microseconds:
(define t0 (gettimeofday))
;; do your work here
(define t1 (gettimeofday))
(format #t "You took ~A seconds and ~A microseconds\n"
        (- (car t1) (car t0)) (- (cdr t1) (cdr t0)))

;; you can also get more detailed info about the real and processor
;; times:
(define runtime (times))
(tms:clock runtime)  ; the current real time
(tms:utime runtime)  ; the CPU time units used by the calling process
(tms:stime runtime)  ; the CPU time units used by the system on behalf
                     ; of the calling process.
(tms:cutime runtime) ; the CPU time units used by terminated child
                     ; processes of the calling process, whose status
                     ; has been collected (e.g., using `waitpid').
(tms:cstime runtime) ; the CPU times units used by the system on
                     ; behalf of terminated child processes

;; you can also use the time module to time execution:
(use-modules (ice-9 time))
(time (sleep 3))
;; clock utime stime cutime cstime gctime
;; 3.01  0.00  0.00   0.00   0.00   0.00
;; 0

Short Sleeps

(sleep i)   ; sleep for i seconds
(usleep i)  ; sleep for i microseconds (not available on all platforms)

Program: hopdelta