time: store time with nanosecond resolution in time.Time, deprecate Time.microsecond, add utility methods and tests (#19062)

This commit is contained in:
Delyan Angelov 2023-08-05 23:41:23 +03:00 committed by GitHub
parent cc97b8df1e
commit b9a523cefd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 265 additions and 199 deletions

View File

@ -74,12 +74,12 @@ fn on_frame(mut app App) {
// draw minute hand // draw minute hand
mut j := f32(n.minute) mut j := f32(n.minute)
if n.second == 59 { // make minute hand move smoothly if n.second == 59 { // make minute hand move smoothly
j += f32(math.sin(f32(n.microsecond) / 1e6 * math.pi / 2.0)) j += f32(math.sin(f32(n.nanosecond) / 1e9 * math.pi / 2.0))
} }
draw_convex_poly_rotate(mut app.gg, app.dpi_scale, app.minute_hand, hand_color, j * 6) draw_convex_poly_rotate(mut app.gg, app.dpi_scale, app.minute_hand, hand_color, j * 6)
// draw second hand with smooth transition // draw second hand with smooth transition
k := f32(n.second) + f32(math.sin(f32(n.microsecond) / 1e6 * math.pi / 2.0)) k := f32(n.second) + f32(math.sin(f32(n.nanosecond) / 1e9 * math.pi / 2.0))
draw_convex_poly_rotate(mut app.gg, app.dpi_scale, app.second_hand, second_hand_color, draw_convex_poly_rotate(mut app.gg, app.dpi_scale, app.second_hand, second_hand_color,
0 + k * 6) 0 + k * 6)

View File

@ -28,7 +28,7 @@ const time_to_test = time.Time{
hour: 21 hour: 21
minute: 23 minute: 23
second: 42 second: 42
microsecond: 123456 nanosecond: 123456789
unix: 332198622 unix: 332198622
} }
@ -38,6 +38,7 @@ assert '1980-07-11 21:23' == time_to_test.format()
assert '1980-07-11 21:23:42' == time_to_test.format_ss() assert '1980-07-11 21:23:42' == time_to_test.format_ss()
assert '1980-07-11 21:23:42.123' == time_to_test.format_ss_milli() assert '1980-07-11 21:23:42.123' == time_to_test.format_ss_milli()
assert '1980-07-11 21:23:42.123456' == time_to_test.format_ss_micro() assert '1980-07-11 21:23:42.123456' == time_to_test.format_ss_micro()
assert '1980-07-11 21:23:42.123456789' == time_to_test.format_ss_nano()
``` ```
You can also parse strings to produce time.Time values, You can also parse strings to produce time.Time values,

View File

@ -31,3 +31,9 @@ fn test_duration_str() {
assert time.Duration(1 * time.hour + 5 * time.second).str() == '1:00:05' assert time.Duration(1 * time.hour + 5 * time.second).str() == '1:00:05'
assert time.Duration(168 * time.hour + 5 * time.minute + 7 * time.second).str() == '168:05:07' assert time.Duration(168 * time.hour + 5 * time.minute + 7 * time.second).str() == '168:05:07'
} }
fn test_duration_debug() {
assert time.Duration(1 * time.nanosecond).debug() == 'Duration: 1ns'
assert time.Duration(169 * time.hour + 5 * time.minute + 7 * time.second).debug() == 'Duration: 7days, 1h, 5m, 7s'
assert (-time.Duration(169 * time.hour + 5 * time.minute + 7 * time.second)).debug() == 'Duration: - 7days, 1h, 5m, 7s'
}

View File

@ -17,7 +17,7 @@ pub fn (t Time) format_ss() string {
// format_ss_milli returns a date string in "YYYY-MM-DD HH:mm:ss.123" format (24h). // format_ss_milli returns a date string in "YYYY-MM-DD HH:mm:ss.123" format (24h).
pub fn (t Time) format_ss_milli() string { pub fn (t Time) format_ss_milli() string {
return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.microsecond / 1000):03d}' return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.nanosecond / 1_000_000):03d}'
} }
// format_rfc3339 returns a date string in "YYYY-MM-DDTHH:mm:ss.123Z" format (24 hours, see https://www.rfc-editor.org/rfc/rfc3339.html) // format_rfc3339 returns a date string in "YYYY-MM-DDTHH:mm:ss.123Z" format (24 hours, see https://www.rfc-editor.org/rfc/rfc3339.html)
@ -25,12 +25,17 @@ pub fn (t Time) format_ss_milli() string {
// It is intended to improve consistency and interoperability, when representing and using date and time in Internet protocols. // It is intended to improve consistency and interoperability, when representing and using date and time in Internet protocols.
pub fn (t Time) format_rfc3339() string { pub fn (t Time) format_rfc3339() string {
u := t.local_to_utc() u := t.local_to_utc()
return '${u.year:04d}-${u.month:02d}-${u.day:02d}T${u.hour:02d}:${u.minute:02d}:${u.second:02d}.${(u.microsecond / 1000):03d}Z' return '${u.year:04d}-${u.month:02d}-${u.day:02d}T${u.hour:02d}:${u.minute:02d}:${u.second:02d}.${(u.nanosecond / 1_000_000):03d}Z'
} }
// format_ss_micro returns a date string in "YYYY-MM-DD HH:mm:ss.123456" format (24h). // format_ss_micro returns a date string in "YYYY-MM-DD HH:mm:ss.123456" format (24h).
pub fn (t Time) format_ss_micro() string { pub fn (t Time) format_ss_micro() string {
return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${t.microsecond:06d}' return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.nanosecond / 1_000):06d}'
}
// format_ss_nano returns a date string in "YYYY-MM-DD HH:mm:ss.123456789" format (24h).
pub fn (t Time) format_ss_nano() string {
return '${t.year:04d}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${t.nanosecond:09d}'
} }
// hhmm returns a date string in "HH:mm" format (24h). // hhmm returns a date string in "HH:mm" format (24h).
@ -381,8 +386,9 @@ pub fn (t Time) get_fmt_time_str(fmt_time FormatTime) string {
.hhmm24 { '${t.hour:02d}:${t.minute:02d}' } .hhmm24 { '${t.hour:02d}:${t.minute:02d}' }
.hhmmss12 { '${hour_}:${t.minute:02d}:${t.second:02d} ${tp}' } .hhmmss12 { '${hour_}:${t.minute:02d}:${t.second:02d} ${tp}' }
.hhmmss24 { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}' } .hhmmss24 { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}' }
.hhmmss24_milli { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.microsecond / 1000):03d}' } .hhmmss24_milli { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.nanosecond / 1_000_000):03d}' }
.hhmmss24_micro { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${t.microsecond:06d}' } .hhmmss24_micro { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.nanosecond / 1_000):06d}' }
.hhmmss24_nano { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${t.nanosecond:06d}' }
else { 'unknown enumeration ${fmt_time}' } else { 'unknown enumeration ${fmt_time}' }
} }
} }

