timeutil

package
v1.148.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 11, 2026 License: MIT Imports: 6 Imported by: 0

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:

  1. 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.
  2. 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

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

Examples

Constants

View Source
const TimeJiraFormat = "2006-01-02T15:04:05.000-0700"

TimeJiraFormat is the Jira date-time format string.

Variables

View Source
var ErrDurationOverflow = errors.New("timeutil: duration out of int64 range")

ErrDurationOverflow is returned when a numeric JSON value is outside the int64 nanosecond range.

View Source
var ErrInvalidDuration = errors.New("timeutil: invalid duration")

ErrInvalidDuration is returned when a JSON value cannot be interpreted as a duration (unparseable string, unparseable number, or an unsupported JSON type).

View Source
var ErrParseDateTime = errors.New("timeutil: unable to parse datetime")

ErrParseDateTime is returned when a JSON string cannot be parsed using type parameter T's layout.

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

func (d DateTime[T]) MarshalJSON() ([]byte, error)

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/nurago/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]) MarshalText

func (d DateTime[T]) MarshalText() ([]byte, error)

MarshalText encodes the date using type parameter T's format. Implementing encoding.TextMarshaler lets DateTime be used as a JSON map key and with text-based encoders (YAML, TOML, flag.TextVar, SQL text columns).

func (DateTime[T]) String

func (d DateTime[T]) String() string

String formats the date as a string according to type parameter T's Format() method.

func (DateTime[T]) Time

func (d DateTime[T]) Time() time.Time

Time returns the underlying time.Time value.

func (*DateTime[T]) UnmarshalJSON

func (d *DateTime[T]) UnmarshalJSON(data []byte) error

UnmarshalJSON parses a JSON date string according to type parameter T's format. A JSON null is a no-op, leaving the value unchanged, matching time.Time.UnmarshalJSON. See DateTime.UnmarshalText for the timezone handling of layouts without an explicit zone.

Example
package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/tecnickcom/nurago/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

func (*DateTime[T]) UnmarshalText

func (d *DateTime[T]) UnmarshalText(data []byte) error

UnmarshalText parses a date string according to type parameter T's format. Layouts without an explicit timezone (e.g. TDateOnly, TTimeOnly, TKitchen, the TStamp variants) are interpreted as UTC.

type DateTimeType

type DateTimeType interface {
	Format() string
}

DateTimeType provides the layout string used by DateTime JSON marshaling/unmarshalling.

type Duration

type Duration time.Duration

Duration aliases time.Duration for JSON marshaling as human-readable strings (e.g., "1h30m") instead of nanoseconds.

func (Duration) MarshalJSON

func (d Duration) MarshalJSON() ([]byte, error)

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/nurago/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) MarshalText

func (d Duration) MarshalText() ([]byte, error)

MarshalText encodes the duration as a human-readable string. Implementing encoding.TextMarshaler lets Duration be used as a JSON map key and with text-based encoders (YAML, TOML, flag.TextVar, SQL text columns).

func (Duration) String

func (d Duration) String() string

String returns the duration as a human-readable string (e.g., "1h30m0s") per time.Duration.String().

func (*Duration) UnmarshalJSON

func (d *Duration) UnmarshalJSON(data []byte) error

UnmarshalJSON parses the duration from either a human-readable string (e.g., "20s", "1h") or a numeric nanosecond value. Numeric values are decoded as int64 first, so integer nanosecond durations beyond float64 precision (>= 2^53) are preserved exactly. Fractional numbers are truncated toward zero, and numbers outside the int64 range return ErrDurationOverflow. A JSON null is a no-op, leaving the value unchanged, matching the convention of the standard library.

Example
package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/tecnickcom/nurago/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

func (*Duration) UnmarshalText

func (d *Duration) UnmarshalText(data []byte) error

UnmarshalText parses a human-readable duration string (e.g. "20s", "1h30m") via time.ParseDuration. Unlike Duration.UnmarshalJSON it does not accept a numeric nanosecond value.

type TANSIC

type TANSIC struct{}

TANSIC selects time.ANSIC.

func (TANSIC) Format

func (TANSIC) Format() string

Format returns the layout string.

type TDateOnly

type TDateOnly struct{}

TDateOnly selects time.DateOnly.

func (TDateOnly) Format

func (TDateOnly) Format() string

Format returns the layout string.

type TDateTime

type TDateTime struct{}

TDateTime selects time.DateTime.

func (TDateTime) Format

func (TDateTime) Format() string

Format returns the layout string.

type TJira

type TJira struct{}

TJira selects TimeJiraFormat.

func (TJira) Format

func (TJira) Format() string

Format returns the layout string.

type TKitchen

type TKitchen struct{}

TKitchen selects time.Kitchen.

func (TKitchen) Format

func (TKitchen) Format() string

Format returns the layout string.

type TLayout

type TLayout struct{}

TLayout selects time.Layout.

func (TLayout) Format

func (TLayout) Format() string

Format returns the layout string.

type TRFC822

type TRFC822 struct{}

TRFC822 selects time.RFC822.

func (TRFC822) Format

func (TRFC822) Format() string

Format returns the layout string.

type TRFC822Z

type TRFC822Z struct{}

TRFC822Z selects time.RFC822Z.

func (TRFC822Z) Format

func (TRFC822Z) Format() string

Format returns the layout string.

type TRFC850

type TRFC850 struct{}

TRFC850 selects time.RFC850.

func (TRFC850) Format

func (TRFC850) Format() string

Format returns the layout string.

type TRFC1123

type TRFC1123 struct{}

TRFC1123 selects time.RFC1123.

func (TRFC1123) Format

func (TRFC1123) Format() string

Format returns the layout string.

type TRFC1123Z

type TRFC1123Z struct{}

TRFC1123Z selects time.RFC1123Z.

func (TRFC1123Z) Format

func (TRFC1123Z) Format() string

Format returns the layout string.

type TRFC3339

type TRFC3339 struct{}

TRFC3339 selects time.RFC3339.

func (TRFC3339) Format

func (TRFC3339) Format() string

Format returns the layout string.

type TRFC3339Nano

type TRFC3339Nano struct{}

TRFC3339Nano selects time.RFC3339Nano.

func (TRFC3339Nano) Format

func (TRFC3339Nano) Format() string

Format returns the layout string.

type TRubyDate

type TRubyDate struct{}

TRubyDate selects time.RubyDate.

func (TRubyDate) Format

func (TRubyDate) Format() string

Format returns the layout string.

type TStamp

type TStamp struct{}

TStamp selects time.Stamp.

func (TStamp) Format

func (TStamp) Format() string

Format returns the layout string.

type TStampMicro

type TStampMicro struct{}

TStampMicro selects time.StampMicro.

func (TStampMicro) Format

func (TStampMicro) Format() string

Format returns the layout string.

type TStampMilli

type TStampMilli struct{}

TStampMilli selects time.StampMilli.

func (TStampMilli) Format

func (TStampMilli) Format() string

Format returns the layout string.

type TStampNano

type TStampNano struct{}

TStampNano selects time.StampNano.

func (TStampNano) Format

func (TStampNano) Format() string

Format returns the layout string.

type TTimeOnly

type TTimeOnly struct{}

TTimeOnly selects time.TimeOnly.

func (TTimeOnly) Format

func (TTimeOnly) Format() string

Format returns the layout string.

type TUnixDate

type TUnixDate struct{}

TUnixDate selects time.UnixDate.

func (TUnixDate) Format

func (TUnixDate) Format() string

Format returns the layout string.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL