From e822f7b81245c919eda8bd8ad4b482df075e0508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= Date: Fri, 24 Jan 2020 20:21:41 +0100 Subject: Start of new date structures. --- module/srfi/srfi-19/alt.scm | 348 +++++++++++++++++++++++++++++++++++++++ module/srfi/srfi-19/alt/util.scm | 107 ++++++++++++ module/srfi/srfi-19/util.scm | 28 ---- 3 files changed, 455 insertions(+), 28 deletions(-) create mode 100644 module/srfi/srfi-19/alt.scm create mode 100644 module/srfi/srfi-19/alt/util.scm (limited to 'module/srfi/srfi-19') diff --git a/module/srfi/srfi-19/alt.scm b/module/srfi/srfi-19/alt.scm new file mode 100644 index 00000000..b3e8a478 --- /dev/null +++ b/module/srfi/srfi-19/alt.scm @@ -0,0 +1,348 @@ +(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