mirror of
https://github.com/sbrow/thor.git
synced 2026-08-26 11:23:32 -04:00
feat: Added Go style date format configuration.
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
package main
|
||||
|
||||
import "base:runtime"
|
||||
import "common"
|
||||
import "core:fmt"
|
||||
import "core:log"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
import "inlined"
|
||||
import "original"
|
||||
|
||||
DATES :: #load(#directory + os.Path_Separator_String + "dates.txt")
|
||||
FORMATS :: #load(#directory + os.Path_Separator_String + "formats.txt")
|
||||
|
||||
ITERATIONS :: 1_000
|
||||
|
||||
dates: []common.Date_Components
|
||||
formats: []string
|
||||
|
||||
formatter :: #type proc(
|
||||
date: common.Date_Components,
|
||||
format: string,
|
||||
allocator: runtime.Allocator,
|
||||
) -> string
|
||||
|
||||
benchmark :: #type proc(
|
||||
options: ^time.Benchmark_Options,
|
||||
allocator: runtime.Allocator,
|
||||
) -> (
|
||||
err: time.Benchmark_Error,
|
||||
)
|
||||
|
||||
Version :: struct {
|
||||
name: string,
|
||||
bench: benchmark,
|
||||
}
|
||||
|
||||
init :: proc() {
|
||||
raw_dates := strings.split_lines(string(DATES))
|
||||
raw_dates = raw_dates[:len(raw_dates) - 1]
|
||||
formats = strings.split_lines(string(FORMATS))
|
||||
|
||||
dates = make([]common.Date_Components, len(raw_dates))
|
||||
|
||||
assert(to_parsed(raw_dates, &dates))
|
||||
}
|
||||
|
||||
to_parsed :: proc(raw_dates: []string, dates: ^[]common.Date_Components) -> bool {
|
||||
for date, i in raw_dates {
|
||||
// log.debugf("parsing '%s'", date)
|
||||
dates[i] = common.parse_iso_date(date) or_return
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
logger_opts: log.Options =
|
||||
(log.Default_Console_Logger_Opts - log.Full_Timestamp_Opts - {.Short_File_Path})
|
||||
console_logger := log.create_console_logger(.Info, logger_opts)
|
||||
context.logger = console_logger
|
||||
defer log.destroy_console_logger(console_logger)
|
||||
|
||||
init()
|
||||
|
||||
versions := [?]Version {
|
||||
{"original", to_benchmark(original.format_date)},
|
||||
{"inlined", to_benchmark(inlined.format_date)},
|
||||
}
|
||||
|
||||
fmt.printfln(
|
||||
"%-10s %14s %10s %14s %12s %8s",
|
||||
"version",
|
||||
"total",
|
||||
"calls",
|
||||
"calls/s",
|
||||
"time/call",
|
||||
"MB/s",
|
||||
)
|
||||
|
||||
for version in versions {
|
||||
defer free_all(context.temp_allocator)
|
||||
|
||||
b: time.Benchmark_Options
|
||||
b.bench = version.bench
|
||||
|
||||
if err := time.benchmark(&b, context.temp_allocator); err != nil {
|
||||
fmt.panicf("%v", err)
|
||||
}
|
||||
|
||||
// fmt.printfln("%v", b)
|
||||
per_call := time.Duration(i64(b.duration) / i64(b.count))
|
||||
fmt.printfln(
|
||||
"%-10s %14v % 10d % 14.0f %12v % 8.2f",
|
||||
version.name,
|
||||
b.duration,
|
||||
b.count,
|
||||
b.rounds_per_second,
|
||||
per_call,
|
||||
b.megabytes_per_second,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
to_benchmark :: proc($f: formatter) -> benchmark {
|
||||
return(
|
||||
proc(
|
||||
opts: ^time.Benchmark_Options,
|
||||
allocator: runtime.Allocator,
|
||||
) -> (
|
||||
err: time.Benchmark_Error,
|
||||
) {
|
||||
for _ in 0 ..< ITERATIONS {
|
||||
for date in dates {
|
||||
for format in formats {
|
||||
f(date, format, allocator)
|
||||
opts.count += 1
|
||||
opts.processed += size_of(date) + size_of(format)
|
||||
}
|
||||
}
|
||||
|
||||
opts.rounds += 1
|
||||
}
|
||||
|
||||
return err
|
||||
} \
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package common
|
||||
|
||||
Date_Components :: struct {
|
||||
year: int,
|
||||
month: int,
|
||||
day: int,
|
||||
hour: int,
|
||||
minute: int,
|
||||
second: int,
|
||||
}
|
||||
|
||||
// TODO: Use some kind of scanner interface
|
||||
parse_iso_date :: proc(iso: string) -> (c: Date_Components, ok: bool) {
|
||||
if len(iso) < 10 {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
c.year = parse_2_digits(iso, 0) * 100 + parse_2_digits(iso, 2)
|
||||
c.month = parse_2_digits(iso, 5)
|
||||
c.day = parse_2_digits(iso, 8)
|
||||
|
||||
if c.month < 1 || c.month > 12 {
|
||||
return {}, false
|
||||
}
|
||||
if c.day < 1 || c.day > 31 {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
if len(iso) >= 19 && (iso[10] == 'T' || iso[10] == 't') {
|
||||
c.hour = parse_2_digits(iso, 11)
|
||||
c.minute = parse_2_digits(iso, 14)
|
||||
c.second = parse_2_digits(iso, 17)
|
||||
}
|
||||
|
||||
return c, true
|
||||
}
|
||||
|
||||
parse_2_digits :: proc(s: string, offset: int) -> int {
|
||||
if offset + 1 >= len(s) {
|
||||
return 0
|
||||
}
|
||||
return (int(s[offset]) - 0x30) * 10 + (int(s[offset + 1]) - 0x30)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
2024-01-05
|
||||
2024-02-14
|
||||
2024-03-08
|
||||
2024-04-22
|
||||
2024-05-01
|
||||
2024-06-19
|
||||
2024-07-04
|
||||
2024-08-30
|
||||
2024-09-11
|
||||
2024-10-31
|
||||
2024-11-27
|
||||
2024-12-25
|
||||
2023-01-15T00:00:00-05:00
|
||||
2023-02-14T09:30:15+01:00
|
||||
2023-03-08T14:45:22-08:00
|
||||
2023-04-22T23:59:59+05:30
|
||||
2023-05-01T06:05:09-04:00
|
||||
2023-06-19T18:20:33+09:00
|
||||
2023-07-04T03:04:05-07:00
|
||||
2023-08-30T11:11:11+00:00
|
||||
2023-09-11T15:04:05-04:00
|
||||
2023-10-31T20:15:30+02:00
|
||||
2023-11-27T07:07:07-06:00
|
||||
2023-12-25T12:30:45+03:00
|
||||
2025-01-15T00:15:00-0500
|
||||
2025-02-14T09:30:15+0100
|
||||
2025-03-08T14:45:22-0800
|
||||
2025-04-22T23:59:59+0530
|
||||
2025-05-01T06:05:09-0400
|
||||
2025-06-19T18:20:33+0900
|
||||
2025-07-04T03:04:05-0700
|
||||
2025-08-30T11:11:11+0000
|
||||
2025-09-11T15:04:05-0400
|
||||
2025-10-31T20:15:30+0200
|
||||
2025-11-27T07:07:07-0600
|
||||
2025-12-25T12:30:45+0300
|
||||
2022-01-01T00:00:00Z
|
||||
2022-02-14T01:30:00Z
|
||||
2022-03-08T11:59:59Z
|
||||
2022-04-22T12:00:00Z
|
||||
2022-05-01T12:00:01Z
|
||||
2022-06-19T13:15:00Z
|
||||
2022-07-04T15:04:05Z
|
||||
2022-08-30T18:45:30Z
|
||||
2022-09-11T20:20:20Z
|
||||
2022-10-31T22:10:10Z
|
||||
2022-11-27T23:59:59Z
|
||||
2022-12-25T09:09:09Z
|
||||
2021-03-14t09:26:53
|
||||
2021-07-20t23:00:00
|
||||
2021-11-05t00:00:00
|
||||
2020-06-15T15:04:05
|
||||
2020-12-31T23:59:59
|
||||
2020-01-01T00:00:00
|
||||
2024-02-29T12:00:00Z
|
||||
2000-02-29T00:00:00Z
|
||||
1999-12-31T23:59:59Z
|
||||
2026-01-01T00:00:00Z
|
||||
2026-07-23T14:56:07-04:00
|
||||
@@ -0,0 +1,46 @@
|
||||
2006
|
||||
06
|
||||
January
|
||||
Jan
|
||||
Monday
|
||||
Mon
|
||||
01
|
||||
02
|
||||
03
|
||||
04
|
||||
05
|
||||
15
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
PM
|
||||
pm
|
||||
MST
|
||||
2006-01-02
|
||||
2006-01-02 15:04:05
|
||||
2 Jan 2006
|
||||
Jan 2, 2006
|
||||
January 2, 2006
|
||||
Monday, January 2, 2006
|
||||
Mon Jan 2 2006
|
||||
Mon Jan 02 2006
|
||||
Mon, 02 Jan 2006 15:04:05 MST
|
||||
01/02/2006
|
||||
02/01/2006
|
||||
2006/01/02
|
||||
1/2/06
|
||||
1/2/06 3:04PM
|
||||
1/2/2006
|
||||
3:04:05 PM
|
||||
3:04 pm
|
||||
15:04:05
|
||||
15:04
|
||||
15:04:05 MST
|
||||
January 2006
|
||||
Jan 06
|
||||
2 January 2006
|
||||
Monday 2 Jan 2006 at 15:04
|
||||
06-01-02
|
||||
3:4:5
|
||||
@@ -0,0 +1,126 @@
|
||||
package inlined
|
||||
|
||||
import "../common"
|
||||
import "core:fmt"
|
||||
import "core:log"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
import "core:time/datetime"
|
||||
|
||||
format_date :: proc(
|
||||
dt: common.Date_Components,
|
||||
fmt: string,
|
||||
allocator := context.temp_allocator,
|
||||
) -> string {
|
||||
b: strings.Builder
|
||||
strings.builder_init_len(&b, len(fmt), allocator)
|
||||
|
||||
for i := 0; i < len(fmt); {
|
||||
matched := match_token(&b, dt, fmt[i:])
|
||||
if matched > 0 {
|
||||
i += matched
|
||||
} else {
|
||||
strings.write_byte(&b, fmt[i])
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
log.debugf("formatted date: '%s'", b.buf)
|
||||
return strings.to_string(b)
|
||||
}
|
||||
|
||||
match_token :: proc(b: ^strings.Builder, dt: common.Date_Components, s: string) -> int {
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"January",
|
||||
) {strings.write_string(b, fmt.tprintf("%s", time.Month(dt.month))); return 7}
|
||||
if strings.has_prefix(s, "Monday") {emit_weekday(b, dt, full = true); return 6}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"2006",
|
||||
) {strings.write_string(b, fmt.tprintf("%04d", dt.year)); return 4}
|
||||
if strings.has_prefix(s, "MST") {strings.write_string(b, "UTC"); return 3}
|
||||
if strings.has_prefix(s, "Jan") {emit_month_abbr(b, dt); return 3}
|
||||
if strings.has_prefix(s, "Mon") {emit_weekday(b, dt, full = false); return 3}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"06",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.year % 100)); return 2}
|
||||
if strings.has_prefix(s, "02") {strings.write_string(b, fmt.tprintf("%02d", dt.day)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"15",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.hour)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"04",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.minute)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"05",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.second)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"01",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.month)); return 2}
|
||||
if strings.has_prefix(s, "03") {emit_hour_12(b, dt, pad = true); return 2}
|
||||
if strings.has_prefix(s, "PM") {
|
||||
strings.write_string(b, "PM" if dt.hour >= 12 else "AM")
|
||||
return 2
|
||||
}
|
||||
if strings.has_prefix(s, "pm") {
|
||||
strings.write_string(b, "pm" if dt.hour >= 12 else "am")
|
||||
return 2
|
||||
}
|
||||
if len(s) >= 1 {
|
||||
switch s[0] {
|
||||
case '2':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.day)); return 1
|
||||
case '1':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.month)); return 1
|
||||
case '4':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.minute)); return 1
|
||||
case '5':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.second)); return 1
|
||||
case '3':
|
||||
emit_hour_12(b, dt, pad = false); return 1
|
||||
case:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
emit_month_abbr :: proc(b: ^strings.Builder, dt: common.Date_Components) {
|
||||
name := fmt.tprintf("%s", time.Month(dt.month))
|
||||
strings.write_string(b, name[:3 if len(name) >= 3 else len(name)])
|
||||
}
|
||||
|
||||
emit_weekday :: proc(b: ^strings.Builder, dt: common.Date_Components, full: bool) {
|
||||
date := datetime.Date {
|
||||
year = i64(dt.year),
|
||||
month = i8(dt.month),
|
||||
day = i8(dt.day),
|
||||
}
|
||||
ordinal, err := datetime.date_to_ordinal(date)
|
||||
if err != .None {
|
||||
strings.write_string(b, "???")
|
||||
return
|
||||
}
|
||||
weekday := datetime.day_of_week(ordinal)
|
||||
name := fmt.tprintf("%s", weekday)
|
||||
if full {
|
||||
strings.write_string(b, name)
|
||||
} else {
|
||||
strings.write_string(b, name[:3 if len(name) >= 3 else len(name)])
|
||||
}
|
||||
}
|
||||
|
||||
emit_hour_12 :: proc(b: ^strings.Builder, dt: common.Date_Components, pad: bool) {
|
||||
h12 := dt.hour % 12
|
||||
if h12 == 0 {h12 = 12}
|
||||
format := "%02d" if pad else "%d"
|
||||
|
||||
fmt.sbprintf(b, format, h12)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
package inplace
|
||||
|
||||
import "../common"
|
||||
import "core:bytes"
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
import "core:time/datetime"
|
||||
|
||||
format_date :: proc(
|
||||
dt: common.Date_Components,
|
||||
fmt: string,
|
||||
allocator := context.temp_allocator,
|
||||
) -> string {
|
||||
b := make([]byte, len(fmt), context.temp_allocator)
|
||||
x := transmute([]byte)(fmt)
|
||||
mem.copy_non_overlapping(&b[0], &x, len(fmt))
|
||||
|
||||
|
||||
return string(b)
|
||||
// for i := 0; i < len(fmt); {
|
||||
// matched := match_token(&b, dt, fmt[i:])
|
||||
// if matched > 0 {
|
||||
// i += matched
|
||||
// } else {
|
||||
// strings.write_byte(&b, fmt[i])
|
||||
// i += 1
|
||||
// }
|
||||
// }
|
||||
|
||||
// log.debugf("formatted date: '%s'", b.buf)
|
||||
// return strings.to_string(b)
|
||||
}
|
||||
|
||||
FULL_MONTH: string : "January"
|
||||
|
||||
match_token :: proc(s: []byte, dt: common.Date_Components) {
|
||||
if bytes.equal(s[:len(FULL_MONTH)], transmute([]u8)FULL_MONTH) {
|
||||
mo := fmt.tprintf("%s", time.Month(dt.month))
|
||||
mem.copy_non_overlapping(&s[0], &(transmute([]u8)mo)[0], len(mo))
|
||||
}
|
||||
/*
|
||||
if strings.has_prefix(s, "Monday") {emit_weekday(b, dt, full = true); return 6}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"2006",
|
||||
) {strings.write_string(b, fmt.tprintf("%04d", dt.year)); return 4}
|
||||
if strings.has_prefix(s, "MST") {strings.write_string(b, "UTC"); return 3}
|
||||
if strings.has_prefix(s, "Jan") {emit_month_abbr(b, dt); return 3}
|
||||
if strings.has_prefix(s, "Mon") {emit_weekday(b, dt, full = false); return 3}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"06",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.year % 100)); return 2}
|
||||
if strings.has_prefix(s, "02") {strings.write_string(b, fmt.tprintf("%02d", dt.day)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"15",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.hour)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"04",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.minute)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"05",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.second)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"01",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.month)); return 2}
|
||||
if strings.has_prefix(s, "03") {emit_hour_12(b, dt, pad = true); return 2}
|
||||
if strings.has_prefix(s, "PM") {
|
||||
strings.write_string(b, "PM" if dt.hour >= 12 else "AM")
|
||||
return 2
|
||||
}
|
||||
if strings.has_prefix(s, "pm") {
|
||||
strings.write_string(b, "pm" if dt.hour >= 12 else "am")
|
||||
return 2
|
||||
}
|
||||
if len(s) >= 1 {
|
||||
switch s[0] {
|
||||
case '2':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.day)); return 1
|
||||
case '1':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.month)); return 1
|
||||
case '4':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.minute)); return 1
|
||||
case '5':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.second)); return 1
|
||||
case '3':
|
||||
emit_hour_12(b, dt, pad = false); return 1
|
||||
case:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
emit_month_abbr :: proc(b: ^strings.Builder, dt: common.Date_Components) {
|
||||
name := fmt.tprintf("%s", time.Month(dt.month))
|
||||
strings.write_string(b, name[:3 if len(name) >= 3 else len(name)])
|
||||
}
|
||||
|
||||
emit_weekday :: proc(b: ^strings.Builder, dt: common.Date_Components, full: bool) {
|
||||
date := datetime.Date {
|
||||
year = i64(dt.year),
|
||||
month = i8(dt.month),
|
||||
day = i8(dt.day),
|
||||
}
|
||||
ordinal, err := datetime.date_to_ordinal(date)
|
||||
if err != .None {
|
||||
strings.write_string(b, "???")
|
||||
return
|
||||
}
|
||||
weekday := datetime.day_of_week(ordinal)
|
||||
name := fmt.tprintf("%s", weekday)
|
||||
if full {
|
||||
strings.write_string(b, name)
|
||||
} else {
|
||||
strings.write_string(b, name[:3 if len(name) >= 3 else len(name)])
|
||||
}
|
||||
}
|
||||
|
||||
emit_hour_12 :: proc(b: ^strings.Builder, dt: common.Date_Components, pad: bool) {
|
||||
h12 := dt.hour % 12
|
||||
if h12 == 0 {h12 = 12}
|
||||
format := "%02d" if pad else "%d"
|
||||
|
||||
fmt.sbprintf(b, format, h12)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
package original
|
||||
|
||||
import "../common"
|
||||
import "core:fmt"
|
||||
import "core:log"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
import "core:time/datetime"
|
||||
|
||||
format_date :: proc(
|
||||
dt: common.Date_Components,
|
||||
fmt: string,
|
||||
allocator := context.temp_allocator,
|
||||
) -> string {
|
||||
b: strings.Builder
|
||||
strings.builder_init(&b, allocator)
|
||||
|
||||
for i := 0; i < len(fmt); {
|
||||
matched := match_token(&b, dt, fmt[i:])
|
||||
if matched > 0 {
|
||||
i += matched
|
||||
} else {
|
||||
strings.write_byte(&b, fmt[i])
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
log.debugf("formatted date: '%s'", b.buf)
|
||||
return strings.to_string(b)
|
||||
}
|
||||
|
||||
match_token :: proc(b: ^strings.Builder, dt: common.Date_Components, s: string) -> int {
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"January",
|
||||
) {strings.write_string(b, fmt.tprintf("%s", time.Month(dt.month))); return 7}
|
||||
if strings.has_prefix(s, "Monday") {emit_weekday(b, dt, full = true); return 6}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"2006",
|
||||
) {strings.write_string(b, fmt.tprintf("%04d", dt.year)); return 4}
|
||||
if strings.has_prefix(s, "MST") {strings.write_string(b, "UTC"); return 3}
|
||||
if strings.has_prefix(s, "Jan") {emit_month_abbr(b, dt); return 3}
|
||||
if strings.has_prefix(s, "Mon") {emit_weekday(b, dt, full = false); return 3}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"06",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.year % 100)); return 2}
|
||||
if strings.has_prefix(s, "02") {strings.write_string(b, fmt.tprintf("%02d", dt.day)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"15",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.hour)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"04",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.minute)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"05",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.second)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"01",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.month)); return 2}
|
||||
if strings.has_prefix(s, "03") {emit_hour_12(b, dt, pad = true); return 2}
|
||||
if strings.has_prefix(s, "PM") {emit_am_pm(b, dt); return 2}
|
||||
if strings.has_prefix(s, "pm") {emit_am_pm_lower(b, dt); return 2}
|
||||
if len(s) >= 1 {
|
||||
switch s[0] {
|
||||
case '2':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.day)); return 1
|
||||
case '1':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.month)); return 1
|
||||
case '4':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.minute)); return 1
|
||||
case '5':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.second)); return 1
|
||||
case '3':
|
||||
emit_hour_12(b, dt, pad = false); return 1
|
||||
case:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
emit_month_abbr :: proc(b: ^strings.Builder, dt: common.Date_Components) {
|
||||
name := fmt.tprintf("%s", time.Month(dt.month))
|
||||
strings.write_string(b, name[:3 if len(name) >= 3 else len(name)])
|
||||
}
|
||||
|
||||
emit_weekday :: proc(b: ^strings.Builder, dt: common.Date_Components, full: bool) {
|
||||
date := datetime.Date {
|
||||
year = i64(dt.year),
|
||||
month = i8(dt.month),
|
||||
day = i8(dt.day),
|
||||
}
|
||||
ordinal, err := datetime.date_to_ordinal(date)
|
||||
if err != .None {
|
||||
strings.write_string(b, "???")
|
||||
return
|
||||
}
|
||||
weekday := datetime.day_of_week(ordinal)
|
||||
name := fmt.tprintf("%s", weekday)
|
||||
if full {
|
||||
strings.write_string(b, name)
|
||||
} else {
|
||||
strings.write_string(b, name[:3 if len(name) >= 3 else len(name)])
|
||||
}
|
||||
}
|
||||
|
||||
emit_hour_12 :: proc(b: ^strings.Builder, dt: common.Date_Components, pad: bool) {
|
||||
h12 := dt.hour % 12
|
||||
if h12 == 0 {h12 = 12}
|
||||
format := "%02d" if pad else "%d"
|
||||
|
||||
fmt.sbprintf(b, format, h12)
|
||||
}
|
||||
|
||||
emit_am_pm :: proc(b: ^strings.Builder, dt: common.Date_Components) {
|
||||
strings.write_string(b, "PM" if dt.hour >= 12 else "AM")
|
||||
}
|
||||
|
||||
emit_am_pm_lower :: proc(b: ^strings.Builder, dt: common.Date_Components) {
|
||||
strings.write_string(b, "pm" if dt.hour >= 12 else "am")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
Date_Components :: struct {
|
||||
year: int,
|
||||
month: int,
|
||||
day: int,
|
||||
hour: int,
|
||||
minute: int,
|
||||
second: int,
|
||||
}
|
||||
|
||||
// TODO: Use some kind of scanner interface
|
||||
parse_iso_date :: proc(iso: string) -> (c: Date_Components, ok: bool) {
|
||||
if len(iso) < 10 {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
c.year = parse_2_digits(iso, 0) * 100 + parse_2_digits(iso, 2)
|
||||
c.month = parse_2_digits(iso, 5)
|
||||
c.day = parse_2_digits(iso, 8)
|
||||
|
||||
if c.month < 1 || c.month > 12 {
|
||||
return {}, false
|
||||
}
|
||||
if c.day < 1 || c.day > 31 {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
if len(iso) >= 19 && (iso[10] == 'T' || iso[10] == 't') {
|
||||
c.hour = parse_2_digits(iso, 11)
|
||||
c.minute = parse_2_digits(iso, 14)
|
||||
c.second = parse_2_digits(iso, 17)
|
||||
}
|
||||
|
||||
return c, true
|
||||
}
|
||||
|
||||
parse_2_digits :: proc(s: string, offset: int) -> int {
|
||||
if offset + 1 >= len(s) {
|
||||
return 0
|
||||
}
|
||||
return (int(s[offset]) - 0x30) * 10 + (int(s[offset + 1]) - 0x30)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package returned
|
||||
|
||||
import "../common"
|
||||
import "core:fmt"
|
||||
import "core:log"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
import "core:time/datetime"
|
||||
|
||||
format_date :: proc(
|
||||
dt: common.Date_Components,
|
||||
fmt: string,
|
||||
allocator := context.temp_allocator,
|
||||
) -> string {
|
||||
b: strings.Builder
|
||||
// strings.builder_init_len(&b, len(fmt), allocator)
|
||||
strings.builder_init(&b, allocator)
|
||||
|
||||
for i := 0; i < len(fmt); {
|
||||
matched := match_token(&b, dt, fmt[i:])
|
||||
if matched > 0 {
|
||||
i += matched
|
||||
} else {
|
||||
strings.write_byte(&b, fmt[i])
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
log.debugf("formatted date: '%s'", b.buf)
|
||||
return strings.to_string(b)
|
||||
}
|
||||
|
||||
match_token :: proc(b: ^strings.Builder, dt: common.Date_Components, s: string) -> int {
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"January",
|
||||
) {strings.write_string(b, fmt.tprintf("%s", time.Month(dt.month))); return 7}
|
||||
if strings.has_prefix(s, "Monday") {emit_weekday(b, dt, full = true); return 6}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"2006",
|
||||
) {strings.write_string(b, fmt.tprintf("%04d", dt.year)); return 4}
|
||||
if strings.has_prefix(s, "MST") {strings.write_string(b, "UTC"); return 3}
|
||||
if strings.has_prefix(s, "Jan") {emit_month_abbr(b, dt); return 3}
|
||||
if strings.has_prefix(s, "Mon") {emit_weekday(b, dt, full = false); return 3}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"06",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.year % 100)); return 2}
|
||||
if strings.has_prefix(s, "02") {strings.write_string(b, fmt.tprintf("%02d", dt.day)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"15",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.hour)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"04",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.minute)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"05",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.second)); return 2}
|
||||
if strings.has_prefix(
|
||||
s,
|
||||
"01",
|
||||
) {strings.write_string(b, fmt.tprintf("%02d", dt.month)); return 2}
|
||||
if strings.has_prefix(s, "03") {emit_hour_12(b, dt, pad = true); return 2}
|
||||
if strings.has_prefix(s, "PM") {
|
||||
strings.write_string(b, "PM" if dt.hour >= 12 else "AM")
|
||||
return 2
|
||||
}
|
||||
if strings.has_prefix(s, "pm") {
|
||||
strings.write_string(b, "pm" if dt.hour >= 12 else "am")
|
||||
return 2
|
||||
}
|
||||
if len(s) >= 1 {
|
||||
switch s[0] {
|
||||
case '2':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.day)); return 1
|
||||
case '1':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.month)); return 1
|
||||
case '4':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.minute)); return 1
|
||||
case '5':
|
||||
strings.write_string(b, fmt.tprintf("%d", dt.second)); return 1
|
||||
case '3':
|
||||
emit_hour_12(b, dt, pad = false); return 1
|
||||
case:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
emit_month_abbr :: proc(b: ^strings.Builder, dt: common.Date_Components) {
|
||||
name := fmt.tprintf("%s", time.Month(dt.month))
|
||||
strings.write_string(b, name[:3 if len(name) >= 3 else len(name)])
|
||||
}
|
||||
|
||||
emit_weekday :: proc(b: ^strings.Builder, dt: common.Date_Components, full: bool) {
|
||||
date := datetime.Date {
|
||||
year = i64(dt.year),
|
||||
month = i8(dt.month),
|
||||
day = i8(dt.day),
|
||||
}
|
||||
ordinal, err := datetime.date_to_ordinal(date)
|
||||
if err != .None {
|
||||
strings.write_string(b, "???")
|
||||
return
|
||||
}
|
||||
weekday := datetime.day_of_week(ordinal)
|
||||
name := fmt.tprintf("%s", weekday)
|
||||
if full {
|
||||
strings.write_string(b, name)
|
||||
} else {
|
||||
strings.write_string(b, name[:3 if len(name) >= 3 else len(name)])
|
||||
}
|
||||
}
|
||||
|
||||
emit_hour_12 :: proc(b: ^strings.Builder, dt: common.Date_Components, pad: bool) {
|
||||
h12 := dt.hour % 12
|
||||
if h12 == 0 {h12 = 12}
|
||||
format := "%02d" if pad else "%d"
|
||||
|
||||
fmt.sbprintf(b, format, h12)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user