| 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 | #include <time.h> |
| 7 | // #include <sysinfoapi.h> |
| 8 | |
| 9 | pub struct C.tm { |
| 10 | pub mut: |
| 11 | tm_year int |
| 12 | tm_mon int |
| 13 | tm_mday int |
| 14 | tm_hour int |
| 15 | tm_min int |
| 16 | tm_sec int |
| 17 | } |
| 18 | |
| 19 | pub struct C._FILETIME { |
| 20 | pub mut: |
| 21 | dwLowDateTime u32 |
| 22 | dwHighDateTime u32 |
| 23 | } |
| 24 | |
| 25 | struct SystemTime { |
| 26 | year u16 |
| 27 | month u16 |
| 28 | day_of_week u16 |
| 29 | day u16 |
| 30 | hour u16 |
| 31 | minute u16 |
| 32 | second u16 |
| 33 | millisecond u16 |
| 34 | } |
| 35 | |
| 36 | fn C.GetSystemTimeAsFileTime(lpSystemTimeAsFileTime &C._FILETIME) |
| 37 | |
| 38 | fn C.FileTimeToSystemTime(lpFileTime &C._FILETIME, lpSystemTime &SystemTime) |
| 39 | |
| 40 | fn C.SystemTimeToTzSpecificLocalTime(lpTimeZoneInformation &C.TIME_ZONE_INFORMATION, lpUniversalTime &SystemTime, |
| 41 | lpLocalTime &SystemTime) |
| 42 | |
| 43 | fn C.localtime_s(t &C.time_t, tm &C.tm) |
| 44 | |
| 45 | fn C.timespec_get(t &C.timespec, base i32) i32 |
| 46 | |
| 47 | // start_time and time_coefficients are needed on Windows because of potential overflows |
| 48 | const start_time = init_win_time_start() |
| 49 | const time_coefficients = init_win_time_coefficients() |
| 50 | const start_local_time = local_as_unix_time() |
| 51 | |
| 52 | // in most systems, these are __quad_t, which is an i64 |
| 53 | pub struct C.timespec { |
| 54 | pub: |
| 55 | tv_sec i64 |
| 56 | tv_nsec i64 |
| 57 | } |
| 58 | |
| 59 | fn C.QueryPerformanceCounter(&u64) C.BOOL |
| 60 | |
| 61 | fn C.QueryPerformanceFrequency(&u64) C.BOOL |
| 62 | |
| 63 | fn make_unix_time(t C.tm) i64 { |
| 64 | return portable_timegm(&t) |
| 65 | } |
| 66 | |
| 67 | struct WinTimeCoefficients { |
| 68 | numer u64 |
| 69 | denom u64 |
| 70 | } |
| 71 | |
| 72 | fn init_win_time_coefficients() WinTimeCoefficients { |
| 73 | mut f := u64(0) |
| 74 | C.QueryPerformanceFrequency(voidptr(&f)) |
| 75 | mut nom := u64(1000000000) |
| 76 | for f % 10 == 0 { |
| 77 | f = f / 10 |
| 78 | nom = nom / 10 |
| 79 | } |
| 80 | res := WinTimeCoefficients{ |
| 81 | numer: nom |
| 82 | denom: f |
| 83 | } |
| 84 | return res |
| 85 | } |
| 86 | |
| 87 | fn init_win_time_start() u64 { |
| 88 | s := u64(0) |
| 89 | C.QueryPerformanceCounter(voidptr(&s)) |
| 90 | return s |
| 91 | } |
| 92 | |
| 93 | // sys_mono_now returns a *monotonically increasing time*, NOT a time adjusted for daylight savings, location etc. |
| 94 | pub fn sys_mono_now() u64 { |
| 95 | tm := u64(0) |
| 96 | C.QueryPerformanceCounter(voidptr(&tm)) // XP or later never fail |
| 97 | return (tm - start_time) * time_coefficients.numer / time_coefficients.denom |
| 98 | } |
| 99 | |
| 100 | // Note: vpc_now is used by `v -profile` . |
| 101 | // It should NOT call *any other v function*, just C functions and casts. |
| 102 | @[inline] |
| 103 | fn vpc_now() u64 { |
| 104 | tm := u64(0) |
| 105 | C.QueryPerformanceCounter(voidptr(&tm)) |
| 106 | return tm |
| 107 | } |
| 108 | |
| 109 | // local_as_unix_time returns the current local time as unix time |
| 110 | fn local_as_unix_time() i64 { |
| 111 | t := C.time(0) |
| 112 | tm := C.localtime(&t) |
| 113 | return make_unix_time(*tm) |
| 114 | } |
| 115 | |
| 116 | // local - return the time `t`, converted to the currently active local timezone. |
| 117 | pub fn (t Time) local() Time { |
| 118 | if t.is_local { |
| 119 | return t |
| 120 | } |
| 121 | st_utc := SystemTime{ |
| 122 | year: u16(t.year) |
| 123 | month: u16(t.month) |
| 124 | day: u16(t.day) |
| 125 | hour: u16(t.hour) |
| 126 | minute: u16(t.minute) |
| 127 | second: u16(t.second) |
| 128 | millisecond: u16(t.nanosecond / 1_000_000) |
| 129 | } |
| 130 | st_local := SystemTime{} |
| 131 | C.SystemTimeToTzSpecificLocalTime(unsafe { nil }, voidptr(&st_utc), voidptr(&st_local)) |
| 132 | t_local := Time{ |
| 133 | year: st_local.year |
| 134 | month: st_local.month |
| 135 | day: st_local.day |
| 136 | hour: st_local.hour |
| 137 | minute: st_local.minute |
| 138 | second: st_local.second // These are the same |
| 139 | nanosecond: int(st_local.millisecond) * 1_000_000 |
| 140 | unix: st_local.unix() |
| 141 | } |
| 142 | return t_local |
| 143 | } |
| 144 | |
| 145 | // win_now calculates current time using winapi to get higher resolution on windows |
| 146 | // GetSystemTimeAsFileTime is used and converted to local time. It can resolve time |
| 147 | // down to millisecond. Other more precise methods can be implemented in the future |
| 148 | fn win_now() Time { |
| 149 | ft_utc := C._FILETIME{} |
| 150 | C.GetSystemTimeAsFileTime(&ft_utc) |
| 151 | st_utc := SystemTime{} |
| 152 | C.FileTimeToSystemTime(&ft_utc, voidptr(&st_utc)) |
| 153 | st_local := SystemTime{} |
| 154 | C.SystemTimeToTzSpecificLocalTime(unsafe { nil }, voidptr(&st_utc), voidptr(&st_local)) |
| 155 | t := Time{ |
| 156 | year: st_local.year |
| 157 | month: st_local.month |
| 158 | day: st_local.day |
| 159 | hour: st_local.hour |
| 160 | minute: st_local.minute |
| 161 | second: st_local.second |
| 162 | nanosecond: int(st_local.millisecond) * 1_000_000 |
| 163 | unix: st_local.unix() |
| 164 | is_local: true |
| 165 | } |
| 166 | return t |
| 167 | } |
| 168 | |
| 169 | // win_utc calculates current time using winapi to get higher resolution on windows |
| 170 | // GetSystemTimeAsFileTime is used. It can resolve time down to millisecond |
| 171 | // other more precise methods can be implemented in the future |
| 172 | fn win_utc() Time { |
| 173 | ft_utc := C._FILETIME{} |
| 174 | C.GetSystemTimeAsFileTime(&ft_utc) |
| 175 | st_utc := SystemTime{} |
| 176 | C.FileTimeToSystemTime(&ft_utc, voidptr(&st_utc)) |
| 177 | t := Time{ |
| 178 | year: st_utc.year |
| 179 | month: st_utc.month |
| 180 | day: st_utc.day |
| 181 | hour: st_utc.hour |
| 182 | minute: st_utc.minute |
| 183 | second: st_utc.second |
| 184 | nanosecond: int(st_utc.millisecond) * 1_000_000 |
| 185 | unix: st_utc.unix() |
| 186 | is_local: false |
| 187 | } |
| 188 | return t |
| 189 | } |
| 190 | |
| 191 | // unix returns Unix time. |
| 192 | fn (st SystemTime) unix() i64 { |
| 193 | tt := C.tm{ |
| 194 | tm_sec: st.second |
| 195 | tm_min: st.minute |
| 196 | tm_hour: st.hour |
| 197 | tm_mday: st.day |
| 198 | tm_mon: st.month - 1 |
| 199 | tm_year: st.year - 1900 |
| 200 | } |
| 201 | return make_unix_time(tt) |
| 202 | } |
| 203 | |
| 204 | // dummy to compile with all compilers |
| 205 | fn darwin_now() Time { |
| 206 | return Time{} |
| 207 | } |
| 208 | |
| 209 | // dummy to compile with all compilers |
| 210 | fn linux_now() Time { |
| 211 | return Time{} |
| 212 | } |
| 213 | |
| 214 | // dummy to compile with all compilers |
| 215 | fn solaris_now() Time { |
| 216 | return Time{} |
| 217 | } |
| 218 | |
| 219 | // dummy to compile with all compilers |
| 220 | fn darwin_utc() Time { |
| 221 | return Time{} |
| 222 | } |
| 223 | |
| 224 | // dummy to compile with all compilers |
| 225 | fn linux_utc() Time { |
| 226 | return Time{} |
| 227 | } |
| 228 | |
| 229 | // dummy to compile with all compilers |
| 230 | fn solaris_utc() Time { |
| 231 | return Time{} |
| 232 | } |
| 233 | |
| 234 | // sleep makes the calling thread sleep for a given duration (in nanoseconds). |
| 235 | pub fn sleep(duration Duration) { |
| 236 | C.Sleep(int(duration / millisecond)) |
| 237 | } |
| 238 | |