View File

@ -3,19 +3,22 @@ module time
// operator `==` returns true if provided time is equal to time // operator `==` returns true if provided time is equal to time
[inline] [inline]
pub fn (t1 Time) == (t2 Time) bool { pub fn (t1 Time) == (t2 Time) bool {
return t1.unix == t2.unix && t1.microsecond == t2.microsecond return t1.unix == t2.unix && t1.nanosecond == t2.nanosecond
} }
// operator `<` returns true if provided time is less than time // operator `<` returns true if provided time is less than time
[inline] [inline]
pub fn (t1 Time) < (t2 Time) bool { pub fn (t1 Time) < (t2 Time) bool {
return t1.unix < t2.unix || (t1.unix == t2.unix && t1.microsecond < t2.microsecond) return t1.unix < t2.unix || (t1.unix == t2.unix && t1.nanosecond < t2.nanosecond)
} }
// Time subtract using operator overloading. // Time subtract using operator overloading.
[inline] [inline]
pub fn (lhs Time) - (rhs Time) Duration { pub fn (lhs Time) - (rhs Time) Duration {
lhs_micro := lhs.unix * 1_000_000 + lhs.microsecond // lhs.unix * 1_000_000_000 + i64(lhs.nanosecond) will overflow i64, for years > 3000 .
rhs_micro := rhs.unix * 1_000_000 + rhs.microsecond // Doing the diff first, and *then* multiplying by `second`, is less likely to overflow,
return (lhs_micro - rhs_micro) * microsecond // since lhs and rhs will be likely close to each other.
unixs := i64(lhs.unix - rhs.unix) * second
nanos := lhs.nanosecond - rhs.nanosecond
return unixs + nanos
} }

View File

