time: add microsecond precision to Time struct
parent
eec5cf1eb1
commit
9c8769503f
|
@ -20,6 +20,7 @@ fn C.sprintf(a ...voidptr) int
|
|||
|
||||
fn C.strlen(s byteptr) int
|
||||
|
||||
fn C.sscanf(byteptr, byteptr,...byteptr) int
|
||||
|
||||
fn C.isdigit(s byteptr) bool
|
||||
// stdio.h
|
||||
|
|
|
@ -103,8 +103,6 @@ pub fn new(uri string) &Client {
|
|||
return ws
|
||||
}
|
||||
|
||||
fn C.sscanf() int
|
||||
|
||||
fn (ws &Client) parse_uri() &Uri {
|
||||
u := urllib.parse(ws.uri) or {
|
||||
panic(err)
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
module time
|
||||
|
||||
// eq returns true if provided time is equal to time
|
||||
pub fn (t1 Time) eq(t2 Time) bool {
|
||||
if t1.unix == t2.unix && t1.microsecond == t2.microsecond {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ne returns true if provided time is not equal to time
|
||||
pub fn (t1 Time) ne(t2 Time) bool {
|
||||
return !t1.eq(t2)
|
||||
}
|
||||
|
||||
// lt returns true if provided time is less than time
|
||||
pub fn (t1 Time) lt(t2 Time) bool {
|
||||
if t1.unix < t2.unix || (t1.unix == t2.unix && t1.microsecond < t2.microsecond) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// le returns true if provided time is less or equal to time
|
||||
pub fn (t1 Time) le(t2 Time) bool {
|
||||
return t1.lt(t2) || t1.eq(t2)
|
||||
}
|
||||
|
||||
// gt returns true if provided time is greater than time
|
||||
pub fn (t1 Time) gt(t2 Time) bool {
|
||||
if t1.unix > t2.unix || (t1.unix == t2.unix && t1.microsecond > t2.microsecond) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ge returns true if provided time is greater or equal to time
|
||||
pub fn (t1 Time) ge(t2 Time) bool {
|
||||
return t1.gt(t2) || t1.eq(t2)
|
||||
}
|
|
@ -0,0 +1,409 @@
|
|||
module time
|
||||
|
||||
fn assert_greater_time(ms int, t1 Time) {
|
||||
time.sleep_ms(ms)
|
||||
t2 := now()
|
||||
assert t2.gt(t1)
|
||||
}
|
||||
|
||||
// Tests the now in all platform and the gt operator function with at least ms resolution
|
||||
fn test_now_always_results_in_greater_time() {
|
||||
t1 := now()
|
||||
$if macos {
|
||||
assert_greater_time(1, t1)
|
||||
return
|
||||
}
|
||||
$if windows {
|
||||
// Lower resolution of time for windows
|
||||
assert_greater_time(15, t1)
|
||||
return
|
||||
}
|
||||
$if linux {
|
||||
assert_greater_time(1, t1)
|
||||
return
|
||||
}
|
||||
$if solaris {
|
||||
assert_greater_time(1, t1)
|
||||
return
|
||||
}
|
||||
// other platforms may have more accurate resolution,
|
||||
// but we do not know that ... so wait at least 1s:
|
||||
assert_greater_time(1001, t1)
|
||||
}
|
||||
|
||||
fn test_time1_should_be_same_as_time2() {
|
||||
|
||||
t1 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 100
|
||||
})
|
||||
|
||||
t2 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 100
|
||||
})
|
||||
|
||||
assert t1.eq(t2)
|
||||
}
|
||||
|
||||
fn test_time1_should_not_be_same_as_time2() {
|
||||
|
||||
t1 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 100
|
||||
})
|
||||
|
||||
// Difference is one microsecond
|
||||
t2 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 101
|
||||
})
|
||||
|
||||
t3 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 0
|
||||
})
|
||||
|
||||
// Difference is one second
|
||||
t4 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 4
|
||||
microsecond: 0
|
||||
})
|
||||
|
||||
assert t1.ne(t2)
|
||||
assert t3.ne(t4)
|
||||
}
|
||||
|
||||
fn test_time1_should_be_greater_than_time2() {
|
||||
|
||||
t1 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 102
|
||||
})
|
||||
|
||||
// Difference is one microsecond
|
||||
t2 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 101
|
||||
})
|
||||
|
||||
t3 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 5
|
||||
microsecond: 0
|
||||
})
|
||||
|
||||
// Difference is one second
|
||||
t4 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 4
|
||||
microsecond: 0
|
||||
})
|
||||
|
||||
assert t1.gt(t2)
|
||||
assert t3.gt(t4)
|
||||
}
|
||||
|
||||
|
||||
fn test_time2_should_be_less_than_time1() {
|
||||
|
||||
t1 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 102
|
||||
})
|
||||
|
||||
// Difference is one microsecond
|
||||
t2 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 101
|
||||
})
|
||||
|
||||
t3 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 0
|
||||
})
|
||||
|
||||
// Difference is one second
|
||||
t4 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 2
|
||||
microsecond: 0
|
||||
})
|
||||
|
||||
assert t2.lt(t1)
|
||||
assert t4.lt(t3)
|
||||
}
|
||||
|
||||
fn test_time1_should_be_greater_or_equal_to_time2_when_gt() {
|
||||
|
||||
t1 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 102
|
||||
})
|
||||
|
||||
// Difference is one microsecond
|
||||
t2 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 101
|
||||
})
|
||||
|
||||
t3 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 5
|
||||
microsecond: 0
|
||||
})
|
||||
|
||||
// Difference is one second
|
||||
t4 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 4
|
||||
microsecond: 0
|
||||
})
|
||||
|
||||
assert t1.ge(t2)
|
||||
assert t3.ge(t4)
|
||||
}
|
||||
|
||||
fn test_time1_should_be_greater_or_equal_to_time2_when_eq() {
|
||||
|
||||
t1 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 100
|
||||
})
|
||||
|
||||
// Difference is one microsecond
|
||||
t2 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 100
|
||||
})
|
||||
|
||||
t3 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 0
|
||||
})
|
||||
|
||||
// Difference is one second
|
||||
t4 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 0
|
||||
})
|
||||
|
||||
assert t1.ge(t2)
|
||||
assert t3.ge(t4)
|
||||
}
|
||||
|
||||
fn test_time1_should_be_less_or_equal_to_time2_when_lt() {
|
||||
|
||||
t1 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 100
|
||||
})
|
||||
|
||||
// Difference is one microsecond
|
||||
t2 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 101
|
||||
})
|
||||
|
||||
t3 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 0
|
||||
})
|
||||
|
||||
// Difference is one second
|
||||
t4 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 4
|
||||
microsecond: 0
|
||||
})
|
||||
|
||||
assert t1.le(t2)
|
||||
assert t3.le(t4)
|
||||
}
|
||||
|
||||
fn test_time1_should_be_less_or_equal_to_time2_when_eq() {
|
||||
|
||||
t1 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 100
|
||||
})
|
||||
|
||||
// Difference is one microsecond
|
||||
t2 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 100
|
||||
})
|
||||
|
||||
t3 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 0
|
||||
})
|
||||
|
||||
// Difference is one second
|
||||
t4 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 0
|
||||
})
|
||||
|
||||
assert t1.le(t2)
|
||||
assert t3.le(t4)
|
||||
}
|
||||
|
||||
fn test_time2_copied_from_time1_should_be_equal() {
|
||||
t1 := new_time( Time {
|
||||
year: 2000
|
||||
month: 5
|
||||
day: 10
|
||||
hour: 22
|
||||
minute: 11
|
||||
second: 3
|
||||
microsecond: 100
|
||||
})
|
||||
t2 := new_time(t1)
|
||||
|
||||
assert t2.eq(t1)
|
||||
}
|
|
@ -47,3 +47,65 @@ pub fn parse_rfc2822(s string) ?Time {
|
|||
|
||||
return parse(tos(tmstr, count))
|
||||
}
|
||||
|
||||
// parse_rfc8601 parses rfc8601 time format yyyy-MM-ddTHH:mm:ss.dddddd+dd:dd as local time
|
||||
// the fraction part is difference in milli seconds and the last part is offset
|
||||
// from UTC time and can be both +/- HH:mm
|
||||
// Remarks: Not all rfc8601 is supported only the 'yyyy-MM-ddTHH:mm:ss.dddddd+dd:dd'
|
||||
pub fn parse_rfc8601(s string) ?Time {
|
||||
|
||||
mut year := 0
|
||||
mut month := 0
|
||||
mut day := 0
|
||||
mut hour := 0
|
||||
mut minute := 0
|
||||
mut second := 0
|
||||
mut mic_second := 0
|
||||
mut time_char := `a`
|
||||
mut plus_min := `a`
|
||||
mut offset_hour := 0
|
||||
mut offset_min := 0
|
||||
|
||||
count := C.sscanf(s.str, "%4d-%2d-%2d%c%2d:%2d:%2d.%6d%c%2d:%2d", &year, &month, &day,
|
||||
&time_char, &hour, &minute,
|
||||
&second, &mic_second, &plus_min,
|
||||
&offset_hour, &offset_min)
|
||||
|
||||
if count != 11 {
|
||||
return error('Invalid 8601 format')
|
||||
}
|
||||
if time_char != `T` && time_char != ` ` {
|
||||
return error('Invalid 8601 format, expected space or `T` as time separator')
|
||||
}
|
||||
|
||||
if plus_min != `+` && plus_min != `-` {
|
||||
return error('Invalid 8601 format, expected `+` or `-` as time separator' )
|
||||
}
|
||||
|
||||
t := new_time(Time{
|
||||
year: year
|
||||
month: month
|
||||
day: day
|
||||
hour: hour
|
||||
minute: minute
|
||||
second: second
|
||||
microsecond: mic_second
|
||||
})
|
||||
mut unix_offset := int(0)
|
||||
|
||||
if offset_hour > 0 {
|
||||
unix_offset += 3600*offset_hour
|
||||
}
|
||||
if offset_min > 0 {
|
||||
unix_offset += 60*offset_min
|
||||
}
|
||||
|
||||
if unix_offset != 0 {
|
||||
if plus_min == `+` {
|
||||
return unix2(int(t.unix) + unix_offset, t.microsecond)
|
||||
} else {
|
||||
return unix2(int(t.unix) - unix_offset, t.microsecond)
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ fn test_parse() {
|
|||
|
||||
fn test_parse_invalid() {
|
||||
s := 'Invalid time string'
|
||||
t := time.parse(s) or {
|
||||
_ := time.parse(s) or {
|
||||
assert true
|
||||
return
|
||||
}
|
||||
|
@ -38,9 +38,37 @@ fn test_parse_rfc2822() {
|
|||
|
||||
fn test_parse_rfc2822_invalid() {
|
||||
s3 := 'Thu 12 Foo 2019 06:07:45 +0800'
|
||||
t3 := time.parse_rfc2822(s3) or {
|
||||
_ := time.parse_rfc2822(s3) or {
|
||||
assert true
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_rfc8601_parse_utc() {
|
||||
ok_format := '2020-06-02T15:38:06.015959+00:00'
|
||||
|
||||
t := time.parse_rfc8601(ok_format) or {panic(err)}
|
||||
|
||||
assert t.year == 2020
|
||||
assert t.month == 6
|
||||
assert t.day == 2
|
||||
assert t.hour == 15
|
||||
assert t.minute == 38
|
||||
assert t.second == 6
|
||||
assert t.microsecond == 15959
|
||||
}
|
||||
|
||||
fn test_rfc8601_parse_cest() {
|
||||
ok_format := '2020-06-02T15:38:06.015959+02:00'
|
||||
|
||||
t := time.parse_rfc8601(ok_format) or {panic(err)}
|
||||
|
||||
assert t.year == 2020
|
||||
assert t.month == 6
|
||||
assert t.day == 2
|
||||
assert t.hour == 17
|
||||
assert t.minute == 38
|
||||
assert t.second == 6
|
||||
assert t.microsecond == 15959
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// tests that use and test private functions
|
||||
module time
|
||||
|
||||
// test the old behavor is same as new, the unix time should always be local time
|
||||
fn test_new_is_same_as_old_for_all_platforms() {
|
||||
t := C.time(0)
|
||||
tm := C.localtime(&t)
|
||||
|
||||
old_time := time.convert_ctime(tm, 0)
|
||||
new_time := time.now()
|
||||
|
||||
diff := new_time.unix - old_time.unix
|
||||
|
||||
// could in very rare cases be that the second changed between calls
|
||||
assert (diff >=0 && diff <=1) == true
|
||||
}
|
|
@ -45,6 +45,7 @@ pub:
|
|||
hour int
|
||||
minute int
|
||||
second int
|
||||
microsecond int
|
||||
unix u64
|
||||
}
|
||||
|
||||
|
@ -86,9 +87,23 @@ fn C.time(t &C.time_t) C.time_t
|
|||
|
||||
// now returns current local time.
|
||||
pub fn now() Time {
|
||||
$if macos {
|
||||
return darwin_now()
|
||||
}
|
||||
$if windows {
|
||||
return win_now()
|
||||
}
|
||||
$if solaris {
|
||||
return solaris_now()
|
||||
}
|
||||
$if linux {
|
||||
return linux_now()
|
||||
}
|
||||
// defaults to most common feature, the microsecond precision is not available
|
||||
// in this API call
|
||||
t := C.time(0)
|
||||
now := C.localtime(&t)
|
||||
return convert_ctime(now)
|
||||
return convert_ctime(now, 0)
|
||||
}
|
||||
|
||||
// smonth returns month name.
|
||||
|
@ -107,6 +122,7 @@ pub fn new_time(t Time) Time {
|
|||
minute: t.minute
|
||||
second: t.second
|
||||
unix: u64(t.unix_time())
|
||||
microsecond: t.microsecond
|
||||
}
|
||||
// TODO Use the syntax below when it works with reserved keywords like `unix`
|
||||
// return {
|
||||
|
@ -264,7 +280,7 @@ pub fn (t Time) str() string {
|
|||
return t.format_ss()
|
||||
}
|
||||
|
||||
fn convert_ctime(t C.tm) Time {
|
||||
fn convert_ctime(t C.tm, microsecond int) Time {
|
||||
return Time{
|
||||
year: t.tm_year + 1900
|
||||
month: t.tm_mon + 1
|
||||
|
@ -272,6 +288,7 @@ fn convert_ctime(t C.tm) Time {
|
|||
hour: t.tm_hour
|
||||
minute: t.tm_min
|
||||
second: t.tm_sec
|
||||
microsecond: microsecond
|
||||
unix: u64(make_unix_time(t))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,11 @@ struct InternalTimeBase {
|
|||
denom u32 = 1
|
||||
}
|
||||
|
||||
pub struct C.timeval {
|
||||
tv_sec u64
|
||||
tv_usec u64
|
||||
}
|
||||
|
||||
fn init_time_base() InternalTimeBase {
|
||||
tb := C.mach_timebase_info_data_t{}
|
||||
C.mach_timebase_info(&tb)
|
||||
|
@ -47,3 +52,29 @@ fn vpc_now_darwin() u64 {
|
|||
}
|
||||
return (tm - start_time) * time_base.numer / time_base.denom
|
||||
}
|
||||
|
||||
// darwin_now returns a better precision current time for Darwin based operating system
|
||||
// this should be implemented with native system calls eventually
|
||||
// but for now a bit tweaky. It uses the deprecated gettimeofday clock to get
|
||||
// the microseconds seconds part and normal local time to get correct local time
|
||||
// if the time has shifted on a second level between calls it uses
|
||||
// zero as microsecond. Not perfect but better that unix time only us a second
|
||||
fn darwin_now() Time {
|
||||
|
||||
// get the high precision time as UTC clock
|
||||
// and use the nanoseconds part
|
||||
tv := C.timeval{}
|
||||
C.gettimeofday(&tv, 0)
|
||||
|
||||
t := C.time(0)
|
||||
tm := C.localtime(&t)
|
||||
|
||||
// if the second part (very rare) is different
|
||||
// microseconds is set to zero since it passed the second
|
||||
// also avoid divide by zero if nsec is zero
|
||||
if int(t) != tv.tv_sec {
|
||||
return convert_ctime(tm, 0)
|
||||
}
|
||||
|
||||
return convert_ctime(tm, int(tv.tv_usec))
|
||||
}
|
||||
|
|
|
@ -3,3 +3,39 @@ module time
|
|||
fn sys_mono_now_darwin() u64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub fn darwin_now() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub fn solaris_now() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
// linux_now returns the local time with high precision for most os:es
|
||||
// this should be implemented with native system calls eventually
|
||||
// but for now a bit tweaky. It uses the realtime clock to get
|
||||
// the nano seconds part and normal local time to get correct local time
|
||||
// if the time has shifted on a second level between calls it uses
|
||||
// zero as microsecond. Not perfect but better that unix time only
|
||||
fn linux_now() Time {
|
||||
|
||||
// get the high precision time as UTC realtime clock
|
||||
// and use the nanoseconds part
|
||||
mut ts := C.timespec{}
|
||||
C.clock_gettime(C.CLOCK_REALTIME, &ts)
|
||||
|
||||
t := C.time(0)
|
||||
tm := C.localtime(&t)
|
||||
|
||||
// if the second part (very rare) is different
|
||||
// microseconds is set to zero since it passed the second
|
||||
// also avoid divide by zero if nsec is zero
|
||||
if int(t) != ts.tv_sec || ts.tv_nsec == 0 {
|
||||
return convert_ctime(tm, 0)
|
||||
}
|
||||
|
||||
return convert_ctime(tm, int(ts.tv_nsec/1000))
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
// that can be found in the LICENSE file.
|
||||
module time
|
||||
|
||||
#include <time.h>
|
||||
|
||||
struct C.tm {
|
||||
tm_sec int
|
||||
tm_min int
|
||||
|
@ -47,3 +49,15 @@ fn vpc_now() u64 {
|
|||
C.clock_gettime(C.CLOCK_MONOTONIC, &ts)
|
||||
return u64(ts.tv_sec) * 1_000_000_000 + u64(ts.tv_nsec)
|
||||
}
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub fn win_now() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub struct C.timeval {
|
||||
tv_sec u64
|
||||
tv_usec u64
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
module time
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub fn linux_now() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
pub fn darwin_now() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
fn solaris_now() Time {
|
||||
// get the high precision time as UTC realtime clock
|
||||
// and use the nanoseconds part
|
||||
mut ts := C.timespec{}
|
||||
C.clock_gettime(C.CLOCK_REALTIME, &ts)
|
||||
t := C.time(0)
|
||||
tm := C.localtime(&t)
|
||||
// if the second part (very rare) is different
|
||||
// microseconds is set to zero since it passed the second
|
||||
// also avoid divide by zero if nsec is zero
|
||||
if int(t) != ts.tv_sec || ts.tv_nsec == 0 {
|
||||
return convert_ctime(tm, 0)
|
||||
}
|
||||
return convert_ctime(tm, int(ts.tv_nsec/1000))
|
||||
}
|
|
@ -149,3 +149,20 @@ fn test_add_days() {
|
|||
fn test_str() {
|
||||
assert '1980-07-11 21:23:42' == time_to_test.str()
|
||||
}
|
||||
|
||||
// not optimal test but will find obvious bugs
|
||||
fn test_now() {
|
||||
now := time.now()
|
||||
|
||||
// The year the test was built
|
||||
assert now.year >= 2020
|
||||
assert now.month > 0
|
||||
assert now.month <= 12
|
||||
assert now.minute >= 0
|
||||
assert now.minute < 60
|
||||
assert now.second >=0
|
||||
assert now.second < 60
|
||||
assert now.microsecond >= 0
|
||||
assert now.microsecond < 1000000
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
// that can be found in the LICENSE file.
|
||||
module time
|
||||
|
||||
#include <time.h>
|
||||
#include <sysinfoapi.h>
|
||||
|
||||
struct C.tm {
|
||||
tm_year int
|
||||
tm_mon int
|
||||
|
@ -12,13 +15,37 @@ struct C.tm {
|
|||
tm_sec int
|
||||
}
|
||||
|
||||
struct C._FILETIME
|
||||
|
||||
struct SystemTime {
|
||||
year u16
|
||||
month u16
|
||||
day_of_week u16
|
||||
day u16
|
||||
hour u16
|
||||
minute u16
|
||||
second u16
|
||||
millisecond u16
|
||||
}
|
||||
|
||||
fn C.GetSystemTimeAsFileTime(lpSystemTimeAsFileTime C._FILETIME)
|
||||
fn C.FileTimeToSystemTime()
|
||||
fn C.SystemTimeToTzSpecificLocalTime()
|
||||
|
||||
const (
|
||||
// start_time is needed on Darwin and Windows because of potential overflows
|
||||
start_time = init_win_time_start()
|
||||
freq_time = init_win_time_freq()
|
||||
start_local_time = local_as_unix_time()
|
||||
)
|
||||
|
||||
// in most systems, these are __quad_t, which is an i64
|
||||
struct C.timespec {
|
||||
tv_sec i64
|
||||
tv_nsec i64
|
||||
}
|
||||
|
||||
|
||||
fn C._mkgmtime(&C.tm) time_t
|
||||
|
||||
fn C.QueryPerformanceCounter(&u64) C.BOOL
|
||||
|
@ -55,3 +82,73 @@ fn vpc_now() u64 {
|
|||
C.QueryPerformanceCounter(&tm)
|
||||
return tm
|
||||
}
|
||||
|
||||
// local_as_unix_time returns the current local time as unix time
|
||||
fn local_as_unix_time() int {
|
||||
t := C.time(0)
|
||||
tm := C.localtime(&t)
|
||||
|
||||
return make_unix_time(tm)
|
||||
}
|
||||
|
||||
// win_now calculates current time using winapi to get higher resolution on windows
|
||||
// GetSystemTimeAsFileTime is used. It can resolve time down to millisecond
|
||||
// other more precice methods can be implemented in the future
|
||||
fn win_now() Time {
|
||||
|
||||
ft_utc := C._FILETIME{}
|
||||
C.GetSystemTimeAsFileTime(&ft_utc)
|
||||
|
||||
st_utc := SystemTime{}
|
||||
C.FileTimeToSystemTime(&ft_utc, &st_utc)
|
||||
|
||||
st_local := SystemTime{}
|
||||
C.SystemTimeToTzSpecificLocalTime(voidptr(0), &st_utc, &st_local)
|
||||
|
||||
t := Time {
|
||||
year: st_local.year
|
||||
month: st_local.month
|
||||
day: st_local.day
|
||||
hour: st_local.hour
|
||||
minute: st_local.minute
|
||||
second: st_local.second
|
||||
microsecond: st_local.millisecond*1000
|
||||
unix: u64(st_local.unix_time())
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// unix_time returns Unix time.
|
||||
pub fn (st SystemTime) unix_time() int {
|
||||
tt := C.tm{
|
||||
tm_sec: st.second
|
||||
tm_min: st.minute
|
||||
tm_hour: st.hour
|
||||
tm_mday: st.day
|
||||
tm_mon: st.month - 1
|
||||
tm_year: st.year - 1900
|
||||
}
|
||||
return make_unix_time(tt)
|
||||
}
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub fn darwin_now() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub fn linux_now() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub fn solaris_now() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub struct C.timeval {
|
||||
tv_sec u64
|
||||
tv_usec u64
|
||||
}
|
||||
|
|
|
@ -24,6 +24,29 @@ pub fn unix(abs int) Time {
|
|||
}
|
||||
}
|
||||
|
||||
// unix2 returns a time struct from Unix time and microsecond value
|
||||
pub fn unix2(abs int, microsecond int) Time {
|
||||
// Split into day and time
|
||||
mut day_offset := abs / seconds_per_day
|
||||
if abs % seconds_per_day < 0 {
|
||||
// Compensate for round towards zero on integers as we want floored instead
|
||||
day_offset--
|
||||
}
|
||||
year,month,day := calculate_date_from_offset(day_offset)
|
||||
hr,min,sec := calculate_time_from_offset(abs % seconds_per_day)
|
||||
return Time{
|
||||
year: year
|
||||
month: month
|
||||
day: day
|
||||
hour: hr
|
||||
minute: min
|
||||
second: sec
|
||||
microsecond: microsecond
|
||||
unix: u64(abs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[inline]
|
||||
fn calculate_date_from_offset(day_offset_ int) (int,int,int) {
|
||||
mut day_offset := day_offset_
|
||||
|
|
Loading…
Reference in New Issue