time: make parse_iso8601 support a date only format (#7277)
parent
88a8507dd8
commit
6a74058190
|
@ -10,11 +10,13 @@ pub fn (t1 Time) eq(t2 Time) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ne returns true if provided time is not equal to time
|
// ne returns true if provided time is not equal to time
|
||||||
|
[inline]
|
||||||
pub fn (t1 Time) ne(t2 Time) bool {
|
pub fn (t1 Time) ne(t2 Time) bool {
|
||||||
return !t1.eq(t2)
|
return !t1.eq(t2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lt returns true if provided time is less than time
|
// lt returns true if provided time is less than time
|
||||||
|
[inline]
|
||||||
pub fn (t1 Time) lt(t2 Time) bool {
|
pub fn (t1 Time) lt(t2 Time) bool {
|
||||||
if t1.unix < t2.unix || (t1.unix == t2.unix && t1.microsecond < t2.microsecond) {
|
if t1.unix < t2.unix || (t1.unix == t2.unix && t1.microsecond < t2.microsecond) {
|
||||||
return true
|
return true
|
||||||
|
@ -23,11 +25,13 @@ pub fn (t1 Time) lt(t2 Time) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// le returns true if provided time is less or equal to time
|
// le returns true if provided time is less or equal to time
|
||||||
|
[inline]
|
||||||
pub fn (t1 Time) le(t2 Time) bool {
|
pub fn (t1 Time) le(t2 Time) bool {
|
||||||
return t1.lt(t2) || t1.eq(t2)
|
return t1.lt(t2) || t1.eq(t2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// gt returns true if provided time is greater than time
|
// gt returns true if provided time is greater than time
|
||||||
|
[inline]
|
||||||
pub fn (t1 Time) gt(t2 Time) bool {
|
pub fn (t1 Time) gt(t2 Time) bool {
|
||||||
if t1.unix > t2.unix || (t1.unix == t2.unix && t1.microsecond > t2.microsecond) {
|
if t1.unix > t2.unix || (t1.unix == t2.unix && t1.microsecond > t2.microsecond) {
|
||||||
return true
|
return true
|
||||||
|
@ -36,6 +40,7 @@ pub fn (t1 Time) gt(t2 Time) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ge returns true if provided time is greater or equal to time
|
// ge returns true if provided time is greater or equal to time
|
||||||
|
[inline]
|
||||||
pub fn (t1 Time) ge(t2 Time) bool {
|
pub fn (t1 Time) ge(t2 Time) bool {
|
||||||
return t1.gt(t2) || t1.eq(t2)
|
return t1.gt(t2) || t1.eq(t2)
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,43 +44,75 @@ pub fn parse_rfc2822(s string) ?Time {
|
||||||
return parse(tos(tmstr, count))
|
return parse(tos(tmstr, count))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----- iso8601 -----
|
||||||
|
const (
|
||||||
|
err_invalid_8601 = error('Invalid 8601 Format')
|
||||||
|
)
|
||||||
|
|
||||||
|
fn parse_iso8601_date(s string) ?(int, int, int) {
|
||||||
|
year, month, day, dummy := 0, 0, 0, byte(0)
|
||||||
|
count := unsafe {C.sscanf(charptr(s.str), '%4d-%2d-%2d%c', &year, &month, &day, &dummy)}
|
||||||
|
if count != 3 {
|
||||||
|
return err_invalid_8601
|
||||||
|
}
|
||||||
|
return year, month, day
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_iso8601_time(s string) ?(int, int, int, int, i64, bool) {
|
||||||
|
hour := 0
|
||||||
|
minute := 0
|
||||||
|
second := 0
|
||||||
|
microsecond := 0
|
||||||
|
plus_min_z := `a`
|
||||||
|
offset_hour := 0
|
||||||
|
offset_minute := 0
|
||||||
|
mut count := unsafe {C.sscanf(charptr(s.str), '%2d:%2d:%2d.%6d%c%2d:%2d', &hour, &minute,
|
||||||
|
&second, µsecond, charptr(&plus_min_z), &offset_hour, &offset_minute)}
|
||||||
|
// Missread microsecond ([Sec Hour Minute].len == 3 < 4)
|
||||||
|
if count < 4 {
|
||||||
|
count = unsafe {C.sscanf(charptr(s.str), '%2d:%2d:%2d%c%2d:%2d', &hour, &minute,
|
||||||
|
&second, charptr(&plus_min_z), &offset_hour, &offset_minute)}
|
||||||
|
count++ // Increment count because skipped microsecond
|
||||||
|
}
|
||||||
|
if count < 4 {
|
||||||
|
return err_invalid_8601
|
||||||
|
}
|
||||||
|
is_local_time := plus_min_z == `a` && count == 4
|
||||||
|
is_utc := plus_min_z == `Z` && count == 5
|
||||||
|
if !(count == 7 || is_local_time || is_utc) {
|
||||||
|
return err_invalid_8601
|
||||||
|
}
|
||||||
|
if plus_min_z != `+` && plus_min_z != `-` && !is_utc && !is_local_time {
|
||||||
|
return error('Invalid 8601 format, expected `Z` or `+` or `-` as time separator')
|
||||||
|
}
|
||||||
|
mut unix_offset := 0
|
||||||
|
if offset_hour > 0 {
|
||||||
|
unix_offset += 3600 * offset_hour
|
||||||
|
}
|
||||||
|
if offset_minute > 0 {
|
||||||
|
unix_offset += 60 * offset_minute
|
||||||
|
}
|
||||||
|
if plus_min_z == `+` {
|
||||||
|
unix_offset *= -1
|
||||||
|
}
|
||||||
|
return hour, minute, second, microsecond, unix_offset, is_local_time
|
||||||
|
}
|
||||||
|
|
||||||
// parse_iso8601 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
|
// the fraction part is difference in milli seconds and the last part is offset
|
||||||
// from UTC time and can be both +/- HH:mm
|
// from UTC time and can be both +/- HH:mm
|
||||||
// remarks: not all iso8601 is supported
|
// remarks: not all iso8601 is supported
|
||||||
// also checks and support for leapseconds should be added in future PR
|
// also checks and support for leapseconds should be added in future PR
|
||||||
pub fn parse_iso8601(s string) ?Time {
|
pub fn parse_iso8601(s string) ?Time {
|
||||||
year := 0
|
t_i := s.index('T') or { -1 }
|
||||||
month := 0
|
parts := if t_i != -1 { [s[..t_i], s[t_i + 1..]] } else { s.split(' ') }
|
||||||
day := 0
|
if !(parts.len == 1 || parts.len == 2) {
|
||||||
hour := 0
|
return err_invalid_8601
|
||||||
minute := 0
|
|
||||||
second := 0
|
|
||||||
mic_second := 0
|
|
||||||
time_char := `a`
|
|
||||||
plus_min_z := `a`
|
|
||||||
offset_hour := 0
|
|
||||||
offset_min := 0
|
|
||||||
mut count := unsafe {C.sscanf(charptr(s.str), '%4d-%2d-%2d%c%2d:%2d:%2d.%6d%c%2d:%2d',
|
|
||||||
&year, &month, &day, charptr(&time_char), &hour, &minute, &second, &mic_second, charptr(&plus_min_z),
|
|
||||||
&offset_hour, &offset_min)}
|
|
||||||
// Missread microsec ([Year Month Day T Sec Hour Minute].len == 7 < 8)
|
|
||||||
if count < 8 {
|
|
||||||
count = unsafe {C.sscanf(charptr(s.str), '%4d-%2d-%2d%c%2d:%2d:%2d%c%2d:%2d',
|
|
||||||
&year, &month, &day, charptr(&time_char), &hour, &minute, &second, charptr(&plus_min_z),
|
|
||||||
&offset_hour, &offset_min)}
|
|
||||||
count++ // Increment count because skipped microsec
|
|
||||||
}
|
}
|
||||||
is_local_time := plus_min_z == `a` && count == 8
|
year, month, day := parse_iso8601_date(parts[0]) ?
|
||||||
is_utc := plus_min_z == `Z` && count == 9
|
mut hour, mut minute, mut second, mut microsecond, mut unix_offset, mut is_local_time := 0, 0, 0, 0, i64(0), true
|
||||||
if count != 11 && !is_local_time && !is_utc {
|
if parts.len == 2 {
|
||||||
return error('Invalid 8601 format')
|
hour, minute, second, microsecond, unix_offset, is_local_time = parse_iso8601_time(parts[1]) ?
|
||||||
}
|
|
||||||
if time_char != `T` && time_char != ` ` {
|
|
||||||
return error('Invalid 8601 format, expected space or `T` as time separator')
|
|
||||||
}
|
|
||||||
if plus_min_z != `+` && plus_min_z != `-` && plus_min_z != `Z` && !is_local_time {
|
|
||||||
return error('Invalid 8601 format, expected `Z` or `+` or `-` as time separator')
|
|
||||||
}
|
}
|
||||||
mut t := new_time(Time{
|
mut t := new_time(Time{
|
||||||
year: year
|
year: year
|
||||||
|
@ -89,27 +121,18 @@ pub fn parse_iso8601(s string) ?Time {
|
||||||
hour: hour
|
hour: hour
|
||||||
minute: minute
|
minute: minute
|
||||||
second: second
|
second: second
|
||||||
microsecond: mic_second
|
microsecond: microsecond
|
||||||
})
|
})
|
||||||
if is_local_time {
|
if is_local_time {
|
||||||
return t // Time already local time
|
return t // Time already local time
|
||||||
}
|
}
|
||||||
mut unix_time := t.unix
|
mut unix_time := t.unix
|
||||||
mut unix_offset := int(0)
|
if unix_offset < 0 {
|
||||||
if offset_hour > 0 {
|
unix_time -= u64(-unix_offset)
|
||||||
unix_offset += 3600 * offset_hour
|
} else if unix_offset > 0 {
|
||||||
}
|
unix_time += u64(unix_offset)
|
||||||
if offset_min > 0 {
|
|
||||||
unix_offset += 60 * offset_min
|
|
||||||
}
|
|
||||||
if unix_offset != 0 {
|
|
||||||
if plus_min_z == `+` {
|
|
||||||
unix_time -= u64(unix_offset)
|
|
||||||
} else {
|
|
||||||
unix_time += u64(unix_offset)
|
|
||||||
}
|
|
||||||
t = unix2(int(unix_time), t.microsecond)
|
|
||||||
}
|
}
|
||||||
|
t = unix2(int(unix_time), t.microsecond)
|
||||||
// Convert the time to local time
|
// Convert the time to local time
|
||||||
return t.to_local_time()
|
return t.to_local_time()
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ fn test_parse_iso8601() {
|
||||||
'2020-06-05T15:38:06.015959+00:00',
|
'2020-06-05T15:38:06.015959+00:00',
|
||||||
'2020-06-05T15:38:06.015959+02:00',
|
'2020-06-05T15:38:06.015959+02:00',
|
||||||
'2020-06-05T15:38:06.015959-02:00',
|
'2020-06-05T15:38:06.015959-02:00',
|
||||||
'2020-11-05T15:38:06.015959Z'
|
'2020-11-05T15:38:06.015959Z',
|
||||||
]
|
]
|
||||||
times := [
|
times := [
|
||||||
[2020, 6, 5, 15, 38, 6, 0],
|
[2020, 6, 5, 15, 38, 6, 0],
|
||||||
|
@ -131,3 +131,18 @@ fn test_parse_iso8601_invalid() {
|
||||||
assert false
|
assert false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_parse_iso8601_date_only() {
|
||||||
|
format := '2020-06-05'
|
||||||
|
t := time.parse_iso8601(format) or {
|
||||||
|
assert false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert t.year == 2020
|
||||||
|
assert t.month == 6
|
||||||
|
assert t.day == 5
|
||||||
|
assert t.hour == 0
|
||||||
|
assert t.minute == 0
|
||||||
|
assert t.second == 0
|
||||||
|
assert t.microsecond == 0
|
||||||
|
}
|
||||||
|
|
|
@ -61,7 +61,6 @@ fn vpc_now_darwin() u64 {
|
||||||
// this should be implemented with native system calls eventually
|
// this should be implemented with native system calls eventually
|
||||||
// but for now a bit tweaky. It uses the deprecated gettimeofday clock to get
|
// but for now a bit tweaky. It uses the deprecated gettimeofday clock to get
|
||||||
// the microseconds seconds part and converts to local time
|
// the microseconds seconds part and converts to local time
|
||||||
[inline]
|
|
||||||
fn darwin_now() Time {
|
fn darwin_now() Time {
|
||||||
// get the high precision time as UTC clock
|
// get the high precision time as UTC clock
|
||||||
tv := C.timeval{}
|
tv := C.timeval{}
|
||||||
|
@ -75,7 +74,6 @@ fn darwin_now() Time {
|
||||||
// this should be implemented with native system calls eventually
|
// this should be implemented with native system calls eventually
|
||||||
// but for now a bit tweaky. It uses the deprecated gettimeofday clock to get
|
// 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
|
// the microseconds seconds part and normal local time to get correct local time
|
||||||
[inline]
|
|
||||||
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{}
|
tv := C.timeval{}
|
||||||
|
|
|
@ -66,7 +66,6 @@ fn vpc_now() u64 {
|
||||||
// linux_now returns the local time with high precision for most os:es
|
// linux_now returns the local time with high precision for most os:es
|
||||||
// this should be implemented properly with support for leap seconds.
|
// this should be implemented properly with support for leap seconds.
|
||||||
// It uses the realtime clock to get and converts it to local time
|
// It uses the realtime clock to get and converts it to local time
|
||||||
[inline]
|
|
||||||
fn linux_now() Time {
|
fn linux_now() Time {
|
||||||
// get the high precision time as UTC realtime clock
|
// get the high precision time as UTC realtime clock
|
||||||
// and use the nanoseconds part
|
// and use the nanoseconds part
|
||||||
|
@ -77,7 +76,6 @@ fn linux_now() Time {
|
||||||
return convert_ctime(loc_tm, int(ts.tv_nsec / 1000))
|
return convert_ctime(loc_tm, int(ts.tv_nsec / 1000))
|
||||||
}
|
}
|
||||||
|
|
||||||
[inline]
|
|
||||||
fn linux_utc() Time {
|
fn linux_utc() Time {
|
||||||
// get the high precision time as UTC realtime clock
|
// get the high precision time as UTC realtime clock
|
||||||
// and use the nanoseconds part
|
// and use the nanoseconds part
|
||||||
|
|
|
@ -3,7 +3,6 @@ module time
|
||||||
// solaris_now returns the local time with high precision for most os:es
|
// solaris_now returns the local time with high precision for most os:es
|
||||||
// this should be implemented properly with support for leap seconds.
|
// this should be implemented properly with support for leap seconds.
|
||||||
// It uses the realtime clock to get and converts it to local time
|
// It uses the realtime clock to get and converts it to local time
|
||||||
[inline]
|
|
||||||
fn solaris_now() Time {
|
fn solaris_now() Time {
|
||||||
// get the high precision time as UTC realtime clock
|
// get the high precision time as UTC realtime clock
|
||||||
// and use the nanoseconds part
|
// and use the nanoseconds part
|
||||||
|
@ -14,7 +13,6 @@ fn solaris_now() Time {
|
||||||
return convert_ctime(loc_tm, int(ts.tv_nsec / 1000))
|
return convert_ctime(loc_tm, int(ts.tv_nsec / 1000))
|
||||||
}
|
}
|
||||||
|
|
||||||
[inline]
|
|
||||||
fn solaris_utc() Time {
|
fn solaris_utc() Time {
|
||||||
// get the high precision time as UTC realtime clock
|
// get the high precision time as UTC realtime clock
|
||||||
// and use the nanoseconds part
|
// and use the nanoseconds part
|
||||||
|
|
|
@ -25,9 +25,7 @@ fn test_is_leap_year() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_days_in_month(month int, year int, expected int) bool {
|
fn check_days_in_month(month int, year int, expected int) bool {
|
||||||
res := time.days_in_month(month, year) or {
|
res := time.days_in_month(month, year) or { return false }
|
||||||
return false
|
|
||||||
}
|
|
||||||
return res == expected
|
return res == expected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,7 +120,6 @@ pub fn (t Time) to_local_time() Time {
|
||||||
// win_now calculates current time using winapi to get higher resolution on windows
|
// win_now calculates current time using winapi to get higher resolution on windows
|
||||||
// GetSystemTimeAsFileTime is used and converted to local time. It can resolve time
|
// 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
|
// down to millisecond. Other more precice methods can be implemented in the future
|
||||||
[inline]
|
|
||||||
fn win_now() Time {
|
fn win_now() Time {
|
||||||
ft_utc := C._FILETIME{}
|
ft_utc := C._FILETIME{}
|
||||||
C.GetSystemTimeAsFileTime(&ft_utc)
|
C.GetSystemTimeAsFileTime(&ft_utc)
|
||||||
|
@ -144,7 +143,6 @@ fn win_now() Time {
|
||||||
// win_utc calculates current time using winapi to get higher resolution on windows
|
// win_utc calculates current time using winapi to get higher resolution on windows
|
||||||
// GetSystemTimeAsFileTime is used. It can resolve time down to millisecond
|
// GetSystemTimeAsFileTime is used. It can resolve time down to millisecond
|
||||||
// other more precice methods can be implemented in the future
|
// other more precice methods can be implemented in the future
|
||||||
[inline]
|
|
||||||
fn win_utc() Time {
|
fn win_utc() Time {
|
||||||
ft_utc := C._FILETIME{}
|
ft_utc := C._FILETIME{}
|
||||||
C.GetSystemTimeAsFileTime(&ft_utc)
|
C.GetSystemTimeAsFileTime(&ft_utc)
|
||||||
|
|
|
@ -46,7 +46,6 @@ pub fn unix2(abs int, microsecond int) Time {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[inline]
|
|
||||||
fn calculate_date_from_offset(day_offset_ int) (int, int, int) {
|
fn calculate_date_from_offset(day_offset_ int) (int, int, int) {
|
||||||
mut day_offset := day_offset_
|
mut day_offset := day_offset_
|
||||||
// Move offset to year 2001 as it's the start of a new 400-year cycle
|
// Move offset to year 2001 as it's the start of a new 400-year cycle
|
||||||
|
@ -112,7 +111,6 @@ fn calculate_date_from_offset(day_offset_ int) (int, int, int) {
|
||||||
return year, estimated_month + 1, day_offset + 1
|
return year, estimated_month + 1, day_offset + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
[inline]
|
|
||||||
fn calculate_time_from_offset(second_offset_ int) (int, int, int) {
|
fn calculate_time_from_offset(second_offset_ int) (int, int, int) {
|
||||||
mut second_offset := second_offset_
|
mut second_offset := second_offset_
|
||||||
if second_offset < 0 {
|
if second_offset < 0 {
|
||||||
|
|
Loading…
Reference in New Issue