(define-module (vcomponent datetime) :use-module (srfi srfi-1) :use-module ((srfi srfi-41) :select (stream-filter)) :use-module ((srfi srfi-41 util) :select (get-stream-interval)) :use-module (vcomponent base) :use-module (datetime) :use-module (datetime timespec) :use-module (datetime zic) :use-module (hnh util) :use-module ((vcomponent recurrence generate) :select (final-event-occurence)) :use-module (ice-9 curried-definitions) :export (#;parse-datetime event-overlaps? overlapping? event-contains? event-zero-length? ev-timevtimezone )) ;;; date time pointer #; (define (parse-datetime dtime) "Parse the given date[time] string into a date object." (string->date dtime (case (string-length dtime) ((8) "~Y~m~d") ; All day ((15) "~Y~m~dT~H~M~S") ; "local" or TZID-param ((16) "~Y~m~dT~H~M~S~z")))) ; UTC-time (define (event-overlaps? event begin end) "Returns if the event overlaps the timespan. Event must have the DTSTART and DTEND protperty set." (timespan-overlaps? (prop event 'DTSTART) (or (prop event 'DTEND) (prop event 'DTSTART)) begin end)) (define (overlapping? event-a event-b) (timespan-overlaps? (prop event-a 'DTSTART) (or (prop event-a 'DTEND) (if (date? (prop event-a 'DTSTART)) (date+ (prop event-a 'DTSTART) (date day: 1)) (prop event-a 'DTSTART))) (prop event-b 'DTSTART) (or (prop event-b 'DTEND) (if (date? (prop event-b 'DTSTART)) (date+ (prop event-b 'DTSTART) (date day: 1)) (prop event-b 'DTSTART))))) (define (event-contains? ev date/-time) "Does event overlap the date that contains time." (let* ((start (as-date date/-time)) (end (date+ start (date day: 1)))) (event-overlaps? ev start end))) (define (event-zero-length? ev) (and (datetime? (prop ev 'DTSTART)) (not (prop ev 'DTEND)))) (define (ev-timevtimezone zoneinfo zone-name event) (define vtimezone (make-vcomponent 'VTIMEZONE)) (define last-until (datetime date: (date month: 1 day: 1))) (define last-offset (timespec-zero)) (set! (prop vtimezone 'TZID) zone-name) (for zone-entry in (filter (relevant-zone-entry? event) (get-zone zoneinfo zone-name)) (cond [(zone-entry-rule zone-entry) timespec? => (lambda (inline-rule) (let ((component (make-vcomponent 'DAYLIGHT)) (new-timespec (timespec-add (zone-entry-stdoff zone-entry) inline-rule))) (set! (prop component 'DTSTART) last-until (prop component 'TZOFFSETFROM) last-offset (prop component 'TZOFFSETTO) new-timespec (prop component 'TZNAME) (zone-entry-format zone-entry) last-until (zone-entry-until zone-entry) last-offset new-timespec) (add-child! vtimezone component)))] [(zone-entry-rule zone-entry) => (lambda (rule-name) (map (lambda (rule) (let ((component (make-vcomponent ;; NOTE the zoneinfo database doesn't ;; come with information if a given ;; rule is in standard or daylight time, ;; since that's mostly nonsencical ;; (e.g. war- and peacetime). ;; But the ical standard requires that, ;; so this is a fair compromize. (if (string-null? (rule-letters rule)) 'STANDARD 'DAYLIGHT))) (new-timespec (timespec-add (zone-entry-stdoff zone-entry) (rule-save rule)))) (set! (prop component 'DTSTART) (rule->dtstart rule) (prop component 'TZOFFSETFROM) last-offset (prop component 'TZOFFSETTO) new-timespec (prop component 'TZNAME) (zone-format (zone-entry-format zone-entry) (rule-letters rule)) ;; NOTE this can both be a number or the ;; symbol 'maximum last-until (zone-entry-until zone-entry) last-offset new-timespec) (awhen (rule->rrule rule) (set! (prop component 'RRULE) it)) (add-child! vtimezone component))) ;; some of the rules might not apply to us since we only ;; started using that rule set later. It's also possible ;; that we stopped using a ruleset which continues existing. ;; ;; Both these are filtered here. (filter (relevant-zone-rule? event) (get-rule zoneinfo rule-name))))] [else ; no rule (let ((component (make-vcomponent 'STANDARD))) ;; DTSTART MUST be a datetime in local time (set! (prop component 'DTSTART) last-until (prop component 'TZOFFSETFROM) last-offset (prop component 'TZOFFSETTO) (zone-entry-stdoff zone-entry) (prop component 'TZNAME) (zone-entry-format zone-entry) last-until (zone-entry-until zone-entry) last-offset (zone-entry-stdoff zone-entry)) (add-child! vtimezone component))])) vtimezone)