time: turn Time.unix to i64, so it can represent times before 1970-01-01, fix time operators, add more tests (#11050)

pull/11051/head
Delyan Angelov 2021-08-04 13:12:02 +03:00 committed by GitHub
parent 1bf6d04e37
commit efa8dcf4d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 107 additions and 40 deletions

View File

@ -38,7 +38,7 @@ pub mut:
state State // current state of connection state State // current state of connection
logger &log.Log // logger used to log messages logger &log.Log // logger used to log messages
resource_name string // name of current resource resource_name string // name of current resource
last_pong_ut u64 // last time in unix time we got a pong message last_pong_ut i64 // last time in unix time we got a pong message
} }
// Flag represents different types of headers in websocket handshake // Flag represents different types of headers in websocket handshake

View File

@ -747,6 +747,6 @@ fn test_utime() {
f.write_string(hello) or { panic(err) } f.write_string(hello) or { panic(err) }
atime := time.now().add_days(2).unix_time() atime := time.now().add_days(2).unix_time()
mtime := time.now().add_days(4).unix_time() mtime := time.now().add_days(4).unix_time()
os.utime(filename, atime, mtime) or { panic(err) } os.utime(filename, int(atime), int(mtime)) or { panic(err) }
assert os.file_last_mod_unix(filename) == mtime assert os.file_last_mod_unix(filename) == mtime
} }

View File

@ -275,7 +275,7 @@ const (
// users or business transactions. // users or business transactions.
// (https://news.ycombinator.com/item?id=14526173) // (https://news.ycombinator.com/item?id=14526173)
pub fn ulid() string { pub fn ulid() string {
return ulid_at_millisecond(time.utc().unix_time_milli()) return ulid_at_millisecond(u64(time.utc().unix_time_milli()))
} }
// ulid_at_millisecond does the same as `ulid` but takes a custom Unix millisecond timestamp via `unix_time_milli`. // ulid_at_millisecond does the same as `ulid` but takes a custom Unix millisecond timestamp via `unix_time_milli`.

View File

@ -37,7 +37,7 @@ fn test_ulids_max_start_character_is_ok() {
} }
fn test_ulids_generated_in_the_same_millisecond_have_the_same_prefix() { fn test_ulids_generated_in_the_same_millisecond_have_the_same_prefix() {
t := time.utc().unix_time_milli() t := u64(time.utc().unix_time_milli())
mut ulid1 := '' mut ulid1 := ''
mut ulid2 := '' mut ulid2 := ''
mut ulid3 := '' mut ulid3 := ''

31
vlib/time/chrono.v 100644
View File

@ -0,0 +1,31 @@
module time
// days_from_civil - return the number of days since the
// Unix epoch 1970-01-01. A detailed description of the algorithm here
// is in: http://howardhinnant.github.io/date_algorithms.html
// Note that it will return negative values for days before 1970-01-01.
pub fn days_from_civil(oy int, m int, d int) int {
y := if m <= 2 { oy - 1 } else { oy }
era := y / 400
yoe := y - era * 400 // [0, 399]
doy := (153 * (m + (if m > 2 { -3 } else { 9 })) + 2) / 5 + d - 1 // [0, 365]
doe := yoe * 365 + yoe / 4 - yoe / 100 + doy // [0, 146096]
return era * 146097 + doe - 719468
}
// portable_timegm does the same as C._mkgmtime, but unlike it,
// can work with dates before the Unix epoch of 1970-01-01 .
pub fn portable_timegm(t &C.tm) i64 {
mut year := t.tm_year + 1900
mut month := t.tm_mon // 0-11
if month > 11 {
year += month / 12
month %= 12
} else if month < 0 {
years_diff := (11 - month) / 12
year -= years_diff
month += 12 * years_diff
}
days_since_1970 := i64(days_from_civil(year, month + 1, t.tm_mday))
return 60 * (60 * (24 * days_since_1970 + t.tm_hour) + t.tm_min) + t.tm_sec
}

View File

@ -9,5 +9,5 @@ const (
// random returns a random time struct in *the past*. // random returns a random time struct in *the past*.
pub fn random() time.Time { pub fn random() time.Time {
return time.unix(int(rand.u64n(misc.start_time_unix))) return time.unix(int(rand.i64n(misc.start_time_unix)))
} }

View File

@ -15,7 +15,7 @@ pub fn (t1 Time) < (t2 Time) bool {
// 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 * 1000 * 1000 + u64(lhs.microsecond) lhs_micro := lhs.unix * 1_000_000 + lhs.microsecond
rhs_micro := rhs.unix * 1000 * 1000 + u64(rhs.microsecond) rhs_micro := rhs.unix * 1_000_000 + rhs.microsecond
return (i64(lhs_micro) - i64(rhs_micro)) * microsecond return (lhs_micro - rhs_micro) * microsecond
} }

View File

@ -131,9 +131,9 @@ pub fn parse_iso8601(s string) ?Time {
} }
mut unix_time := t.unix mut unix_time := t.unix
if unix_offset < 0 { if unix_offset < 0 {
unix_time -= u64(-unix_offset) unix_time -= (-unix_offset)
} else if unix_offset > 0 { } else if unix_offset > 0 {
unix_time += u64(unix_offset) unix_time += unix_offset
} }
t = unix2(i64(unix_time), t.microsecond) t = unix2(i64(unix_time), t.microsecond)
return t return t

View File

@ -50,7 +50,7 @@ pub:
minute int minute int
second int second int
microsecond int microsecond int
unix u64 unix i64
} }
// FormatDelimiter contains different time formats. // FormatDelimiter contains different time formats.
@ -162,7 +162,7 @@ pub fn new_time(t Time) Time {
tm_mon: t.month - 1 tm_mon: t.month - 1
tm_year: t.year - 1900 tm_year: t.year - 1900
} }
utime := u64(make_unix_time(tt)) utime := make_unix_time(tt)
return Time{ return Time{
...t ...t
unix: utime unix: utime
@ -171,21 +171,21 @@ pub fn new_time(t Time) Time {
// unix_time returns Unix time. // unix_time returns Unix time.
[inline] [inline]
pub fn (t Time) unix_time() int { pub fn (t Time) unix_time() i64 {
return int(t.unix) return t.unix
} }
// unix_time_milli returns Unix time with millisecond resolution. // unix_time_milli returns Unix time with millisecond resolution.
[inline] [inline]
pub fn (t Time) unix_time_milli() u64 { pub fn (t Time) unix_time_milli() i64 {
return t.unix * 1000 + u64(t.microsecond / 1000) return t.unix * 1000 + (t.microsecond / 1000)
} }
// add returns a new time that duration is added // add returns a new time that duration is added
pub fn (t Time) add(d Duration) Time { pub fn (t Time) add(d Duration) Time {
microseconds := i64(t.unix) * 1000 * 1000 + t.microsecond + d.microseconds() microseconds := i64(t.unix) * 1_000_000 + t.microsecond + d.microseconds()
unix := microseconds / (1000 * 1000) unix := microseconds / 1_000_000
micro := microseconds % (1000 * 1000) micro := microseconds % 1_000_000
return unix2(unix, int(micro)) return unix2(unix, int(micro))
} }
@ -358,6 +358,11 @@ pub fn (t Time) str() string {
return t.format_ss() return t.format_ss()
} }
// str returns time in the same format as `parse` expects ("YYYY-MM-DD HH:MM:SS").
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} }'
}
// 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, microsecond int) Time {
return Time{ return Time{
@ -368,7 +373,7 @@ fn convert_ctime(t C.tm, microsecond int) Time {
minute: t.tm_min minute: t.tm_min
second: t.tm_sec second: t.tm_sec
microsecond: time.microsecond microsecond: time.microsecond
unix: u64(make_unix_time(t)) unix: make_unix_time(t)
} }
} }

