Skip to content

Commit 68023c2

Browse files
jackmath5261-bitJackChen
andauthored
[210_15] srfi-19 time-utc date相互转化实现 (#403)
* time-utc convert data * 文档 --------- Co-authored-by: JackChen <17683835261@163.com>
1 parent 60db3af commit 68023c2

File tree

3 files changed

+175
-1
lines changed

3 files changed

+175
-1
lines changed

devel/210_15.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# [210_15] srfi-19 time-utc date相互转化实现
2+
3+
## 添加 srfi-19 time-utc date相互转化实现
4+
5+
## 如何测试
6+
7+
```shell
8+
# 可能需要清除缓存
9+
# rm .xmake/ build/ -r
10+
xmake f -vyD
11+
xmake b goldfish
12+
./bin/goldfish tests/goldfish/liii/time-test.scm
13+
```
14+
15+

goldfish/srfi/srfi-19.scm

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
date-nanosecond date-second date-minute date-hour
7676
date-day date-month date-year date-zone-offset
7777
;; Time/Date/Julian Day/Modified Julian Day Converters
78+
time-utc->date date->time-utc
7879
;; Date to String/String to Date Converters
7980
date->string)
8081
(begin
@@ -415,7 +416,63 @@
415416
;; Time/Date/Julian Day/Modified Julian Day Converters
416417
;; ====================
417418

418-
;; TODO
419+
(define (priv:days-before-year year)
420+
(+ (* 365 year)
421+
(floor-quotient year 4)
422+
(- (floor-quotient year 100))
423+
(floor-quotient year 400)))
424+
425+
(define (priv:days-since-epoch year month day)
426+
(+ (- (priv:days-before-year year)
427+
(priv:days-before-year 1970))
428+
(- (priv:year-day day month year) 1)))
429+
430+
(define (priv:civil-from-days days)
431+
;; Howard Hinnant's algorithm, adapted for proleptic Gregorian calendar
432+
(let* ((z (+ days 719468))
433+
(era (if (>= z 0)
434+
(floor-quotient z 146097)
435+
(floor-quotient (- z 146096) 146097)))
436+
(doe (- z (* era 146097))) ; [0, 146096]
437+
(yoe (floor-quotient (- doe (floor-quotient doe 1460)
438+
(- (floor-quotient doe 36524))
439+
(floor-quotient doe 146096))
440+
365))
441+
(y (+ yoe (* era 400)))
442+
(doy (- doe (+ (* 365 yoe)
443+
(floor-quotient yoe 4)
444+
(- (floor-quotient yoe 100)))))
445+
(mp (floor-quotient (+ (* 5 doy) 2) 153))
446+
(d (+ (- doy (floor-quotient (+ (* 153 mp) 2) 5)) 1))
447+
(m (+ mp (if (< mp 10) 3 -9)))
448+
(y (if (<= m 2) (+ y 1) y)))
449+
(values y m d)))
450+
451+
(define* (time-utc->date time-utc (tz-offset 0))
452+
(unless (and (time? time-utc) (eq? (time-type time-utc) TIME-UTC))
453+
(error 'wrong-type-arg "time-utc->date: time-utc must be a TIME-UTC object" time-utc))
454+
(unless (integer? tz-offset)
455+
(error 'wrong-type-arg "time-utc->date: tz-offset must be an integer" tz-offset))
456+
(let* ((sec (+ (time-second time-utc) tz-offset))
457+
(nsec (time-nanosecond time-utc)))
458+
(receive (days day-sec) (floor/ sec priv:SID)
459+
(receive (year month day) (priv:civil-from-days days)
460+
(receive (hour rem1) (floor/ day-sec 3600)
461+
(receive (minute second) (floor/ rem1 60)
462+
(make-date nsec second minute hour day month year tz-offset)))))))
463+
464+
(define (date->time-utc date)
465+
(unless (date? date)
466+
(error 'wrong-type-arg "date->time-utc: date must be a date object" date))
467+
(let* ((days (priv:days-since-epoch (date-year date)
468+
(date-month date)
469+
(date-day date)))
470+
(local-sec (+ (* days priv:SID)
471+
(* (date-hour date) 3600)
472+
(* (date-minute date) 60)
473+
(date-second date)))
474+
(utc-sec (- local-sec (date-zone-offset date))))
475+
(make-time TIME-UTC (date-nanosecond date) utc-sec)))
419476

420477
;; ====================
421478
;; Date to String/String to Date Converters

tests/goldfish/liii/time-test.scm

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,108 @@ wrong-type-arg
807807
(check-true (undefined? (date-year (make-time TIME-UTC 0 0))))
808808
(check-catch 'wrong-type-arg (date-zone-offset #f))
809809

810+
;; ====================
811+
;; Time/Date Converters
812+
;; ====================
813+
814+
#|
815+
time-utc->date
816+
将 TIME-UTC 时间对象转换为日期对象。
817+
818+
date->time-utc
819+
将日期对象转换为 TIME-UTC 时间对象。
820+
|#
821+
822+
;; time-utc->date basic (UTC)
823+
(let* ((t (make-time TIME-UTC 0 0))
824+
(d (time-utc->date t 0)))
825+
(check (date-year d) => 1970)
826+
(check (date-month d) => 1)
827+
(check (date-day d) => 1)
828+
(check (date-hour d) => 0)
829+
(check (date-minute d) => 0)
830+
(check (date-second d) => 0)
831+
(check (date-zone-offset d) => 0))
832+
833+
;; time-utc->date with positive tz offset (+8)
834+
(let* ((t (make-time TIME-UTC 0 0))
835+
(d (time-utc->date t 28800)))
836+
(check (date-year d) => 1970)
837+
(check (date-month d) => 1)
838+
(check (date-day d) => 1)
839+
(check (date-hour d) => 8)
840+
(check (date-minute d) => 0)
841+
(check (date-second d) => 0)
842+
(check (date-zone-offset d) => 28800))
843+
844+
;; time-utc->date with negative tz offset (-1 hour)
845+
(let* ((t (make-time TIME-UTC 0 0))
846+
(d (time-utc->date t -3600)))
847+
(check (date-year d) => 1969)
848+
(check (date-month d) => 12)
849+
(check (date-day d) => 31)
850+
(check (date-hour d) => 23)
851+
(check (date-minute d) => 0)
852+
(check (date-second d) => 0)
853+
(check (date-zone-offset d) => -3600))
854+
855+
;; time-utc->date before 1970
856+
(let* ((t (make-time TIME-UTC 0 -1))
857+
(d (time-utc->date t 0)))
858+
(check (date-year d) => 1969)
859+
(check (date-month d) => 12)
860+
(check (date-day d) => 31)
861+
(check (date-hour d) => 23)
862+
(check (date-minute d) => 59)
863+
(check (date-second d) => 59))
864+
865+
;; time-utc->date negative day boundaries
866+
(let* ((t (make-time TIME-UTC 0 -86400))
867+
(d (time-utc->date t 0)))
868+
(check (date-year d) => 1969)
869+
(check (date-month d) => 12)
870+
(check (date-day d) => 31)
871+
(check (date-hour d) => 0)
872+
(check (date-minute d) => 0)
873+
(check (date-second d) => 0))
874+
875+
(let* ((t (make-time TIME-UTC 0 -86401))
876+
(d (time-utc->date t 0)))
877+
(check (date-year d) => 1969)
878+
(check (date-month d) => 12)
879+
(check (date-day d) => 30)
880+
(check (date-hour d) => 23)
881+
(check (date-minute d) => 59)
882+
(check (date-second d) => 59))
883+
884+
;; date->time-utc basic
885+
(let* ((d (make-date 0 0 0 8 1 1 1970 28800))
886+
(t (date->time-utc d)))
887+
(check (time-type t) => TIME-UTC)
888+
(check (time-second t) => 0)
889+
(check (time-nanosecond t) => 0))
890+
891+
;; round-trip date -> time -> date with same tz-offset
892+
(let* ((d1 (make-date 123456789 45 30 14 25 12 2023 28800))
893+
(t (date->time-utc d1))
894+
(d2 (time-utc->date t (date-zone-offset d1))))
895+
(check (date-year d2) => (date-year d1))
896+
(check (date-month d2) => (date-month d1))
897+
(check (date-day d2) => (date-day d1))
898+
(check (date-hour d2) => (date-hour d1))
899+
(check (date-minute d2) => (date-minute d1))
900+
(check (date-second d2) => (date-second d1))
901+
(check (date-nanosecond d2) => (date-nanosecond d1))
902+
(check (date-zone-offset d2) => (date-zone-offset d1)))
903+
904+
;; converter error conditions
905+
(check-catch 'wrong-type-arg
906+
(time-utc->date (make-time TIME-TAI 0 0) 0))
907+
(check-catch 'wrong-type-arg
908+
(time-utc->date (make-time TIME-UTC 0 0) "bad-offset"))
909+
(check-catch 'wrong-type-arg
910+
(date->time-utc "not-a-date"))
911+
810912
;; ====================
811913
;; Date to String/String to Date Converters
812914
;; ====================

0 commit comments

Comments
 (0)