From 9c8769503fdd41e8cbcc7a250594f8cdb8794f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20Hellstr=C3=B6m?= Date: Sun, 7 Jun 2020 15:19:09 +0200 Subject: [PATCH] time: add microsecond precision to Time struct --- vlib/builtin/cfns.c.v | 1 + vlib/net/websocket/ws.v | 4 +- vlib/time/operator.v | 40 ++++ vlib/time/operator_test.v | 409 +++++++++++++++++++++++++++++++++++++ vlib/time/parse.v | 62 ++++++ vlib/time/parse_test.v | 32 ++- vlib/time/private_test.v | 16 ++ vlib/time/time.v | 35 +++- vlib/time/time_darwin.c.v | 31 +++ vlib/time/time_linux.c.v | 38 +++- vlib/time/time_nix.c.v | 14 ++ vlib/time/time_solaris.c.v | 26 +++ vlib/time/time_test.v | 17 ++ vlib/time/time_windows.c.v | 101 ++++++++- vlib/time/unix.v | 23 +++ 15 files changed, 832 insertions(+), 17 deletions(-) create mode 100644 vlib/time/operator.v create mode 100644 vlib/time/operator_test.v create mode 100644 vlib/time/private_test.v create mode 100644 vlib/time/time_solaris.c.v diff --git a/vlib/builtin/cfns.c.v b/vlib/builtin/cfns.c.v index 1d25deaa55..aa62df1dd9 100644 --- a/vlib/builtin/cfns.c.v +++ b/vlib/builtin/cfns.c.v @@ -20,6 +20,7 @@ fn C.sprintf(a ...voidptr) int fn C.strlen(s byteptr) int +fn C.sscanf(byteptr, byteptr,...byteptr) int fn C.isdigit(s byteptr) bool // stdio.h diff --git a/vlib/net/websocket/ws.v b/vlib/net/websocket/ws.v index ee81ef93ea..1605112029 100644 --- a/vlib/net/websocket/ws.v +++ b/vlib/net/websocket/ws.v @@ -103,8 +103,6 @@ pub fn new(uri string) &Client { return ws } -fn C.sscanf() int - fn (ws &Client) parse_uri() &Uri { u := urllib.parse(ws.uri) or { panic(err) @@ -572,7 +570,7 @@ fn (mut ws Client) send_control_frame(code OPCode, frame_typ string, payload []b mut bytes_written := -1 if ws.socket.sockfd <= 0 { l.e('No socket opened.') - unsafe { + unsafe { payload.free() } l.c('send_control_frame: error sending ${frame_typ} control frame.') diff --git a/vlib/time/operator.v b/vlib/time/operator.v new file mode 100644 index 0000000000..46f1035dfa --- /dev/null +++ b/vlib/time/operator.v @@ -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) +} \ No newline at end of file diff --git a/vlib/time/operator_test.v b/vlib/time/operator_test.v new file mode 100644 index 0000000000..5eaad58660 --- /dev/null +++ b/vlib/time/operator_test.v @@ -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) +} diff --git a/vlib/time/parse.v b/vlib/time/parse.v index 156c1032b2..bb0265057b 100644 --- a/vlib/time/parse.v +++ b/vlib/time/parse.v @@ -47,3 +47,65 @@ pub fn parse_rfc2822(s string) ?Time { return parse(tos(tmstr, count)) } + +// parse_rfc8601 parses rfc8601 time format yyyy-MM-ddTHH:mm:ss.dddddd+dd:dd as local time +// the fraction part is difference in milli seconds and the last part is offset +// from UTC time and can be both +/- HH:mm +// Remarks: Not all rfc8601 is supported only the 'yyyy-MM-ddTHH:mm:ss.dddddd+dd:dd' +pub fn parse_rfc8601(s string) ?Time { + + mut year := 0 + mut month := 0 + mut day := 0 + mut hour := 0 + mut minute := 0 + mut second := 0 + mut mic_second := 0 + mut time_char := `a` + mut plus_min := `a` + mut offset_hour := 0 + mut offset_min := 0 + + count := C.sscanf(s.str, "%4d-%2d-%2d%c%2d:%2d:%2d.%6d%c%2d:%2d", &year, &month, &day, + &time_char, &hour, &minute, + &second, &mic_second, &plus_min, + &offset_hour, &offset_min) + + if count != 11 { + return error('Invalid 8601 format') + } + if time_char != `T` && time_char != ` ` { + return error('Invalid 8601 format, expected space or `T` as time separator') + } + + if plus_min != `+` && plus_min != `-` { + return error('Invalid 8601 format, expected `+` or `-` as time separator' ) + } + + t := new_time(Time{ + year: year + month: month + day: day + hour: hour + minute: minute + second: second + microsecond: mic_second + }) + mut unix_offset := int(0) + + if offset_hour > 0 { + unix_offset += 3600*offset_hour + } + if offset_min > 0 { + unix_offset += 60*offset_min + } + + if unix_offset != 0 { + if plus_min == `+` { + return unix2(int(t.unix) + unix_offset, t.microsecond) + } else { + return unix2(int(t.unix) - unix_offset, t.microsecond) + } + } + return t +} diff --git a/vlib/time/parse_test.v b/vlib/time/parse_test.v index 892436c5dd..4e2e184d79 100644 --- a/vlib/time/parse_test.v +++ b/vlib/time/parse_test.v @@ -12,7 +12,7 @@ fn test_parse() { fn test_parse_invalid() { s := 'Invalid time string' - t := time.parse(s) or { + _ := time.parse(s) or { assert true return } @@ -38,9 +38,37 @@ fn test_parse_rfc2822() { fn test_parse_rfc2822_invalid() { s3 := 'Thu 12 Foo 2019 06:07:45 +0800' - t3 := time.parse_rfc2822(s3) or { + _ := time.parse_rfc2822(s3) or { assert true return } assert false } + +fn test_rfc8601_parse_utc() { + ok_format := '2020-06-02T15:38:06.015959+00:00' + + t := time.parse_rfc8601(ok_format) or {panic(err)} + + assert t.year == 2020 + assert t.month == 6 + assert t.day == 2 + assert t.hour == 15 + assert t.minute == 38 + assert t.second == 6 + assert t.microsecond == 15959 +} + +fn test_rfc8601_parse_cest() { + ok_format := '2020-06-02T15:38:06.015959+02:00' + + t := time.parse_rfc8601(ok_format) or {panic(err)} + + assert t.year == 2020 + assert t.month == 6 + assert t.day == 2 + assert t.hour == 17 + assert t.minute == 38 + assert t.second == 6 + assert t.microsecond == 15959 +} \ No newline at end of file diff --git a/vlib/time/private_test.v b/vlib/time/private_test.v new file mode 100644 index 0000000000..6b2c8462d7 --- /dev/null +++ b/vlib/time/private_test.v @@ -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 +} \ No newline at end of file diff --git a/vlib/time/time.v b/vlib/time/time.v index 7dae0906bf..eea68d4b9c 100644 --- a/vlib/time/time.v +++ b/vlib/time/time.v @@ -39,13 +39,14 @@ const ( pub struct Time { pub: - year int - month int - day int - hour int - minute int - second int - unix u64 + year int + month int + day int + hour int + minute int + second int + microsecond int + unix u64 } pub enum FormatTime { @@ -86,9 +87,23 @@ fn C.time(t &C.time_t) C.time_t // now returns current local time. pub fn now() Time { + $if macos { + return darwin_now() + } + $if windows { + return win_now() + } + $if solaris { + return solaris_now() + } + $if linux { + return linux_now() + } + // defaults to most common feature, the microsecond precision is not available + // in this API call t := C.time(0) now := C.localtime(&t) - return convert_ctime(now) + return convert_ctime(now, 0) } // smonth returns month name. @@ -107,6 +122,7 @@ pub fn new_time(t Time) Time { minute: t.minute second: t.second unix: u64(t.unix_time()) + microsecond: t.microsecond } // TODO Use the syntax below when it works with reserved keywords like `unix` // return { @@ -264,7 +280,7 @@ pub fn (t Time) str() string { return t.format_ss() } -fn convert_ctime(t C.tm) Time { +fn convert_ctime(t C.tm, microsecond int) Time { return Time{ year: t.tm_year + 1900 month: t.tm_mon + 1 @@ -272,6 +288,7 @@ fn convert_ctime(t C.tm) Time { hour: t.tm_hour minute: t.tm_min second: t.tm_sec + microsecond: microsecond unix: u64(make_unix_time(t)) } } diff --git a/vlib/time/time_darwin.c.v b/vlib/time/time_darwin.c.v index a1e5c5b805..7bbaa3f2df 100644 --- a/vlib/time/time_darwin.c.v +++ b/vlib/time/time_darwin.c.v @@ -23,6 +23,11 @@ struct InternalTimeBase { denom u32 = 1 } +pub struct C.timeval { + tv_sec u64 + tv_usec u64 +} + fn init_time_base() InternalTimeBase { tb := C.mach_timebase_info_data_t{} C.mach_timebase_info(&tb) @@ -47,3 +52,29 @@ fn vpc_now_darwin() u64 { } return (tm - start_time) * time_base.numer / time_base.denom } + +// darwin_now returns a better precision current time for Darwin based operating system +// this should be implemented with native system calls eventually +// but for now a bit tweaky. It uses the deprecated gettimeofday clock to get +// the microseconds seconds part and normal local time to get correct local time +// if the time has shifted on a second level between calls it uses +// zero as microsecond. Not perfect but better that unix time only us a second +fn darwin_now() Time { + + // get the high precision time as UTC clock + // and use the nanoseconds part + tv := C.timeval{} + C.gettimeofday(&tv, 0) + + t := C.time(0) + tm := C.localtime(&t) + + // if the second part (very rare) is different + // microseconds is set to zero since it passed the second + // also avoid divide by zero if nsec is zero + if int(t) != tv.tv_sec { + return convert_ctime(tm, 0) + } + + return convert_ctime(tm, int(tv.tv_usec)) +} diff --git a/vlib/time/time_linux.c.v b/vlib/time/time_linux.c.v index 0362400bd5..86c8bd9dc2 100644 --- a/vlib/time/time_linux.c.v +++ b/vlib/time/time_linux.c.v @@ -1,5 +1,41 @@ module time -fn sys_mono_now_darwin() u64 { +fn sys_mono_now_darwin() u64 { return 0 } + +// dummy to compile with all compilers +pub fn darwin_now() Time { + return Time{} +} + +// dummy to compile with all compilers +pub fn solaris_now() Time { + return Time{} +} + +// linux_now returns the local time with high precision for most os:es +// this should be implemented with native system calls eventually +// but for now a bit tweaky. It uses the realtime clock to get +// the nano seconds part and normal local time to get correct local time +// if the time has shifted on a second level between calls it uses +// zero as microsecond. Not perfect but better that unix time only +fn linux_now() Time { + + // get the high precision time as UTC realtime clock + // and use the nanoseconds part + mut ts := C.timespec{} + C.clock_gettime(C.CLOCK_REALTIME, &ts) + + t := C.time(0) + tm := C.localtime(&t) + + // if the second part (very rare) is different + // microseconds is set to zero since it passed the second + // also avoid divide by zero if nsec is zero + if int(t) != ts.tv_sec || ts.tv_nsec == 0 { + return convert_ctime(tm, 0) + } + + return convert_ctime(tm, int(ts.tv_nsec/1000)) +} diff --git a/vlib/time/time_nix.c.v b/vlib/time/time_nix.c.v index c1f366a6f9..6a3e7b00cb 100644 --- a/vlib/time/time_nix.c.v +++ b/vlib/time/time_nix.c.v @@ -3,6 +3,8 @@ // that can be found in the LICENSE file. module time +#include + struct C.tm { tm_sec int tm_min int @@ -47,3 +49,15 @@ fn vpc_now() u64 { C.clock_gettime(C.CLOCK_MONOTONIC, &ts) return u64(ts.tv_sec) * 1_000_000_000 + u64(ts.tv_nsec) } + +// dummy to compile with all compilers +pub fn win_now() Time { + return Time{} +} + + +// dummy to compile with all compilers +pub struct C.timeval { + tv_sec u64 + tv_usec u64 +} \ No newline at end of file diff --git a/vlib/time/time_solaris.c.v b/vlib/time/time_solaris.c.v new file mode 100644 index 0000000000..611adbe559 --- /dev/null +++ b/vlib/time/time_solaris.c.v @@ -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)) +} diff --git a/vlib/time/time_test.v b/vlib/time/time_test.v index 1e6f789682..71fd8a2f40 100644 --- a/vlib/time/time_test.v +++ b/vlib/time/time_test.v @@ -149,3 +149,20 @@ fn test_add_days() { fn test_str() { assert '1980-07-11 21:23:42' == time_to_test.str() } + +// not optimal test but will find obvious bugs +fn test_now() { + now := time.now() + + // The year the test was built + assert now.year >= 2020 + assert now.month > 0 + assert now.month <= 12 + assert now.minute >= 0 + assert now.minute < 60 + assert now.second >=0 + assert now.second < 60 + assert now.microsecond >= 0 + assert now.microsecond < 1000000 + +} diff --git a/vlib/time/time_windows.c.v b/vlib/time/time_windows.c.v index 47f8298290..1400f4e2f0 100644 --- a/vlib/time/time_windows.c.v +++ b/vlib/time/time_windows.c.v @@ -3,6 +3,9 @@ // that can be found in the LICENSE file. module time +#include +#include + struct C.tm { tm_year int tm_mon int @@ -12,13 +15,37 @@ struct C.tm { tm_sec int } +struct C._FILETIME + +struct SystemTime { + year u16 + month u16 + day_of_week u16 + day u16 + hour u16 + minute u16 + second u16 + millisecond u16 +} + +fn C.GetSystemTimeAsFileTime(lpSystemTimeAsFileTime C._FILETIME) +fn C.FileTimeToSystemTime() +fn C.SystemTimeToTzSpecificLocalTime() const ( // start_time is needed on Darwin and Windows because of potential overflows - start_time = init_win_time_start() - freq_time = init_win_time_freq() + start_time = init_win_time_start() + freq_time = init_win_time_freq() + start_local_time = local_as_unix_time() ) +// in most systems, these are __quad_t, which is an i64 +struct C.timespec { + tv_sec i64 + tv_nsec i64 +} + + fn C._mkgmtime(&C.tm) time_t fn C.QueryPerformanceCounter(&u64) C.BOOL @@ -55,3 +82,73 @@ fn vpc_now() u64 { C.QueryPerformanceCounter(&tm) return tm } + +// local_as_unix_time returns the current local time as unix time +fn local_as_unix_time() int { + t := C.time(0) + tm := C.localtime(&t) + + return make_unix_time(tm) +} + +// win_now calculates current time using winapi to get higher resolution on windows +// GetSystemTimeAsFileTime is used. It can resolve time down to millisecond +// other more precice methods can be implemented in the future +fn win_now() Time { + + ft_utc := C._FILETIME{} + C.GetSystemTimeAsFileTime(&ft_utc) + + st_utc := SystemTime{} + C.FileTimeToSystemTime(&ft_utc, &st_utc) + + st_local := SystemTime{} + C.SystemTimeToTzSpecificLocalTime(voidptr(0), &st_utc, &st_local) + + t := Time { + year: st_local.year + month: st_local.month + day: st_local.day + hour: st_local.hour + minute: st_local.minute + second: st_local.second + microsecond: st_local.millisecond*1000 + unix: u64(st_local.unix_time()) + } + + return t +} + +// unix_time returns Unix time. +pub fn (st SystemTime) unix_time() int { + tt := C.tm{ + tm_sec: st.second + tm_min: st.minute + tm_hour: st.hour + tm_mday: st.day + tm_mon: st.month - 1 + tm_year: st.year - 1900 + } + return make_unix_time(tt) +} + +// dummy to compile with all compilers +pub fn darwin_now() Time { + return Time{} +} + +// dummy to compile with all compilers +pub fn linux_now() Time { + return Time{} +} + +// dummy to compile with all compilers +pub fn solaris_now() Time { + return Time{} +} + +// dummy to compile with all compilers +pub struct C.timeval { + tv_sec u64 + tv_usec u64 +} diff --git a/vlib/time/unix.v b/vlib/time/unix.v index 6856884150..3c4b8d5aab 100644 --- a/vlib/time/unix.v +++ b/vlib/time/unix.v @@ -24,6 +24,29 @@ pub fn unix(abs int) Time { } } +// unix2 returns a time struct from Unix time and microsecond value +pub fn unix2(abs int, microsecond int) Time { + // Split into day and time + mut day_offset := abs / seconds_per_day + if abs % seconds_per_day < 0 { + // Compensate for round towards zero on integers as we want floored instead + day_offset-- + } + year,month,day := calculate_date_from_offset(day_offset) + hr,min,sec := calculate_time_from_offset(abs % seconds_per_day) + return Time{ + year: year + month: month + day: day + hour: hr + minute: min + second: sec + microsecond: microsecond + unix: u64(abs) + } +} + + [inline] fn calculate_date_from_offset(day_offset_ int) (int,int,int) { mut day_offset := day_offset_