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
- Variables
- func DateTimeToDay(sec int64) int64
- func DayToTime(day int64) time.Time
- func DayToUnix(day int64) int64
- func DayToUnixFloat(day float64) float64
- func IsWholeDay(sec int64) bool
- func LocalDay(sec int64, z *Zone) int64
- func LocalMidnightUTC(day int64, z *Zone) int64
- func MonthStart(day int64) time.Time
- func QuarterStart(day int64) time.Time
- func TimeToDay(t time.Time) int64
- func YearStart(day int64) time.Time
- type Cache
- type Parts
- type Zone
Constants ¶
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.
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 ¶
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 ¶
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 DayToUnixFloat ¶
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 ¶
IsWholeDay reports whether sec (epoch seconds) falls exactly on a UTC midnight boundary, for either sign.
func LocalDay ¶
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 ¶
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 ¶
MonthStart returns UTC midnight of the first day of the calendar month containing epoch day day.
func QuarterStart ¶
QuarterStart returns UTC midnight of the first day of the calendar quarter (Jan / Apr / Jul / Oct) 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 ¶
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 ¶
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 ¶
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 ¶
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) Offset ¶
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.