View File

@ -0,0 +1,33 @@
import time
fn test_add_to_day_in_the_previous_century() ? {
a := time.parse_iso8601('1900-01-01') ?
aa := a.add_days(180)
dump(a.debug())
dump(aa.debug())
assert aa.ymmdd() == '1900-06-29'
}
fn test_add_to_day_in_the_past() ? {
a := time.parse_iso8601('1990-03-01') ?
aa := a.add_days(180)
assert aa.ymmdd() == '1990-08-27'
}
fn test_add_to_day_in_the_recent_past() ? {
a := time.parse_iso8601('2021-03-01') ?
aa := a.add_days(180)
assert aa.ymmdd() == '2021-08-28'
}
fn test_add_to_day_in_the_future_1() ? {
a := time.parse_iso8601('3000-11-01') ?
aa := a.add_days(180)
assert aa.ymmdd() == '3001-04-30'
}
fn test_add_to_day_in_the_future_2() ? {
a := time.parse_iso8601('3000-12-30') ?
aa := a.add_days(180)
assert aa.ymmdd() == '3001-06-28'
}

View File

@ -23,8 +23,8 @@ fn C.timegm(&C.tm) C.time_t
// fn C.gmtime_r(&tm, &gbuf) // fn C.gmtime_r(&tm, &gbuf)
fn C.localtime_r(t &time_t, tm &C.tm) fn C.localtime_r(t &time_t, tm &C.tm)
fn make_unix_time(t C.tm) int { fn make_unix_time(t C.tm) i64 {
return int(C.timegm(&t)) return i64(C.timegm(&t))
} }
// local returns t with the location set to local time. // local returns t with the location set to local time.

View File

