Documentation
¶
Overview ¶
Package gotime converts human time expressions into precise, computable value objects.
Parse entry points ¶
Two layers, picked by what you know up front:
- Typed helpers — ParseInstant, ParseDateTime, ParseLocalDateTime, ParseDate, ParseTime, ParseDuration, ParsePeriod, ParseInterval. Each returns (T, error) and is the default path for application code.
- Parse — diagnostic / dispatch entry. Returns a ParseResult with Status / Kind / Candidates / Warnings. Reach for it when you do not know the kind ahead of time, or when you need ambiguity candidates, warnings, or zone metadata. Dispatch through Status, Kind, and the comma-ok ParseResult accessors.
Current time ¶
Now / NowIn / TodayIn return the current moment or calendar date. There is no zero-argument Today: a calendar date requires an explicit zone.
Panic convention ¶
MustLoadZone panics only for invalid fixed IANA zone identifiers and is intended for package-level or test-time initialization.
Formatting is out of scope ¶
Localization and display formatting are intentionally out of scope. Bridge gotime values to an external renderer through the Instant.Std, DateTime.Std, and Duration.Std adapters.
Example — typed helper (most common) ¶
dt, err := gotime.ParseDateTime("2026-03-27T13:00:00",
gotime.WithZone(gotime.MustLoadZone("Asia/Tokyo")),
)
if err != nil {
return err
}
fmt.Println(dt.Std().Format("2006-01-02 15:04 MST"))
Example — natural language with locale ¶
Natural date/datetime expressions require an explicit reference instant. Product code can pass Now at the boundary; tests usually pass a fixed Instant.
dt, err := gotime.ParseDateTime("tomorrow at 3pm",
gotime.WithInputLocale(language.English),
gotime.WithZone(gotime.MustLoadZone("America/New_York")),
gotime.WithReference(gotime.Now()),
)
Example (JsonRoundTrip) ¶
Value objects serialize to stable JSON schemas. All types round-trip through json.Marshal / json.Unmarshal without losing precision.
package main
import (
"fmt"
"time"
"github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
gotime "github.com/agentable/go-time"
)
func mustDate(year int, month time.Month, day int) gotime.Date {
d, err := gotime.NewDate(year, month, day)
if err != nil {
panic(err)
}
return d
}
func mustTime(hour, minute, second int) gotime.Time {
tm, err := gotime.NewTime(hour, minute, second)
if err != nil {
panic(err)
}
return tm
}
func mustDateTime(d gotime.Date, tm gotime.Time, z gotime.Zone) gotime.DateTime {
dt, err := gotime.NewDateTime(d, tm, z)
if err != nil {
panic(err)
}
return dt
}
func main() {
zone := gotime.MustLoadZone("Asia/Tokyo")
dt := mustDateTime(
mustDate(2026, time.March, 27),
mustTime(13, 0, 0),
zone,
)
b, _ := json.Marshal(dt, jsontext.WithIndent(" "))
fmt.Println(string(b))
}
Output: { "kind": "datetime", "instant": "2026-03-27T04:00:00Z", "zone": "Asia/Tokyo" }
Index ¶
- Constants
- Variables
- func ZoneCatalogVersion() string
- func Zones() []string
- type Date
- func (d Date) Add(p Period) (Date, error)
- func (d Date) After(other Date) bool
- func (d Date) Before(other Date) bool
- func (d Date) Compare(other Date) int
- func (d Date) Day() int
- func (d Date) DaysInMonth() int
- func (d Date) DaysUntil(other Date) int
- func (d Date) Equal(other Date) bool
- func (d Date) ISOWeek() (year, week int)
- func (d Date) IsLeapYear() bool
- func (d Date) IsZero() bool
- func (d Date) MarshalJSON() ([]byte, error)
- func (d Date) Month() time.Month
- func (d Date) PeriodUntil(other Date) (Period, error)
- func (d Date) String() string
- func (d *Date) UnmarshalJSON(b []byte) error
- func (d Date) Weekday() time.Weekday
- func (d Date) Year() int
- func (d Date) YearDay() int
- type DateTime
- func (dt DateTime) Add(d Duration) (DateTime, error)
- func (dt DateTime) AddPeriod(p Period) (LocalResolution, error)
- func (dt DateTime) After(other DateTime) bool
- func (dt DateTime) Before(other DateTime) bool
- func (dt DateTime) Clock() Time
- func (dt DateTime) Compare(other DateTime) int
- func (dt DateTime) Date() Date
- func (dt DateTime) Equal(other DateTime) bool
- func (dt DateTime) In(z Zone) (DateTime, error)
- func (dt DateTime) Instant() Instant
- func (dt DateTime) IsZero() bool
- func (dt DateTime) MarshalJSON() ([]byte, error)
- func (dt DateTime) Std() time.Time
- func (dt DateTime) String() string
- func (dt DateTime) Sub(other DateTime) Duration
- func (dt *DateTime) UnmarshalJSON(b []byte) error
- func (dt DateTime) Zone() Zone
- type Duration
- func (d Duration) Abs() Duration
- func (d Duration) Decompose() DurationComponents
- func (d Duration) ISO8601() string
- func (d Duration) InHours() float64
- func (d Duration) InMinutes() float64
- func (d Duration) InSeconds() float64
- func (d Duration) IsNegative() bool
- func (d Duration) IsZero() bool
- func (d Duration) MarshalJSON() ([]byte, error)
- func (d Duration) Milliseconds() int64
- func (d Duration) Nanoseconds() int64
- func (d Duration) Std() time.Duration
- func (d Duration) String() string
- func (d *Duration) UnmarshalJSON(b []byte) error
- type DurationComponents
- type ErrorCode
- type Instant
- func (i Instant) Add(d Duration) Instant
- func (i Instant) After(other Instant) bool
- func (i Instant) Before(other Instant) bool
- func (i Instant) Compare(other Instant) int
- func (i Instant) Equal(other Instant) bool
- func (i Instant) In(z Zone) (DateTime, error)
- func (i Instant) IsZero() bool
- func (i Instant) MarshalJSON() ([]byte, error)
- func (i Instant) Std() time.Time
- func (i Instant) String() string
- func (i Instant) Sub(other Instant) Duration
- func (i Instant) UnixMilli() int64
- func (i Instant) UnixNano() int64
- func (i *Instant) UnmarshalJSON(b []byte) error
- type Interval
- func (iv Interval) Adjacent(other Interval) bool
- func (iv Interval) Contains(i Instant) bool
- func (iv Interval) End() Instant
- func (iv Interval) Expand(before, after Duration) (Interval, error)
- func (iv Interval) Intersect(other Interval) (Interval, bool)
- func (iv Interval) IsZero() bool
- func (iv Interval) Length() Duration
- func (iv Interval) MarshalJSON() ([]byte, error)
- func (iv Interval) Overlaps(other Interval) bool
- func (iv Interval) Shift(d Duration) Interval
- func (iv Interval) Start() Instant
- func (iv Interval) StdRange() (start, end time.Time)
- func (iv Interval) String() string
- func (iv Interval) Union(other Interval) (Interval, error)
- func (iv *Interval) UnmarshalJSON(b []byte) error
- type Kind
- type LocalDateTime
- type LocalResolution
- type LocalResolutionStatus
- type Option
- type ParseResult
- func (r ParseResult) Date() (Date, bool)
- func (r ParseResult) DateTime() (DateTime, bool)
- func (r ParseResult) Duration() (Duration, bool)
- func (r ParseResult) Instant() (Instant, bool)
- func (r ParseResult) Interval() (Interval, bool)
- func (r ParseResult) LocalDateTime() (LocalDateTime, bool)
- func (r ParseResult) MarshalJSON() ([]byte, error)
- func (r ParseResult) Period() (Period, bool)
- func (r ParseResult) Time() (Time, bool)
- type Period
- func (p Period) Abs() (Period, error)
- func (p Period) Add(other Period) (Period, error)
- func (p Period) ISO8601() string
- func (p Period) IsNegative() bool
- func (p Period) IsZero() bool
- func (p Period) MarshalJSON() ([]byte, error)
- func (p Period) Negate() (Period, error)
- func (p Period) String() string
- func (p Period) Sub(other Period) (Period, error)
- func (p *Period) UnmarshalJSON(b []byte) error
- type Status
- type Time
- func (t Time) After(other Time) bool
- func (t Time) Before(other Time) bool
- func (t Time) Equal(other Time) bool
- func (t Time) Hour() int
- func (t Time) IsZero() bool
- func (t Time) MarshalJSON() ([]byte, error)
- func (t Time) Minute() int
- func (t Time) Nanosecond() int
- func (t Time) Second() int
- func (t Time) String() string
- func (t *Time) UnmarshalJSON(b []byte) error
- type TimeError
- type Warning
- type WarningCode
- type Zone
Examples ¶
Constants ¶
const ( Nanosecond Duration = 1 Microsecond = 1000 * Nanosecond Millisecond = 1000 * Microsecond Second = 1000 * Millisecond Minute = 60 * Second Hour = 60 * Minute )
Duration unit constants mirror time.Duration's constants and support const arithmetic such as 5 * gotime.Minute.
Variables ¶
var ( ErrEmptyInput = errors.New("gotime: empty input") ErrInvalidFormat = errors.New("gotime: invalid format") ErrInvalidDate = errors.New("gotime: invalid date") ErrInvalidTime = errors.New("gotime: invalid time") ErrInvalidDuration = errors.New("gotime: invalid duration") ErrInvalidPeriod = errors.New("gotime: invalid period") ErrInvalidZone = errors.New("gotime: invalid zone") ErrAmbiguousDate = errors.New("gotime: ambiguous date") ErrNonexistentTime = errors.New("gotime: nonexistent local time") ErrDuplicateTime = errors.New("gotime: duplicate local time") ErrIntervalReversed = errors.New("gotime: interval end before start") ErrIntervalsDisjoint = errors.New("gotime: intervals disjoint") ErrUnparseable = errors.New("gotime: unparseable") ErrOverflow = errors.New("gotime: overflow") ErrIncompatibleTypes = errors.New("gotime: incompatible types") )
Sentinel errors — one per ErrorCode. Use these with errors.Is for control-flow matching. Use errors.As(&te) to extract Input/Hint/Message from a returned *TimeError.
var UTC = Zone{/* contains filtered or unexported fields */}
UTC is the UTC timezone.
Functions ¶
func ZoneCatalogVersion ¶ added in v0.6.4
func ZoneCatalogVersion() string
ZoneCatalogVersion returns the IANA tzdb version used to generate Zones. It does not describe the transition-rule data used by time.LoadLocation.
Types ¶
type Date ¶
type Date struct {
// contains filtered or unexported fields
}
Date is a calendar date without time or timezone information.
func DateFromTime ¶
DateFromTime extracts the date from a time.Time using its location. It returns ErrOverflow when the projected year is outside 0000..9999.
func TodayIn ¶
TodayIn returns the current calendar date in zone z.
There is no zero-arg Today() helper: a calendar date is only meaningful relative to a zone. Applications that need the host's zone must discover or configure its IANA identifier and pass the resulting Zone explicitly.
func (Date) Add ¶
Add returns a new Date advanced by p (calendar arithmetic). Month/year arithmetic applies end-of-month clamping. To move back, pass a negative Period literal or handle the error returned by Period.Negate. Add returns ErrInvalidDate for an invalid receiver and ErrOverflow when the result would leave the supported civil year domain.
func (Date) DaysInMonth ¶
DaysInMonth returns the number of days in the date's month.
func (Date) DaysUntil ¶ added in v0.6.4
DaysUntil returns the signed number of calendar days from d to other.
func (Date) IsLeapYear ¶
IsLeapYear reports whether the date's year is a leap year.
func (Date) MarshalJSON ¶
MarshalJSON encodes d as {"kind":"date","value":"YYYY-MM-DD"}.
func (Date) PeriodUntil ¶ added in v0.6.4
PeriodUntil returns the signed greedy calendar period from d to other. It prefers years, then months, then days; use DaysUntil for exact day counts. It returns ErrInvalidDate if either endpoint is invalid.
func (*Date) UnmarshalJSON ¶
UnmarshalJSON decodes d from {"kind":"date","value":"YYYY-MM-DD"}.
type DateTime ¶
type DateTime struct {
// contains filtered or unexported fields
}
DateTime is a date and time in a specific timezone. It is the "human-readable" view of a moment.
func DateTimeFromTime ¶
DateTimeFromTime creates a DateTime from a stdlib time.Time and a Zone. It returns ErrOverflow when projection into z produces a year outside 0000..9999.
func NewDateTime ¶
NewDateTime creates a DateTime from a Date, clock Time, and Zone.
func ParseDateTime ¶
ParseDateTime parses input and returns a DateTime or an error.
func (DateTime) Add ¶
Add returns a new DateTime advanced by d using exact (nanosecond) arithmetic. To move back, pass a negative Duration: dt.Add(-30 * gotime.Minute). It returns ErrOverflow when the result leaves the supported civil year domain.
func (DateTime) AddPeriod ¶
func (dt DateTime) AddPeriod(p Period) (LocalResolution, error)
AddPeriod advances dt by p using calendar arithmetic and resolves the target local time in the same zone. Gaps and overlaps remain explicit in the result.
Example ¶
Calendar math preserves wall-clock time across DST transitions and clamps end-of-month overflows. Exact math always adds precise durations.
package main
import (
"fmt"
"time"
gotime "github.com/agentable/go-time"
)
func mustDate(year int, month time.Month, day int) gotime.Date {
d, err := gotime.NewDate(year, month, day)
if err != nil {
panic(err)
}
return d
}
func mustTime(hour, minute, second int) gotime.Time {
tm, err := gotime.NewTime(hour, minute, second)
if err != nil {
panic(err)
}
return tm
}
func mustDateTime(d gotime.Date, tm gotime.Time, z gotime.Zone) gotime.DateTime {
dt, err := gotime.NewDateTime(d, tm, z)
if err != nil {
panic(err)
}
return dt
}
func main() {
zone := gotime.MustLoadZone("America/New_York")
jan31 := mustDateTime(
mustDate(2026, time.January, 31),
mustTime(12, 0, 0),
zone,
)
febResolution, err := jan31.AddPeriod(gotime.Months(1))
if err != nil {
panic(err)
}
feb, err := febResolution.Only()
if err != nil {
panic(err)
}
fmt.Println("Jan 31 + 1 month:", feb.Date())
exact, err := jan31.Add(48 * gotime.Hour)
if err != nil {
panic(err)
}
fmt.Println("Jan 31 + 48h:", exact.Date(), exact.Clock())
}
Output: Jan 31 + 1 month: 2026-02-28 Jan 31 + 48h: 2026-02-02 12:00:00
func (DateTime) In ¶
In converts dt to the same absolute moment expressed in zone z. It returns ErrOverflow when the projected year is outside 0000..9999.
func (DateTime) MarshalJSON ¶
MarshalJSON encodes dt as {"kind":"datetime","instant":"<RFC3339Nano UTC>","zone":"<IANA id>"}.
func (DateTime) Std ¶
Std returns the underlying time.Time in dt's zone. Use this to bridge dt to any API expecting a stdlib time.Time (e.g. a formatter).
Example ¶
Std returns the underlying time.Time. Use it to bridge a DateTime to any API expecting a stdlib time.Time — for example, a github.com/agentable/go-intl DateTimeFormat or any other formatter.
package main
import (
"fmt"
"time"
gotime "github.com/agentable/go-time"
)
func mustDate(year int, month time.Month, day int) gotime.Date {
d, err := gotime.NewDate(year, month, day)
if err != nil {
panic(err)
}
return d
}
func mustTime(hour, minute, second int) gotime.Time {
tm, err := gotime.NewTime(hour, minute, second)
if err != nil {
panic(err)
}
return tm
}
func mustDateTime(d gotime.Date, tm gotime.Time, z gotime.Zone) gotime.DateTime {
dt, err := gotime.NewDateTime(d, tm, z)
if err != nil {
panic(err)
}
return dt
}
func main() {
zone := gotime.MustLoadZone("Asia/Tokyo")
dt := mustDateTime(
mustDate(2026, time.March, 27),
mustTime(13, 0, 0),
zone,
)
// Hand off to stdlib formatter.
fmt.Println(dt.Std().Format("2006-01-02 15:04 MST"))
}
Output: 2026-03-27 13:00 JST
func (DateTime) Sub ¶
Sub returns the Duration from other to dt, mirroring time.Time.Sub. Use Add for arithmetic — there is no Sub(Duration) form.
func (*DateTime) UnmarshalJSON ¶
UnmarshalJSON decodes dt from {"kind":"datetime","instant":"<RFC3339Nano UTC>","zone":"<IANA id>"}.
type Duration ¶
Duration represents an exact elapsed time with nanosecond precision. It is a typed alias for time.Duration so that const arithmetic works: callers write 5 * gotime.Minute exactly like stdlib.
Use Duration for exact-time math (timers, spans, sampling intervals). Use Period for calendar-aware math (months, years, "next Monday"). They are deliberately distinct types — the type system prevents mixing.
Day is intentionally NOT a Duration constant. A calendar day is a Period concept (Days(n)) and crosses DST boundaries safely; an exact 24-hour span is 24 * Hour. Conflating them is a frequent source of bugs.
func ParseDuration ¶
ParseDuration parses input and returns a Duration or an error. Rejects ISO 8601 inputs with calendar components (P1Y, P5D) — use ParsePeriod for those.
func (Duration) Decompose ¶
func (d Duration) Decompose() DurationComponents
Decompose breaks d into renderable slots: Hours, Minutes, Seconds, Milliseconds, Microseconds, and Nanoseconds. Days/Months/Years are not extracted — Duration is exact wall-clock-free time, and elevating 24h to "1 day" presumes a calendar relationship the type does not carry. Callers can roll Hours into Days themselves if desired.
The sign of d is preserved across every non-zero slot; for example (-2*Hour - 30*Minute).Decompose() yields Hours=-2, Minutes=-30.
Example ¶
Decompose breaks a Duration into the ECMA-402 DurationFormat slots (Hours/Minutes/Seconds/Milliseconds/Microseconds/Nanoseconds). The shape mirrors durationformat.Duration so a struct conversion is enough to hand it off — gotime never imports the formatter.
package main
import (
"fmt"
gotime "github.com/agentable/go-time"
)
func main() {
d := 1*gotime.Hour + 30*gotime.Minute + 250*gotime.Millisecond
c := d.Decompose()
fmt.Printf("Hours=%d Minutes=%d Milliseconds=%d\n", c.Hours, c.Minutes, c.Milliseconds)
}
Output: Hours=1 Minutes=30 Milliseconds=250
func (Duration) ISO8601 ¶
ISO8601 returns the ISO 8601 duration string, e.g. "PT1H30M", "-PT30M", "PT0S". Sub-second precision uses fractional seconds.
func (Duration) IsNegative ¶
IsNegative reports whether d is negative.
func (Duration) MarshalJSON ¶
MarshalJSON encodes d as {"kind":"duration","iso":"<ISO8601>"}. The ISO 8601 string is the single source of truth; callers that need structured slots can run d.Decompose() at the call site.
func (Duration) Milliseconds ¶
Milliseconds returns d in whole milliseconds.
func (Duration) Nanoseconds ¶
Nanoseconds returns d in nanoseconds.
func (Duration) String ¶
String returns d in the same format as time.Duration.String() ("1h30m0s", "500ms"). Identical to stdlib so round-trip with time.Duration is exact and callers can rely on a single well-known Stringer contract.
func (*Duration) UnmarshalJSON ¶
UnmarshalJSON decodes d from {"kind":"duration","iso":"<ISO8601>",...}.
type DurationComponents ¶
type DurationComponents struct {
// Hours is the whole-hour slot.
Hours int64
// Minutes is the remaining whole-minute slot.
Minutes int64
// Seconds is the remaining whole-second slot.
Seconds int64
// Milliseconds is the remaining whole-millisecond slot.
Milliseconds int64
// Microseconds is the remaining whole-microsecond slot.
Microseconds int64
// Nanoseconds is the remaining nanosecond slot.
Nanoseconds int64
}
DurationComponents is a Duration broken into renderable clock slots: Hours through Nanoseconds. It mirrors the slot shape consumed by external duration formatters (e.g. an ECMA-402 Intl.DurationFormat implementation) so a caller can copy the fields directly:
c := d.Decompose()
out, _ := f.Format(durationformat.Duration{
Hours: c.Hours,
Minutes: c.Minutes,
// ...
})
gotime never imports any duration-formatter package — this type holds the shape, never the rendering. There are intentionally no calendar fields (Years/Months/Days): those belong to Period, whose fields are already exported, so widening them into a second struct would be duplication.
DurationComponents is a computed bridge, not a wire format. It is not marshalled by any value object; Duration's JSON contains only the ISO 8601 string. Run Decompose at the call site when you need structured slots.
type ErrorCode ¶
type ErrorCode string
ErrorCode is the stable machine-readable JSON/wire category.
const ( // CodeEmptyInput reports that the input string was empty. CodeEmptyInput ErrorCode = "EMPTY_INPUT" // CodeInvalidFormat reports a syntactically invalid input shape. CodeInvalidFormat ErrorCode = "INVALID_FORMAT" // CodeInvalidDate reports an invalid calendar date. CodeInvalidDate ErrorCode = "INVALID_DATE" // CodeInvalidTime reports an invalid clock time. CodeInvalidTime ErrorCode = "INVALID_TIME" // CodeInvalidDuration reports an invalid duration string. CodeInvalidDuration ErrorCode = "INVALID_DURATION" // CodeInvalidPeriod reports an invalid period string. CodeInvalidPeriod ErrorCode = "INVALID_PERIOD" // CodeInvalidZone reports an invalid or unresolvable timezone identifier. CodeInvalidZone ErrorCode = "INVALID_ZONE" // CodeAmbiguousDate reports multiple plausible date interpretations. CodeAmbiguousDate ErrorCode = "AMBIGUOUS_DATE" // CodeNonexistentTime reports a local time that falls in a DST gap. CodeNonexistentTime ErrorCode = "NONEXISTENT_LOCAL_TIME" // CodeDuplicateTime reports a local time that occurs twice during DST fall-back. CodeDuplicateTime ErrorCode = "DUPLICATE_LOCAL_TIME" // CodeIntervalReversed reports an Interval where end is before start. CodeIntervalReversed ErrorCode = "INTERVAL_END_BEFORE_START" // CodeIntervalsDisjoint reports an Interval Union where the inputs do not touch. CodeIntervalsDisjoint ErrorCode = "INTERVALS_DISJOINT" // CodeUnparseable reports input that no parser could interpret. CodeUnparseable ErrorCode = "UNPARSEABLE" // CodeOverflow reports arithmetic overflow. CodeOverflow ErrorCode = "OVERFLOW" // CodeIncompatibleTypes reports an operation on incompatible time types. CodeIncompatibleTypes ErrorCode = "INCOMPATIBLE_TYPES" )
Error code constants — string-typed, prefixed Code*. These are the stable codes that appear in JSON, logs, and tooling. Pair each with a sentinel error below for use in errors.Is matching.
type Instant ¶
type Instant struct {
// contains filtered or unexported fields
}
Instant is an absolute UTC moment with nanosecond precision. It is the preferred type for storage, logging, and cross-system transfer.
func InstantFromTime ¶
InstantFromTime creates an Instant from a time.Time, forcing UTC.
func Now ¶
func Now() Instant
Now returns the current moment as an Instant (UTC, no monotonic reading).
func ParseInstant ¶
ParseInstant parses input and returns an Instant or an error.
func UnixMillis ¶
UnixMillis creates an Instant from a Unix timestamp in milliseconds.
func UnixSeconds ¶
UnixSeconds creates an Instant from a Unix timestamp in seconds.
func (Instant) Compare ¶
Compare returns -1 if i < other, 0 if i == other, 1 if i > other. Use with slices.MinFunc / slices.MaxFunc / cmp.Or for selection and clamping:
earliest := slices.MinFunc(times, gotime.Instant.Compare)
func (Instant) In ¶
In projects the Instant into a timezone, returning a DateTime. It returns ErrOverflow when the projected year is outside 0000..9999.
func (Instant) MarshalJSON ¶
MarshalJSON encodes i as {"kind":"instant","iso":"<RFC3339Nano UTC>"}.
func (*Instant) UnmarshalJSON ¶
UnmarshalJSON decodes i from {"kind":"instant","iso":"<RFC3339Nano>",...}.
type Interval ¶
type Interval struct {
// contains filtered or unexported fields
}
Interval is a half-open time range [start, end) bounded by two Instants. Interval is zone-free — projection zone for display is the caller's concern; bridge via iv.StdRange() and format outside this package. Arithmetic is UTC-based on the underlying Instants.
func NewInterval ¶
NewInterval creates an Interval. Returns ErrIntervalReversed if end < start.
func NewIntervalEndingAt ¶ added in v0.6.4
NewIntervalEndingAt creates an Interval from an end Instant and non-negative length.
func NewIntervalStartingAt ¶ added in v0.6.4
NewIntervalStartingAt creates an Interval from a start Instant and non-negative length.
func ParseInterval ¶
ParseInterval parses input and returns an Interval or an error.
func (Interval) Adjacent ¶
Adjacent reports whether iv and other share exactly one boundary with no overlap and no gap. For half-open intervals, [a, b) and [b, c) are adjacent.
func (Interval) Contains ¶
Contains reports whether i is within the half-open interval [start, end).
func (Interval) Expand ¶
Expand returns a new Interval with start moved back by before and end moved forward by after.
func (Interval) Intersect ¶
Intersect returns the overlapping portion of iv and other. Returns (zero, false) if they are disjoint.
func (Interval) MarshalJSON ¶
MarshalJSON encodes iv as {"kind":"interval","start":"<RFC3339Nano>","end":"<RFC3339Nano>"}.
func (Interval) Overlaps ¶
Overlaps reports whether iv and other share any moment. Half-open intervals [a, b) and [b, c) do NOT overlap — they are adjacent.
func (Interval) StdRange ¶
StdRange returns the underlying start and end as stdlib time.Time values in UTC. Use this to bridge an Interval to any API expecting (time.Time, time.Time).
func (Interval) Union ¶
Union returns the smallest interval containing both iv and other. Adjacent intervals ([a, b) and [b, c)) can be unioned. Returns an error if the intervals are disjoint with a gap between them.
func (*Interval) UnmarshalJSON ¶
UnmarshalJSON decodes iv from {"kind":"interval","start":"<RFC3339Nano>","end":"<RFC3339Nano>"}.
type Kind ¶
type Kind string
Kind identifies the type of the parsed value.
const ( // KindInstant identifies an absolute UTC timestamp. KindInstant Kind = "instant" // KindDateTime identifies a zoned local date-time. KindDateTime Kind = "datetime" // KindLocalDateTime identifies a local date-time without zone or offset. KindLocalDateTime Kind = "local_datetime" // KindDate identifies a calendar date. KindDate Kind = "date" // KindTime identifies a clock time. KindTime Kind = "time" // KindDuration identifies an exact-time duration. KindDuration Kind = "duration" // KindPeriod identifies a calendar period (years/months/days). KindPeriod Kind = "period" // KindInterval identifies a half-open time interval. KindInterval Kind = "interval" )
type LocalDateTime ¶ added in v0.6.4
type LocalDateTime struct {
// Date is the calendar date component.
Date Date
// Time is the clock time component.
Time Time
}
LocalDateTime is a calendar date plus a clock time, without a zone. Resolving it in a Zone may produce zero, one, or multiple DateTime candidates.
func NewLocalDateTime ¶ added in v0.6.4
func NewLocalDateTime(d Date, t Time) LocalDateTime
NewLocalDateTime combines a Date and Time without resolving them into a zone.
func ParseLocalDateTime ¶ added in v0.6.4
func ParseLocalDateTime(input string, opts ...Option) (LocalDateTime, error)
ParseLocalDateTime parses input and returns a LocalDateTime or an error.
func (LocalDateTime) MarshalJSON ¶ added in v0.6.4
func (ldt LocalDateTime) MarshalJSON() ([]byte, error)
MarshalJSON encodes ldt as {"kind":"local_datetime","value":"YYYY-MM-DDTHH:MM:SS"}.
func (LocalDateTime) Resolve ¶ added in v0.6.4
func (ldt LocalDateTime) Resolve(z Zone) LocalResolution
Resolve projects ldt into z and classifies DST gaps and overlaps explicitly.
func (LocalDateTime) String ¶ added in v0.6.4
func (ldt LocalDateTime) String() string
String returns the ISO 8601 local date-time string "YYYY-MM-DDTHH:MM:SS[.fraction]".
func (*LocalDateTime) UnmarshalJSON ¶ added in v0.6.4
func (ldt *LocalDateTime) UnmarshalJSON(b []byte) error
UnmarshalJSON decodes ldt from {"kind":"local_datetime","value":"YYYY-MM-DDTHH:MM:SS[.fraction]"}.
type LocalResolution ¶ added in v0.6.4
type LocalResolution struct {
// Status classifies the local time projection.
Status LocalResolutionStatus
// Zone is the zone used for resolution. A zero Zone resolves as UTC.
Zone Zone
// Local is the unresolved local date-time.
Local LocalDateTime
// Candidates holds the matching DateTime values in chronological order.
Candidates []DateTime
}
LocalResolution holds the result of resolving a LocalDateTime in a Zone.
func (LocalResolution) Only ¶ added in v0.6.4
func (r LocalResolution) Only() (DateTime, error)
Only returns the single resolved DateTime, or an error if resolution did not produce exactly one candidate.
type LocalResolutionStatus ¶ added in v0.6.4
type LocalResolutionStatus string
LocalResolutionStatus describes how a LocalDateTime maps into a Zone.
const ( // LocalInvalid means the local date or clock time is invalid. LocalInvalid LocalResolutionStatus = "invalid" // LocalResolved means the local time maps to exactly one DateTime. LocalResolved LocalResolutionStatus = "resolved" // LocalNonexistent means the local time falls in a DST gap. LocalNonexistent LocalResolutionStatus = "nonexistent" // LocalAmbiguous means the local time maps to multiple DateTime candidates. LocalAmbiguous LocalResolutionStatus = "ambiguous" )
type Option ¶
type Option func(*config)
Option is a functional option for Parse.
func WithInputLocale ¶
WithInputLocale sets the language used to interpret natural-language input. Standard formats (RFC 3339, ISO 8601) are language-independent and ignore it.
The argument is a golang.org/x/text/language Tag — the de facto standard representation of BCP-47 language tags in Go. Construct it via language.MustParse("zh-Hans"), language.Chinese, or similar.
Only the language identity is consumed; Unicode -u- extensions (hour cycle, calendar, numbering system) are ignored because they describe display behavior, not parsing behavior.
Chinese requires script subtags: language.SimplifiedChinese (zh-Hans) or language.TraditionalChinese (zh-Hant). The bare language.Chinese ("zh") tag does not activate the Chinese natural-language parser.
For slash dates (e.g. "04/05/2026"), en-US selects month-first while en-GB and en-AU select day-first. Unsupported tags, including bare en and en-CA, use validity inference: one valid interpretation resolves, two are Ambiguous, and zero are Invalid. Unicode -u- extensions do not change the supported locale's order.
Example ¶
WithInputLocale enables natural-language parsing in the given language. Use a golang.org/x/text/language Tag — the standard Go BCP-47 type — to avoid binding gotime to any specific i18n library.
package main
import (
"fmt"
"time"
"golang.org/x/text/language"
gotime "github.com/agentable/go-time"
)
func main() {
ref := gotime.InstantFromTime(time.Date(2026, 3, 30, 12, 0, 0, 0, time.UTC))
r := gotime.Parse("明天",
gotime.WithInputLocale(language.SimplifiedChinese),
gotime.WithZone(gotime.MustLoadZone("Asia/Shanghai")),
gotime.WithReference(ref),
)
if r.Status != gotime.StatusResolved {
return
}
d, _ := r.Date()
fmt.Println(d)
}
Output: 2026-03-31
func WithReference ¶
WithReference sets the reference instant for relative-time expressions. Relative natural date and datetime expressions also require WithZone.
type ParseResult ¶
type ParseResult struct {
// Status reports whether parsing resolved, remained ambiguous, or failed.
Status Status
// Kind identifies the semantic type of the parsed value when Status is Resolved or Ambiguous.
Kind Kind
// Input is the original input string.
Input string
// Zone is the zone applied when the input did not carry its own zone or offset.
Zone Zone
// Reference is the reference instant used for relative expressions.
Reference Instant
// HasZone reports whether the input explicitly included timezone or offset information.
HasZone bool
// Warnings describes lossy assumptions made while parsing.
Warnings []Warning
// Candidates holds the alternative interpretations when Status is Ambiguous.
// Each candidate is itself a StatusResolved ParseResult.
Candidates []ParseResult
// Error describes the failure when Status is Invalid.
Error *TimeError
// contains filtered or unexported fields
}
ParseResult holds the outcome of Parse. Check Status before calling the typed accessors (DateTime, Date, …).
func Parse ¶
func Parse(input string, opts ...Option) ParseResult
Parse is the inspection / dispatch entry point. It accepts any supported input (ISO 8601, RFC 3339, natural language, ranges) and returns a ParseResult describing the outcome. It never returns a Go error — semantic states (ambiguous / invalid) live on ParseResult.Status.
When you already know which concrete type you expect, prefer the typed helpers (ParseInstant, ParseDateTime, ParseLocalDateTime, ParseDate, ParseTime, ParseDuration, ParsePeriod, ParseInterval) — they return (T, error) directly by narrowing the same shared parse result.
Reach for Parse when you need any of:
- Polymorphic dispatch on Kind via Status, Kind, and comma-ok accessors.
- Access to ParseResult.Candidates for ambiguous inputs.
- Access to ParseResult.Warnings, ParseResult.HasZone, or ParseResult.Reference metadata.
Example ¶
Parse accepts ISO 8601, RFC 3339, and natural language. The three-status result model (Resolved / Ambiguous / Invalid) lets callers handle any input without panicking.
package main
import (
"fmt"
gotime "github.com/agentable/go-time"
)
func main() {
r := gotime.Parse("2026-03-27T13:00:00+09:00")
if r.Status != gotime.StatusResolved {
fmt.Println("unexpected status:", r.Status)
return
}
i, _ := r.Instant()
fmt.Println(i)
}
Output: 2026-03-27T04:00:00Z
func (ParseResult) Date ¶
func (r ParseResult) Date() (Date, bool)
Date returns the parsed Date. ok is false unless Status is Resolved and Kind == KindDate.
func (ParseResult) DateTime ¶
func (r ParseResult) DateTime() (DateTime, bool)
DateTime returns the parsed DateTime. ok is false unless Status is Resolved and Kind == KindDateTime.
func (ParseResult) Duration ¶
func (r ParseResult) Duration() (Duration, bool)
Duration returns the parsed Duration. ok is false unless Status is Resolved and Kind == KindDuration.
func (ParseResult) Instant ¶
func (r ParseResult) Instant() (Instant, bool)
Instant returns the parsed Instant. ok is false unless Status is Resolved and Kind == KindInstant.
func (ParseResult) Interval ¶
func (r ParseResult) Interval() (Interval, bool)
Interval returns the parsed Interval. ok is false unless Status is Resolved and Kind == KindInterval.
func (ParseResult) LocalDateTime ¶ added in v0.6.4
func (r ParseResult) LocalDateTime() (LocalDateTime, bool)
LocalDateTime returns the parsed LocalDateTime. ok is false unless Status is Resolved and Kind == KindLocalDateTime.
func (ParseResult) MarshalJSON ¶
func (r ParseResult) MarshalJSON() ([]byte, error)
MarshalJSON serializes r to the stable JSON schema defined in SPECS/20-parsing.md.
func (ParseResult) Period ¶
func (r ParseResult) Period() (Period, bool)
Period returns the parsed Period. ok is false unless Status is Resolved and Kind == KindPeriod.
func (ParseResult) Time ¶
func (r ParseResult) Time() (Time, bool)
Time returns the parsed Time. ok is false unless Status is Resolved and Kind == KindTime.
type Period ¶
type Period struct {
// Years is the calendar-year offset.
Years int32 `json:"years,omitzero"`
// Months is the calendar-month offset.
Months int32 `json:"months,omitzero"`
// Days is the calendar-day offset.
Days int32 `json:"days,omitzero"`
}
Period represents a calendar offset in years, months, and days. It is the calendar-aware counterpart to Duration: Period operations preserve wall-clock time across DST transitions and apply end-of-month clamping for month/year arithmetic.
Use Period for "next month", "in 7 days", recurring schedules. Use Duration for exact-time math.
Fields are exported so callers can construct via struct literal:
p := gotime.Period{Years: 1, Months: 3, Days: 7}
func Days ¶
Days returns Period{Days: n}. These are calendar days (not 24-hour spans); they preserve wall-clock time across DST boundaries. For exact 24-hour math write 24 * gotime.Hour.
func NewPeriod ¶ added in v0.6.4
NewPeriod creates a Period from calendar year, month, and day components.
func ParsePeriod ¶
ParsePeriod parses input and returns a Period or an error. Rejects ISO 8601 inputs with clock components (PT1H) — use ParseDuration for those.
func (Period) Abs ¶
Abs returns p with all fields made non-negative, or ErrOverflow when a component is math.MinInt32.
func (Period) Add ¶
Add returns p + other componentwise, or ErrOverflow when a component exceeds int32.
func (Period) ISO8601 ¶
ISO8601 returns the ISO 8601 representation of p, e.g. "P1Y3M7D". A zero Period returns "P0D". Negative components produce a leading "-".
func (Period) IsNegative ¶
IsNegative reports whether any field of p is negative. A Period with mixed signs is not normalized — use Negate to flip all fields.
func (Period) MarshalJSON ¶
MarshalJSON encodes p as {"kind":"period","iso":"<ISO8601>"}. The ISO 8601 string is the single source of truth; callers that need structured slots can read p.Years / p.Months / p.Days directly.
func (Period) Negate ¶
Negate returns -p (all fields flipped), or ErrOverflow when a component is math.MinInt32.
func (Period) Sub ¶
Sub returns p - other componentwise, or ErrOverflow when a component exceeds int32.
func (*Period) UnmarshalJSON ¶
UnmarshalJSON decodes p from {"kind":"period","iso":"<ISO8601>",...}.
type Status ¶
type Status string
Status is the outcome of a Parse call.
const ( // StatusResolved means Parse found exactly one interpretation. StatusResolved Status = "resolved" // StatusAmbiguous means Parse found multiple plausible interpretations. StatusAmbiguous Status = "ambiguous" // StatusInvalid means Parse could not parse the input. StatusInvalid Status = "invalid" )
type Time ¶
type Time struct {
// contains filtered or unexported fields
}
Time represents a clock time without a date or timezone.
func NewTimeNanos ¶
NewTimeNanos creates a Time with sub-second precision.
func TimeFromTime ¶
TimeFromTime extracts the clock time from a time.Time.
func (Time) MarshalJSON ¶
MarshalJSON encodes t as {"kind":"time","value":"HH:MM:SS[.fraction]"}.
func (Time) Nanosecond ¶
Nanosecond returns the nanosecond component (0-999999999).
func (*Time) UnmarshalJSON ¶
UnmarshalJSON decodes t from {"kind":"time","value":"HH:MM:SS[.nnnnnnnnn]"}.
type TimeError ¶
type TimeError struct {
// Code is the stable machine-readable error code.
Code ErrorCode `json:"code"`
// Message is the human-readable error summary.
Message string `json:"message,omitzero"`
// Input is the original input that triggered the error.
Input string `json:"input,omitzero"`
// Hint explains how to correct the input.
Hint string `json:"hint,omitzero"`
// Err is the sentinel identity for errors.Is.
Err error `json:"-"`
}
TimeError is the structured error type for every failure in this package. It composes with the standard library: errors.Is matches the Err sentinel, while errors.As extracts Input, Hint, Message, and Code for inspection.
type Warning ¶
type Warning struct {
// Code classifies the warning.
Code WarningCode `json:"code"`
// Message is a short human-readable description.
Message string `json:"message"`
// Hint suggests how to silence the warning.
Hint string `json:"hint,omitempty"`
}
Warning is a non-fatal advisory about a lossy assumption made during parsing.
type WarningCode ¶
type WarningCode string
WarningCode classifies a parse warning. Warnings are non-fatal lossy assumptions; they never change Status.
const ( // WarnAssumedZone reports that a default Zone was applied because the // input did not carry an explicit zone or offset. WarnAssumedZone WarningCode = "assumed_zone" // WarnTruncatedPrecision reports that input precision exceeded what the // target type can represent and was truncated. WarnTruncatedPrecision WarningCode = "truncated_precision" // WarnInferredCalendar reports that a date/calendar interpretation was // inferred from locale or candidate ordering. WarnInferredCalendar WarningCode = "inferred_calendar" // WarnDuplicateTime reports that a DST fall-back local time candidate is // one of multiple valid instants. WarnDuplicateTime WarningCode = "duplicate_time" )
type Zone ¶
type Zone struct {
// contains filtered or unexported fields
}
Zone represents an IANA timezone identity.
func MustLoadZone ¶
MustLoadZone is like LoadZone but panics if id is invalid. It is intended for use with fixed IANA identifiers in variable initializations.
func ResolveZone ¶
ResolveZone resolves a timezone identifier by trying exact IANA names, case-insensitive IANA matches, and Windows timezone names. Legacy IANA aliases such as "US/Eastern" are handled by Go's time.LoadLocation.
func (Zone) Location ¶
Location returns the underlying *time.Location for stdlib interop. The zero Zone falls back to time.UTC.
func (Zone) MarshalJSON ¶
MarshalJSON encodes z as {"kind":"zone","id":"<IANA id>"}. The output is deterministic and never depends on time.Now().
func (*Zone) UnmarshalJSON ¶
UnmarshalJSON decodes z from {"kind":"zone","id":"<IANA id>",...}.
Source Files
¶
- current.go
- date.go
- datetime.go
- doc.go
- duration.go
- duration_components.go
- errors.go
- instant.go
- interval.go
- local_datetime.go
- options.go
- parse.go
- parse_date_time_impl.go
- parse_datetime_impl.go
- parse_duration_period_impl.go
- parse_helpers.go
- parse_impl.go
- parse_interval_impl.go
- parse_natural_dispatch.go
- parse_slash.go
- parse_typed.go
- period.go
- time.go
- wire_json.go
- zone.go
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
natural
Package natural implements the internal natural-language parsing layer for gotime.
|
Package natural implements the internal natural-language parsing layer for gotime. |
|
zone
Package zone provides internal IANA timezone data and DST projection utilities.
|
Package zone provides internal IANA timezone data and DST projection utilities. |
|
zone/geniana
command
The geniana command generates the internal IANA timezone catalog.
|
The geniana command generates the internal IANA timezone catalog. |
|
zone/genwindows
command
The genwindows command generates the Windows-to-IANA mapping from a pinned Unicode CLDR windowsZones.xml release.
|
The genwindows command generates the Windows-to-IANA mapping from a pinned Unicode CLDR windowsZones.xml release. |