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.strlen(s byteptr) int
|
||||||
|
|
||||||
|
fn C.sscanf(byteptr, byteptr,...byteptr) int
|
||||||
|
|
||||||
fn C.isdigit(s byteptr) bool
|
fn C.isdigit(s byteptr) bool
|
||||||
// stdio.h
|
// stdio.h
|
||||||
|
|
|
@ -103,8 +103,6 @@ pub fn new(uri string) &Client {
|
||||||
return ws
|
return ws
|
||||||
}
|
}
|
||||||
|
|
||||||
fn C.sscanf() int
|
|
||||||
|
|
||||||
fn (ws &Client) parse_uri() &Uri {
|
fn (ws &Client) parse_uri() &Uri {
|
||||||
u := urllib.parse(ws.uri) or {
|
u := urllib.parse(ws.uri) or {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -572,7 +570,7 @@ fn (mut ws Client) send_control_frame(code OPCode, frame_typ string, payload []b
|
||||||
mut bytes_written := -1
|
mut bytes_written := -1
|
||||||
if ws.socket.sockfd <= 0 {
|
if ws.socket.sockfd <= 0 {
|
||||||
l.e('No socket opened.')
|
l.e('No socket opened.')
|
||||||
unsafe {
|
unsafe {
|
||||||
payload.free()
|
payload.free()
|
||||||
}
|
}
|
||||||
l.c('send_control_frame: error sending ${frame_typ} control frame.')
|
l.c('send_control_frame: error sending ${frame_typ} control frame.')
|
||||||
|
|
|
@ -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))
|
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() {
|
fn test_parse_invalid() {
|
||||||
s := 'Invalid time string'
|
s := 'Invalid time string'
|
||||||
t := time.parse(s) or {
|
_ := time.parse(s) or {
|
||||||
assert true
|
assert true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -38,9 +38,37 @@ fn test_parse_rfc2822() {
|
||||||
|
|
||||||
fn test_parse_rfc2822_invalid() {
|
fn test_parse_rfc2822_invalid() {
|
||||||
s3 := 'Thu 12 Foo 2019 06:07:45 +0800'
|
s3 := 'Thu 12 Foo 2019 06:07:45 +0800'
|
||||||
t3 := time.parse_rfc2822(s3) or {
|
_ := time.parse_rfc2822(s3) or {
|
||||||
assert true
|
assert true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
assert false
|
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
|
||||||
|
}
|
|
@ -39,13 +39,14 @@ const (
|
||||||
|
|
||||||
pub struct Time {
|
pub struct Time {
|
||||||
pub:
|
pub:
|
||||||
year int
|
year int
|
||||||
month int
|
month int
|
||||||
day int
|
day int
|
||||||
hour int
|
hour int
|
||||||
minute int
|
minute int
|
||||||
second int
|
second int
|
||||||
unix u64
|
microsecond int
|
||||||
|
unix u64
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum FormatTime {
|
pub enum FormatTime {
|
||||||
|
@ -86,9 +87,23 @@ fn C.time(t &C.time_t) C.time_t
|
||||||
|
|
||||||
// now returns current local time.
|
// now returns current local time.
|
||||||
pub fn now() 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)
|
t := C.time(0)
|
||||||
now := C.localtime(&t)
|
now := C.localtime(&t)
|
||||||
return convert_ctime(now)
|
return convert_ctime(now, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// smonth returns month name.
|
// smonth returns month name.
|
||||||
|
@ -107,6 +122,7 @@ pub fn new_time(t Time) Time {
|
||||||
minute: t.minute
|
minute: t.minute
|
||||||
second: t.second
|
second: t.second
|
||||||
unix: u64(t.unix_time())
|
unix: u64(t.unix_time())
|
||||||
|
microsecond: t.microsecond
|
||||||
}
|
}
|
||||||
// TODO Use the syntax below when it works with reserved keywords like `unix`
|
// TODO Use the syntax below when it works with reserved keywords like `unix`
|
||||||
// return {
|
// return {
|
||||||
|
@ -264,7 +280,7 @@ pub fn (t Time) str() string {
|
||||||
return t.format_ss()
|
return t.format_ss()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_ctime(t C.tm) Time {
|
fn convert_ctime(t C.tm, microsecond int) Time {
|
||||||
return Time{
|
return Time{
|
||||||
year: t.tm_year + 1900
|
year: t.tm_year + 1900
|
||||||
month: t.tm_mon + 1
|
month: t.tm_mon + 1
|
||||||
|
@ -272,6 +288,7 @@ fn convert_ctime(t C.tm) Time {
|
||||||
hour: t.tm_hour
|
hour: t.tm_hour
|
||||||
minute: t.tm_min
|
minute: t.tm_min
|
||||||
second: t.tm_sec
|
second: t.tm_sec
|
||||||
|
microsecond: microsecond
|
||||||
unix: u64(make_unix_time(t))
|
unix: u64(make_unix_time(t))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,11 @@ struct InternalTimeBase {
|
||||||
denom u32 = 1
|
denom u32 = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct C.timeval {
|
||||||
|
tv_sec u64
|
||||||
|
tv_usec u64
|
||||||
|
}
|
||||||
|
|
||||||
fn init_time_base() InternalTimeBase {
|
fn init_time_base() InternalTimeBase {
|
||||||
tb := C.mach_timebase_info_data_t{}
|
tb := C.mach_timebase_info_data_t{}
|
||||||
C.mach_timebase_info(&tb)
|
C.mach_timebase_info(&tb)
|
||||||
|
@ -47,3 +52,29 @@ fn vpc_now_darwin() u64 {
|
||||||
}
|
}
|
||||||
return (tm - start_time) * time_base.numer / time_base.denom
|
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))
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,41 @@
|
||||||
module time
|
module time
|
||||||
|
|
||||||
fn sys_mono_now_darwin() u64 {
|
fn sys_mono_now_darwin() u64 {
|
||||||
return 0
|
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.
|
// that can be found in the LICENSE file.
|
||||||
module time
|
module time
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
struct C.tm {
|
struct C.tm {
|
||||||
tm_sec int
|
tm_sec int
|
||||||
tm_min int
|
tm_min int
|
||||||
|
@ -47,3 +49,15 @@ fn vpc_now() u64 {
|
||||||
C.clock_gettime(C.CLOCK_MONOTONIC, &ts)
|
C.clock_gettime(C.CLOCK_MONOTONIC, &ts)
|
||||||
return u64(ts.tv_sec) * 1_000_000_000 + u64(ts.tv_nsec)
|
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() {
|
fn test_str() {
|
||||||
assert '1980-07-11 21:23:42' == time_to_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.
|
// that can be found in the LICENSE file.
|
||||||
module time
|
module time
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <sysinfoapi.h>
|
||||||
|
|
||||||
struct C.tm {
|
struct C.tm {
|
||||||
tm_year int
|
tm_year int
|
||||||
tm_mon int
|
tm_mon int
|
||||||
|
@ -12,13 +15,37 @@ struct C.tm {
|
||||||
tm_sec int
|
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 (
|
const (
|
||||||
// start_time is needed on Darwin and Windows because of potential overflows
|
// start_time is needed on Darwin and Windows because of potential overflows
|
||||||
start_time = init_win_time_start()
|
start_time = init_win_time_start()
|
||||||
freq_time = init_win_time_freq()
|
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._mkgmtime(&C.tm) time_t
|
||||||
|
|
||||||
fn C.QueryPerformanceCounter(&u64) C.BOOL
|
fn C.QueryPerformanceCounter(&u64) C.BOOL
|
||||||
|
@ -55,3 +82,73 @@ fn vpc_now() u64 {
|
||||||
C.QueryPerformanceCounter(&tm)
|
C.QueryPerformanceCounter(&tm)
|
||||||
return 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]
|
[inline]
|
||||||
fn calculate_date_from_offset(day_offset_ int) (int,int,int) {
|
fn calculate_date_from_offset(day_offset_ int) (int,int,int) {
|
||||||
mut day_offset := day_offset_
|
mut day_offset := day_offset_
|
||||||
|
|
Loading…
Reference in New Issue