temporal

package
v1.0.0-alpha.3 Latest Latest
Warning

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

Go to latest
Published: Oct 3, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package temporal is the single home of Pulse's epoch-day and calendar arithmetic. A `date` cell holds whole epoch DAYS and a `datetime` cell holds whole epoch SECONDS (naive UTC); every conversion between the two, and every calendar boundary computed from a day, goes through this package rather than open-coding the 86,400 factor at a call site.

It is a leaf over the stdlib plus the public `errors` package (for the coded PULSE_TIMEZONE_UNKNOWN refusal): it must never import the public `encoding` package (which forwards into it) or any other Pulse package. It is also the only package that embeds the tz database and resolves IANA zones (zone.go).

Index

Constants

View Source
const SecondsPerDay = 86400

SecondsPerDay is the conversion factor between the `datetime` (epoch seconds) and `date` (epoch days) on-wire representations. Untyped so it composes with int64, uint64 and float64 arithmetic alike.

View Source
const TZDataVersion = "2026c"

TZDataVersion is the IANA tz database release embedded in zoneinfo.zip. Go's zip carries no version entry, so the value is recorded when the file is refreshed (from the DATA= line of the toolchain's lib/time/update.bash) and pinned to the file's bytes by tzdataSHA256: TestTZDataVersion_MatchesEmbeddedZip fails when the zip changes without both constants being updated.

Variables

View Source
var UTC = &Zone{name: "UTC", loc: time.UTC, utc: true}

UTC is the UTC sentinel: zero offset everywhere, no table, and every lookup short-circuits. LoadZone("UTC") returns exactly this pointer.

Functions

func DateTimeToDay

func DateTimeToDay(sec int64) int64

DateTimeToDay truncates epoch seconds (naive UTC) to the epoch day that contains the instant. Truncation is toward the PAST, never toward zero and never rounding: 1969-12-31T23:59:59Z (sec -1) is day -1, not day 0.

func DayToTime

func DayToTime(day int64) time.Time

DayToTime returns UTC midnight of epoch day day.

func DayToUnix

func DayToUnix(day int64) int64

DayToUnix returns the epoch seconds of UTC midnight of epoch day day.

func DayToUnixFloat

func DayToUnixFloat(day float64) float64

DayToUnixFloat is DayToUnix over float64 for callers (SPSS) that carry day counts as doubles. The multiply is exact for every day count a `date` cell can hold.

func IsWholeDay

func IsWholeDay(sec int64) bool

IsWholeDay reports whether sec (epoch seconds) falls exactly on a UTC midnight boundary, for either sign.

func LocalDay

func LocalDay(sec int64, z *Zone) int64

LocalDay returns the local epoch day of instant sec (epoch seconds) in zone z: floor((sec + z.Offset(sec)) / SecondsPerDay), truncating toward the PAST exactly as DateTimeToDay does. LocalDay(sec, UTC) == DateTimeToDay(sec). Allocation-free.

func LocalMidnightUTC

func LocalMidnightUTC(day int64, z *Zone) int64

LocalMidnightUTC returns the UTC instant (epoch seconds) at which local epoch day day begins in zone z: the FIRST instant s whose wall clock in z reads day 00:00:00 or later. Under UTC it is day * SecondsPerDay.

Where a DST transition skips local midnight (America/Santiago, America/Havana and, historically, Asia/Beirut spring forward from 00:00 to 01:00), there is no instant reading 00:00, and the result is the first instant that exists on that local day — the transition instant, whose wall clock reads 01:00. Where a transition repeats the hour before midnight (a fall-back from 24:00 to 23:00), the day begins at the single instant reading 00:00, after the repeated hour.

Invariants: LocalMidnightUTC(LocalDay(s, z), z) <= s for every s, and LocalDay(LocalMidnightUTC(d, z), z) == d for every local day d that has at least one instant. A day that a zone skips ENTIRELY (Pacific/Apia, 2011-12-30) has no instant; the result is then the first instant of the next day that exists, so LocalDay of it is greater than d.

func MonthStart

func MonthStart(day int64) time.Time

MonthStart returns UTC midnight of the first day of the calendar month containing epoch day day.

func QuarterStart

func QuarterStart(day int64) time.Time

QuarterStart returns UTC midnight of the first day of the calendar quarter (Jan / Apr / Jul / Oct) containing epoch day day.