@ -39,7 +39,7 @@ fn test_time1_should_be_same_as_time2() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 100 nanosecond: 100
}) })
t2 := new_time(Time{ t2 := new_time(Time{
year: 2000 year: 2000
@ -48,7 +48,7 @@ fn test_time1_should_be_same_as_time2() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 100 nanosecond: 100
}) })
assert t1 == t2 assert t1 == t2
} }
@ -61,9 +61,9 @@ fn test_time1_should_not_be_same_as_time2() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 100 nanosecond: 100
}) })
// Difference is one microsecond // Difference is one nanosecond
t2 := new_time(Time{ t2 := new_time(Time{
year: 2000 year: 2000
month: 5 month: 5
@ -71,7 +71,7 @@ fn test_time1_should_not_be_same_as_time2() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 101 nanosecond: 101
}) })
t3 := new_time(Time{ t3 := new_time(Time{
year: 2000 year: 2000
@ -80,7 +80,7 @@ fn test_time1_should_not_be_same_as_time2() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 0 nanosecond: 0
}) })
// Difference is one second // Difference is one second
t4 := new_time(Time{ t4 := new_time(Time{
@ -90,7 +90,7 @@ fn test_time1_should_not_be_same_as_time2() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 4 second: 4
microsecond: 0 nanosecond: 0
}) })
assert t1 != t2 assert t1 != t2
assert t3 != t4 assert t3 != t4
@ -104,9 +104,9 @@ fn test_time1_should_be_greater_than_time2() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 102 nanosecond: 102
}) })
// Difference is one microsecond // Difference is one nanosecond
t2 := new_time(Time{ t2 := new_time(Time{
year: 2000 year: 2000
month: 5 month: 5
@ -114,7 +114,7 @@ fn test_time1_should_be_greater_than_time2() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 101 nanosecond: 101
}) })
t3 := new_time(Time{ t3 := new_time(Time{
year: 2000 year: 2000
@ -123,7 +123,7 @@ fn test_time1_should_be_greater_than_time2() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 5 second: 5
microsecond: 0 nanosecond: 0
}) })
// Difference is one second // Difference is one second
t4 := new_time(Time{ t4 := new_time(Time{
@ -133,7 +133,7 @@ fn test_time1_should_be_greater_than_time2() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 4 second: 4
microsecond: 0 nanosecond: 0
}) })
assert t1 > t2 assert t1 > t2
assert t3 > t4 assert t3 > t4
@ -147,9 +147,9 @@ fn test_time2_should_be_less_than_time1() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 102 nanosecond: 102
}) })
// Difference is one microsecond // Difference is one nanosecond
t2 := new_time(Time{ t2 := new_time(Time{
year: 2000 year: 2000
month: 5 month: 5
@ -157,7 +157,7 @@ fn test_time2_should_be_less_than_time1() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 101 nanosecond: 101
}) })
t3 := new_time(Time{ t3 := new_time(Time{
year: 2000 year: 2000
@ -166,7 +166,7 @@ fn test_time2_should_be_less_than_time1() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 0 nanosecond: 0
}) })
// Difference is one second // Difference is one second
t4 := new_time(Time{ t4 := new_time(Time{
@ -176,7 +176,7 @@ fn test_time2_should_be_less_than_time1() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 2 second: 2
microsecond: 0 nanosecond: 0
}) })
assert t2 < t1 assert t2 < t1
assert t4 < t3 assert t4 < t3
@ -190,9 +190,9 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_gt() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 102 nanosecond: 102
}) })
// Difference is one microsecond // Difference is one nanosecond
t2 := new_time(Time{ t2 := new_time(Time{
year: 2000 year: 2000
month: 5 month: 5
@ -200,7 +200,7 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_gt() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 101 nanosecond: 101
}) })
t3 := new_time(Time{ t3 := new_time(Time{
year: 2000 year: 2000
@ -209,7 +209,7 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_gt() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 5 second: 5
microsecond: 0 nanosecond: 0
}) })
// Difference is one second // Difference is one second
t4 := new_time(Time{ t4 := new_time(Time{
@ -219,7 +219,7 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_gt() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 4 second: 4
microsecond: 0 nanosecond: 0
}) })
assert t1 >= t2 assert t1 >= t2
assert t3 >= t4 assert t3 >= t4
@ -233,9 +233,9 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_eq() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 100 nanosecond: 100
}) })
// Difference is one microsecond // Difference is one nanosecond
t2 := new_time(Time{ t2 := new_time(Time{
year: 2000 year: 2000
month: 5 month: 5
@ -243,7 +243,7 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_eq() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 100 nanosecond: 100
}) })
t3 := new_time(Time{ t3 := new_time(Time{
year: 2000 year: 2000
@ -252,7 +252,7 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_eq() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 0 nanosecond: 0
}) })
// Difference is one second // Difference is one second
t4 := new_time(Time{ t4 := new_time(Time{
@ -262,7 +262,7 @@ fn test_time1_should_be_greater_or_equal_to_time2_when_eq() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 0 nanosecond: 0
}) })
assert t1 >= t2 assert t1 >= t2
assert t3 >= t4 assert t3 >= t4
@ -276,9 +276,9 @@ fn test_time1_should_be_less_or_equal_to_time2_when_lt() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 100 nanosecond: 100
}) })
// Difference is one microsecond // Difference is one nanosecond
t2 := new_time(Time{ t2 := new_time(Time{
year: 2000 year: 2000
month: 5 month: 5
@ -286,7 +286,7 @@ fn test_time1_should_be_less_or_equal_to_time2_when_lt() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 101 nanosecond: 101
}) })
t3 := new_time(Time{ t3 := new_time(Time{
year: 2000 year: 2000
@ -295,7 +295,7 @@ fn test_time1_should_be_less_or_equal_to_time2_when_lt() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 0 nanosecond: 0
}) })
// Difference is one second // Difference is one second
t4 := new_time(Time{ t4 := new_time(Time{
@ -305,7 +305,7 @@ fn test_time1_should_be_less_or_equal_to_time2_when_lt() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 4 second: 4
microsecond: 0 nanosecond: 0
}) })
assert t1 <= t2 assert t1 <= t2
assert t3 <= t4 assert t3 <= t4
@ -319,9 +319,9 @@ fn test_time1_should_be_less_or_equal_to_time2_when_eq() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 100 nanosecond: 100
}) })
// Difference is one microsecond // Difference is one nanosecond
t2 := new_time(Time{ t2 := new_time(Time{
year: 2000 year: 2000
month: 5 month: 5
@ -329,7 +329,7 @@ fn test_time1_should_be_less_or_equal_to_time2_when_eq() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 100 nanosecond: 100
}) })
t3 := new_time(Time{ t3 := new_time(Time{
year: 2000 year: 2000
@ -338,7 +338,7 @@ fn test_time1_should_be_less_or_equal_to_time2_when_eq() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 0 nanosecond: 0
}) })
// Difference is one second // Difference is one second
t4 := new_time(Time{ t4 := new_time(Time{
@ -348,7 +348,7 @@ fn test_time1_should_be_less_or_equal_to_time2_when_eq() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 0 nanosecond: 0
}) })
assert t1 <= t2 assert t1 <= t2
assert t3 <= t4 assert t3 <= t4
@ -362,7 +362,7 @@ fn test_time2_copied_from_time1_should_be_equal() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 100 nanosecond: 100
}) })
t2 := new_time(t1) t2 := new_time(t1)
assert t2 == t1 assert t2 == t1
@ -370,8 +370,8 @@ fn test_time2_copied_from_time1_should_be_equal() {
fn test_subtract() { fn test_subtract() {
d_seconds := 3 d_seconds := 3
d_microseconds := 13 d_nanoseconds := 13
duration := d_seconds * second + d_microseconds * microsecond duration := d_seconds * second + d_nanoseconds * nanosecond
t1 := new_time(Time{ t1 := new_time(Time{
year: 2000 year: 2000
month: 5 month: 5
@ -379,9 +379,9 @@ fn test_subtract() {
hour: 22 hour: 22
minute: 11 minute: 11
second: 3 second: 3
microsecond: 100 nanosecond: 100
}) })
t2 := unix2(i64(t1.unix) + d_seconds, t1.microsecond + d_microseconds) t2 := unix_nanosecond(i64(t1.unix) + d_seconds, t1.nanosecond + d_nanoseconds)
d1 := t2 - t1 d1 := t2 - t1
d2 := t1 - t2 d2 := t1 - t2
assert d1 > 0 assert d1 > 0

View File

@ -35,13 +35,13 @@ pub fn parse_rfc3339(s string) !Time {
} }
// Check if sn is time only // Check if sn is time only
if !parts[0].contains('-') && parts[0].contains(':') { if !parts[0].contains('-') && parts[0].contains(':') {
mut hour_, mut minute_, mut second_, mut microsecond_, mut unix_offset, mut is_local_time := 0, 0, 0, 0, i64(0), true mut hour_, mut minute_, mut second_, mut microsecond_, mut nanosecond_, mut unix_offset, mut is_local_time := 0, 0, 0, 0, 0, i64(0), true
hour_, minute_, second_, microsecond_, unix_offset, is_local_time = parse_iso8601_time(parts[0])! hour_, minute_, second_, microsecond_, nanosecond_, unix_offset, is_local_time = parse_iso8601_time(parts[0])!
t = new_time(Time{ t = new_time(Time{
hour: hour_ hour: hour_
minute: minute_ minute: minute_
second: second_ second: second_
microsecond: microsecond_ nanosecond: nanosecond_
}) })
if is_local_time { if is_local_time {
return t // Time is already local time return t // Time is already local time
@ -52,7 +52,7 @@ pub fn parse_rfc3339(s string) !Time {
} else if unix_offset > 0 { } else if unix_offset > 0 {
unix_time += unix_offset unix_time += unix_offset
} }
t = unix2(i64(unix_time), t.microsecond) t = unix_nanosecond(i64(unix_time), t.nanosecond)
return t return t
} }
@ -171,9 +171,9 @@ pub fn parse_iso8601(s string) !Time {
return error_invalid_time(12, 'malformed date') return error_invalid_time(12, 'malformed date')
} }
year, month, day := parse_iso8601_date(parts[0])! year, month, day := parse_iso8601_date(parts[0])!
mut hour_, mut minute_, mut second_, mut microsecond_, mut unix_offset, mut is_local_time := 0, 0, 0, 0, i64(0), true mut hour_, mut minute_, mut second_, mut microsecond_, mut nanosecond_, mut unix_offset, mut is_local_time := 0, 0, 0, 0, 0, i64(0), true
if parts.len == 2 { if parts.len == 2 {
hour_, minute_, second_, microsecond_, unix_offset, is_local_time = parse_iso8601_time(parts[1])! hour_, minute_, second_, microsecond_, nanosecond_, unix_offset, is_local_time = parse_iso8601_time(parts[1])!
} }
mut t := new_time( mut t := new_time(
year: year year: year
@ -182,7 +182,7 @@ pub fn parse_iso8601(s string) !Time {
hour: hour_ hour: hour_
minute: minute_ minute: minute_
second: second_ second: second_
microsecond: microsecond_ nanosecond: nanosecond_
) )
if is_local_time { if is_local_time {
return t // Time already local time return t // Time already local time
@ -193,7 +193,7 @@ pub fn parse_iso8601(s string) !Time {
} else if unix_offset > 0 { } else if unix_offset > 0 {
unix_time += unix_offset unix_time += unix_offset
} }
t = unix2(i64(unix_time), t.microsecond) t = unix_nanosecond(i64(unix_time), t.nanosecond)
return t return t
} }
@ -237,7 +237,7 @@ fn parse_iso8601_date(s string) !(int, int, int) {
return year, month, day return year, month, day
} }
fn parse_iso8601_time(s string) !(int, int, int, int, i64, bool) { fn parse_iso8601_time(s string) !(int, int, int, int, int, i64, bool) {
hour_ := 0 hour_ := 0
minute_ := 0 minute_ := 0
second_ := 0 second_ := 0
@ -281,6 +281,7 @@ fn parse_iso8601_time(s string) !(int, int, int, int, i64, bool) {
if count < 4 { if count < 4 {
return error_invalid_time(10, 'malformed date') return error_invalid_time(10, 'malformed date')
} }
nanosecond_ = microsecond_ * 1000
} }
is_local_time := plus_min_z == `a` && count == 4 is_local_time := plus_min_z == `a` && count == 4
is_utc := plus_min_z == `Z` && count == 5 is_utc := plus_min_z == `Z` && count == 5
@ -300,5 +301,6 @@ fn parse_iso8601_time(s string) !(int, int, int, int, i64, bool) {
if plus_min_z == `+` { if plus_min_z == `+` {
unix_offset *= -1 unix_offset *= -1
} }
return hour_, minute_, second_, microsecond_, unix_offset, is_local_time // eprintln('parse_iso8601_time s: $s | hour_: $hour_ | minute_: $minute_ | second_: $second_ | microsecond_: $microsecond_ | nanosecond_: $nanosecond_ | unix_offset: $unix_offset | is_local_time: $is_local_time')
return hour_, minute_, second_, microsecond_, nanosecond_, unix_offset, is_local_time
} }

View File

@ -65,11 +65,11 @@ fn test_parse_iso8601() {
] ]
times := [ times := [
[2020, 6, 5, 15, 38, 6, 0], [2020, 6, 5, 15, 38, 6, 0],
[2020, 6, 5, 15, 38, 6, 15959], [2020, 6, 5, 15, 38, 6, 15959000],
[2020, 6, 5, 15, 38, 6, 15959], [2020, 6, 5, 15, 38, 6, 15959000],
[2020, 6, 5, 13, 38, 6, 15959], [2020, 6, 5, 13, 38, 6, 15959000],
[2020, 6, 5, 17, 38, 6, 15959], [2020, 6, 5, 17, 38, 6, 15959000],
[2020, 11, 5, 15, 38, 6, 15959], [2020, 11, 5, 15, 38, 6, 15959000],
] ]
for i, format in formats { for i, format in formats {
t := time.parse_iso8601(format) or { t := time.parse_iso8601(format) or {
@ -89,8 +89,8 @@ fn test_parse_iso8601() {
assert t.minute == minute assert t.minute == minute
second := times[i][5] second := times[i][5]
assert t.second == second assert t.second == second
microsecond := times[i][6] nanosecond := times[i][6]
assert t.microsecond == microsecond assert t.nanosecond == nanosecond
} }
} }
@ -107,7 +107,7 @@ fn test_parse_iso8601_local() {
assert t.hour == 15 assert t.hour == 15
assert t.minute == 38 assert t.minute == 38
assert t.second == 6 assert t.second == 6
assert t.microsecond == 15959 assert t.nanosecond == 15959_000
} }
fn test_parse_iso8601_invalid() { fn test_parse_iso8601_invalid() {
@ -145,7 +145,7 @@ fn test_parse_iso8601_date_only() {
assert t.hour == 0 assert t.hour == 0
assert t.minute == 0 assert t.minute == 0
assert t.second == 0 assert t.second == 0
assert t.microsecond == 0 assert t.nanosecond == 0
} }
fn check_invalid_date(s string) { fn check_invalid_date(s string) {

View File

@ -53,13 +53,6 @@ pub fn utc() Time {
return solaris_utc() return solaris_utc()
} }
return linux_utc() return linux_utc()
/*
// defaults to most common feature, the microsecond precision is not available
// in this API call
t := C.time(0)
_ = C.time(&t)
return unix2(i64(t), 0)
*/
} }
// new_time returns a time struct with the calculated Unix time. // new_time returns a time struct with the calculated Unix time.
@ -90,7 +83,7 @@ pub fn ticks() i64 {
} $else { } $else {
ts := C.timeval{} ts := C.timeval{}
C.gettimeofday(&ts, 0) C.gettimeofday(&ts, 0)
return i64(ts.tv_sec * u64(1000) + (ts.tv_usec / u64(1000))) return i64(ts.tv_sec * u64(1000) + (ts.tv_usec / u64(1_000)))
} }
// t := i64(C.mach_absolute_time()) // t := i64(C.mach_absolute_time())
// # Nanoseconds elapsedNano = AbsoluteToNanoseconds( *(AbsoluteTime *) &t ); // # Nanoseconds elapsedNano = AbsoluteToNanoseconds( *(AbsoluteTime *) &t );
@ -105,7 +98,7 @@ pub fn (t Time) str() string {
} }
// convert_ctime converts a C time to V time. // convert_ctime converts a C time to V time.
fn convert_ctime(t C.tm, microsecond int) Time { fn convert_ctime(t C.tm, nanosecond 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
@ -113,7 +106,7 @@ fn convert_ctime(t C.tm, microsecond int) 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 nanosecond: nanosecond
unix: make_unix_time(t) unix: make_unix_time(t)
// for the actual code base when we // for the actual code base when we
// call convert_ctime, it is always // call convert_ctime, it is always

View File

@ -40,15 +40,17 @@ pub const (
// Time contains various time units for a point in time. // Time contains various time units for a point in time.
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
microsecond int nanosecond int
unix i64 unix i64
is_local bool // used to make time.now().local().local() == time.now().local() is_local bool // used to make time.now().local().local() == time.now().local()
//
microsecond int [deprecated: 'use t.nanosecond / 1000 instead'; deprecated_after: '2023-08-05']
} }
// FormatDelimiter contains different time formats. // FormatDelimiter contains different time formats.
@ -59,6 +61,7 @@ pub enum FormatTime {
hhmmss24 hhmmss24
hhmmss24_milli hhmmss24_milli
hhmmss24_micro hhmmss24_micro
hhmmss24_nano
no_time no_time
} }
@ -99,7 +102,7 @@ pub fn (t Time) smonth() string {
return time.months_string[i * 3..(i + 1) * 3] return time.months_string[i * 3..(i + 1) * 3]
} }
// unix_time returns the UNIX time. // unix_time returns the UNIX time with second resolution.
[inline] [inline]
pub fn (t Time) unix_time() i64 { pub fn (t Time) unix_time() i64 {
return t.unix return t.unix
@ -108,18 +111,39 @@ pub fn (t Time) unix_time() i64 {
// unix_time_milli returns the UNIX time with millisecond resolution. // unix_time_milli returns the UNIX time with millisecond resolution.
[inline] [inline]
pub fn (t Time) unix_time_milli() i64 { pub fn (t Time) unix_time_milli() i64 {
return t.unix * 1000 + (t.microsecond / 1000) return t.unix * 1_000 + (i64(t.nanosecond) / 1_000_000)
}
// unix_time_micro returns the UNIX time with microsecond resolution.
[inline]
pub fn (t Time) unix_time_micro() i64 {
return t.unix * 1_000_000 + (i64(t.nanosecond) / 1_000)
}
// unix_time_nano returns the UNIX time with nanosecond resolution.
[inline]
pub fn (t Time) unix_time_nano() i64 {
// TODO: use i128 here, when V supports it, since the following expression overflows for years like 3001:
return t.unix * 1_000_000_000 + i64(t.nanosecond)
} }
// add returns a new time with the given duration added. // add returns a new time with the given duration added.
pub fn (t Time) add(d Duration) Time { pub fn (t Time) add(d Duration) Time {
microseconds := i64(t.unix) * 1_000_000 + t.microsecond + d.microseconds() // This expression overflows i64 for big years (and we do not have i128 yet):
unix := microseconds / 1_000_000 // nanos := t.unix * 1_000_000_000 + i64(t.nanosecond) <-
micro := microseconds % 1_000_000 // ... so instead, handle the addition manually in parts ¯\_(ツ)_/¯
if t.is_local { mut unixs := t.unix
return unix2(unix, int(micro)).as_local() mut nanos := i64(t.nanosecond) + d.nanoseconds()
unixs += nanos / time.second
nanos = nanos % time.second
if nanos < 0 {
unixs--
nanos += time.second
} }
return unix2(unix, int(micro)) if t.is_local {
return unix_nanosecond(unixs, int(nanos)).as_local()
}
return unix_nanosecond(unixs, int(nanos))
} }
// add_seconds returns a new time struct with an added number of seconds. // add_seconds returns a new time struct with an added number of seconds.
@ -311,9 +335,9 @@ pub fn days_in_month(month int, year int) !int {
return res return res
} }
// debug returns detailed breakdown of time (`Time{ year: YYYY month: MM day: dd hour: HH: minute: mm second: ss microsecond: micros unix: unix }`) // debug returns detailed breakdown of time (`Time{ year: YYYY month: MM day: dd hour: HH: minute: mm second: ss nanosecond: nanos unix: unix }`)
pub fn (t Time) debug() string { pub fn (t Time) debug() string {
return 'Time{ year: ${t.year:04} month: ${t.month:02} day: ${t.day:02} hour: ${t.hour:02} minute: ${t.minute:02} second: ${t.second:02} microsecond: ${t.microsecond:06} unix: ${t.unix:07} }' return 'Time{ year: ${t.year:04} month: ${t.month:02} day: ${t.day:02} hour: ${t.hour:02} minute: ${t.minute:02} second: ${t.second:02} nanosecond: ${t.nanosecond:09} unix: ${t.unix:07} }'
} }
// A lot of these are taken from the Go library. // A lot of these are taken from the Go library.
@ -326,6 +350,7 @@ pub const (
second = Duration(1000 * millisecond) second = Duration(1000 * millisecond)
minute = Duration(60 * second) minute = Duration(60 * second)
hour = Duration(60 * minute) hour = Duration(60 * minute)
// day = Duration(24 * hour)
infinite = Duration(i64(9223372036854775807)) infinite = Duration(i64(9223372036854775807))
) )
@ -348,23 +373,22 @@ pub fn (d Duration) milliseconds() i64 {
// consider all of them in sub-one intervals // consider all of them in sub-one intervals
// seconds returns the duration as a floating point number of seconds. // seconds returns the duration as a floating point number of seconds.
pub fn (d Duration) seconds() f64 { pub fn (d Duration) seconds() f64 {
sec := d / time.second return f64(d) / f64(time.second)
nsec := d % time.second
return f64(sec) + f64(nsec) / time.second
} }
// minutes returns the duration as a floating point number of minutes. // minutes returns the duration as a floating point number of minutes.
pub fn (d Duration) minutes() f64 { pub fn (d Duration) minutes() f64 {
min := d / time.minute return f64(d) / f64(time.minute)
nsec := d % time.minute
return f64(min) + f64(nsec) / time.minute
} }
// hours returns the duration as a floating point number of hours. // hours returns the duration as a floating point number of hours.
pub fn (d Duration) hours() f64 { pub fn (d Duration) hours() f64 {
hr := d / time.hour return f64(d) / f64(time.hour)
nsec := d % time.hour }
return f64(hr) + f64(nsec) / time.hour
// days returns the duration as a floating point number of days.
pub fn (d Duration) days() f64 {
return f64(d) / f64(time.hour * 24)
} }
// str pretty prints the duration // str pretty prints the duration
@ -412,6 +436,35 @@ pub fn (d Duration) str() string {
return '${ns}ns' return '${ns}ns'
} }
// debug returns a detailed breakdown of the Duration, as: 'Duration: - 50days, 4h, 3m, 7s, 541ms, 78us, 9ns'
pub fn (d Duration) debug() string {
mut res := []string{}
mut x := i64(d)
mut sign := ''
if x < 0 {
sign = '- '
x = -x
}
for label, v in {
'days': 24 * time.hour
'h': time.hour
'm': time.minute
's': time.second
'ms': time.millisecond
'us': time.microsecond
} {
if x > v {
xx := x / v
x = x % v
res << xx.str() + label
}
}
if x > 0 {
res << '${x}ns'
}
return 'Duration: ${sign}${res.join(', ')}'
}
// offset returns time zone UTC offset in seconds. // offset returns time zone UTC offset in seconds.
pub fn offset() int { pub fn offset() int {
t := utc() t := utc()

View File

@ -3,8 +3,6 @@ import time
fn test_add_to_day_in_the_previous_century() { fn test_add_to_day_in_the_previous_century() {
a := time.parse_iso8601('1900-01-01')! a := time.parse_iso8601('1900-01-01')!
aa := a.add_days(180) aa := a.add_days(180)
dump(a.debug())
dump(aa.debug())
assert aa.ymmdd() == '1900-06-30' assert aa.ymmdd() == '1900-06-30'
} }
@ -23,6 +21,8 @@ fn test_add_to_day_in_the_recent_past() {
fn test_add_to_day_in_the_future_1() { fn test_add_to_day_in_the_future_1() {
a := time.parse_iso8601('3000-11-01')! a := time.parse_iso8601('3000-11-01')!
aa := a.add_days(180) aa := a.add_days(180)
dump(a.debug())
dump(aa.debug())
assert aa.ymmdd() == '3001-04-30' assert aa.ymmdd() == '3001-04-30'
} }

View File

@ -2,11 +2,10 @@ module time
#include <mach/mach_time.h> #include <mach/mach_time.h>
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 const start_time = C.mach_absolute_time()
start_time = C.mach_absolute_time()
time_base = init_time_base() const time_base = init_time_base()
)
[typedef] [typedef]
struct C.mach_timebase_info_data_t { struct C.mach_timebase_info_data_t {
@ -25,11 +24,6 @@ struct InternalTimeBase {
denom u32 = 1 denom u32 = 1
} }
pub struct C.timeval {
tv_sec u64
tv_usec u64
}
fn init_time_base() C.mach_timebase_info_data_t { fn init_time_base() C.mach_timebase_info_data_t {
tb := C.mach_timebase_info_data_t{} tb := C.mach_timebase_info_data_t{}
C.mach_timebase_info(&tb) C.mach_timebase_info(&tb)
@ -62,29 +56,22 @@ fn vpc_now_darwin() u64 {
return (tm - time.start_time) * time.time_base.numer / time.time_base.denom return (tm - time.start_time) * time.time_base.numer / time.time_base.denom
} }
// darwin_now returns a better precision current time for Darwin based operating system // darwin_now returns a better precision current time for macos
// 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 converts to local time
fn darwin_now() Time { fn darwin_now() Time {
// get the high precision time as UTC clock // get the high precision time as UTC realtime clock, and use the nanoseconds part
tv := C.timeval{} mut ts := C.timespec{}
C.gettimeofday(&tv, 0) C.clock_gettime(C.CLOCK_REALTIME, &ts)
loc_tm := C.tm{} loc_tm := C.tm{}
asec := voidptr(&tv.tv_sec) C.localtime_r(voidptr(&ts.tv_sec), &loc_tm)
C.localtime_r(asec, &loc_tm) return convert_ctime(loc_tm, int(ts.tv_nsec))
return convert_ctime(loc_tm, int(tv.tv_usec))
} }
// darwin_utc returns a better precision current time for Darwin based operating system // darwin_utc returns a better precision current time for macos
// 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
fn darwin_utc() Time { fn darwin_utc() Time {
// get the high precision time as UTC clock // get the high precision time as UTC clock
tv := C.timeval{} mut ts := C.timespec{}
C.gettimeofday(&tv, 0) C.clock_gettime(C.CLOCK_REALTIME, &ts)
return unix2(i64(tv.tv_sec), int(tv.tv_usec)) return unix_nanosecond(i64(ts.tv_sec), int(ts.tv_nsec))
} }
// dummy to compile with all compilers // dummy to compile with all compilers

View File

@ -36,7 +36,7 @@ pub fn (t Time) local() Time {
} }
loc_tm := C.tm{} loc_tm := C.tm{}
C.localtime_r(voidptr(&t.unix), &loc_tm) C.localtime_r(voidptr(&t.unix), &loc_tm)
return convert_ctime(loc_tm, t.microsecond) return convert_ctime(loc_tm, t.nanosecond)
} }
// in most systems, these are __quad_t, which is an i64 // in most systems, these are __quad_t, which is an i64
@ -58,7 +58,7 @@ pub fn sys_mono_now() u64 {
} $else { } $else {
ts := C.timespec{} ts := C.timespec{}
C.clock_gettime(C.CLOCK_MONOTONIC, &ts) C.clock_gettime(C.CLOCK_MONOTONIC, &ts)
return u64(ts.tv_sec) * 1000000000 + u64(ts.tv_nsec) return u64(ts.tv_sec) * 1_000_000_000 + u64(ts.tv_nsec)
} }
} }
@ -68,7 +68,7 @@ pub fn sys_mono_now() u64 {
fn vpc_now() u64 { fn vpc_now() u64 {
ts := C.timespec{} ts := C.timespec{}
C.clock_gettime(C.CLOCK_MONOTONIC, &ts) C.clock_gettime(C.CLOCK_MONOTONIC, &ts)
return u64(ts.tv_sec) * 1000000000 + u64(ts.tv_nsec) return u64(ts.tv_sec) * 1_000_000_000 + u64(ts.tv_nsec)
} }
// The linux_* functions are placed here, since they're used on Android as well // The linux_* functions are placed here, since they're used on Android as well
@ -83,7 +83,7 @@ fn linux_now() Time {
C.clock_gettime(C.CLOCK_REALTIME, &ts) C.clock_gettime(C.CLOCK_REALTIME, &ts)
loc_tm := C.tm{} loc_tm := C.tm{}
C.localtime_r(voidptr(&ts.tv_sec), &loc_tm) C.localtime_r(voidptr(&ts.tv_sec), &loc_tm)
return convert_ctime(loc_tm, int(ts.tv_nsec / 1000)) return convert_ctime(loc_tm, int(ts.tv_nsec))
} }
fn linux_utc() Time { fn linux_utc() Time {
@ -91,7 +91,7 @@ fn linux_utc() Time {
// and use the nanoseconds part // and use the nanoseconds part
mut ts := C.timespec{} mut ts := C.timespec{}
C.clock_gettime(C.CLOCK_REALTIME, &ts) C.clock_gettime(C.CLOCK_REALTIME, &ts)
return unix2(i64(ts.tv_sec), int(ts.tv_nsec / 1000)) return unix_nanosecond(i64(ts.tv_sec), int(ts.tv_nsec))
} }
// dummy to compile with all compilers // dummy to compile with all compilers
@ -104,12 +104,6 @@ fn win_utc() Time {
return Time{} return Time{}
} }
// dummy to compile with all compilers
pub struct C.timeval {
tv_sec u64
tv_usec u64
}
// return absolute timespec for now()+d // return absolute timespec for now()+d
pub fn (d Duration) timespec() C.timespec { pub fn (d Duration) timespec() C.timespec {
mut ts := C.timespec{} mut ts := C.timespec{}

View File

@ -10,7 +10,7 @@ fn solaris_now() Time {
C.clock_gettime(C.CLOCK_REALTIME, &ts) C.clock_gettime(C.CLOCK_REALTIME, &ts)
loc_tm := C.tm{} loc_tm := C.tm{}
C.localtime_r(voidptr(&ts.tv_sec), &loc_tm) C.localtime_r(voidptr(&ts.tv_sec), &loc_tm)
return convert_ctime(loc_tm, int(ts.tv_nsec / 1000)) return convert_ctime(loc_tm, int(ts.tv_nsec))
} }
fn solaris_utc() Time { fn solaris_utc() Time {
@ -18,7 +18,7 @@ fn solaris_utc() Time {
// and use the nanoseconds part // and use the nanoseconds part
mut ts := C.timespec{} mut ts := C.timespec{}
C.clock_gettime(C.CLOCK_REALTIME, &ts) C.clock_gettime(C.CLOCK_REALTIME, &ts)
return unix2(i64(ts.tv_sec), int(ts.tv_nsec / 1000)) return unix_nanosecond(i64(ts.tv_sec), int(ts.tv_nsec))
} }
// dummy to compile with all compilers // dummy to compile with all compilers

View File

@ -1,18 +1,16 @@
import time import time
import math import math
const ( const time_to_test = time.Time{
time_to_test = time.Time{ year: 1980
year: 1980 month: 7
month: 7 day: 11
day: 11 hour: 21
hour: 21 minute: 23
minute: 23 second: 42
second: 42 nanosecond: 123456789
microsecond: 123456 unix: 332198622
unix: 332198622 }
}
)
fn test_is_leap_year() { fn test_is_leap_year() {
// 1996 % 4 = 0 and 1996 % 100 > 0 // 1996 % 4 = 0 and 1996 % 100 > 0
@ -83,6 +81,14 @@ fn test_unix() {
assert t6.second == 29 assert t6.second == 29
} }
fn test_format_rfc3339() {
// assert '1980-07-11T19:23:42.123Z'
res := time_to_test.format_rfc3339()
assert res.ends_with('23:42.123Z')
assert res.starts_with('1980-07-1')
assert res.contains('T')
}
fn test_format_ss() { fn test_format_ss() {
assert '11.07.1980 21:23:42' == time_to_test.get_fmt_str(.dot, .hhmmss24, .ddmmyyyy) assert '11.07.1980 21:23:42' == time_to_test.get_fmt_str(.dot, .hhmmss24, .ddmmyyyy)
} }
@ -93,20 +99,18 @@ fn test_format_ss_milli() {
assert '1980-07-11 21:23:42.123' == time_to_test.format_ss_milli() assert '1980-07-11 21:23:42.123' == time_to_test.format_ss_milli()
} }
fn test_format_rfc3339() {
// assert '1980-07-11T19:23:42.123Z'
res := time_to_test.format_rfc3339()
assert res.ends_with('23:42.123Z')
assert res.starts_with('1980-07-1')
assert res.contains('T')
}
fn test_format_ss_micro() { fn test_format_ss_micro() {
assert '11.07.1980 21:23:42.123456' == time_to_test.get_fmt_str(.dot, .hhmmss24_micro, assert '11.07.1980 21:23:42.123456' == time_to_test.get_fmt_str(.dot, .hhmmss24_micro,
.ddmmyyyy) .ddmmyyyy)
assert '1980-07-11 21:23:42.123456' == time_to_test.format_ss_micro() assert '1980-07-11 21:23:42.123456' == time_to_test.format_ss_micro()
} }
fn test_format_ss_nano() {
assert '11.07.1980 21:23:42.123456789' == time_to_test.get_fmt_str(.dot, .hhmmss24_nano,
.ddmmyyyy)
assert '1980-07-11 21:23:42.123456789' == time_to_test.format_ss_nano()
}
fn test_smonth() { fn test_smonth() {
month_names := ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', month_names := ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov',
'Dec'] 'Dec']
@ -180,21 +184,29 @@ fn test_weekday_str() {
fn test_add() { fn test_add() {
d_seconds := 3 d_seconds := 3
d_microseconds := 13 d_nanoseconds := 13
duration := time.Duration(d_seconds * time.second + d_microseconds * time.microsecond) duration := time.Duration(d_seconds * time.second + d_nanoseconds * time.nanosecond)
// dump(duration.debug())
t1 := time_to_test t1 := time_to_test
// dump(t1.debug())
t2 := time_to_test.add(duration) t2 := time_to_test.add(duration)
// dump(t2.debug())
assert t2.second == t1.second + d_seconds assert t2.second == t1.second + d_seconds
assert t2.microsecond == t1.microsecond + d_microseconds assert t2.nanosecond == t1.nanosecond + d_nanoseconds
assert t2.unix == t1.unix + d_seconds assert t2.unix == t1.unix + d_seconds
assert t2.is_local == t1.is_local assert t2.is_local == t1.is_local
//
t3 := time_to_test.add(-duration) t3 := time_to_test.add(-duration)
// dump(t3.debug())
assert t3.second == t1.second - d_seconds assert t3.second == t1.second - d_seconds
assert t3.microsecond == t1.microsecond - d_microseconds assert t3.nanosecond == t1.nanosecond - d_nanoseconds
assert t3.unix == t1.unix - d_seconds assert t3.unix == t1.unix - d_seconds
assert t3.is_local == t1.is_local assert t3.is_local == t1.is_local
//
t4 := time_to_test.as_local() t4 := time_to_test.as_local()
// dump(t4.debug())
t5 := t4.add(duration) t5 := t4.add(duration)
// dump(t5.debug())
assert t5.is_local == t4.is_local assert t5.is_local == t4.is_local
} }
@ -220,13 +232,14 @@ fn test_now() {
assert now.minute < 60 assert now.minute < 60
assert now.second >= 0 assert now.second >= 0
assert now.second <= 60 // <= 60 cause of leap seconds assert now.second <= 60 // <= 60 cause of leap seconds
assert now.microsecond >= 0 assert now.nanosecond >= 0
assert now.microsecond < 1000000 assert now.nanosecond < time.second
} }
fn test_utc() { fn test_utc() {
now := time.utc() now := time.utc()
// The year the test was built // The year the test was built
// dump(now.debug())
assert now.year >= 2020 assert now.year >= 2020
assert now.month > 0 assert now.month > 0
assert now.month <= 12 assert now.month <= 12
@ -234,20 +247,20 @@ fn test_utc() {
assert now.minute < 60 assert now.minute < 60
assert now.second >= 0 assert now.second >= 0
assert now.second <= 60 // <= 60 cause of leap seconds assert now.second <= 60 // <= 60 cause of leap seconds
assert now.microsecond >= 0 assert now.nanosecond >= 0
assert now.microsecond < 1000000 assert now.nanosecond < time.second
} }
fn test_unix_time() { fn test_unix_time() {
t1 := time.utc() t1 := time.utc()
time.sleep(50 * time.millisecond) time.sleep(50 * time.millisecond)
t2 := time.utc() t2 := time.utc()
eprintln('t1: ${t1}') eprintln(' t1: ${t1}')
eprintln('t2: ${t2}') eprintln(' t2: ${t2}')
ut1 := t1.unix_time() ut1 := t1.unix_time()
ut2 := t2.unix_time() ut2 := t2.unix_time()
eprintln('ut1: ${ut1}') eprintln(' ut1: ${ut1}')
eprintln('ut2: ${ut2}') eprintln(' ut2: ${ut2}')
assert ut2 - ut1 < 2 assert ut2 - ut1 < 2
// //
utm1 := t1.unix_time_milli() utm1 := t1.unix_time_milli()

View File

@ -39,6 +39,8 @@ fn C.SystemTimeToTzSpecificLocalTime(lpTimeZoneInformation &C.TIME_ZONE_INFORMAT
fn C.localtime_s(t &C.time_t, tm &C.tm) fn C.localtime_s(t &C.time_t, tm &C.tm)
fn C.timespec_get(t &C.timespec, base int) int
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()
@ -107,7 +109,7 @@ pub fn (t Time) local() Time {
hour: u16(t.hour) hour: u16(t.hour)
minute: u16(t.minute) minute: u16(t.minute)
second: u16(t.second) second: u16(t.second)
millisecond: u16(t.microsecond / 1000) millisecond: u16(t.nanosecond / 1_000_000)
} }
st_local := SystemTime{} st_local := SystemTime{}
C.SystemTimeToTzSpecificLocalTime(unsafe { nil }, &st_utc, &st_local) C.SystemTimeToTzSpecificLocalTime(unsafe { nil }, &st_utc, &st_local)
@ -118,7 +120,7 @@ pub fn (t Time) local() Time {
hour: st_local.hour hour: st_local.hour
minute: st_local.minute minute: st_local.minute
second: st_local.second // These are the same second: st_local.second // These are the same
microsecond: int(st_local.millisecond) * 1000 nanosecond: int(st_local.millisecond) * 1_000_000
unix: st_local.unix_time() unix: st_local.unix_time()
} }
return t_local return t_local
@ -141,7 +143,7 @@ fn win_now() Time {
hour: st_local.hour hour: st_local.hour
minute: st_local.minute minute: st_local.minute
second: st_local.second second: st_local.second
microsecond: int(st_local.millisecond) * 1000 nanosecond: int(st_local.millisecond) * 1_000_000
unix: st_local.unix_time() unix: st_local.unix_time()
is_local: true is_local: true
} }
@ -163,7 +165,7 @@ fn win_utc() Time {
hour: st_utc.hour hour: st_utc.hour
minute: st_utc.minute minute: st_utc.minute
second: st_utc.second second: st_utc.second
microsecond: int(st_utc.millisecond) * 1000 nanosecond: int(st_utc.millisecond) * 1_000_000
unix: st_utc.unix_time() unix: st_utc.unix_time()
is_local: false is_local: false
} }
@ -213,12 +215,6 @@ fn solaris_utc() Time {
return Time{} return Time{}
} }
// dummy to compile with all compilers
pub struct C.timeval {
tv_sec u64
tv_usec u64
}
// sleep makes the calling thread sleep for a given duration (in nanoseconds). // sleep makes the calling thread sleep for a given duration (in nanoseconds).
pub fn sleep(duration Duration) { pub fn sleep(duration Duration) {
C.Sleep(int(duration / millisecond)) C.Sleep(int(duration / millisecond))

View File

@ -3,7 +3,7 @@
// that can be found in the LICENSE file. // that can be found in the LICENSE file.
module time module time
// unix returns a time struct from Unix time. // unix returns a time struct from an Unix timestamp (number of seconds since 1970-01-01)
pub fn unix(abs i64) Time { pub fn unix(abs i64) Time {
// Split into day and time // Split into day and time
mut day_offset := abs / seconds_per_day mut day_offset := abs / seconds_per_day
@ -24,8 +24,20 @@ pub fn unix(abs i64) Time {
} }
} }
// unix2 returns a time struct from Unix time and microsecond value // unix2 returns a Time struct, given an Unix timestamp in seconds, and a microsecond value
[deprecated: 'use unix_microsecond(unix_ts, us) instead']
[deprecated_after: '2023-09-05']
pub fn unix2(abs i64, microsecond int) Time { pub fn unix2(abs i64, microsecond int) Time {
return unix_nanosecond(abs, microsecond * 1000)
}
// unix_microsecond returns a Time struct, given an Unix timestamp in seconds, and a microsecond value
pub fn unix_microsecond(abs i64, microsecond int) Time {
return unix_nanosecond(abs, microsecond * 1000)
}
// unix_nanosecond returns a Time struct, given an Unix timestamp in seconds, and a nanosecond value
pub fn unix_nanosecond(abs i64, nanosecond int) Time {
// Split into day and time // Split into day and time
mut day_offset := abs / seconds_per_day mut day_offset := abs / seconds_per_day
if abs % seconds_per_day < 0 { if abs % seconds_per_day < 0 {
@ -41,7 +53,7 @@ pub fn unix2(abs i64, microsecond int) Time {
hour: hr hour: hr
minute: min minute: min
second: sec second: sec
microsecond: microsecond nanosecond: nanosecond
unix: abs unix: abs
} }
} }