(define-module (srfi srfi-19 alt) :export (date? year month day hour minute second time? datetime? ) :use-module (srfi srfi-1) :use-module (srfi srfi-9) :use-module (srfi srfi-9 gnu) :use-module (ice-9 match) :use-module (util) ) (define-many define-public (jan january ) 1 (feb february ) 2 (mar mars ) 3 (apr april ) 4 (may ) 5 (jun june ) 6 (jul july ) 7 (aug august ) 8 (sep september ) 9 (oct october ) 10 (nov november ) 11 (dec december ) 12 ) (define-immutable-record-type (make-date year month day) date? (year year) (month month) (day day)) (set-record-type-printer! (lambda (r p) (format p "~4'0d­~2'0d­~2'0d" (year r) (month r) (day r)))) (define*-public (date key: (year 0) (month 0) (day 0)) (make-date year month day)) ;; int -> bool (define-public (leap-year? year) (and (zero? (remainder year 4)) (or (zero? (remainder year 400)) (not (zero? (remainder year 100)))))) ;; Returns number of days month for a given date. Just looks at the year and month components. (define-public (days-in-month date) (case* (month date) ((jan mar may jul aug oct dec) 31) ((apr jun sep nov) 30) ((feb) (if (leap-year? (year date)) 29 28)))) (define-public (days-in-year date) (if (leap-year? (year date)) 366 365)) ;; 2020-01-10 + 0-0-30 = 2020-02-09 ;; 10 + 30 = 40 ; day + day ;; 40 > 31 ; target days > days in month ;; 2020-02-00 + 0-0- (40 - 31) ; ;; 2020-02-09 (define-public (date= a b) (and (= (year a) (year b)) (= (month a) (month b)) (= (day a) (day b)))) (define-public date=? date=) (define (date+% base change) ;; while (day base) > (days-in-month base) ;; month++; days -= (days-in-month base) (define days-fixed (let loop ((target (set (day base) = (+ (day change))))) (if (> (day target) (days-in-month target)) (loop (set-> target (month = (+ 1)) (day = (- (days-in-month target))))) target))) ;; while (month base) > 12 ;; year++; month -= 12 (define months-fixed (let loop ((target (set (month days-fixed) = (+ (month change))))) (if (> (month target) 12) (loop (set-> target (year = (+ 1)) (month = (- 12)))) target))) (set (year months-fixed) = (+ (year change)))) (define-public (date+ base . rest) (fold date+% base rest)) (define-public (date- base change) (define-values (days-fixed change*) (let loop ((target base) (change change)) (if (>= (day change) (day target)) (loop (set-> target (month = (- 1)) (day (days-in-month (set (month target) = (- 1))))) (set (day change) = (- (day target)))) (values (set (day target) = (- (day change))) (set (day change) 0))))) (define-values (month-fixed change**) (let loop ((target days-fixed) (change change*)) (if (>= (month change) (month target)) (loop (set-> target (year = (- 1)) (month 12)) (set (month change) = (- (month target)))) (values (set (month target) = (- (month change))) (set (month change) 0))))) ;; change** should here should have both month and date = 0 (set (year month-fixed) = (- (year change)))) (define-public (time- base change) (define-values (second-fixed change*) (let loop ((target base) (change change)) (if (> (second change) (second target)) (loop (set-> target (minute = (- 1)) (second 60)) (set (second change) = (- (second target)))) (values (set (second target) = (- (second change))) (set (second change) 0))))) (define-values (minute-fixed change**) (let loop ((target second-fixed) (change change*)) (if (> (minute change) (minute target)) (loop (set-> target (hour = (- 1)) (minute 60)) (set (minute change) = (- (minute target)))) (values (set (minute target) = (- (minute change))) (set (minute change) 0))))) ;; change** should here should have both month and date = 0 (set (hour month-fixed) = (- (hour change))) ) (define-immutable-record-type