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
logger &log.Log // logger used to log messages
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

View File

@ -747,6 +747,6 @@ fn test_utime() {
f.write_string(hello) or { panic(err) }
atime := time.now().add_days(2).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
}

View File

@ -275,7 +275,7 @@ const (
// users or business transactions.
// (https://news.ycombinator.com/item?id=14526173)
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`.

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() {
t := time.utc().unix_time_milli()
t := u64(time.utc().unix_time_milli())
mut ulid1 := ''
mut ulid2 := ''
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*.
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.
[inline]
pub fn (lhs Time) - (rhs Time) Duration {
lhs_micro := lhs.unix * 1000 * 1000 + u64(lhs.microsecond)
rhs_micro := rhs.unix * 1000 * 1000 + u64(rhs.microsecond)
return (i64(lhs_micro) - i64(rhs_micro)) * microsecond
lhs_micro := lhs.unix * 1_000_000 + lhs.microsecond
rhs_micro := rhs.unix * 1_000_000 + rhs.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
if unix_offset < 0 {
unix_time -= u64(-unix_offset)
unix_time -= (-unix_offset)
} else if unix_offset > 0 {
unix_time += u64(unix_offset)
unix_time += unix_offset
}
t = unix2(i64(unix_time), t.microsecond)
return t

View File

@ -50,7 +50,7 @@ pub:
minute int
second int
microsecond int
unix u64
unix i64
}
// FormatDelimiter contains different time formats.
@ -162,7 +162,7 @@ pub fn new_time(t Time) Time {
tm_mon: t.month - 1
tm_year: t.year - 1900
}
utime := u64(make_unix_time(tt))
utime := make_unix_time(tt)
return Time{
...t
unix: utime
@ -171,21 +171,21 @@ pub fn new_time(t Time) Time {
// unix_time returns Unix time.
[inline]
pub fn (t Time) unix_time() int {
return int(t.unix)
pub fn (t Time) unix_time() i64 {
return t.unix
}
// unix_time_milli returns Unix time with millisecond resolution.
[inline]
pub fn (t Time) unix_time_milli() u64 {
return t.unix * 1000 + u64(t.microsecond / 1000)
pub fn (t Time) unix_time_milli() i64 {
return t.unix * 1000 + (t.microsecond / 1000)
}
// add returns a new time that duration is added
pub fn (t Time) add(d Duration) Time {
microseconds := i64(t.unix) * 1000 * 1000 + t.microsecond + d.microseconds()
unix := microseconds / (1000 * 1000)
micro := microseconds % (1000 * 1000)
microseconds := i64(t.unix) * 1_000_000 + t.microsecond + d.microseconds()
unix := microseconds / 1_000_000
micro := microseconds % 1_000_000
return unix2(unix, int(micro))
}
@ -358,6 +358,11 @@ pub fn (t Time) str() string {
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.
fn convert_ctime(t C.tm, microsecond int) Time {
return Time{
@ -368,7 +373,7 @@ fn convert_ctime(t C.tm, microsecond int) Time {
minute: t.tm_min
second: t.tm_sec
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.localtime_r(t &time_t, tm &C.tm)
fn make_unix_time(t C.tm) int {
return int(C.timegm(&t))
fn make_unix_time(t C.tm) i64 {
return i64(C.timegm(&t))
}
// 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)
assert t2.second == t1.second + d_seconds
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)
assert t3.second == t1.second - d_seconds
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() {
num_of_days := 3
t := time_to_test.add_days(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() {
@ -217,8 +217,8 @@ fn test_unix_time() {
//
utm1 := t1.unix_time_milli()
utm2 := t2.unix_time_milli()
assert (utm1 - u64(ut1) * 1000) < 1000
assert (utm2 - u64(ut2) * 1000) < 1000
assert (utm1 - ut1 * 1000) < 1000
assert (utm2 - ut2 * 1000) < 1000
//
// println('utm1: $utm1 | utm2: $utm2')
assert utm2 - utm1 > 2

View File

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

View File

@ -20,7 +20,7 @@ pub fn unix(abs int) Time {
hour: hr
minute: min
second: sec
unix: u64(abs)
unix: abs
}
}
@ -42,7 +42,7 @@ pub fn unix2(abs i64, microsecond int) Time {
minute: min
second: sec
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 {
mut files_salt := ''
mut latest_modified := u64(0)
mut latest_modified := i64(0)
for asset in am.get_assets(asset_type) {
files_salt += asset.file_path
if asset.last_modified.unix > latest_modified {
@ -151,7 +151,7 @@ fn (mut am AssetManager) add(asset_type string, file string) bool {
asset := Asset{
file_path: file
last_modified: time.Time{
unix: u64(os.file_last_mod_unix(file))
unix: os.file_last_mod_unix(file)
}
}
if asset_type == 'css' {