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
|
||||
[inline]
|
||||
pub fn (t1 Time) ne(t2 Time) bool {
|
||||
return !t1.eq(t2)
|
||||
}
|
||||
|
||||
// lt returns true if provided time is less than time
|
||||
[inline]
|
||||
pub fn (t1 Time) lt(t2 Time) bool {
|
||||
if t1.unix < t2.unix || (t1.unix == t2.unix && t1.microsecond < t2.microsecond) {
|
||||
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
|
||||
[inline]
|
||||
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
|
||||
[inline]
|
||||
pub fn (t1 Time) gt(t2 Time) bool {
|
||||
if t1.unix > t2.unix || (t1.unix == t2.unix && t1.microsecond > t2.microsecond) {
|
||||
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
|
||||
[inline]
|
||||
pub fn (t1 Time) ge(t2 Time) bool {
|
||||
return t1.gt(t2) || t1.eq(t2)
|
||||
}
|
||||
|
|
|
@ -44,43 +44,75 @@ pub fn parse_rfc2822(s string) ?Time {
|
|||
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
|
||||
// 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 iso8601 is supported
|
||||
// also checks and support for leapseconds should be added in future PR
|
||||
pub fn parse_iso8601(s string) ?Time {
|
||||
year := 0
|
||||
month := 0
|
||||
day := 0
|
||||
hour := 0
|
||||
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
|
||||
t_i := s.index('T') or { -1 }
|
||||
parts := if t_i != -1 { [s[..t_i], s[t_i + 1..]] } else { s.split(' ') }
|
||||
if !(parts.len == 1 || parts.len == 2) {
|
||||
return err_invalid_8601
|
||||
}
|
||||
is_local_time := plus_min_z == `a` && count == 8
|
||||
is_utc := plus_min_z == `Z` && count == 9
|
||||
if count != 11 && !is_local_time && !is_utc {
|
||||
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_z != `+` && plus_min_z != `-` && plus_min_z != `Z` && !is_local_time {
|
||||
return error('Invalid 8601 format, expected `Z` or `+` or `-` as time separator')
|
||||
year, month, day := parse_iso8601_date(parts[0]) ?
|
||||
mut hour, mut minute, mut second, mut microsecond, mut unix_offset, mut is_local_time := 0, 0, 0, 0, i64(0), true
|
||||
if parts.len == 2 {
|
||||
hour, minute, second, microsecond, unix_offset, is_local_time = parse_iso8601_time(parts[1]) ?
|
||||
}
|
||||
mut t := new_time(Time{
|
||||
year: year
|
||||
|
@ -89,27 +121,18 @@ pub fn parse_iso8601(s string) ?Time {
|
|||
hour: hour
|
||||
minute: minute
|
||||
second: second
|
||||
microsecond: mic_second
|
||||
microsecond: microsecond
|
||||
})
|
||||
if is_local_time {
|
||||
return t // Time already local time
|
||||
}
|
||||
mut unix_time := t.unix
|
||||
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_z == `+` {
|
||||
unix_time -= u64(unix_offset)
|
||||
} else {
|
||||
if unix_offset < 0 {
|
||||
unix_time -= u64(-unix_offset)
|
||||
} else if unix_offset > 0 {
|
||||
unix_time += u64(unix_offset)
|
||||
}
|
||||
t = unix2(int(unix_time), t.microsecond)
|
||||
}
|
||||
// Convert the time 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+02:00',
|
||||
'2020-06-05T15:38:06.015959-02:00',
|
||||
'2020-11-05T15:38:06.015959Z'
|
||||
'2020-11-05T15:38:06.015959Z',
|
||||
]
|
||||
times := [
|
||||
[2020, 6, 5, 15, 38, 6, 0],
|
||||
|
@ -131,3 +131,18 @@ fn test_parse_iso8601_invalid() {
|
|||
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
|
||||
// but for now a bit tweaky. It uses the deprecated gettimeofday clock to get
|
||||
// the microseconds seconds part and converts to local time
|
||||
[inline]
|
||||
fn darwin_now() Time {
|
||||
// get the high precision time as UTC clock
|
||||
tv := C.timeval{}
|
||||
|
@ -75,7 +74,6 @@ fn darwin_now() Time {
|
|||
// 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{}
|
||||
|
|
|
@ -66,7 +66,6 @@ fn vpc_now() u64 {
|
|||
// 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
|
||||
|
@ -77,7 +76,6 @@ fn linux_now() Time {
|
|||
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
|
||||
|
|
|
@ -3,7 +3,6 @@ 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
|
||||
|
@ -14,7 +13,6 @@ fn solaris_now() Time {
|
|||
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
|
||||
|
|
|
@ -25,9 +25,7 @@ fn test_is_leap_year() {
|
|||
}
|
||||
|
||||
fn check_days_in_month(month int, year int, expected int) bool {
|
||||
res := time.days_in_month(month, year) or {
|
||||
return false
|
||||
}
|
||||
res := time.days_in_month(month, year) or { return false }
|
||||
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
|
||||
// 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{}
|
||||
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
|
||||
// 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)
|
||||
|
|
|
@ -46,7 +46,6 @@ pub fn unix2(abs int, microsecond int) Time {
|
|||
}
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn calculate_date_from_offset(day_offset_ int) (int, int, int) {
|
||||
mut day_offset := day_offset_
|
||||
// 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
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn calculate_time_from_offset(second_offset_ int) (int, int, int) {
|
||||
mut second_offset := second_offset_
|
||||
if second_offset < 0 {
|
||||
|
|
Loading…
Reference in New Issue