mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 23:58:50 +00:00
Add WiP datetime package and tests.
A new package `core:time/datetime` has been added which can represent moments much further in the past and future than `core:time`. It is based on *the* reference work on the subject, Calendrical Calculations Ultimate Edition, Reingold & Dershowitz. More procedures will be added to it in the future, to for example calculate the 3rd Thursday in March to figure out holidays. The package has been tested for more than a year and can handle dates 25 quadrillion years into the past and future with 64-bit day ordinals, or 5 million with 32-bit ones. This also fixes a longstanding bug where converting between YYYY-MM:DD hh:mm:ss and `time.Time` and back could result in a mismatch. RFC 3339 timestamps can now also be parsed using the `core:time` package.
This commit is contained in:
+26
-54
@@ -1,6 +1,7 @@
|
||||
package time
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:intrinsics"
|
||||
import dt "core:time/datetime"
|
||||
|
||||
Duration :: distinct i64
|
||||
|
||||
@@ -299,10 +300,6 @@ _time_abs :: proc "contextless" (t: Time) -> u64 {
|
||||
|
||||
@(private)
|
||||
_abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
|
||||
_is_leap_year :: proc "contextless" (year: int) -> bool {
|
||||
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
||||
}
|
||||
|
||||
d := abs / SECONDS_PER_DAY
|
||||
|
||||
// 400 year cycles
|
||||
@@ -335,7 +332,7 @@ _abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Mon
|
||||
|
||||
day = yday
|
||||
|
||||
if _is_leap_year(year) {
|
||||
if is_leap_year(year) {
|
||||
switch {
|
||||
case day > 31+29-1:
|
||||
day -= 1
|
||||
@@ -360,57 +357,32 @@ _abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Mon
|
||||
return
|
||||
}
|
||||
|
||||
datetime_to_time :: proc "contextless" (year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) {
|
||||
divmod :: proc "contextless" (year: int, divisor: int) -> (div: int, mod: int) {
|
||||
if divisor <= 0 {
|
||||
intrinsics.debug_trap()
|
||||
}
|
||||
div = int(year / divisor)
|
||||
mod = year % divisor
|
||||
return
|
||||
}
|
||||
_is_leap_year :: proc "contextless" (year: int) -> bool {
|
||||
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
||||
components_to_time :: proc "contextless" (year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) {
|
||||
this_date := dt.DateTime{date={year, month, day}, time={hour, minute, second, nsec}}
|
||||
return compound_to_time(this_date)
|
||||
}
|
||||
|
||||
compound_to_time :: proc "contextless" (datetime: dt.DateTime) -> (t: Time, ok: bool) {
|
||||
unix_epoch := dt.DateTime{{1970, 1, 1}, {0, 0, 0, 0}}
|
||||
delta, err := dt.sub(datetime, unix_epoch)
|
||||
ok = err == .None
|
||||
|
||||
seconds := delta.days * 86_400 + delta.seconds
|
||||
nanoseconds := i128(seconds) * 1e9 + i128(delta.nanos)
|
||||
|
||||
// Can this moment be represented in i64 worth of nanoseconds?
|
||||
// min(Time): 1677-09-21 00:12:44.145224192 +0000 UTC
|
||||
// max(Time): 2262-04-11 23:47:16.854775807 +0000 UTC
|
||||
if nanoseconds < i128(min(i64)) || nanoseconds > i128(max(i64)) {
|
||||
return {}, false
|
||||
}
|
||||
return Time{_nsec=i64(nanoseconds)}, true
|
||||
}
|
||||
|
||||
datetime_to_time :: proc{components_to_time, compound_to_time}
|
||||
|
||||
ok = true
|
||||
|
||||
_y := year - 1970
|
||||
_m := month - 1
|
||||
_d := day - 1
|
||||
|
||||
if month < 1 || month > 12 {
|
||||
_m %= 12; ok = false
|
||||
}
|
||||
if day < 1 || day > 31 {
|
||||
_d %= 31; ok = false
|
||||
}
|
||||
|
||||
s := i64(0)
|
||||
div, mod := divmod(_y, 400)
|
||||
days := div * DAYS_PER_400_YEARS
|
||||
|
||||
div, mod = divmod(mod, 100)
|
||||
days += div * DAYS_PER_100_YEARS
|
||||
|
||||
div, mod = divmod(mod, 4)
|
||||
days += (div * DAYS_PER_4_YEARS) + (mod * 365)
|
||||
|
||||
days += int(days_before[_m]) + _d
|
||||
|
||||
if _is_leap_year(year) && _m >= 2 {
|
||||
days += 1
|
||||
}
|
||||
|
||||
s += i64(days) * SECONDS_PER_DAY
|
||||
s += i64(hour) * SECONDS_PER_HOUR
|
||||
s += i64(minute) * SECONDS_PER_MINUTE
|
||||
s += i64(second)
|
||||
|
||||
t._nsec = (s * 1e9) + i64(nsec)
|
||||
|
||||
return
|
||||
is_leap_year :: proc "contextless" (year: int) -> (leap: bool) {
|
||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
|
||||
}
|
||||
|
||||
days_before := [?]i32{
|
||||
|
||||
Reference in New Issue
Block a user