|
3 | 3 | // that can be found in the LICENSE file.
|
4 | 4 | module time
|
5 | 5 |
|
| 6 | +// parse_rfc3339 returns time from a date string in RFC 3339 datetime format. |
| 7 | +pub fn parse_rfc3339(s string) ?Time { |
| 8 | + if s == '' { |
| 9 | + return error_invalid_time(0) |
| 10 | + } |
| 11 | + mut t := parse_iso8601(s) or { Time{} } |
| 12 | + // If parse_iso8601 DID NOT result in default values (i.e. date was parsed correctly) |
| 13 | + if t != Time{} { |
| 14 | + return t |
| 15 | + } |
| 16 | + |
| 17 | + t_i := s.index('T') or { -1 } |
| 18 | + parts := if t_i != -1 { [s[..t_i], s[t_i + 1..]] } else { s.split(' ') } |
| 19 | + |
| 20 | + // Check if s is date only |
| 21 | + if !parts[0].contains_any(' Z') && parts[0].contains('-') { |
| 22 | + year, month, day := parse_iso8601_date(s) ? |
| 23 | + t = new_time(Time{ |
| 24 | + year: year |
| 25 | + month: month |
| 26 | + day: day |
| 27 | + }) |
| 28 | + return t |
| 29 | + } |
| 30 | + // Check if s is time only |
| 31 | + if !parts[0].contains('-') && parts[0].contains(':') { |
| 32 | + mut hour_, mut minute_, mut second_, mut microsecond_, mut unix_offset, mut is_local_time := 0, 0, 0, 0, i64(0), true |
| 33 | + hour_, minute_, second_, microsecond_, unix_offset, is_local_time = parse_iso8601_time(parts[0]) ? |
| 34 | + t = new_time(Time{ |
| 35 | + hour: hour_ |
| 36 | + minute: minute_ |
| 37 | + second: second_ |
| 38 | + microsecond: microsecond_ |
| 39 | + }) |
| 40 | + if is_local_time { |
| 41 | + return t // Time is already local time |
| 42 | + } |
| 43 | + mut unix_time := t.unix |
| 44 | + if unix_offset < 0 { |
| 45 | + unix_time -= (-unix_offset) |
| 46 | + } else if unix_offset > 0 { |
| 47 | + unix_time += unix_offset |
| 48 | + } |
| 49 | + t = unix2(i64(unix_time), t.microsecond) |
| 50 | + return t |
| 51 | + } |
| 52 | + |
| 53 | + return error_invalid_time(9) |
| 54 | +} |
| 55 | + |
| 56 | +// parse returns time from a date string in "YYYY-MM-DD HH:MM:SS" format. |
| 57 | +pub fn parse(s string) ?Time { |
| 58 | + if s == '' { |
| 59 | + return error_invalid_time(0) |
| 60 | + } |
| 61 | + pos := s.index(' ') or { return error_invalid_time(1) } |
| 62 | + symd := s[..pos] |
| 63 | + ymd := symd.split('-') |
| 64 | + if ymd.len != 3 { |
| 65 | + return error_invalid_time(2) |
| 66 | + } |
| 67 | + shms := s[pos..] |
| 68 | + hms := shms.split(':') |
| 69 | + hour_ := hms[0][1..] |
| 70 | + minute_ := hms[1] |
| 71 | + second_ := hms[2] |
| 72 | + // |
| 73 | + iyear := ymd[0].int() |
| 74 | + imonth := ymd[1].int() |
| 75 | + iday := ymd[2].int() |
| 76 | + ihour := hour_.int() |
| 77 | + iminute := minute_.int() |
| 78 | + isecond := second_.int() |
| 79 | + // eprintln('>> iyear: $iyear | imonth: $imonth | iday: $iday | ihour: $ihour | iminute: $iminute | isecond: $isecond') |
| 80 | + if iyear > 9999 || iyear < -9999 { |
| 81 | + return error_invalid_time(3) |
| 82 | + } |
| 83 | + if imonth > 12 || imonth < 1 { |
| 84 | + return error_invalid_time(4) |
| 85 | + } |
| 86 | + if iday > 31 || iday < 1 { |
| 87 | + return error_invalid_time(5) |
| 88 | + } |
| 89 | + if ihour > 23 || ihour < 0 { |
| 90 | + return error_invalid_time(6) |
| 91 | + } |
| 92 | + if iminute > 59 || iminute < 0 { |
| 93 | + return error_invalid_time(7) |
| 94 | + } |
| 95 | + if isecond > 59 || isecond < 0 { |
| 96 | + return error_invalid_time(8) |
| 97 | + } |
| 98 | + res := new_time(Time{ |
| 99 | + year: iyear |
| 100 | + month: imonth |
| 101 | + day: iday |
| 102 | + hour: ihour |
| 103 | + minute: iminute |
| 104 | + second: isecond |
| 105 | + }) |
| 106 | + return res |
| 107 | +} |
| 108 | + |
| 109 | +// parse_iso8601 parses rfc8601 time format yyyy-MM-ddTHH:mm:ss.dddddd+dd:dd as local time |
| 110 | +// the fraction part is difference in milli seconds and the last part is offset |
| 111 | +// from UTC time and can be both +/- HH:mm |
| 112 | +// remarks: not all iso8601 is supported |
| 113 | +// also checks and support for leapseconds should be added in future PR |
| 114 | +pub fn parse_iso8601(s string) ?Time { |
| 115 | + if s == '' { |
| 116 | + return error_invalid_time(0) |
| 117 | + } |
| 118 | + t_i := s.index('T') or { -1 } |
| 119 | + parts := if t_i != -1 { [s[..t_i], s[t_i + 1..]] } else { s.split(' ') } |
| 120 | + if !(parts.len == 1 || parts.len == 2) { |
| 121 | + return error_invalid_time(12) |
| 122 | + } |
| 123 | + year, month, day := parse_iso8601_date(parts[0]) ? |
| 124 | + mut hour_, mut minute_, mut second_, mut microsecond_, mut unix_offset, mut is_local_time := 0, 0, 0, 0, i64(0), true |
| 125 | + if parts.len == 2 { |
| 126 | + hour_, minute_, second_, microsecond_, unix_offset, is_local_time = parse_iso8601_time(parts[1]) ? |
| 127 | + } |
| 128 | + mut t := new_time( |
| 129 | + year: year |
| 130 | + month: month |
| 131 | + day: day |
| 132 | + hour: hour_ |
| 133 | + minute: minute_ |
| 134 | + second: second_ |
| 135 | + microsecond: microsecond_ |
| 136 | + ) |
| 137 | + if is_local_time { |
| 138 | + return t // Time already local time |
| 139 | + } |
| 140 | + mut unix_time := t.unix |
| 141 | + if unix_offset < 0 { |
| 142 | + unix_time -= (-unix_offset) |
| 143 | + } else if unix_offset > 0 { |
| 144 | + unix_time += unix_offset |
| 145 | + } |
| 146 | + t = unix2(i64(unix_time), t.microsecond) |
| 147 | + return t |
| 148 | +} |
| 149 | + |
6 | 150 | // parse_rfc2822 returns time from a date string in RFC 2822 datetime format.
|
7 | 151 | pub fn parse_rfc2822(s string) ?Time {
|
8 | 152 | if s == '' {
|
|
0 commit comments