diff --git a/docs/Datetime.html b/docs/Datetime.html index e8fd667..276dcb2 100644 --- a/docs/Datetime.html +++ b/docs/Datetime.html @@ -411,7 +411,7 @@
- (Fn [Int] Datetime) + (Fn [Long] Datetime)
(from-unix-timestamp ts)
@@ -1253,7 +1253,7 @@
defn
- (Fn [(Ref Datetime a)] Int)
+ (Fn [(Ref Datetime a)] Long)
(to-unix-timestamp dt)
diff --git a/test/time.carp b/test/time.carp
index 1964a50..262415d 100644
--- a/test/time.carp
+++ b/test/time.carp
@@ -22,6 +22,21 @@
(let [billion (Uint64.from-long 1000000000l)]
(+ (* (Uint64.from-long secs) billion) (Uint64.from-long ns))))
+; a Datetime with a time-of-day and no timezone.
+(defn at [y mo d h mi s]
+ (Datetime.init y
+ mo
+ d
+ (Maybe.Just h)
+ (Maybe.Just mi)
+ (Maybe.Just s)
+ (Maybe.Nothing)
+ (Maybe.Nothing)))
+
+; seconds since the epoch, from a day count and a time of day (see `nanos`).
+(defn epoch-at [days tod]
+ (+ (* (Long.from-int days) 86400l) (Long.from-int tod)))
+
; n UTF-8 continuation bytes, which decode to no character at all.
(defn conts [n] (String.from-bytes &(Array.replicate n &128b)))
@@ -257,7 +272,7 @@
"different dates are not equal")
(assert-equal test
- 0
+ 0l
(Datetime.to-unix-timestamp
&(Datetime.init 1970
1
@@ -271,7 +286,7 @@
; midnight EST (UTC-5) is 05:00 UTC, i.e. 18000 seconds past the epoch; the
; timezone offset must be subtracted from the wall-clock, not added
(assert-equal test
- 18000
+ 18000l
(Datetime.to-unix-timestamp
&(Datetime.init 1970
1
@@ -284,7 +299,7 @@
"to-unix-timestamp subtracts a negative (west-of-UTC) offset")
; 1970-01-02 00:00 at UTC+5 is 1970-01-01 19:00 UTC = 86400 - 18000 = 68400
(assert-equal test
- 68400
+ 68400l
(Datetime.to-unix-timestamp
&(Datetime.init 1970
1
@@ -304,7 +319,7 @@
(Maybe.Just 0)
(Maybe.Nothing)
(Maybe.Nothing))
- &(Datetime.from-unix-timestamp 0)
+ &(Datetime.from-unix-timestamp 0l)
"from-unix-timestamp 0 is epoch")
; Feb 29 leap year round-trip (1972 is a leap year, ts = 68169600)
(assert-equal test
@@ -353,7 +368,7 @@
; is one non-leap year (365 days) past the epoch; this input previously
; SIGABRTed in both directions.
(assert-equal test
- 31536000
+ 31536000l
(Datetime.to-unix-timestamp
&(Datetime.init 1971
1
@@ -373,12 +388,12 @@
(Maybe.Just 0)
(Maybe.Nothing)
(Maybe.Nothing))
- &(Datetime.from-unix-timestamp 31536000)
+ &(Datetime.from-unix-timestamp 31536000l)
"from-unix-timestamp of 1971-01-01")
; 2023-12-31 23:59:59 (one second before 2024) previously produced a day-ZERO
; date of 2024-1-0
(assert-equal test
- 1704067199
+ 1704067199l
(Datetime.to-unix-timestamp
&(Datetime.init 2023
12
@@ -398,11 +413,11 @@
(Maybe.Just 59)
(Maybe.Nothing)
(Maybe.Nothing))
- &(Datetime.from-unix-timestamp 1704067199)
+ &(Datetime.from-unix-timestamp 1704067199l)
"from-unix-timestamp of 2023-12-31 23:59:59")
; 1000000000 is 2001-09-09 01:46:40 UTC
(assert-equal test
- 1000000000
+ 1000000000l
(Datetime.to-unix-timestamp
&(Datetime.init 2001
9
@@ -422,11 +437,11 @@
(Maybe.Just 40)
(Maybe.Nothing)
(Maybe.Nothing))
- &(Datetime.from-unix-timestamp 1000000000)
+ &(Datetime.from-unix-timestamp 1000000000l)
"from-unix-timestamp of 2001-09-09 01:46:40")
; the 32-bit time_t maximum: 2038-01-19 03:14:07 UTC = 2147483647
(assert-equal test
- 2147483647
+ 2147483647l
(Datetime.to-unix-timestamp
&(Datetime.init 2038
1
@@ -446,12 +461,47 @@
(Maybe.Just 7)
(Maybe.Nothing)
(Maybe.Nothing))
- &(Datetime.from-unix-timestamp 2147483647)
+ &(Datetime.from-unix-timestamp 2147483647l)
"from-unix-timestamp at the 32-bit boundary")
+ (assert-equal test
+ (+ (Long.from-int Int.MAX) 1l)
+ (Datetime.to-unix-timestamp &(at 2038 1 19 3 14 8))
+ "to-unix-timestamp one second past the 32-bit maximum")
+ (assert-equal test
+ &@"2038-01-19 03:14:08"
+ &(stamp &(Datetime.from-unix-timestamp (+ (Long.from-int Int.MAX) 1l)))
+ "from-unix-timestamp one second past the 32-bit maximum")
+ ; 2100-01-01 is 47482 days past the epoch, 2262-04-11 is 106751
+ (assert-equal test
+ (epoch-at 47482 0)
+ (Datetime.to-unix-timestamp &(at 2100 1 1 0 0 0))
+ "to-unix-timestamp of 2100-01-01")
+ (assert-equal test
+ &@"2100-01-01 00:00:00"
+ &(stamp &(Datetime.from-unix-timestamp (epoch-at 47482 0)))
+ "from-unix-timestamp of 2100-01-01")
+ (assert-equal test
+ (epoch-at 106751 0)
+ (Datetime.to-unix-timestamp &(at 2262 4 11 0 0 0))
+ "to-unix-timestamp of 2262-04-11")
+ (assert-equal test
+ &@"2262-04-11 12:34:56"
+ &(stamp
+ &(Datetime.from-unix-timestamp
+ (Datetime.to-unix-timestamp &(at 2262 4 11 12 34 56))))
+ "from-unix-timestamp round-trips a date past 2038")
+ (assert-equal test
+ (- (Long.from-int Int.MIN) 1l)
+ (Datetime.to-unix-timestamp &(at 1901 12 13 20 45 51))
+ "to-unix-timestamp one second before the 32-bit minimum")
+ (assert-equal test
+ &@"1901-12-13 20:45:51"
+ &(stamp &(Datetime.from-unix-timestamp (- (Long.from-int Int.MIN) 1l)))
+ "from-unix-timestamp one second before the 32-bit minimum")
; negative timestamps (pre-1970). / and mod truncate toward zero in Carp, so
; the time-of-day must be floored to stay in 0..86399.
(assert-equal test
- -1
+ -1l
(Datetime.to-unix-timestamp
&(Datetime.init 1969
12
@@ -471,7 +521,7 @@
(Maybe.Just 59)
(Maybe.Nothing)
(Maybe.Nothing))
- &(Datetime.from-unix-timestamp -1)
+ &(Datetime.from-unix-timestamp -1l)
"from-unix-timestamp of -1 is one second before the epoch")
(assert-equal test
&(Datetime.init 1969
@@ -482,7 +532,7 @@
(Maybe.Just 0)
(Maybe.Nothing)
(Maybe.Nothing))
- &(Datetime.from-unix-timestamp -86400)
+ &(Datetime.from-unix-timestamp -86400l)
"from-unix-timestamp of -86400 is exactly one day before the epoch")
(assert-equal test
&(Datetime.init 1969
@@ -507,7 +557,7 @@
; leap-day boundary, cross-checked against known UTC values: 1972-02-29 is
; 68169600 and the following day 1972-03-01 is exactly 86400 seconds later
(assert-equal test
- 68169600
+ 68169600l
(Datetime.to-unix-timestamp
&(Datetime.init 1972
2
@@ -527,10 +577,10 @@
(Maybe.Just 0)
(Maybe.Nothing)
(Maybe.Nothing))
- &(Datetime.from-unix-timestamp 68169600)
+ &(Datetime.from-unix-timestamp 68169600l)
"from-unix-timestamp of the leap day 1972-02-29")
(assert-equal test
- 68256000
+ 68256000l
(Datetime.to-unix-timestamp
&(Datetime.init 1972
3
diff --git a/time.carp b/time.carp
index d01f965..0b19afe 100644
--- a/time.carp
+++ b/time.carp
@@ -382,15 +382,18 @@ a UNIX timestamp, i.e. the number of seconds elapsed since the 1st of January,
mm (Maybe.from @(minutes dt) 0)
ss (Maybe.from @(seconds dt) 0)]
(-
- (+ (* days DAY) (+ (* hh HOUR) (+ (* mm MINUTE) ss)))
- (Long.to-int @(Timezone.delta &tz)))))
+ (+
+ (* (Long.from-int days) (Long.from-int DAY))
+ (Long.from-int (+ (* hh HOUR) (+ (* mm MINUTE) ss))))
+ @(Timezone.delta &tz))))
(doc from-unix-timestamp "returns the `Datetime` equivalent to the UNIX
timestamp `ts`, i.e. the number of seconds elapsed since the 1st of January,
1970.")
(defn from-unix-timestamp [ts]
- (let-do [days (/ ts DAY)
- secs (- ts (* days DAY))]
+ (let-do [day-secs (Long.from-int DAY)
+ days (Long.to-int (/ ts day-secs))
+ secs (Long.to-int (- ts (* (Long.from-int days) day-secs)))]
; / and mod truncate toward zero, so a negative ts can leave secs
; negative; borrow a day so the time-of-day stays in 0..86399
(when-do (< secs 0) (set! days (Int.dec days)) (set! secs (+ secs DAY)))