func TimeToDay

func TimeToDay(t time.Time) int64

TimeToDay returns the epoch day containing t's instant, with the same toward-the-past truncation as DateTimeToDay. t's location is ignored; only the instant matters.

func YearStart

func YearStart(day int64) time.Time

YearStart returns UTC midnight of January 1 of the calendar year containing epoch day day.

Types

type Cache

type Cache struct {
	// contains filtered or unexported fields
}

Cache memoises LoadZone by name. LoadZone builds a transition table on every call, so a long-lived owner (one per pulse.Pulse instance) keeps one Cache and resolves every request-time zone through it. Only successful loads are cached: a refused name is re-validated (and re-refused) on every call, so a cache can never mask an error.

The zero value is ready to use and safe for concurrent use.

func (*Cache) Load

func (c *Cache) Load(name string) (*Zone, error)

Load returns the Zone for name, loading and caching it on first use. It returns exactly what LoadZone returns for an unknown name (a PULSE_TIMEZONE_UNKNOWN *errors.CodedError); "UTC" is the UTC sentinel. Concurrent first loads of one name may each build a table, but every caller observes the single stored pointer.

type Parts

type Parts struct {
	Year    int
	Month   time.Month
	Day     int // day of month, 1..31
	Hour    int // 0..23
	Minute  int // 0..59
	Second  int // 0..59
	Weekday time.Weekday
	YearDay int // day of year, 1..366
	ISOYear int // ISO 8601 week-numbering year
	ISOWeek int // ISO 8601 week, 1..53
}

Parts is the wall-clock decomposition of an instant in a zone.

func LocalParts

func LocalParts(sec int64, z *Zone) Parts

LocalParts returns the wall-clock fields of instant sec (epoch seconds) in zone z. Every field equals what time.Unix(sec, 0).In(z.Location()) reports. Allocation-free.

type Zone

type Zone struct {
	// contains filtered or unexported fields
}

Zone is a validated IANA time zone with a precomputed transition table for allocation-free offset lookup. A *Zone is immutable apart from its lookup cache, which is atomic, so one *Zone is safe to share across goroutines. A Zone must not be copied after LoadZone returns it.

func LoadZone

func LoadZone(name string) (*Zone, error)

LoadZone resolves name to a Zone. The validity rule is strict: name is exactly "UTC" (the UTC sentinel), or an IANA Area/Location name — it contains a '/', every '/'-separated segment opens with an ASCII upper-case letter and carries only letters, digits, '_', '-' and '+' — with an exact, case-sensitive entry in Pulse's embedded tz database (zoneinfo.zip, version TZDataVersion). $ZONEINFO, the host zoneinfo directory and time/tzdata are never consulted, so the accepted set and every offset are identical on every host. "Etc/UTC", "Etc/GMT-5" and "Europe/Berlin" are accepted. Empty, "Local", abbreviations and legacy names without a '/' ("EST", "MST", "GMT", "EST5EDT") and offset strings ("+05:00") are refused with a PULSE_TIMEZONE_UNKNOWN *errors.CodedError whose details carry the name under errors.DetailTimeZone.

func (*Zone) IsUTC

func (z *Zone) IsUTC() bool

IsUTC reports whether the zone is UTC-equivalent: the UTC sentinel, or a named zone whose offset is zero at every instant ("Etc/UTC", "Etc/GMT", "Etc/Zulu", "Etc/UCT"). Local-day arithmetic in such a zone is identical to UTC's. A zone that merely sits at offset zero today (e.g. "Africa/Abidjan", which had a local-mean-time offset before 1912, or "Europe/London" in winter) is NOT UTC-equivalent.

func (*Zone) Location

func (z *Zone) Location() *time.Location

Location returns the underlying *time.Location.

func (*Zone) Name

func (z *Zone) Name() string

Name returns the zone name as given to LoadZone.

func (*Zone) Offset

func (z *Zone) Offset(sec int64) int32

Offset returns the zone's offset from UTC, in seconds east, in effect at instant sec (epoch seconds). It equals `_, off := time.Unix(sec, 0).In(loc).Zone()` everywhere, and is allocation-free: the UTC sentinel short-circuits, an instant in the span of the previous hit returns from the cache, other in-window instants binary-search the table, and out-of-window instants defer to the stdlib.

Jump to

Keyboard shortcuts

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