@ -160,18 +160,18 @@ fn test_add() {
t2 := time_to_test.add(duration) t2 := time_to_test.add(duration)
assert t2.second == t1.second + d_seconds assert t2.second == t1.second + d_seconds
assert t2.microsecond == t1.microsecond + d_microseconds assert t2.microsecond == t1.microsecond + d_microseconds
assert t2.unix == t1.unix + u64(d_seconds) assert t2.unix == t1.unix + d_seconds
t3 := time_to_test.add(-duration) t3 := time_to_test.add(-duration)
assert t3.second == t1.second - d_seconds assert t3.second == t1.second - d_seconds
assert t3.microsecond == t1.microsecond - d_microseconds assert t3.microsecond == t1.microsecond - d_microseconds
assert t3.unix == t1.unix - u64(d_seconds) assert t3.unix == t1.unix - d_seconds
} }
fn test_add_days() { fn test_add_days() {
num_of_days := 3 num_of_days := 3
t := time_to_test.add_days(num_of_days) t := time_to_test.add_days(num_of_days)
assert t.day == time_to_test.day + num_of_days assert t.day == time_to_test.day + num_of_days
assert t.unix == time_to_test.unix + 86400 * u64(num_of_days) assert t.unix == time_to_test.unix + 86400 * num_of_days
} }
fn test_str() { fn test_str() {
@ -217,8 +217,8 @@ fn test_unix_time() {
// //
utm1 := t1.unix_time_milli() utm1 := t1.unix_time_milli()
utm2 := t2.unix_time_milli() utm2 := t2.unix_time_milli()
assert (utm1 - u64(ut1) * 1000) < 1000 assert (utm1 - ut1 * 1000) < 1000
assert (utm2 - u64(ut2) * 1000) < 1000 assert (utm2 - ut2 * 1000) < 1000
// //
// println('utm1: $utm1 | utm2: $utm2') // println('utm1: $utm1 | utm2: $utm2')
assert utm2 - utm1 > 2 assert utm2 - utm1 > 2

View File

@ -52,14 +52,12 @@ struct C.timespec {
tv_nsec i64 tv_nsec i64
} }
fn C._mkgmtime(&C.tm) C.time_t
fn C.QueryPerformanceCounter(&u64) C.BOOL fn C.QueryPerformanceCounter(&u64) C.BOOL
fn C.QueryPerformanceFrequency(&u64) C.BOOL fn C.QueryPerformanceFrequency(&u64) C.BOOL
fn make_unix_time(t C.tm) int { fn make_unix_time(t C.tm) i64 {
return int(C._mkgmtime(&t)) return portable_timegm(&t)
} }
fn init_win_time_freq() u64 { fn init_win_time_freq() u64 {
@ -91,7 +89,7 @@ fn vpc_now() u64 {
} }
// local_as_unix_time returns the current local time as unix time // local_as_unix_time returns the current local time as unix time
fn local_as_unix_time() int { fn local_as_unix_time() i64 {
t := C.time(0) t := C.time(0)
tm := C.localtime(&t) tm := C.localtime(&t)
return make_unix_time(tm) return make_unix_time(tm)
@ -117,7 +115,7 @@ pub fn (t Time) local() Time {
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: st_local.millisecond * 1000 microsecond: st_local.millisecond * 1000
unix: u64(st_local.unix_time()) unix: st_local.unix_time()
} }
return t_local return t_local
} }
@ -140,7 +138,7 @@ fn win_now() Time {
minute: st_local.minute minute: st_local.minute
second: st_local.second second: st_local.second
microsecond: st_local.millisecond * 1000 microsecond: st_local.millisecond * 1000
unix: u64(st_local.unix_time()) unix: st_local.unix_time()
} }
return t return t
} }
@ -161,13 +159,13 @@ fn win_utc() Time {
minute: st_utc.minute minute: st_utc.minute
second: st_utc.second second: st_utc.second
microsecond: st_utc.millisecond * 1000 microsecond: st_utc.millisecond * 1000
unix: u64(st_utc.unix_time()) unix: st_utc.unix_time()
} }
return t return t
} }
// unix_time returns Unix time. // unix_time returns Unix time.
pub fn (st SystemTime) unix_time() int { pub fn (st SystemTime) unix_time() i64 {
tt := C.tm{ tt := C.tm{
tm_sec: st.second tm_sec: st.second
tm_min: st.minute tm_min: st.minute

View File

@ -20,7 +20,7 @@ pub fn unix(abs int) Time {
hour: hr hour: hr
minute: min minute: min
second: sec second: sec
unix: u64(abs) unix: abs
} }
} }
@ -42,7 +42,7 @@ pub fn unix2(abs i64, microsecond int) Time {
minute: min minute: min
second: sec second: sec
microsecond: microsecond microsecond: microsecond
unix: u64(abs) unix: abs
} }
} }

View File

@ -106,7 +106,7 @@ fn (am AssetManager) combine(asset_type string, to_file bool) string {
fn (am AssetManager) get_cache_key(asset_type string) string { fn (am AssetManager) get_cache_key(asset_type string) string {
mut files_salt := '' mut files_salt := ''
mut latest_modified := u64(0) mut latest_modified := i64(0)
for asset in am.get_assets(asset_type) { for asset in am.get_assets(asset_type) {
files_salt += asset.file_path files_salt += asset.file_path
if asset.last_modified.unix > latest_modified { if asset.last_modified.unix > latest_modified {
@ -151,7 +151,7 @@ fn (mut am AssetManager) add(asset_type string, file string) bool {
asset := Asset{ asset := Asset{
file_path: file file_path: file
last_modified: time.Time{ last_modified: time.Time{
unix: u64(os.file_last_mod_unix(file)) unix: os.file_last_mod_unix(file)
} }
} }
if asset_type == 'css' { if asset_type == 'css' {