timeutil

timeutil provides two JSON-friendly time types.

Part of nurago, a collection of independent Go packages for backend services.

import "github.com/tecnickcom/nurago/pkg/timeutil"

Package timeutil provides two JSON-friendly time types. The standard library’s time.Time marshals as RFC-3339 only, and time.Duration marshals as a raw nanosecond integer, which mismatch APIs that expect human-readable strings like “1h30m” or “2023-01-02T15:04:05Z”. DateTime and Duration marshal to and from such strings, and the datetime format is selected by a type parameter checked at compile time.

Types

  • [DateTime]: a generic type DateTime[T DateTimeType] that wraps time.Time and implements json.Marshaler and json.Unmarshaler using the format string returned by the type parameter T. It also implements encoding.TextMarshaler and encoding.TextUnmarshaler, so it can be used as a JSON map key and with text-based encoders (YAML, TOML, flag.TextVar).
  • [DateTimeType]: the single-method interface (Format() string) that type parameters must satisfy.
  • Built-in format types implement DateTimeType for every datetime layout in the standard library (TRFC3339, TRFC3339Nano, TRFC822, TRFC822Z, TRFC850, TRFC1123, TRFC1123Z, TUnixDate, TANSIC, TRubyDate, TKitchen, TStamp, TStampMilli, TStampMicro, TStampNano, TLayout, TDateTime, TDateOnly, TTimeOnly), plus TJira for Jira’s timestamp format.
  • [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. It also implements encoding.TextMarshaler and encoding.TextUnmarshaler for use as a map key and with text-based encoders.

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"`
}

When To Use

  • An API expects durations as “1h30m” rather than as nanosecond integers.
  • Timestamps must use a format other than RFC 3339, chosen at compile time.

Example

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"

Full source is in example_datetime_test.go. More runnable examples are on pkg.go.dev.

Dependencies

This package reaches no external module: it uses only the Go standard library.