123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978 |
- package time
- import "base:intrinsics"
- import dt "core:time/datetime"
- /*
- Type representing duration, with nanosecond precision.
- This is the regular Unix timestamp, scaled to nanosecond precision.
- */
- Duration :: distinct i64
- /*
- The duration equal to one nanosecond (1e-9 seconds).
- */
- Nanosecond :: Duration(1)
- /*
- The duration equal to one microsecond (1e-6 seconds).
- */
- Microsecond :: 1000 * Nanosecond
- /*
- The duration equal to one millisecond (1e-3 seconds).
- */
- Millisecond :: 1000 * Microsecond
- /*
- The duration equal to one second.
- */
- Second :: 1000 * Millisecond
- /*
- The duration equal to one minute (60 seconds).
- */
- Minute :: 60 * Second
- /*
- The duration equal to one hour (3600 seconds).
- */
- Hour :: 60 * Minute
- /*
- Minimum representable duration.
- */
- MIN_DURATION :: Duration(-1 << 63)
- /*
- Maximum representable duration.
- */
- MAX_DURATION :: Duration(1<<63 - 1)
- /*
- Value specifying whether the time procedures are supported by the current
- platform.
- */
- IS_SUPPORTED :: _IS_SUPPORTED
- /*
- Specifies time since the UNIX epoch, with nanosecond precision.
- Capable of representing any time within the following range:
- - `min: 1677-09-21 00:12:44.145224192 +0000 UTC`
- - `max: 2262-04-11 23:47:16.854775807 +0000 UTC`
- */
- Time :: struct {
- _nsec: i64, // Measured in UNIX nanonseconds
- }
- /*
- Type representing a month.
- */
- Month :: enum int {
- January = 1,
- February,
- March,
- April,
- May,
- June,
- July,
- August,
- September,
- October,
- November,
- December,
- }
- /*
- Type representing a weekday.
- */
- Weekday :: enum int {
- Sunday = 0,
- Monday,
- Tuesday,
- Wednesday,
- Thursday,
- Friday,
- Saturday,
- }
- /*
- Type representing a stopwatch.
- The stopwatch is used for measuring the total time in multiple "runs". When the
- stopwatch is started, it starts counting time. When the stopwatch is stopped,
- the difference in time between the last start and the stop is added to the
- total. When the stopwatch resets, the total is reset.
- */
- Stopwatch :: struct {
- running: bool,
- _start_time: Tick,
- _accumulation: Duration,
- }
- /*
- Obtain the current time.
- */
- now :: proc "contextless" () -> Time {
- return _now()
- }
- /*
- Sleep for the specified duration.
- */
- sleep :: proc "contextless" (d: Duration) {
- _sleep(d)
- }
- /*
- Start the stopwatch.
- */
- stopwatch_start :: proc "contextless" (stopwatch: ^Stopwatch) {
- if !stopwatch.running {
- stopwatch._start_time = tick_now()
- stopwatch.running = true
- }
- }
- /*
- Stop the stopwatch.
- */
- stopwatch_stop :: proc "contextless" (stopwatch: ^Stopwatch) {
- if stopwatch.running {
- stopwatch._accumulation += tick_diff(stopwatch._start_time, tick_now())
- stopwatch.running = false
- }
- }
- /*
- Reset the stopwatch.
- */
- stopwatch_reset :: proc "contextless" (stopwatch: ^Stopwatch) {
- stopwatch._accumulation = {}
- stopwatch.running = false
- }
- /*
- Obtain the total time, counted by the stopwatch.
- This procedure obtains the total time, counted by the stopwatch. If the stopwatch
- isn't stopped at the time of calling this procedure, the time between the last
- start and the current time is also accounted for.
- */
- stopwatch_duration :: proc "contextless" (stopwatch: Stopwatch) -> Duration {
- if !stopwatch.running {
- return stopwatch._accumulation
- }
- return stopwatch._accumulation + tick_diff(stopwatch._start_time, tick_now())
- }
- /*
- Calculate the duration elapsed between two times.
- */
- diff :: proc "contextless" (start, end: Time) -> Duration {
- d := end._nsec - start._nsec
- return Duration(d)
- }
- /*
- Calculate the duration elapsed since a specific time.
- */
- since :: proc "contextless" (start: Time) -> Duration {
- return diff(start, now())
- }
- /*
- Obtain the number of nanoseconds in a duration.
- */
- duration_nanoseconds :: proc "contextless" (d: Duration) -> i64 {
- return i64(d)
- }
- /*
- Obtain the number of microseconds in a duration.
- */
- duration_microseconds :: proc "contextless" (d: Duration) -> f64 {
- return duration_seconds(d) * 1e6
- }
- /*
- Obtain the number of milliseconds in a duration.
- */
- duration_milliseconds :: proc "contextless" (d: Duration) -> f64 {
- return duration_seconds(d) * 1e3
- }
- /*
- Obtain the number of seconds in a duration.
- */
- duration_seconds :: proc "contextless" (d: Duration) -> f64 {
- sec := d / Second
- nsec := d % Second
- return f64(sec) + f64(nsec)/1e9
- }
- /*
- Obtain the number of minutes in a duration.
- */
- duration_minutes :: proc "contextless" (d: Duration) -> f64 {
- min := d / Minute
- nsec := d % Minute
- return f64(min) + f64(nsec)/(60*1e9)
- }
- /*
- Obtain the number of hours in a duration.
- */
- duration_hours :: proc "contextless" (d: Duration) -> f64 {
- hour := d / Hour
- nsec := d % Hour
- return f64(hour) + f64(nsec)/(60*60*1e9)
- }
- /*
- Round a duration to a specific unit
- This procedure rounds the duration to a specific unit
- **Note**: Any duration can be supplied as a unit.
- Inputs:
- - d: The duration to round
- - m: The unit to round to
- Returns:
- - The duration `d`, rounded to the unit specified by `m`
- Example:
- time.duration_round(my_duration, time.Second)
- */
- duration_round :: proc "contextless" (d, m: Duration) -> Duration {
- _less_than_half :: #force_inline proc "contextless" (x, y: Duration) -> bool {
- return u64(x)+u64(x) < u64(y)
- }
- if m <= 0 {
- return d
- }
- r := d % m
- if d < 0 {
- r = -r
- if _less_than_half(r, m) {
- return d + r
- }
- if d1 := d-m+r; d1 < d {
- return d1
- }
- return MIN_DURATION
- }
- if _less_than_half(r, m) {
- return d - r
- }
- if d1 := d+m-r; d1 > d {
- return d1
- }
- return MAX_DURATION
- }
- /*
- Truncate the duration to the specified unit.
- This procedure truncates the duration `d` to the unit specified by `m`.
- **Note**: Any duration can be supplied as a unit.
- Inputs:
- - d: The duration to truncate.
- - m: The unit to truncate to.
- Returns:
- - The duration `d`, truncated to the unit specified by `m`.
- Example:
- time.duration_round(my_duration, time.Second)
- */
- duration_truncate :: proc "contextless" (d, m: Duration) -> Duration {
- return d if m <= 0 else d - d%m
- }
- /*
- Parse time into date components.
- */
- date :: proc "contextless" (t: Time) -> (year: int, month: Month, day: int) {
- year, month, day, _ = _abs_date(_time_abs(t), true)
- return
- }
- /*
- Obtain the year of the date specified by time.
- */
- year :: proc "contextless" (t: Time) -> (year: int) {
- year, _, _, _ = _date(t, true)
- return
- }
- /*
- Obtain the month of the date specified by time.
- */
- month :: proc "contextless" (t: Time) -> (month: Month) {
- _, month, _, _ = _date(t, true)
- return
- }
- /*
- Obtain the day of the date specified by time.
- */
- day :: proc "contextless" (t: Time) -> (day: int) {
- _, _, day, _ = _date(t, true)
- return
- }
- /*
- Obtain the week day of the date specified by time.
- */
- weekday :: proc "contextless" (t: Time) -> (weekday: Weekday) {
- abs := _time_abs(t)
- sec := (abs + u64(Weekday.Monday) * SECONDS_PER_DAY) % SECONDS_PER_WEEK
- return Weekday(int(sec) / SECONDS_PER_DAY)
- }
- /*
- Obtain the time components from a time, a duration or a stopwatch's total.
- */
- clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
- /*
- Obtain the time components from a time.
- */
- clock_from_time :: proc "contextless" (t: Time) -> (hour, min, sec: int) {
- return clock_from_seconds(_time_abs(t))
- }
- /*
- Obtain the time components from a duration.
- */
- clock_from_duration :: proc "contextless" (d: Duration) -> (hour, min, sec: int) {
- return clock_from_seconds(u64(d/1e9))
- }
- /*
- Obtain the time components from a stopwatch's total.
- */
- clock_from_stopwatch :: proc "contextless" (s: Stopwatch) -> (hour, min, sec: int) {
- return clock_from_duration(stopwatch_duration(s))
- }
- /*
- Obtain the time components from the number of seconds.
- */
- clock_from_seconds :: proc "contextless" (nsec: u64) -> (hour, min, sec: int) {
- sec = int(nsec % SECONDS_PER_DAY)
- hour = sec / SECONDS_PER_HOUR
- sec -= hour * SECONDS_PER_HOUR
- min = sec / SECONDS_PER_MINUTE
- sec -= min * SECONDS_PER_MINUTE
- return
- }
- MIN_HMS_LEN :: 8
- MIN_HMS_12_LEN :: 11
- MIN_YYYY_DATE_LEN :: 10
- MIN_YY_DATE_LEN :: 8
- /*
- Formats a `Time` as a 24-hour `hh:mm:ss` string.
- **Does not allocate**
- Inputs:
- - t: The Time to format.
- - buf: The backing buffer to use.
- Returns:
- - res: The formatted string, backed by buf
- Example:
- buf: [MIN_HMS_LEN]u8
- now := time.now()
- fmt.println(time.to_string_hms(now, buf[:]))
- */
- time_to_string_hms :: proc(t: Time, buf: []u8) -> (res: string) #no_bounds_check {
- assert(len(buf) >= MIN_HMS_LEN)
- h, m, s := clock(t)
- buf[7] = '0' + u8(s % 10); s /= 10
- buf[6] = '0' + u8(s)
- buf[5] = ':'
- buf[4] = '0' + u8(m % 10); m /= 10
- buf[3] = '0' + u8(m)
- buf[2] = ':'
- buf[1] = '0' + u8(h % 10); h /= 10
- buf[0] = '0' + u8(h)
- return string(buf[:MIN_HMS_LEN])
- }
- /*
- Formats a `Duration` as a 24-hour `hh:mm:ss` string.
- **Does not allocate**
- Inputs:
- - d: The Duration to format.
- - buf: The backing buffer to use.
- Returns:
- - res: The formatted string, backed by buf
- Example:
- buf: [MIN_HMS_LEN]u8
- d := time.since(earlier)
- fmt.println(time.to_string_hms(now, buf[:]))
- */
- duration_to_string_hms :: proc(d: Duration, buf: []u8) -> (res: string) #no_bounds_check {
- return time_to_string_hms(Time{_nsec=i64(d)}, buf)
- }
- to_string_hms :: proc{time_to_string_hms, duration_to_string_hms}
- /*
- Formats a `Time` as a 12-hour `hh:mm:ss pm` string
- **Does not allocate**
- Inputs:
- - t: The Time to format
- - buf: The backing buffer to use
- - ampm: An optional pair of am/pm strings to use in place of the default
- Returns:
- - res: The formatted string, backed by buf
- Example:
- buf: [64]u8
- now := time.now()
- fmt.println(time.to_string_hms_12(now, buf[:]))
- fmt.println(time.to_string_hms_12(now, buf[:], {"㏂", "㏘"}))
- */
- to_string_hms_12 :: proc(t: Time, buf: []u8, ampm: [2]string = {" am", " pm"}) -> (res: string) #no_bounds_check {
- assert(len(buf) >= MIN_HMS_LEN + max(len(ampm[0]), len(ampm[1])))
- h, m, s := clock(t)
- _h := h % 12
- buf[7] = '0' + u8(s % 10); s /= 10
- buf[6] = '0' + u8(s)
- buf[5] = ':'
- buf[4] = '0' + u8(m % 10); m /= 10
- buf[3] = '0' + u8(m)
- buf[2] = ':'
- buf[1] = '0' + u8(_h% 10); _h /= 10
- buf[0] = '0' + u8(_h)
- if h < 13 {
- copy(buf[8:], ampm[0])
- return string(buf[:MIN_HMS_LEN+len(ampm[0])])
- } else {
- copy(buf[8:], ampm[1])
- return string(buf[:MIN_HMS_LEN+len(ampm[1])])
- }
- }
- /*
- Formats a Time as a yyyy-mm-dd date string.
- Inputs:
- - t: The Time to format.
- - buf: The backing buffer to use.
- Returns:
- - res: The formatted string, backed by `buf`.
- Example:
- buf: [MIN_YYYY_DATE_LEN]u8
- now := time.now()
- fmt.println(time.to_string_yyyy_mm_dd(now, buf[:]))
- */
- to_string_yyyy_mm_dd :: proc(t: Time, buf: []u8) -> (res: string) #no_bounds_check {
- assert(len(buf) >= MIN_YYYY_DATE_LEN)
- y, _m, d := date(t)
- m := u8(_m)
- buf[9] = '0' + u8(d % 10); d /= 10
- buf[8] = '0' + u8(d % 10)
- buf[7] = '-'
- buf[6] = '0' + u8(m % 10); m /= 10
- buf[5] = '0' + u8(m % 10)
- buf[4] = '-'
- buf[3] = '0' + u8(y % 10); y /= 10
- buf[2] = '0' + u8(y % 10); y /= 10
- buf[1] = '0' + u8(y % 10); y /= 10
- buf[0] = '0' + u8(y)
- return string(buf[:MIN_YYYY_DATE_LEN])
- }
- /*
- Formats a Time as a yy-mm-dd date string.
- Inputs:
- - t: The Time to format.
- - buf: The backing buffer to use.
- Returns:
- - res: The formatted string, backed by `buf`.
- Example:
- buf: [MIN_YY_DATE_LEN]u8
- now := time.now()
- fmt.println(time.to_string_yy_mm_dd(now, buf[:]))
- */
- to_string_yy_mm_dd :: proc(t: Time, buf: []u8) -> (res: string) #no_bounds_check {
- assert(len(buf) >= MIN_YY_DATE_LEN)
- y, _m, d := date(t)
- y %= 100; m := u8(_m)
- buf[7] = '0' + u8(d % 10); d /= 10
- buf[6] = '0' + u8(d % 10)
- buf[5] = '-'
- buf[4] = '0' + u8(m % 10); m /= 10
- buf[3] = '0' + u8(m % 10)
- buf[2] = '-'
- buf[1] = '0' + u8(y % 10); y /= 10
- buf[0] = '0' + u8(y)
- return string(buf[:MIN_YY_DATE_LEN])
- }
- /*
- Formats a Time as a dd-mm-yyyy date string.
- Inputs:
- - t: The Time to format.
- - buf: The backing buffer to use.
- Returns:
- - res: The formatted string, backed by `buf`.
- Example:
- buf: [MIN_YYYY_DATE_LEN]u8
- now := time.now()
- fmt.println(time.to_string_dd_mm_yyyy(now, buf[:]))
- */
- to_string_dd_mm_yyyy :: proc(t: Time, buf: []u8) -> (res: string) #no_bounds_check {
- assert(len(buf) >= MIN_YYYY_DATE_LEN)
- y, _m, d := date(t)
- m := u8(_m)
- buf[9] = '0' + u8(y % 10); y /= 10
- buf[8] = '0' + u8(y % 10); y /= 10
- buf[7] = '0' + u8(y % 10); y /= 10
- buf[6] = '0' + u8(y)
- buf[5] = '-'
- buf[4] = '0' + u8(m % 10); m /= 10
- buf[3] = '0' + u8(m % 10)
- buf[2] = '-'
- buf[1] = '0' + u8(d % 10); d /= 10
- buf[0] = '0' + u8(d % 10)
- return string(buf[:MIN_YYYY_DATE_LEN])
- }
- /*
- Formats a Time as a dd-mm-yy date string.
- Inputs:
- - t: The Time to format.
- - buf: The backing buffer to use.
- Returns:
- - res: The formatted string, backed by `buf`.
- Example:
- buf: [MIN_YY_DATE_LEN]u8
- now := time.now()
- fmt.println(time.to_string_dd_mm_yy(now, buf[:]))
- */
- to_string_dd_mm_yy :: proc(t: Time, buf: []u8) -> (res: string) #no_bounds_check {
- assert(len(buf) >= MIN_YY_DATE_LEN)
- y, _m, d := date(t)
- y %= 100; m := u8(_m)
- buf[7] = '0' + u8(y % 10); y /= 10
- buf[6] = '0' + u8(y)
- buf[5] = '-'
- buf[4] = '0' + u8(m % 10); m /= 10
- buf[3] = '0' + u8(m % 10)
- buf[2] = '-'
- buf[1] = '0' + u8(d % 10); d /= 10
- buf[0] = '0' + u8(d % 10)
- return string(buf[:MIN_YY_DATE_LEN])
- }
- /*
- Formats a Time as a mm-dd-yyyy date string.
- Inputs:
- - t: The Time to format.
- - buf: The backing buffer to use.
- Returns:
- - res: The formatted string, backed by `buf`.
- Example:
- buf: [MIN_YYYY_DATE_LEN]u8
- now := time.now()
- fmt.println(time.to_string_mm_dd_yyyy(now, buf[:]))
- */
- to_string_mm_dd_yyyy :: proc(t: Time, buf: []u8) -> (res: string) #no_bounds_check {
- assert(len(buf) >= MIN_YYYY_DATE_LEN)
- y, _m, d := date(t)
- m := u8(_m)
- buf[9] = '0' + u8(y % 10); y /= 10
- buf[8] = '0' + u8(y % 10); y /= 10
- buf[7] = '0' + u8(y % 10); y /= 10
- buf[6] = '0' + u8(y)
- buf[5] = '-'
- buf[4] = '0' + u8(d % 10); d /= 10
- buf[3] = '0' + u8(d % 10)
- buf[2] = '-'
- buf[1] = '0' + u8(m % 10); m /= 10
- buf[0] = '0' + u8(m % 10)
- return string(buf[:MIN_YYYY_DATE_LEN])
- }
- /*
- Formats a Time as a mm-dd-yy date string.
- Inputs:
- - t: The Time to format.
- - buf: The backing buffer to use.
- Returns:
- - res: The formatted string, backed by `buf`.
- Example:
- buf: [MIN_YY_DATE_LEN]u8
- now := time.now()
- fmt.println(time.to_string_mm_dd_yy(now, buf[:]))
- */
- to_string_mm_dd_yy :: proc(t: Time, buf: []u8) -> (res: string) #no_bounds_check {
- assert(len(buf) >= MIN_YY_DATE_LEN)
- y, _m, d := date(t)
- y %= 100; m := u8(_m)
- buf[7] = '0' + u8(y % 10); y /= 10
- buf[6] = '0' + u8(y)
- buf[5] = '-'
- buf[4] = '0' + u8(d % 10); d /= 10
- buf[3] = '0' + u8(d % 10)
- buf[2] = '-'
- buf[1] = '0' + u8(m % 10); m /= 10
- buf[0] = '0' + u8(m % 10)
- return string(buf[:MIN_YY_DATE_LEN])
- }
- /*
- Read the timestamp counter of the CPU.
- */
- read_cycle_counter :: proc "contextless" () -> u64 {
- return u64(intrinsics.read_cycle_counter())
- }
- /*
- Obtain time from unix seconds and unix nanoseconds.
- */
- unix :: proc "contextless" (sec: i64, nsec: i64) -> Time {
- sec, nsec := sec, nsec
- if nsec < 0 || nsec >= 1e9 {
- n := nsec / 1e9
- sec += n
- nsec -= n * 1e9
- if nsec < 0 {
- nsec += 1e9
- sec -= 1
- }
- }
- return Time{(sec*1e9 + nsec)}
- }
- /*
- Obtain time from unix nanoseconds.
- */
- from_nanoseconds :: #force_inline proc "contextless" (nsec: i64) -> Time {
- return Time{nsec}
- }
- /*
- Alias for `time_to_unix`.
- */
- to_unix_seconds :: time_to_unix
- /*
- Obtain the Unix timestamp in seconds from a Time.
- */
- time_to_unix :: proc "contextless" (t: Time) -> i64 {
- return t._nsec/1e9
- }
- /*
- Alias for `time_to_unix_nano`.
- */
- to_unix_nanoseconds :: time_to_unix_nano
- /*
- Obtain the Unix timestamp in nanoseconds from a Time.
- */
- time_to_unix_nano :: proc "contextless" (t: Time) -> i64 {
- return t._nsec
- }
- /*
- Add duration to a time.
- */
- time_add :: proc "contextless" (t: Time, d: Duration) -> Time {
- return Time{t._nsec + i64(d)}
- }
- /*
- Accurate sleep
- This procedure sleeps for the duration specified by `d`, very accurately.
- **Note**: Implementation borrowed from: [this source](https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/)
- **Note(linux)**: The accuracy is within around 4µs (microseconds), in the worst case.
- **Note(windows)**: The accuracy depends but is comparable with regular sleep in
- the worst case. To get the same kind of accuracy as on Linux, have your program
- call `windows.timeBeginPeriod(1)` to tell Windows to use a more accurate timer
- for your process. Additionally your program should call `windows.timeEndPeriod(1)`
- once you're done with `accurate_sleep`.
- */
- accurate_sleep :: proc "contextless" (d: Duration) {
- to_sleep, estimate, mean, m2, count: Duration
- to_sleep = d
- estimate = 5 * Millisecond
- mean = 5 * Millisecond
- count = 1
- for to_sleep > estimate {
- start := tick_now()
- sleep(1 * Millisecond)
- observed := tick_since(start)
- to_sleep -= observed
- count += 1
- delta := observed - mean
- mean += delta / count
- m2 += delta * (observed - mean)
- stddev := intrinsics.sqrt(f64(m2) / f64(count - 1))
- estimate = mean + Duration(stddev)
- }
- start := tick_now()
- for to_sleep > tick_since(start) {
- // prevent the spinlock from taking the thread hostage, still accurate enough
- _yield()
- // NOTE: it might be possible that it yields for too long, in that case it should spinlock freely for a while
- // TODO: needs actual testing done to check if that's the case
- }
- }
- ABSOLUTE_ZERO_YEAR :: i64(-292277022399) // Day is chosen so that 2001-01-01 is Monday in the calculations
- ABSOLUTE_TO_INTERNAL :: i64(-9223371966579724800) // i64((ABSOLUTE_ZERO_YEAR - 1) * 365.2425 * SECONDS_PER_DAY);
- INTERNAL_TO_ABSOLUTE :: -ABSOLUTE_TO_INTERNAL
- UNIX_TO_INTERNAL :: i64((1969*365 + 1969/4 - 1969/100 + 1969/400) * SECONDS_PER_DAY)
- INTERNAL_TO_UNIX :: -UNIX_TO_INTERNAL
- WALL_TO_INTERNAL :: i64((1884*365 + 1884/4 - 1884/100 + 1884/400) * SECONDS_PER_DAY)
- INTERNAL_TO_WALL :: -WALL_TO_INTERNAL
- UNIX_TO_ABSOLUTE :: UNIX_TO_INTERNAL + INTERNAL_TO_ABSOLUTE
- ABSOLUTE_TO_UNIX :: -UNIX_TO_ABSOLUTE
- @(private)
- _date :: proc "contextless" (t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
- year, month, day, yday = _abs_date(_time_abs(t), full)
- return
- }
- @(private)
- _time_abs :: proc "contextless" (t: Time) -> u64 {
- return u64(t._nsec/1e9 + UNIX_TO_ABSOLUTE)
- }
- @(private)
- _abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
- d := abs / SECONDS_PER_DAY
- // 400 year cycles
- n := d / DAYS_PER_400_YEARS
- y := 400 * n
- d -= DAYS_PER_400_YEARS * n
- // Cut-off 100 year cycles
- n = d / DAYS_PER_100_YEARS
- n -= n >> 2
- y += 100 * n
- d -= DAYS_PER_100_YEARS * n
- // Cut-off 4 year cycles
- n = d / DAYS_PER_4_YEARS
- y += 4 * n
- d -= DAYS_PER_4_YEARS * n
- n = d / 365
- n -= n >> 2
- y += n
- d -= 365 * n
- year = int(i64(y) + ABSOLUTE_ZERO_YEAR)
- yday = int(d)
- if !full {
- return
- }
- day = yday
- if is_leap_year(year) {
- switch {
- case day > 31+29-1:
- day -= 1
- case day == 31+29-1:
- month = .February
- day = 29
- return
- }
- }
- month = Month(day / 31)
- end := int(days_before[int(month)+1])
- begin: int
- if day >= end {
- (^int)(&month)^ += 1
- begin = end
- } else {
- begin = int(days_before[month])
- }
- (^int)(&month)^ += 1 // January is 1
- day = day - begin + 1
- return
- }
- /*
- Convert datetime components into time.
- This procedure calculates the time from datetime components supplied in the
- arguments to this procedure. If the datetime components don't represent a valid
- datetime, the function returns `false` in the second argument.
- */
- components_to_time :: proc "contextless" (#any_int year, #any_int month, #any_int day, #any_int hour, #any_int minute, #any_int second: i64, #any_int nsec := i64(0)) -> (t: Time, ok: bool) {
- this_date, err := dt.components_to_datetime(year, month, day, hour, minute, second, nsec)
- if err != .None {
- return
- }
- return compound_to_time(this_date)
- }
- /*
- Convert datetime into time.
- If the datetime represents a time outside of a valid range, `false` is returned
- as the second return value. See `Time` for the representable range.
- */
- 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
- }
- /*
- Convert datetime components into time.
- */
- datetime_to_time :: proc{components_to_time, compound_to_time}
- /*
- Check if a year is a leap year.
- */
- is_leap_year :: proc "contextless" (year: int) -> (leap: bool) {
- return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
- }
- /*
- Days before each month in a year, not counting the leap day on february 29th.
- */
- @(rodata)
- days_before := [?]i32{
- 0,
- 31,
- 31 + 28,
- 31 + 28 + 31,
- 31 + 28 + 31 + 30,
- 31 + 28 + 31 + 30 + 31,
- 31 + 28 + 31 + 30 + 31 + 30,
- 31 + 28 + 31 + 30 + 31 + 30 + 31,
- 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
- 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
- 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
- 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
- 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
- }
- /*
- Number of seconds in a minute (without leap seconds).
- */
- SECONDS_PER_MINUTE :: 60
- /*
- Number of seconds in an hour (without leap seconds).
- */
- SECONDS_PER_HOUR :: 60 * SECONDS_PER_MINUTE
- /*
- Number of seconds in a day (without leap seconds).
- */
- SECONDS_PER_DAY :: 24 * SECONDS_PER_HOUR
- /*
- Number of seconds in a week (without leap seconds).
- */
- SECONDS_PER_WEEK :: 7 * SECONDS_PER_DAY
- /*
- Days in 400 years, with leap days.
- */
- DAYS_PER_400_YEARS :: 365*400 + 97
- /*
- Days in 100 years, with leap days.
- */
- DAYS_PER_100_YEARS :: 365*100 + 24
- /*
- Days in 4 years, with leap days.
- */
- DAYS_PER_4_YEARS :: 365*4 + 1
|