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, ParseDate, ParseTime, ParseDuration, ParsePeriod, ParseInterval. Each returns (T, error) and is the default path for application code.
- Parse — inspection / 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. Use ParseResult.Value with a Go type switch for polymorphic dispatch.
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 a formatter (for example github.com/agentable/go-intl) via the Instant.Std, DateTime.Std, and Duration.Std adapters.
Example — typed helper (most common) ¶
dt, err := gotime.ParseDateTime("2026-03-27T13:00:00+09:00")
if err != nil {
return err
}
fmt.Println(dt.Std().Format("2006-01-02 15:04 MST"))
Example — natural language with locale ¶
Relative expressions ("tomorrow", "in 2 hours") default to time.Now(); pass WithReference only when you need a fixed reference for testing.
dt, err := gotime.ParseDateTime("tomorrow at 3pm",
gotime.WithInputLocale(language.English),
gotime.WithZone(gotime.MustLoadZone("America/New_York")),
)
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", "value": "2026-03-27T13:00:00+09:00", "zone": "Asia/Tokyo", "calendar": "iso8601" }
Index ¶
- Constants
- Variables
- func Zones() []string
- type Date
- func (d Date) Add(p Period) Date
- 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) 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) Std(z Zone) time.Time
- func (d Date) String() string
- func (d Date) Sub(other Date) Period
- 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
- func (dt DateTime) AddPeriod(p Period) DateTime
- 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
- 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) RFC5545() string
- 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
- 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
- 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 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) MarshalJSON() ([]byte, error)
- func (r ParseResult) Period() (Period, bool)
- func (r ParseResult) Time() (Time, bool)
- func (r ParseResult) Value() any
- type Period
- func (p Period) Abs() Period
- func (p Period) Add(other Period) Period
- 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
- func (p Period) RFC5545() string
- func (p Period) String() string
- func (p Period) Sub(other Period) Period
- 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) Std(on Date, z Zone) time.Time
- func (t Time) String() string
- func (t *Time) UnmarshalJSON(b []byte) error
- type TimeError
- type Warning
- type WarningCode
- type Zone
- func (z Zone) Abbreviation(i Instant) string
- func (z Zone) Equal(other Zone) bool
- func (z Zone) ID() string
- func (z Zone) IsDST(i Instant) bool
- func (z Zone) IsZero() bool
- func (z Zone) Location() *time.Location
- func (z Zone) MarshalJSON() ([]byte, error)
- func (z Zone) OffsetAt(i Instant) string
- func (z Zone) Snapshot(i Instant) ZoneSnapshot
- func (z Zone) String() string
- func (z *Zone) UnmarshalJSON(b []byte) error
- type ZoneSnapshot
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") ErrAmbiguousTime = errors.New("gotime: ambiguous time") ErrAmbiguousZone = errors.New("gotime: ambiguous zone") 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.
Functions ¶
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.
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, and inheriting time.Local from the runtime environment produces date drift across container/CI/server boundaries. Callers that want process-local behavior must spell it out: TodayIn(Local).
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 negated Period: d.Add(gotime.Days(3).Negate()).
func (Date) DaysInMonth ¶
DaysInMonth returns the number of days in the date's month.
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","calendar":"iso8601"}.
func (Date) Std ¶
Std returns the Date as a time.Time at 00:00:00 in zone z. Use this to bridge a Date to any API expecting a stdlib time.Time.
func (Date) Sub ¶
Sub returns the calendar difference from other to d as a Period (with Years, Months, Days set; signed by direction). Mirrors time.Time.Sub. Use Add for arithmetic — there is no Sub(Period) form.
func (*Date) UnmarshalJSON ¶
UnmarshalJSON decodes d from {"kind":"date","value":"YYYY-MM-DD"[,"calendar":"..."]}.
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.
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).
func (DateTime) AddPeriod ¶
AddPeriod returns a new DateTime advanced by p (calendar arithmetic), preserving the local wall-clock time across DST transitions. Month/year arithmetic applies end-of-month clamping (Jan 31 + 1 month = Feb 28/29). To move back, pass a negated Period: dt.AddPeriod(gotime.Months(1).Negate()).
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,
)
feb := jan31.AddPeriod(gotime.Months(1))
fmt.Println("Jan 31 + 1 month:", feb.Date())
exact := jan31.Add(48 * gotime.Hour)
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) MarshalJSON ¶
MarshalJSON encodes dt as {"kind":"datetime","value":"<RFC3339Nano>","zone":"<IANA id>","calendar":"iso8601"}.
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","value":"<RFC3339Nano>","zone":"<IANA id>"[,"calendar":"..."]}. The zone field is preferred; if absent the offset embedded in value is used.
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) RFC5545 ¶
RFC5545 formats d per RFC 5545 §3.3.6 using W/D/H/M/S designators only. Duration cannot carry months/years, so this never returns an error.
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 int64
Minutes int64
Seconds int64
Milliseconds int64
Microseconds int64
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" // CodeAmbiguousTime reports multiple plausible time interpretations. CodeAmbiguousTime ErrorCode = "AMBIGUOUS_TIME" // CodeAmbiguousZone reports timezone input matching multiple zones. CodeAmbiguousZone ErrorCode = "AMBIGUOUS_ZONE" // 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) MarshalJSON ¶
MarshalJSON encodes i as {"kind":"instant","iso":"<RFC3339Nano UTC>","epoch_ms":N}.
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 IntervalOf ¶
IntervalOf creates an Interval from a start Instant and a non-negative Duration. Negative durations are treated as their absolute value.
func NewInterval ¶
NewInterval creates an Interval. Returns ErrIntervalReversed if end < start.
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" // 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 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"), the locale also disambiguates month-first (en-US, en-CA, en-AU) vs day-first ordering. With no locale, only-valid interpretations resolve, otherwise the result is Ambiguous with both candidates.
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.
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, ParseDate, ParseTime, ParseDuration, ParsePeriod, ParseInterval) — they return (T, error) directly and skip the Status / Kind dispatch entirely.
Reach for Parse when you need any of:
- Polymorphic dispatch on Kind via ParseResult.Value and a Go type switch.
- 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
}
dt, _ := r.DateTime()
fmt.Println(dt)
}
Output: 2026-03-27T13:00:00+09:00
func (ParseResult) Date ¶
func (r ParseResult) Date() (Date, bool)
Date returns the parsed Date. ok is false unless Kind == KindDate.
func (ParseResult) DateTime ¶
func (r ParseResult) DateTime() (DateTime, bool)
DateTime returns the parsed DateTime. ok is false unless Kind == KindDateTime.
func (ParseResult) Duration ¶
func (r ParseResult) Duration() (Duration, bool)
Duration returns the parsed Duration. ok is false unless Kind == KindDuration.
func (ParseResult) Instant ¶
func (r ParseResult) Instant() (Instant, bool)
Instant returns the parsed Instant. ok is false unless Kind == KindInstant.
func (ParseResult) Interval ¶
func (r ParseResult) Interval() (Interval, bool)
Interval returns the parsed Interval. ok is false unless Kind == KindInterval.
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 Kind == KindPeriod.
func (ParseResult) Time ¶
func (r ParseResult) Time() (Time, bool)
Time returns the parsed Time. ok is false unless Kind == KindTime.
func (ParseResult) Value ¶
func (r ParseResult) Value() any
Value returns the parsed value as an untyped any so callers can dispatch via a Go type switch. The concrete type is one of Instant, DateTime, Date, Time, Duration, Period, or Interval — matching r.Kind. Returns nil when r.Status is not StatusResolved, so a type switch with a nil case (or default) naturally handles ambiguous / invalid inputs.
switch v := result.Value().(type) {
case gotime.DateTime: handle(v)
case gotime.Date: handle(v)
case nil: // ambiguous or invalid — inspect result.Candidates / result.Error
}
When you already know the target type, prefer the typed helpers (ParseDateTime, ParseDate, ...). The comma-ok accessors (ParseResult.DateTime etc.) remain available for callers that use Parse for Warnings / Candidates but still know the Kind statically.
type Period ¶
type Period struct {
Years int32 `json:"years,omitzero"`
Months int32 `json:"months,omitzero"`
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 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) 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) RFC5545 ¶
RFC5545 formats p per RFC 5545 §3.3.6. RFC 5545 disallows months/years in DURATION values, but Period intentionally carries calendar offsets; we still emit P{n}Y, P{n}M, P{n}D — callers responsible for validating the receiving system supports this. For weeks-aligned days, emit "P{n}W".
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","precision":"second"} or with sub-second value and appropriate precision when nanoseconds are non-zero.
func (Time) Nanosecond ¶
Nanosecond returns the nanosecond component (0-999999999).
func (Time) Std ¶
Std returns the clock time projected onto Date d in Zone z as a time.Time. Use this to bridge a clock Time to any API expecting a stdlib time.Time.
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 or fixed offset.
var Local Zone
Local is the process-local timezone reported by the host system.
var UTC Zone
UTC is the UTC timezone.
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, Windows timezone names, and fixed UTC offsets. Legacy IANA aliases such as "US/Eastern" are handled by Go's time.LoadLocation.
func (Zone) Abbreviation ¶
Abbreviation returns the timezone abbreviation (e.g., "JST", "EDT") at the given Instant.
func (Zone) IsDST ¶
IsDST reports whether the zone is observing Daylight Saving Time at the given Instant.
func (Zone) IsZero ¶
IsZero reports whether z is the zero value (no zone explicitly set). Note that the zero Zone is still safe to operate on: Location falls back to time.UTC. Use IsZero only when you need to detect "was this set?".
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() — time-dependent display data lives in Zone.Snapshot(at).
func (Zone) OffsetAt ¶
OffsetAt returns the UTC offset of the zone at the given Instant as a "+HH:MM" or "-HH:MM" string.
func (Zone) Snapshot ¶
func (z Zone) Snapshot(i Instant) ZoneSnapshot
Snapshot returns a point-in-time view of z (offset, DST, abbreviation) at i. The snapshot is decoupled from JSON serialization — callers requiring time-dependent fields compute and embed them explicitly.
func (*Zone) UnmarshalJSON ¶
UnmarshalJSON decodes z from {"kind":"zone","id":"<IANA id>",...}.
type ZoneSnapshot ¶
type ZoneSnapshot struct {
// ID is the IANA zone identifier.
ID string `json:"id"`
// Offset is the UTC offset at the snapshot time, formatted as "+HH:MM".
Offset string `json:"offset"`
// DST reports whether the zone is observing Daylight Saving Time.
DST bool `json:"dst"`
// Abbreviation is the zone abbreviation (e.g. "JST", "PDT").
Abbreviation string `json:"abbreviation"`
}
ZoneSnapshot is a point-in-time snapshot of a Zone's display data. It is intentionally cheap and copy-safe — callers compute it on demand.
Source Files
¶
- current.go
- date.go
- datetime.go
- doc.go
- duration.go
- duration_components.go
- errors.go
- instant.go
- interval.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
- 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. |