v / vlib / time / time_nix.c.v
153 lines · 135 sloc · 3.95 KB · 5d979e313177cbe023e15a958a8dd7261f49402b
Raw
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.
4module time
5
6#flag darwin -I@VEXEROOT/thirdparty/legacy/include/LegacySupport
7#include <time.h>
8#include <errno.h>
9
10pub struct C.tm {
11pub 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
24fn C.timegm(&C.tm) C.time_t
25
26// preferring localtime_r over the localtime because
27// from docs localtime_r is thread safe,
28fn C.localtime_r(t &C.time_t, tm &C.tm)
29
30fn 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]
36fn 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.
51pub 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
62pub struct C.timespec {
63pub 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`
69fn C.clock_gettime(i32, &C.timespec) i32
70
71fn 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.
74pub 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]
87fn 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
98fn 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
108fn 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
117fn win_now() Time {
118 return Time{}
119}
120
121// dummy to compile with all compilers
122fn win_utc() Time {
123 return Time{}
124}
125
126// return absolute timespec for now()+d
127pub 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).
142pub 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