fix: strict ClockTime parsing and ISODuration SQL

Validate constructors, strict HH:MM:SS parse, UTC ClockTimeFromTime,
ISODuration Value/Scan, Compare and TextMarshaler, codec.go split.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-29 18:01:08 -07:00
parent 49ac7dd22a
commit a1284075aa
7 changed files with 409 additions and 187 deletions
+27 -12
View File
@@ -1,21 +1,36 @@
# clocktime
clocktime provides a type that holds a calendar-independent time of day value in 24-hour format.
Time-of-day and ISO 8601 duration types for APIs, GraphQL, and SQL.
clocktime provides basic types that extend Go's built-in time.Time to provide time-of-day and duration support.
## Install
## `clocktime.ClockTime
```bash
go get gitea.auvem.com/go-toolkit/clocktime
```
Holds calendar-independent time-of-day in 24-hour format.
## ClockTime
- Converts to and from `HH:MM:SS` format (subset of ISO8061)
- Marshals to `[]byte` containing string `HH:MM:SS` for SQL
- Marshals to `string` in format `HH:MM:SS` for JSON & gqlgen
```go
ct, err := clocktime.ParseClockTime("09:30:00")
t := ct.Time() // 1970-01-01 UTC anchor
```
## `clocktime.Duration`
- Strict `HH:MM:SS` parsing via [ParseClockTime]
- SQL `TIME` columns: stored as `[]byte` / string; scanned from `time.Time`, `[]byte`, or `string`
- Optional fields: use `*ClockTime`; `IsZero()` is true only for nil pointers
- Midnight `00:00:00` is valid; use `IsMidnight()` to detect it
Wraps time.Duration with prioritized ISO8061 support and opinionated marshalling.
## ISODuration
- Converts to and from ISO8061 format (utilises [sosodev/duration](https://github.com/sosodev/duration))
- Marshals to `uint64` with nanosecond precision. Largest representatable duration is about 290 years, limited by underlying time.Duration type.
- Marshals to `string` in ISO8061 format for JSON & gqlgen
```go
d, err := clocktime.ParseISODuration("PT1H30M")
ns := d.Duration()
```
- JSON/GQL: ISO 8601 strings
- SQL: `int64` nanoseconds (strings accepted on scan)
- Calendar durations (`P1M`, `P1Y`) convert to approximate nanoseconds and may not round-trip
## gqlgen
Both types implement `MarshalGQL` / `UnmarshalGQL`.