time: fix iso8601 parser and utc time
parent
8f9f426479
commit
2dc547a45c
|
@ -1,6 +1,7 @@
|
|||
module time
|
||||
|
||||
// eq returns true if provided time is equal to time
|
||||
[inline]
|
||||
pub fn (t1 Time) eq(t2 Time) bool {
|
||||
if t1.unix == t2.unix && t1.microsecond == t2.microsecond {
|
||||
return true
|
||||
|
|
|
@ -48,11 +48,12 @@ 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
|
||||
// parse_iso8601 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 {
|
||||
// remarks: not all iso8601 is supported only the 'yyyy-MM-ddTHH:mm:ss.dddddd+dd:dd'
|
||||
// also checks and support for leapseconds should be added in future PR
|
||||
pub fn parse_iso8601(s string) ?Time {
|
||||
|
||||
mut year := 0
|
||||
mut month := 0
|
||||
|
@ -82,7 +83,8 @@ pub fn parse_rfc8601(s string) ?Time {
|
|||
return error('Invalid 8601 format, expected `+` or `-` as time separator' )
|
||||
}
|
||||
|
||||
t := new_time(Time{
|
||||
|
||||
mut t := new_time(Time{
|
||||
year: year
|
||||
month: month
|
||||
day: day
|
||||
|
@ -91,6 +93,8 @@ pub fn parse_rfc8601(s string) ?Time {
|
|||
second: second
|
||||
microsecond: mic_second
|
||||
})
|
||||
|
||||
mut unix_time := t.unix
|
||||
mut unix_offset := int(0)
|
||||
|
||||
if offset_hour > 0 {
|
||||
|
@ -102,10 +106,14 @@ pub fn parse_rfc8601(s string) ?Time {
|
|||
|
||||
if unix_offset != 0 {
|
||||
if plus_min == `+` {
|
||||
return unix2(int(t.unix) + unix_offset, t.microsecond)
|
||||
unix_time -= u64(unix_offset)
|
||||
} else {
|
||||
return unix2(int(t.unix) - unix_offset, t.microsecond)
|
||||
unix_time += u64(unix_offset)
|
||||
}
|
||||
t = unix2(int(unix_time), t.microsecond)
|
||||
}
|
||||
return t
|
||||
|
||||
// Convert the time to local time
|
||||
|
||||
return to_local_time(t)
|
||||
}
|
||||
|
|
|
@ -45,30 +45,27 @@ fn test_parse_rfc2822_invalid() {
|
|||
assert false
|
||||
}
|
||||
|
||||
fn test_rfc8601_parse_utc() {
|
||||
ok_format := '2020-06-02T15:38:06.015959+00:00'
|
||||
fn test_iso8601_parse_utc_diff() {
|
||||
format_utc := '2020-06-05T15:38:06.015959+00:00'
|
||||
format_cest := '2020-06-05T15:38:06.015959+02:00'
|
||||
|
||||
t := time.parse_rfc8601(ok_format) or {panic(err)}
|
||||
t_utc := time.parse_iso8601(format_utc) or {panic(err)}
|
||||
t_cest := time.parse_iso8601(format_cest) 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
|
||||
}
|
||||
assert t_utc.year == 2020
|
||||
assert t_cest.year == 2020
|
||||
assert t_utc.month == 6
|
||||
assert t_cest.month == 6
|
||||
assert t_utc.day == 5
|
||||
assert t_cest.day == 5
|
||||
// if it was formatted in utc it should be
|
||||
// two hours before if it was formatted in
|
||||
// cest time
|
||||
assert t_utc.hour == (t_cest.hour + 2)
|
||||
assert t_utc.minute == 38
|
||||
assert t_cest.minute == 38
|
||||
assert t_utc.second == 6
|
||||
assert t_cest.second == 6
|
||||
assert t_utc.microsecond == 15959
|
||||
assert t_cest.microsecond == 15959
|
||||
}
|
|
@ -85,6 +85,7 @@ pub struct C.timeval {
|
|||
fn C.localtime(t &C.time_t) &C.tm
|
||||
fn C.time(t &C.time_t) C.time_t
|
||||
|
||||
|
||||
// now returns current local time.
|
||||
pub fn now() Time {
|
||||
$if macos {
|
||||
|
@ -106,6 +107,27 @@ pub fn now() Time {
|
|||
return convert_ctime(now, 0)
|
||||
}
|
||||
|
||||
// utc returns the current time in utc
|
||||
pub fn utc() Time {
|
||||
$if macos {
|
||||
return darwin_utc()
|
||||
}
|
||||
$if windows {
|
||||
return win_utc()
|
||||
}
|
||||
$if solaris {
|
||||
return solaris_utc()
|
||||
}
|
||||
$if linux {
|
||||
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(int(t), 0)
|
||||
}
|
||||
|
||||
// smonth returns month name.
|
||||
pub fn (t Time) smonth() string {
|
||||
i := t.month - 1
|
||||
|
|
|
@ -56,25 +56,30 @@ fn vpc_now_darwin() u64 {
|
|||
// 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
|
||||
// the microseconds seconds part and converts to local time
|
||||
[inline]
|
||||
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)
|
||||
loc_tm := C.tm{}
|
||||
C.localtime_r(&tv.tv_sec, &loc_tm)
|
||||
|
||||
// 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))
|
||||
return convert_ctime(loc_tm, int(tv.tv_usec))
|
||||
}
|
||||
|
||||
// darwin_utc 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
|
||||
[inline]
|
||||
fn darwin_utc() Time {
|
||||
|
||||
// get the high precision time as UTC clock
|
||||
tv := C.timeval{}
|
||||
C.gettimeofday(&tv, 0)
|
||||
|
||||
return unix2(int(tv.tv_sec), int(tv.tv_usec))
|
||||
}
|
||||
|
|
|
@ -1,5 +1,32 @@
|
|||
module time
|
||||
|
||||
// linux_now returns the local time with high precision for most os:es
|
||||
// this should be implemented properly with support for leap seconds.
|
||||
// It uses the realtime clock to get and converts it to local time
|
||||
[inline]
|
||||
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)
|
||||
|
||||
loc_tm := C.tm{}
|
||||
C.localtime_r(&ts.tv_sec, &loc_tm)
|
||||
|
||||
return convert_ctime(loc_tm, int(ts.tv_nsec/1000))
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn linux_utc() 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)
|
||||
|
||||
return unix2(int(ts.tv_sec), int(ts.tv_nsec/1000))
|
||||
}
|
||||
|
||||
fn sys_mono_now_darwin() u64 {
|
||||
return 0
|
||||
}
|
||||
|
@ -14,28 +41,12 @@ 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))
|
||||
// dummy to compile with all compilers
|
||||
pub fn darwin_utc() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub fn solaris_utc() Time {
|
||||
return Time{}
|
||||
}
|
|
@ -12,14 +12,25 @@ struct C.tm {
|
|||
tm_mday int
|
||||
tm_mon int
|
||||
tm_year int
|
||||
tm_wday int
|
||||
tm_yday int
|
||||
tm_isdst int
|
||||
}
|
||||
|
||||
fn C.timegm(&tm) time_t
|
||||
fn C.localtime_r(t &C.time_t, tm &C.tm )
|
||||
|
||||
fn make_unix_time(t C.tm) int {
|
||||
return int(C.timegm(&t))
|
||||
}
|
||||
|
||||
fn to_local_time(t Time) Time {
|
||||
loc_tm := C.tm{}
|
||||
C.localtime_r(&t.unix, &loc_tm)
|
||||
|
||||
return convert_ctime(loc_tm, t.microsecond)
|
||||
}
|
||||
|
||||
type time_t voidptr
|
||||
|
||||
// in most systems, these are __quad_t, which is an i64
|
||||
|
@ -50,14 +61,20 @@ fn vpc_now() u64 {
|
|||
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 fn win_utc() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub struct C.timeval {
|
||||
tv_sec u64
|
||||
tv_usec u64
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,48 @@
|
|||
module time
|
||||
|
||||
// solaris_now returns the local time with high precision for most os:es
|
||||
// this should be implemented properly with support for leap seconds.
|
||||
// It uses the realtime clock to get and converts it to local time
|
||||
[inline]
|
||||
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)
|
||||
|
||||
loc_tm := C.tm{}
|
||||
C.localtime_r(&ts.tv_sec, &loc_tm)
|
||||
|
||||
return convert_ctime(loc_tm, int(ts.tv_nsec/1000))
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn solaris_utc() 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)
|
||||
|
||||
return unix2(int(ts.tv_sec), int(ts.tv_nsec/1000))
|
||||
}
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub fn linux_now() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
// dummy to compile with all compilers
|
||||
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))
|
||||
// dummy to compile with all compilers
|
||||
pub fn linux_utc() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub fn darwin_utc() Time {
|
||||
return Time{}
|
||||
}
|
|
@ -161,8 +161,23 @@ fn test_now() {
|
|||
assert now.minute >= 0
|
||||
assert now.minute < 60
|
||||
assert now.second >=0
|
||||
assert now.second < 60
|
||||
assert now.second <= 60 // <= 60 cause of leap seconds
|
||||
assert now.microsecond >= 0
|
||||
assert now.microsecond < 1000000
|
||||
|
||||
}
|
||||
|
||||
fn test_utc() {
|
||||
now := time.utc()
|
||||
|
||||
// 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 // <= 60 cause of leap seconds
|
||||
assert now.microsecond >= 0
|
||||
assert now.microsecond < 1000000
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ struct SystemTime {
|
|||
fn C.GetSystemTimeAsFileTime(lpSystemTimeAsFileTime C._FILETIME)
|
||||
fn C.FileTimeToSystemTime()
|
||||
fn C.SystemTimeToTzSpecificLocalTime()
|
||||
fn C.localtime_s(t &C.time_t, tm &C.tm )
|
||||
|
||||
const (
|
||||
// start_time is needed on Darwin and Windows because of potential overflows
|
||||
|
@ -91,9 +92,36 @@ fn local_as_unix_time() int {
|
|||
return make_unix_time(tm)
|
||||
}
|
||||
|
||||
fn to_local_time(t Time) Time {
|
||||
st_utc := SystemTime{
|
||||
year: u16(t.year)
|
||||
month: u16(t.month)
|
||||
day: u16(t.day)
|
||||
hour: u16(t.hour)
|
||||
minute: u16(t.minute)
|
||||
second: u16(t.second)
|
||||
}
|
||||
st_local := SystemTime{}
|
||||
C.SystemTimeToTzSpecificLocalTime(voidptr(0), &st_utc, &st_local)
|
||||
|
||||
t_local := 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
|
||||
// These are the same
|
||||
microsecond: t.microsecond
|
||||
unix: t.unix
|
||||
}
|
||||
return t_local
|
||||
}
|
||||
|
||||
// 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
|
||||
// GetSystemTimeAsFileTime is used and converted to local time. It can resolve time
|
||||
// down to millisecond. Other more precice methods can be implemented in the future
|
||||
[inline]
|
||||
fn win_now() Time {
|
||||
|
||||
ft_utc := C._FILETIME{}
|
||||
|
@ -119,6 +147,32 @@ fn win_now() Time {
|
|||
return t
|
||||
}
|
||||
|
||||
// win_utc 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
|
||||
[inline]
|
||||
fn win_utc() Time {
|
||||
|
||||
ft_utc := C._FILETIME{}
|
||||
C.GetSystemTimeAsFileTime(&ft_utc)
|
||||
|
||||
st_utc := SystemTime{}
|
||||
C.FileTimeToSystemTime(&ft_utc, &st_utc)
|
||||
|
||||
t := Time {
|
||||
year: st_utc.year
|
||||
month: st_utc.month
|
||||
day: st_utc.day
|
||||
hour: st_utc.hour
|
||||
minute: st_utc.minute
|
||||
second: st_utc.second
|
||||
microsecond: st_utc.millisecond*1000
|
||||
unix: u64(st_utc.unix_time())
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// unix_time returns Unix time.
|
||||
pub fn (st SystemTime) unix_time() int {
|
||||
tt := C.tm{
|
||||
|
@ -147,6 +201,21 @@ pub fn solaris_now() Time {
|
|||
return Time{}
|
||||
}
|
||||
|
||||
pub fn darwin_utc() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub fn linux_utc() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub fn solaris_utc() Time {
|
||||
return Time{}
|
||||
}
|
||||
|
||||
|
||||
// dummy to compile with all compilers
|
||||
pub struct C.timeval {
|
||||
tv_sec u64
|
||||
|
|
Loading…
Reference in New Issue