aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2024-09-22 19:29:15 +0100
committergingerBill <bill@gingerbill.org>2024-09-22 19:29:15 +0100
commitf7d74ff3a8596efef67d151ffb758ed085e94be0 (patch)
treeb6df329898654250df112da846646a6bc5063dda /core
parent95721fe29670e404a2fb5e5fe5e51b39f5c7b9d6 (diff)
parentba3d545c9625cbfa7a4d52df39ad71c1ec2150d3 (diff)
Merge branch 'master' of https://github.com/odin-lang/Odin
Diffstat (limited to 'core')
-rw-r--r--core/time/rfc3339.odin108
1 files changed, 107 insertions, 1 deletions
diff --git a/core/time/rfc3339.odin b/core/time/rfc3339.odin
index e4c6565d6..20e8ea0bb 100644
--- a/core/time/rfc3339.odin
+++ b/core/time/rfc3339.odin
@@ -187,4 +187,110 @@ scan_digits :: proc(s: string, sep: string, count: int) -> (res: int, ok: bool)
found_sep |= rune(s[count]) == v
}
return res, found_sep
-} \ No newline at end of file
+}
+
+/*
+Serialize the timestamp as a RFC 3339 string.
+
+The boolean `ok` is false if the `time` is not a valid datetime, or if allocating the result string fails.
+
+**Inputs**:
+- `utc_offset`: offset in minutes wrt UTC (ie. the timezone)
+- `include_nanos`: whether to include nanoseconds in the result.
+*/
+time_to_rfc3339 :: proc(time: Time, utc_offset : int = 0, include_nanos := true, allocator := context.allocator) -> (res: string, ok: bool) {
+ utc_offset := utc_offset
+
+ // convert to datetime
+ datetime := time_to_datetime(time) or_return
+
+ if datetime.year < 0 || datetime.year >= 10_000 { return "", false }
+
+ temp_string := [36]u8{}
+ offset : uint = 0
+
+ print_as_fixed_int :: proc(dst: []u8, offset: ^uint, width: i8, i: i64) {
+ i := i
+ width := width
+ for digit_idx in 0..<width {
+ last_digit := i % 10
+ dst[offset^ + uint(width) - uint(digit_idx)-1] = '0' + u8(last_digit)
+ i = i / 10
+ }
+
+ offset^ += uint(width)
+ }
+
+ print_as_fixed_int(temp_string[:], &offset, 4, datetime.year)
+ temp_string[offset] = '-'
+ offset += 1
+ print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.month))
+ temp_string[offset] = '-'
+ offset += 1
+ print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.day))
+ temp_string[offset] = 'T'
+ offset += 1
+ print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.hour))
+ temp_string[offset] = ':'
+ offset += 1
+ print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.minute))
+ temp_string[offset] = ':'
+ offset += 1
+ print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.second))
+
+ // turn 123_450_000 to 12345, 5
+ strip_trailing_zeroes_nanos :: proc(n: i64) -> (res: i64, n_digits: i8) {
+ res = n
+ n_digits = 9
+ for res % 10 == 0 {
+ res = res / 10
+ n_digits -= 1
+ }
+ return
+ }
+
+ // pre-epoch times: turn, say, -400ms to +600ms for display
+ nanos := time._nsec % 1_000_000_000
+ if nanos < 0 {
+ nanos += 1_000_000_000
+ }
+
+ if nanos != 0 && include_nanos {
+ temp_string[offset] = '.'
+ offset += 1
+
+ // remove trailing zeroes
+ nanos_nonzero, n_digits := strip_trailing_zeroes_nanos(nanos)
+ assert(nanos_nonzero != 0)
+
+ // write digits, right-to-left
+ for digit_idx : i8 = n_digits-1; digit_idx >= 0; digit_idx -= 1 {
+ digit := u8(nanos_nonzero % 10)
+ temp_string[offset + uint(digit_idx)] = '0' + u8(digit)
+ nanos_nonzero /= 10
+ }
+ offset += uint(n_digits)
+ }
+
+ if utc_offset == 0 {
+ temp_string[offset] = 'Z'
+ offset += 1
+ } else {
+ temp_string[offset] = utc_offset > 0 ? '+' : '-'
+ offset += 1
+ utc_offset = abs(utc_offset)
+ print_as_fixed_int(temp_string[:], &offset, 2, i64(utc_offset / 60))
+ temp_string[offset] = ':'
+ offset += 1
+ print_as_fixed_int(temp_string[:], &offset, 2, i64(utc_offset % 60))
+ }
+
+ res_as_slice, res_alloc := make_slice([]u8, len=offset, allocator = allocator)
+ if res_alloc != nil {
+ return "", false
+ }
+
+ copy(res_as_slice, temp_string[:offset])
+
+ return string(res_as_slice), true
+}