| 1 | // Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | module time |
| 5 | |
| 6 | #flag darwin -I@VEXEROOT/thirdparty/legacy/include/LegacySupport |
| 7 | #include <time.h> |
| 8 | #include <errno.h> |
| 9 | |
| 10 | pub struct C.tm { |
| 11 | pub mut: |
| 12 | tm_sec int |
| 13 | tm_min int |
| 14 | tm_hour int |
| 15 | tm_mday int |
| 16 | tm_mon int |
| 17 | tm_year int |
| 18 | tm_wday int |
| 19 | tm_yday int |
| 20 | tm_isdst int |
| 21 | tm_gmtoff int |
| 22 | } |
| 23 | |
| 24 | fn C.timegm(&C.tm) C.time_t |
| 25 | |
| 26 | // preferring localtime_r over the localtime because |
| 27 | // from docs localtime_r is thread safe, |
| 28 | fn C.localtime_r(t &C.time_t, tm &C.tm) |
| 29 | |
| 30 | fn make_unix_time(t C.tm) i64 { |
| 31 | return unsafe { i64(C.timegm(&t)) } |
| 32 | } |
| 33 | |
| 34 | // localtime_r already resolves the local UTC offset, so callers can reuse it. |
| 35 | @[inline] |
| 36 | fn convert_ctime_with_unix(t C.tm, nanosecond int, unix i64) Time { |
| 37 | return Time{ |
| 38 | year: t.tm_year + 1900 |
| 39 | month: t.tm_mon + 1 |
| 40 | day: t.tm_mday |
| 41 | hour: t.tm_hour |
| 42 | minute: t.tm_min |
| 43 | second: t.tm_sec |
| 44 | nanosecond: nanosecond |
| 45 | unix: unix |
| 46 | is_local: true |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | // local returns t with the location set to local time. |
| 51 | pub fn (t Time) local() Time { |
| 52 | if t.is_local { |
| 53 | return t |
| 54 | } |
| 55 | loc_tm := C.tm{} |
| 56 | t_ := t.unix() |
| 57 | C.localtime_r(voidptr(&t_), &loc_tm) |
| 58 | return convert_ctime_with_unix(loc_tm, t.nanosecond, t_ + i64(loc_tm.tm_gmtoff)) |
| 59 | } |
| 60 | |
| 61 | // in most systems, these are __quad_t, which is an i64 |
| 62 | pub struct C.timespec { |
| 63 | pub mut: |
| 64 | tv_sec i64 |
| 65 | tv_nsec i64 |
| 66 | } |
| 67 | |
| 68 | // the first arg is defined in include/bits/types.h as `__S32_TYPE`, which is `int` |
| 69 | fn C.clock_gettime(i32, &C.timespec) i32 |
| 70 | |
| 71 | fn C.nanosleep(req &C.timespec, rem &C.timespec) i32 |
| 72 | |
| 73 | // sys_mono_now returns a *monotonically increasing time*, NOT a time adjusted for daylight savings, location etc. |
| 74 | pub fn sys_mono_now() u64 { |
| 75 | $if macos { |
| 76 | return sys_mono_now_darwin() |
| 77 | } $else { |
| 78 | ts := C.timespec{} |
| 79 | C.clock_gettime(C.CLOCK_MONOTONIC, &ts) |
| 80 | return u64(ts.tv_sec) * 1_000_000_000 + u64(ts.tv_nsec) |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | // Note: vpc_now is used by `v -profile` . |
| 85 | // It should NOT call *any other v function*, just C functions and casts. |
| 86 | @[inline] |
| 87 | fn vpc_now() u64 { |
| 88 | ts := C.timespec{} |
| 89 | C.clock_gettime(C.CLOCK_MONOTONIC, &ts) |
| 90 | return u64(ts.tv_sec) * 1_000_000_000 + u64(ts.tv_nsec) |
| 91 | } |
| 92 | |
| 93 | // The linux_* functions are placed here, since they're used on Android as well |
| 94 | // TODO: should `$if linux {}` be parsed on Android as well? (Android runs under the Linux kernel) |
| 95 | // linux_now returns the local time with high precision for most os:es |
| 96 | // this should be implemented properly with support for leap seconds. |
| 97 | // It uses the realtime clock to get and converts it to local time |
| 98 | fn linux_now() Time { |
| 99 | // get the high precision time as UTC realtime clock |
| 100 | // and use the nanoseconds part |
| 101 | mut ts := C.timespec{} |
| 102 | C.clock_gettime(C.CLOCK_REALTIME, &ts) |
| 103 | loc_tm := C.tm{} |
| 104 | C.localtime_r(voidptr(&ts.tv_sec), &loc_tm) |
| 105 | return convert_ctime_with_unix(loc_tm, int(ts.tv_nsec), i64(ts.tv_sec) + i64(loc_tm.tm_gmtoff)) |
| 106 | } |
| 107 | |
| 108 | fn linux_utc() Time { |
| 109 | // get the high precision time as UTC realtime clock |
| 110 | // and use the nanoseconds part |
| 111 | mut ts := C.timespec{} |
| 112 | C.clock_gettime(C.CLOCK_REALTIME, &ts) |
| 113 | return unix_nanosecond(i64(ts.tv_sec), int(ts.tv_nsec)) |
| 114 | } |
| 115 | |
| 116 | // dummy to compile with all compilers |
| 117 | fn win_now() Time { |
| 118 | return Time{} |
| 119 | } |
| 120 | |
| 121 | // dummy to compile with all compilers |
| 122 | fn win_utc() Time { |
| 123 | return Time{} |
| 124 | } |
| 125 | |
| 126 | // return absolute timespec for now()+d |
| 127 | pub fn (d Duration) timespec() C.timespec { |
| 128 | mut ts := C.timespec{} |
| 129 | C.clock_gettime(C.CLOCK_REALTIME, &ts) |
| 130 | d_sec := d / second |
| 131 | d_nsec := d % second |
| 132 | ts.tv_sec += d_sec |
| 133 | ts.tv_nsec += d_nsec |
| 134 | if ts.tv_nsec > i64(second) { |
| 135 | ts.tv_nsec -= i64(second) |
| 136 | ts.tv_sec++ |
| 137 | } |
| 138 | return ts |
| 139 | } |
| 140 | |
| 141 | // sleep suspends the execution of the calling thread for a given duration (in nanoseconds). |
| 142 | pub fn sleep(duration Duration) { |
| 143 | mut req := C.timespec{duration / second, duration % second} |
| 144 | rem := C.timespec{} |
| 145 | for C.nanosleep(&req, &rem) < 0 { |
| 146 | if C.errno == C.EINTR { |
| 147 | // Interrupted by a signal handler |
| 148 | req = rem |
| 149 | } else { |
| 150 | break |
| 151 | } |
| 152 | } |
| 153 | } |
| 154 | |