Documentation
¶
Overview ¶
Package timeutil solves the recurring friction of marshaling time values to and from JSON in Go services. The standard library's time.Time marshals as RFC-3339 only, and time.Duration marshals as a raw nanosecond integer — both mismatches for APIs that expect human-readable strings like "1h30m" or "2023-01-02T15:04:05Z". This package provides two drop-in types that fix both problems, plus a compile-time-safe mechanism for customizing the datetime format without runtime configuration.
Problem ¶
Services that exchange time values over JSON face two common issues:
- Different API contracts require different datetime formats (RFC-3339, RFC-822, date-only, kitchen time, …). Switching formats means changing marshal/unmarshal logic scattered across the codebase, or wrapping time.Time with a custom type for every format.
- Duration values deserialised from JSON as raw nanosecond integers are unreadable in configuration files and API payloads. Human operators and API consumers expect strings like "30s" or "5m".
Key Features ¶
- DateTime: a generic type `DateTime[T DateTimeType]` that wraps time.Time and implements json.Marshaler / json.Unmarshaler using the format string returned by the type parameter T. Switching formats is a one-character type-argument change — no runtime configuration, no string constants, caught by the compiler.
- DateTimeType: the single-method interface (`Format() string`) that type parameters must satisfy, making it trivial to define custom formats.
- Built-in format types: ready-to-use implementations of DateTimeType for every format constant in the standard library — TRFC3339, TRFC3339Nano, TRFC822, TRFC822Z, TRFC850, TRFC1123, TRFC1123Z, TUnixDate, TANSIC, TRubyDate, TKitchen, TStamp, TStampMilli, TStampMicro, TStampNano, TLayout, and TDateOnly / TTimeOnly — covering every use case without additional dependencies.
- Duration: an alias for time.Duration that marshals as a human-readable string (e.g. "1h30m0s") and unmarshals from both string and numeric JSON values, making duration fields self-documenting in configuration files and API responses.
Usage ¶
Typed datetime in a struct:
type Event struct {
StartedAt timeutil.DateTime[timeutil.TRFC3339] `json:"started_at"`
EndedAt timeutil.DateTime[timeutil.TDateOnly] `json:"ended_at"`
}
e := Event{
StartedAt: timeutil.DateTime[timeutil.TRFC3339](time.Now()),
}
b, _ := json.Marshal(e)
// {"started_at":"2023-01-02T15:04:05Z","ended_at":"2023-01-02"}
Human-readable duration in configuration:
type Config struct {
Timeout timeutil.Duration `json:"timeout"`
}
var cfg Config
_ = json.Unmarshal([]byte(`{"timeout":"30s"}`), &cfg)
// cfg.Timeout is equivalent to 30 * time.Second
Custom format:
type TYearMonth struct{}
func (TYearMonth) Format() string { return "2006-01" }
type Report struct {
Month timeutil.DateTime[TYearMonth] `json:"month"`
}
Index ¶
- Constants
- type DateTime
- type DateTimeType
- type Duration
- type TANSIC
- type TDateOnly
- type TDateTime
- type TJira
- type TKitchen
- type TLayout
- type TRFC822
- type TRFC822Z
- type TRFC850
- type TRFC1123
- type TRFC1123Z
- type TRFC3339
- type TRFC3339Nano
- type TRubyDate
- type TStamp
- type TStampMicro
- type TStampMilli
- type TStampNano
- type TTimeOnly
- type TUnixDate
Examples ¶
Constants ¶
const TimeJiraFormat = "2006-01-02T15:04:05.000-0700"
TimeJiraFormat is the Jira date-time format string.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type DateTime ¶
type DateTime[T DateTimeType] time.Time
DateTime wraps time.Time and applies the format defined by T during JSON (un)marshaling.
func (DateTime[T]) MarshalJSON ¶
MarshalJSON encodes the date as a JSON string using type parameter T's format.
Example ¶
package main
import (
"encoding/json"
"fmt"
"log"
"time"
"github.com/tecnickcom/gogen/pkg/timeutil"
)
func main() {
dt := timeutil.DateTime[timeutil.TRFC3339](time.Date(2023, 1, 2, 15, 4, 5, 0, time.UTC))
b, err := json.Marshal(dt)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
}
Output: "2023-01-02T15:04:05Z"
func (DateTime[T]) String ¶
String formats the date as a string according to type parameter T's Format() method.
func (*DateTime[T]) UnmarshalJSON ¶
UnmarshalJSON parses a JSON date string according to type parameter T's format, using UTC as the timezone.
Example ¶
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/tecnickcom/gogen/pkg/timeutil"
)
func main() {
var dt timeutil.DateTime[timeutil.TRFC3339]
data := []byte(`"2023-01-02T15:04:05Z"`)
err := json.Unmarshal(data, &dt)
if err != nil {
log.Fatal(err)
}
fmt.Println(dt.String())
}
Output: 2023-01-02T15:04:05Z
type DateTimeType ¶
type DateTimeType interface {
Format() string
}
DateTimeType provides the layout string used by DateTime JSON marshaling/unmarshalling.
type Duration ¶
Duration aliases time.Duration for JSON marshaling as human-readable strings (e.g., "1h30m") instead of nanoseconds.
func (Duration) MarshalJSON ¶
MarshalJSON encodes the duration as a human-readable string (e.g., "20s", "1h") rather than a raw nanosecond integer.
Example ¶
package main
import (
"encoding/json"
"fmt"
"log"
"time"
"github.com/tecnickcom/gogen/pkg/timeutil"
)
func main() {
data := timeutil.Duration(7*time.Hour + 11*time.Minute + 13*time.Second)
enc, err := json.Marshal(data)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(enc))
}
Output: "7h11m13s"
func (Duration) String ¶
String returns the duration as a human-readable string (e.g., "1h30m0s") per time.Duration.String().
func (*Duration) UnmarshalJSON ¶
UnmarshalJSON parses the duration from either a human-readable string (e.g., "20s", "1h") or a numeric nanosecond value.
Example ¶
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/tecnickcom/gogen/pkg/timeutil"
)
func main() {
var d timeutil.Duration
data := []byte(`"7h11m13s"`)
err := json.Unmarshal(data, &d)
if err != nil {
log.Fatal(err)
}
fmt.Println(d.String())
}
Output: 7h11m13s