years

package module
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2026 License: MIT Imports: 14 Imported by: 2

README

years

Everything has a time. Travel it.

A time toolkit for Go: parsing, formatting, humanizing, and navigating time-based data.

Go Reference Go Version


years bundles the time helpers you keep rewriting. One parser accepts Go layouts, unix timestamps, and human aliases. Around it: zero-safe formatting with canonical layouts, "3h ago" humanization, fluent in-place mutation, and a Voyager that traverses anything with a time, such as calendar-structured file trees.

t, _ := years.JustParse("2024-05-26") // ISO dates & RFC3339 out of the box
t, _ = years.JustParse("1717852417")  // unix timestamps
t, _ = years.JustParse("yesterday")   // human aliases

years.Humanize(t) // "1d ago"

Install

go get github.com/amberpixels/years

Quick Start

package main

import (
	"fmt"

	"github.com/amberpixels/years"
)

func main() {
	created, _ := years.JustParse("2024-05-26 13:45")

	fmt.Println(years.Format(created, years.LayoutHumanDate)) // "May 26, 2024"
	fmt.Println(years.Humanize(created))                      // "2y ago"

	years.Mutate(&created).TruncateToDay().SetYear(2025)
	fmt.Println(years.Format(created, years.LayoutDate)) // "2025-05-26"

	d, _ := years.ParseISODuration("PT1H2M3S")
	fmt.Println(years.FormatDurationClock(d)) // "1:02:03"
}

Parsing

With no setup, the default parser reads RFC3339 timestamps, 2006-01-02 15:04[:05], plain dates, unix-second timestamps, and aliases:

t, _ := years.JustParse("2024-05-26T13:45:00Z")
t, _ = years.JustParse("1717852417")
t, _ = years.JustParse("next-week")

// Strict single-layout parsing, like time.Parse:
t, _ = years.Parse("2006-01-02", "2024-05-26")

Aliases resolve against the package clock: today, yesterday, tomorrow, last-workday, this-week, last-week, next-week, last-weekend, next-weekend, this-month, last-month, next-month, this-year, last-year, next-year.

last-workday is the most recent working day before today: on a Monday, that is Friday. Weeks start on Sunday and working days are Monday to Friday; WithWeekStart and WithWorkdays change them.

Layouts may also contain a timestamp part: U@ for unix seconds, U@000 for milliseconds, U@000000 / U@000000000 for micro/nanoseconds:

p := years.NewParser(
	years.AcceptUnixMilli(),
	years.AcceptAliases(),
	years.WithLayouts("2006", "2006-01", "2006-Jan-02"),
)

t, _ = p.Parse("logs-U@000.log", "logs-1717852417000.log") // 2024-06-08 13:13:37 UTC
t, _ = p.JustParse("2020-01")

A Go layout may start and/or end with *, which matches any run of characters, including none. It reads dates wrapped in text no layout can describe, such as JavaScript's Date.toString():

t, _ = years.Parse("Mon Jan 02 2006 15:04:05 GMT-0700*",
	"Thu Apr 24 2025 11:52:05 GMT+0300 (Eastern European Summer Time)") // 2025-04-24 11:52:05 +0300
t, _ = years.Parse("*2006-01-02*", "[2025-04-24]")

* is allowed only as the first or last character. Parse with a layout that has * anywhere else returns an error; in a WithLayouts list such a layout never matches. A trailing * keeps the longest prefix that parses and a leading * the shortest skip, so 2006-1-2* reads 2025-4-24 x as April 24, not April 2. With both, the longest substring that parses wins, leftmost first. Wildcard layouts are lenient, so in a WithLayouts list put them after the strict ones.

For the global parser, SetParserDefaults replaces its options, ExtendParserDefaults appends, and ResetParserDefaults restores the built-in behavior:

years.ExtendParserDefaults(years.WithLayouts("2006, Jan 2"))
t, _ = years.JustParse("2020, Dec 1")

// Monday-start weeks and a Sunday-to-Thursday working week:
years.ExtendParserDefaults(
	years.WithWeekStart(time.Monday),
	years.WithWorkdays(time.Sunday, time.Monday, time.Tuesday, time.Wednesday, time.Thursday),
)
period, _ := years.JustParsePeriod("last-workday")

By default, a layout value with no offset is read in UTC (2026-10-20 is UTC midnight). Aliases and relative offsets resolve in the clock's zone, which is local time for the standard clock. WithLocation puts both in one zone: offset-less values are read in it and day and week boundaries are cut in it. Values with an explicit offset (Z, +02:00) keep their own:

years.ExtendParserDefaults(years.WithLocation(time.Local))
t, _ = years.JustParse("2026-10-20") // local midnight

Options passed to NewParser replace the defaults instead of adding to them. A custom parser that should resolve aliases lists AcceptAliases() alongside WithWeekStart or WithWorkdays.

Formatting

Named display layouts, so you stop re-typing reference-time strings, plus zero- and nil-safe helpers:

t := time.Date(2025, time.April, 30, 13, 45, 0, 0, time.UTC)

years.Format(t, years.LayoutDate)          // "2025-04-30"
years.Format(t, years.LayoutDateTime)      // "2025-04-30 13:45:00"
years.Format(t, years.LayoutDateTimeShort) // "2025-04-30 13:45"
years.Format(t, years.LayoutHuman)         // "Apr 30, 2025 13:45"
years.Format(t, years.LayoutHumanDate)     // "Apr 30, 2025"

years.Format(time.Time{}, years.LayoutDate) // "" - the zero time renders empty
years.FormatPtr(nil, years.LayoutDate)      // "" - nil-safe

Durations

Parse ISO-8601 durations (e.g. YouTube's contentDetails.duration), and render durations media-clock style or in a compact human form:

d, _ := years.ParseISODuration("PT15M30S") // 15m30s
years.FormatDurationClock(d)               // "15:30"
years.FormatDurationClock(time.Hour + 2*time.Minute + 3*time.Second) // "1:02:03"

years.HumanizeDuration(90 * time.Minute) // "1h 30m"
years.HumanizeDuration(45 * time.Second) // "45s"

Relative Time

years.Humanize(time.Now().Add(-3 * time.Hour)) // "3h ago"
years.Humanize(time.Now().Add(48 * time.Hour)) // "in 2d"

// Clock-free variant (no global clock needed; handy in tests):
years.HumanizeFrom(base, base.Add(-5*time.Minute)) // "5m ago"

Humanize reads the package clock (years.Now()), so tests can make it deterministic via years.SetStdClock.

Mutating Time

Mutate wraps a *time.Time with fluent setters and truncation helpers that modify it in place:

t := time.Date(2025, time.April, 30, 13, 45, 59, 0, time.UTC)

years.Mutate(&t).TruncateToWeek(time.Monday) // t is now 2025-04-28 00:00:00
years.Mutate(&t).SetMonth(time.December).SetDay(24).SetHour(18)

Truncation covers every unit from TruncateToSecond to TruncateToYear; setters run from SetYear to SetNanosecond.

Voyager: Navigating Time-Based Data

Anything implementing Waypoint (an identifier, a time, optional children) can be traversed with a Voyager. Strings work out of the box:

dates := []string{"2024-03-01", "2024-01-01", "2024-02-01"}

v := years.NewVoyager(years.WaypointGroupFromStrings(dates))
_ = v.Traverse(func(w years.Waypoint) {
	fmt.Println(w.Identifier()) // 2024-03-01, 2024-02-01, 2024-01-01
}, years.O_PAST()) // newest first

Pass a non-default layout to the waypoints: years.WaypointGroupFromStrings(dates, "2006, Jan 2").

Files and directories named after dates form navigable calendars. Given a tree like calendar/2024/Jan/2024-01-15.txt:

wf, err := years.NewTimeNamedWaypointFile("calendar", "2006/Jan/2006-01-02.txt")
if err != nil {
	panic(err)
}

v := years.NewVoyager(wf)
_ = v.Traverse(func(w years.Waypoint) {
	fmt.Println(w.Identifier()) // file paths, oldest first
}, years.O_FUTURE(), years.O_LEAVES_ONLY())

// Jump straight to a date (aliases work here too):
found, _ := v.Navigate("yesterday")

Traverse options: O_PAST / O_FUTURE for direction, O_LEAVES_ONLY / O_CONTAINERS_ONLY / O_ALL for node filtering, O_NON_CALENDAR to include nodes without a parsed time. For time from file metadata (modification/creation/access) instead of names, see NewWaypointFile.

Schedules

The schedule subpackage models recurring weekly time windows - working hours, quiet hours, availability:

import "github.com/amberpixels/years/schedule"

work := schedule.Schedule{
	Days:      []time.Weekday{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday},
	StartHour: 9,
	EndHour:   17,
	Gaps:      []schedule.TimeRange{{StartHour: 12, EndHour: 13}}, // lunch break
}

work.Contains(t)         // true on a Monday at 10:30
work.SlotsForDay(t)      // [09:00-12:00, 13:00-17:00]
work.AvailableMinutes(t) // 420
work.NextMatchingDay(t)  // next scheduled day

MultiSlotSchedule covers disjoint (even cross-midnight) windows per day, and CompositeSchedule merges several schedules into one.

Week patterns

WeekPattern is a recurring wall-clock predicate such as "weekends" or "nights", with no anchor in absolute time (unlike aliases: last-week resolves to one concrete week). ParsePatterns turns a human vocabulary into patterns; comma means union:

patterns, _ := schedule.ParsePatterns("weekends,mornings")

patterns.Contains(t) // true on Saturday 15:00, or on any day at 09:30

The built-in vocabulary (case-insensitive, plural-tolerant): weekday names (monday(s)...), weekend(s), workingday(s)/workday(s)/weekday(s), and the day parts of DefaultDayPartition:

part span
night 22:00-05:00 (wraps midnight)
morning 05:00-12:00
lunchtime 12:00-14:00
afternoon 14:00-17:00
evening 17:00-22:00

The partition covers the full day exactly once (enforced by Validate), so PartOf doubles as a group-by key:

schedule.DefaultDayPartition.PartOf(t).Name // "morning" | "lunchtime" | ...

A cross-midnight span belongs to the day it starts on, so Sunday night includes Monday 01:00. Patterns read the given time's wall clock as-is; there is no timezone field, so convert times to the zone you mean first. Add domain words and custom partitions with Vocabulary:

vocab := schedule.DefaultVocabulary()
vocab.Extra = map[string]schedule.WeekPatterns{"officehours": {{
	Days:  []time.Weekday{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday},
	Spans: []schedule.DaySlot{{Start: schedule.TimeOfDay{Hour: 9}, End: schedule.TimeOfDay{Hour: 17}}},
}}}

patterns, _ := vocab.ParsePatterns("officehours")

Feedback

years is a solo, opinionated project - but if you stumbled upon it and have ideas, questions, or bug reports, an issue is always welcome :)

License

MIT © amberpixels

Documentation

Index

Constants

View Source
const (
	// LayoutDate is a calendar date: "2006-01-02".
	LayoutDate = "2006-01-02"
	// LayoutDateTime is a date with full wall-clock time: "2006-01-02 15:04:05".
	LayoutDateTime = "2006-01-02 15:04:05"
	// LayoutDateTimeShort is a date with minute precision: "2006-01-02 15:04".
	LayoutDateTimeShort = "2006-01-02 15:04"
	// LayoutHuman is a friendly date+time: "Jan 2, 2006 15:04".
	LayoutHuman = "Jan 2, 2006 15:04"
	// LayoutHumanDate is a friendly date: "Jan 2, 2006".
	LayoutHumanDate = "Jan 2, 2006"
)

Display layouts, the formatting counterparts to the parser: build a time with Parse or JustParse, render it with Format and one of these layouts.

View Source
const (
	LayoutTimestampSeconds      = "U@"
	LayoutTimestampMilliseconds = "U@000"
	LayoutTimestampMicroseconds = "U@000000"
	LayoutTimestampNanoseconds  = "U@000000000"
)

Unix timestamp tokens: U@ reads seconds and each 000 adds three digits of precision, so "logs-U@000.log" reads milliseconds.

Variables

View Source
var DateUnitsDict = struct {
	Day   DateUnit
	Month DateUnit
	Year  DateUnit

	UnixSecond      DateUnit
	UnixMillisecond DateUnit
	UnixMicrosecond DateUnit
	UnixNanosecond  DateUnit
}{
	Day:   Day,
	Month: Month,
	Year:  Year,

	UnixSecond:      UnixSecond,
	UnixMillisecond: UnixMillisecond,
	UnixMicrosecond: UnixMicrosecond,
	UnixNanosecond:  UnixNanosecond,
}

DateUnitsDict holds all available DateUnits.

DefaultLayouts are the unambiguous Go layouts the default parser accepts, so years.JustParse reads RFC3339 timestamps and plain dates with no setup. They run most specific first because the first match wins. RFC3339Nano stands alone: its trailing-.9 form matches with or without a fractional second.

View Source
var DefaultParser = func() *Parser {
	return NewParser(defaultParserOptions...)
}

DefaultParser builds a fresh parser from the current package defaults, so SetParserDefaults takes effect on the next call.

View Source
var ErrInvalidISODuration = errors.New("invalid ISO-8601 duration")

ErrInvalidISODuration is returned by ParseISODuration for input that is not a supported ISO-8601 duration.

View Source
var LayoutFormatDict = struct {
	GoFormat      LayoutFormat
	UnixTimestamp LayoutFormat
}{
	GoFormat:      LayoutFormatGo,
	UnixTimestamp: LayoutFormatUnixTimestamp,
}

LayoutFormatDict holds all available LayoutFormats.

Functions

func ExtendParserDefaults

func ExtendParserDefaults(opts ...ParserOption)

func Format added in v0.0.10

func Format(t time.Time, layout string) string

Format renders t using layout, returning "" for the zero time so callers don't emit a meaningless "0001-01-01". It is the formatting counterpart to JustParse.

func FormatDurationClock added in v0.0.10

func FormatDurationClock(d time.Duration) string

FormatDurationClock renders d in colon-separated media style: "M:SS" when under an hour (e.g. "15:30") and "H:MM:SS" otherwise (e.g. "1:02:03"). Negative durations are treated as zero. Sub-second remainders are truncated.

func FormatPtr added in v0.0.10

func FormatPtr(t *time.Time, layout string) string

FormatPtr is the nil-safe variant of Format: a nil (or zero) time yields "".

func Humanize added in v0.0.10

func Humanize(t time.Time) string

Humanize renders t relative to the current time (the package clock, see Now), automatically choosing past ("3d ago") or future ("in 3d") phrasing; gaps under a minute render as "just now". It is the Go counterpart to the "time ago" helpers usually hand-written on the frontend. For deterministic results in tests, override the clock via SetStdClock or use HumanizeFrom.

func HumanizeDuration added in v0.0.10

func HumanizeDuration(d time.Duration) string

HumanizeDuration renders d as a compact, human-friendly approximation: the two most-significant non-zero units, e.g. 2h5m3s -> "2h 5m", 90m -> "1h 30m", 1d30m -> "1d 30m", 45s -> "45s". A duration with a single non-zero unit renders it alone (3h -> "3h"). Sub-second durations render as "0s". The sign is dropped (magnitude only); use Humanize/HumanizeFrom for signed, relative "ago"/"in" phrasing.

func HumanizeFrom added in v0.0.10

func HumanizeFrom(base, t time.Time) string

HumanizeFrom is Humanize with an explicit base time instead of the package clock, so it needs no clock and is convenient for tests: it describes t as seen from base ("3d ago" when t precedes base, "in 3d" when it follows).

func JustParse added in v0.0.2

func JustParse(value string) (time.Time, error)

JustParse calls JustParse of a default parser.

func JustParseRaw added in v0.0.5

func JustParseRaw(value any) (time.Time, error)

JustParseRaw attempts to convert or parse any value into a time.Time. - If value is time.Time (or custom type convertible to time.Time) the underlying time.Time is returned. - If value is a string or custom string type, passes to JustParse. - If value implements fmt.Stringer, uses its String() to be parsed via JustParse. - If value is a numeric type (int, uint, float), stringifies and passes to JustParse. - If value is nil, it returns a zero time.Time. Returns an error for unsupported types.

func Now added in v0.0.9

func Now() time.Time

Now returns the current time according to the package-level clock. By default it is equivalent to time.Now(); tests can override the source via SetStdClock, making any code that relies on years.Now() deterministic.

func Parse

func Parse(layout, value string) (time.Time, error)

Parse calls Parse of a default parser.

func ParseISODuration added in v0.0.10

func ParseISODuration(s string) (time.Duration, error)

ParseISODuration parses an ISO-8601 duration string into a time.Duration. It accepts the day+time subset (PnDTnHnMnS); calendar components (weeks, months, years) are rejected. An empty string is a zero duration with no error; any other input that carries no components (e.g. "P", "PT") is an error.

func ResetParserDefaults

func ResetParserDefaults()

ResetParserDefaults restores the package defaults: unix seconds, aliases, relative offsets and DefaultLayouts.

func SetParserDefaults

func SetParserDefaults(opts ...ParserOption)

func SetStdClock

func SetStdClock(c Clock)

SetStdClock sets the package clock. It is meant for tests, to make time.Now() mockable.

Types

type Clock

type Clock interface {
	Now() time.Time
}

type DateUnit

type DateUnit int

DateUnit is the unit of a date (Day, Month, Year) or of a unix timestamp.

const (
	// Day is the day of the month.
	// TODO(nice-to-have) support day of the week + day of the year.
	Day  DateUnit = 1 << iota
	Week          // not supported yet
	Month
	Quarter // not supported yet
	Year

	// UnixSecond and the finer Unix units are the precision of a unix
	// timestamp layout (U@, U@000, ...).
	UnixSecond
	UnixMillisecond
	UnixMicrosecond
	UnixNanosecond
)
const UnitUndefined DateUnit = 0

func (DateUnit) Defined

func (du DateUnit) Defined() bool

func (DateUnit) String

func (du DateUnit) String() string

type LayoutDetails

type LayoutDetails struct {
	// MinimalUnit is the finest unit in the layout: Day for "2006-01-02",
	// Month for "2006-01".
	MinimalUnit DateUnit

	// Format is how the layout is read: as a Go layout or a unix timestamp.
	Format LayoutFormat

	// Units lists every unit found in the layout.
	Units []DateUnit
}

LayoutDetails stores parsed meta information about a layout string such as "2006-01-02".

func ParseLayout

func ParseLayout(layout string) *LayoutDetails

ParseLayout parses given layout string and returns LayoutDetails, or nil when it finds no date unit. Units are found by substring heuristics, so an unusual layout can be misread.

func (*LayoutDetails) HasUnit

func (lm *LayoutDetails) HasUnit(q DateUnit) bool

type LayoutFormat

type LayoutFormat int
const (
	// LayoutFormatGo is a format that is supported by Go time.Parse.
	LayoutFormatGo LayoutFormat = 1 << iota
	// LayoutFormatUnixTimestamp is a format that parses time from a Unix timestamp (seconds to nanoseconds).
	LayoutFormatUnixTimestamp
)
const LayoutFormatUndefined LayoutFormat = 0

func (LayoutFormat) String

func (lf LayoutFormat) String() string

type MutatingTime

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

MutatingTime is a wrapper around a time.Time pointer, providing fluent setter methods that mutate the underlying time.

Example:

t, _ := time.Parse("2006-01-02 15:04:05", "2025-04-30 13:45:00")
Mutate(&t).SetMonth(time.April).SetYear(2021)
// t is now 2021-04-30 13:45:00.

Use Mutate to obtain a MutatingTime for in-place modifications.

func Mutate

func Mutate(v *time.Time) *MutatingTime

Mutate returns a MutatingTime for the given *time.Time.

func (*MutatingTime) SetDay

func (mt *MutatingTime) SetDay(day int) *MutatingTime

SetDay sets the day of the month to the provided value.

func (*MutatingTime) SetHour

func (mt *MutatingTime) SetHour(hour int) *MutatingTime

SetHour sets the hour (0-23). Panics if out of range.

func (*MutatingTime) SetMillisecond added in v0.0.5

func (mt *MutatingTime) SetMillisecond(ms int) *MutatingTime

SetMillisecond sets the millisecond (0-999) by overriding the nanosecond field. Panics if out of range.

func (*MutatingTime) SetMinute

func (mt *MutatingTime) SetMinute(minute int) *MutatingTime

SetMinute sets the minute (0-59). Panics if out of range.

func (*MutatingTime) SetMonth

func (mt *MutatingTime) SetMonth(month time.Month) *MutatingTime

SetMonth sets the month to the provided value.

func (*MutatingTime) SetNanosecond

func (mt *MutatingTime) SetNanosecond(nano int) *MutatingTime

SetNanosecond sets the nanosecond (0-999,999,999). Panics if out of range.

func (*MutatingTime) SetSecond

func (mt *MutatingTime) SetSecond(second int) *MutatingTime

SetSecond sets the second (0-59). Panics if out of range.

func (*MutatingTime) SetYear

func (mt *MutatingTime) SetYear(year int) *MutatingTime

SetYear sets the year to the provided value.

func (*MutatingTime) Time

func (mt *MutatingTime) Time() time.Time

Time returns the underlying time.Time value.

func (*MutatingTime) TruncateToDay

func (mt *MutatingTime) TruncateToDay() *MutatingTime

TruncateToDay moves the time to the start of its day: 00:00:00, or the first instant after midnight on a day whose midnight falls in a DST gap.

func (*MutatingTime) TruncateToHour added in v0.0.9

func (mt *MutatingTime) TruncateToHour() *MutatingTime

TruncateToHour sets the minute, second, and nanosecond to zero.

func (*MutatingTime) TruncateToMinute added in v0.0.9

func (mt *MutatingTime) TruncateToMinute() *MutatingTime

TruncateToMinute sets the second and nanosecond to zero.

func (*MutatingTime) TruncateToMonth added in v0.0.9

func (mt *MutatingTime) TruncateToMonth() *MutatingTime

TruncateToMonth moves the time to the first day of its month at 00:00:00.

func (*MutatingTime) TruncateToSecond added in v0.0.9

func (mt *MutatingTime) TruncateToSecond() *MutatingTime

TruncateToSecond sets the nanosecond to zero, keeping everything down to the second.

func (*MutatingTime) TruncateToWeek added in v0.0.9

func (mt *MutatingTime) TruncateToWeek(weekStartsOn time.Weekday) *MutatingTime

TruncateToWeek moves the time back to the start of its week (at 00:00:00), where weekStartsOn selects which weekday begins the week (e.g. time.Monday for ISO-8601 weeks, time.Sunday for US-style weeks).

func (*MutatingTime) TruncateToYear added in v0.0.9

func (mt *MutatingTime) TruncateToYear() *MutatingTime

TruncateToYear moves the time to January 1st of its year at 00:00:00.

type Parser

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

func NewParser

func NewParser(options ...ParserOption) *Parser

NewParser builds a parser from options, or from the package defaults (see SetParserDefaults) when given none; given options replace the defaults rather than extend them. It starts on the package clock, with Sunday week starts and Monday to Friday working days.

func (*Parser) JustParse

func (p *Parser) JustParse(value string) (time.Time, error)

JustParse is Parse("", value): every accepted layout is tried.

func (*Parser) JustParsePeriod added in v0.2.0

func (p *Parser) JustParsePeriod(value string) (Period, error)

JustParsePeriod is a shortcut for ParsePeriod("", value).

func (*Parser) Parse

func (p *Parser) Parse(layout, value string) (time.Time, error)

Parse parses value with layout, or with each of the parser's layouts when layout is empty; a given layout is strict and its mismatch is an error. An all-digit value is tried as an epoch first when a unix unit is accepted; accepted aliases and relative offsets are tried after the layouts.

func (*Parser) ParseEpoch added in v0.0.3

func (p *Parser) ParseEpoch(v int64) (time.Time, bool, error)

ParseEpoch reads v as a unix timestamp in every enabled unit and keeps the readings that land in [1970, 3000). It returns the coarsest one (seconds, then milli, micro, nano) and reports whether more than one unit fit; enable a single unit when values near 1970 must not be ambiguous.

func (*Parser) ParsePeriod added in v0.2.0

func (p *Parser) ParsePeriod(layout, value string) (Period, error)

ParsePeriod parses value into the Period it denotes, using the given layout (or all of the parser's accepted layouts when layout is empty):

  • a registered alias resolves to its calendar period ("yesterday" to that whole day, "last-week" to that whole week);
  • a relative offset ("7d", "2w") resolves to [now - offset, now);
  • a value matching a date-only layout resolves to that whole day;
  • anything else Parse understands (timestamps, epochs) resolves to an instant period.

type ParserOption

type ParserOption func(*Parser)

func AcceptAliases

func AcceptAliases() ParserOption

func AcceptRelativeOffsets added in v0.2.0

func AcceptRelativeOffsets() ParserOption

AcceptRelativeOffsets opts in to duration-offset expressions ("12h", "7d", "2w", "3mo", "1y"). An offset denotes the period of the last N units (see ParsePeriod); the instant Parse returns that period's start, i.e. now - offset.

func AcceptUnixMicro

func AcceptUnixMicro() ParserOption

func AcceptUnixMilli

func AcceptUnixMilli() ParserOption

func AcceptUnixNano

func AcceptUnixNano() ParserOption

func AcceptUnixSeconds

func AcceptUnixSeconds() ParserOption

func GetParserDefaults

func GetParserDefaults() []ParserOption

func WithCustomAliases

func WithCustomAliases(customAliases map[string]func(time.Time) time.Time) ParserOption

WithCustomAliases merges the given aliases into the parser's alias table (custom entries win over same-named core ones). The caller's map is copied, never mutated, and the option may be given more than once.

func WithCustomClock

func WithCustomClock(c Clock) ParserOption

WithCustomClock makes the parser read the current time from c instead of the package clock (see SetStdClock).

func WithLayouts

func WithLayouts(layouts ...string) ParserOption

func WithLocation added in v0.6.0

func WithLocation(loc *time.Location) ParserOption

WithLocation reads layouts that carry no offset in loc, as time.ParseInLocation does, and resolves aliases and relative offsets on loc's calendar. Values with an explicit offset keep it. A nil loc leaves the setting unchanged.

func WithWeekStart added in v0.6.0

func WithWeekStart(d time.Weekday) ParserOption

WithWeekStart sets the weekday the week-based aliases ("this-week", "last-week", "next-week") start on. Defaults to time.Sunday. A value outside Sunday..Saturday leaves the week start unchanged.

func WithWorkdays added in v0.6.0

func WithWorkdays(days ...time.Weekday) ParserOption

WithWorkdays sets which weekdays are working days, used by the "last-workday" alias. Defaults to Monday through Friday. The given days replace the set; called with no valid days it leaves the set unchanged, as an empty working week has no last working day.

type Period added in v0.2.0

type Period struct {
	Start time.Time
	End   time.Time
}

Period is a half-open time interval [Start, End). Calendar expressions denote periods, not instants: "yesterday" is a whole day, "last-week" a whole week, "2024-03-06" a day. Start and End serve directly as a range query's inclusive lower and exclusive upper bound. A point in time (an RFC3339 timestamp, a unix epoch) is an instant period: End equals Start.

func InstantPeriod added in v0.2.0

func InstantPeriod(t time.Time) Period

InstantPeriod wraps a single point in time as a zero-length Period.

func JustParsePeriod added in v0.2.0

func JustParsePeriod(value string) (Period, error)

JustParsePeriod calls JustParsePeriod of a default parser.

func ParsePeriod added in v0.2.0

func ParsePeriod(layout, value string) (Period, error)

ParsePeriod calls ParsePeriod of a default parser.

func (Period) Contains added in v0.2.0

func (p Period) Contains(t time.Time) bool

Contains reports whether t falls inside the half-open interval; an instant period contains exactly its own point.

func (Period) Duration added in v0.2.0

func (p Period) Duration() time.Duration

func (Period) IsInstant added in v0.2.0

func (p Period) IsInstant() bool

IsInstant reports whether the period is a zero-length point in time.

func (Period) IsZero added in v0.2.0

func (p Period) IsZero() bool

type StdClock

type StdClock struct{}

func (*StdClock) Now

func (c *StdClock) Now() time.Time

type TimeNamedWaypointFile

type TimeNamedWaypointFile struct {
	*WaypointFile
	// contains filtered or unexported fields
}

TimeNamedWaypointFile is a Waypoint for files/directories whose time is parsed from their names, joined with their parents' names when needed.

func NewTimeNamedWaypointFile

func NewTimeNamedWaypointFile(
	path string, fullLayout string,
	parentArg ...*TimeNamedWaypointFile,
) (*TimeNamedWaypointFile, error)

NewTimeNamedWaypointFile reads the time of path and every entry below it from their names, using the matching segments of fullLayout, a path-separated layout such as "2006/Jan/2006-01-02.txt". A name that does not parse becomes a non-calendar waypoint.

func (*TimeNamedWaypointFile) Time

func (w *TimeNamedWaypointFile) Time() time.Time

type TimeNamedWaypointFiles

type TimeNamedWaypointFiles []*TimeNamedWaypointFile

type TraverseDirection

type TraverseDirection string

TraverseDirection is a direction for traversing (e.g. past or future).

const (
	TraverseDirectionPast   TraverseDirection = "past"
	TraverseDirectionFuture TraverseDirection = "future"
)

type TraverseNodesMode

type TraverseNodesMode string

TraverseNodesMode specifies which type of nodes to traverse (e.g. leaves only or containers only).

const (
	TraverseLeavesOnly     TraverseNodesMode = "leaves_only"
	TraverseContainersOnly TraverseNodesMode = "containers_only"
	TraverseAllNodes       TraverseNodesMode = "all"
)

type TraverseOption

type TraverseOption func(*traverseConfig)

TraverseOption defines functional options for the Traverse function.

func O_ALL

func O_ALL() TraverseOption

O_ALL returns a TraverseOption for traversing all nodes.

func O_CONTAINERS_ONLY

func O_CONTAINERS_ONLY() TraverseOption

O_CONTAINERS_ONLY returns a TraverseOption for traversing only container nodes.

func O_FUTURE

func O_FUTURE() TraverseOption

O_FUTURE returns a TraverseOption for traversing in Future direction.

func O_LEAVES_ONLY

func O_LEAVES_ONLY() TraverseOption

O_LEAVES_ONLY returns a TraverseOption for traversing only leaf nodes.

func O_NON_CALENDAR

func O_NON_CALENDAR() TraverseOption

O_NON_CALENDAR returns a TraverseOption for including non calendar nodes.

func O_PAST

func O_PAST() TraverseOption

O_PAST returns a TraverseOption for traversing in Past direction.

type Voyager

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

Voyager walks a waypoint tree in time order and finds waypoints by time.

func NewVoyager

func NewVoyager(root Waypoint, parserArg ...*Parser) *Voyager

NewVoyager wraps root for traversal. The optional parser reads Navigate and Find queries; it defaults to NewParser().

func (*Voyager) Find

func (v *Voyager) Find(timeStr string) ([]Waypoint, error)

Find returns every Waypoint whose time equals the parsed timeStr, e.g. Find("yesterday") returns all waypoints at the start of yesterday.

func (*Voyager) Navigate

func (v *Voyager) Navigate(to string) (Waypoint, error)

Navigate returns the first waypoint, in traversal order, whose time equals the parsed to, or nil when none does: Navigate("yesterday") finds the waypoint at the start of yesterday.

func (*Voyager) Traverse

func (v *Voyager) Traverse(cb func(w Waypoint), opts ...TraverseOption) error

Traverse calls cb for the root and every descendant in time order: newest first by default (O_PAST), oldest first with O_FUTURE. O_LEAVES_ONLY, O_CONTAINERS_ONLY and O_ALL (default) pick the nodes; waypoints with no time are skipped unless O_NON_CALENDAR is given.

type Waypoint

type Waypoint interface {
	// Identifier returns the identifier of the object.
	// E.g. for file waypoints it can be file path.
	Identifier() string

	// Time returns the time of the object.
	Time() time.Time

	// IsContainer returns true if the object can contain other objects.
	// E.g. for directories, it should return true.
	IsContainer() bool

	// Children returns the children of the object if it's a container.
	// E.g. for directories, it should return the list of files and directories inside.
	Children() []Waypoint
}

Waypoint is an interface for objects that have a time.

func AllChildren

func AllChildren(w Waypoint) []Waypoint

AllChildren returns every descendant of w, depth first, each parent before its own children.

func NewWaypointGroup

func NewWaypointGroup(identifier string, waypoints ...Waypoint) Waypoint

NewWaypointGroup creates a group for given waypoints.

func WaypointGroupFromStrings

func WaypointGroupFromStrings(timeStrings []string, layoutArg ...string) Waypoint

func WaypointsFromStrings

func WaypointsFromStrings(timeStrings []string, layoutArg ...string) []Waypoint

type WaypointFile

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

WaypointFile is a Waypoint implementation for files/directories.

func NewWaypointFile

func NewWaypointFile(path string, timeGetter func(timeSpec times.Timespec) time.Time) (*WaypointFile, error)

NewWaypointFile builds a waypoint for path and, for a directory, every entry below it. timeGetter picks which file time (birth, modification, ...) each waypoint carries. An entry that fails to stat is logged and skipped.

func (*WaypointFile) Children

func (w *WaypointFile) Children() []Waypoint

func (*WaypointFile) Identifier

func (w *WaypointFile) Identifier() string

func (*WaypointFile) IsContainer

func (w *WaypointFile) IsContainer() bool

func (*WaypointFile) Time

func (w *WaypointFile) Time() time.Time

type WaypointFiles

type WaypointFiles []*WaypointFile

type WaypointGroup

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

WaypointGroup is a Waypoint that only contains other waypoints.

func (*WaypointGroup) Children

func (wg *WaypointGroup) Children() []Waypoint

func (*WaypointGroup) Identifier

func (wg *WaypointGroup) Identifier() string

func (*WaypointGroup) IsContainer

func (wg *WaypointGroup) IsContainer() bool

func (*WaypointGroup) Time

func (wg *WaypointGroup) Time() time.Time

Time returns the zero time: a group has no time of its own. TODO(nice-to-have): make it configurable, e.g. the min, max or first child's time.

type WaypointString

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

WaypointString is a Waypoint implementation for a string that represents time.

func NewWaypointString

func NewWaypointString(v string, layoutArg ...string) *WaypointString

NewWaypointString parses v with the optional layout, or the parser's layouts when none is given. A v that does not parse becomes a non-calendar waypoint with a zero time and an empty identifier.

func (*WaypointString) Children

func (w *WaypointString) Children() []Waypoint

func (*WaypointString) Identifier

func (w *WaypointString) Identifier() string

func (*WaypointString) IsContainer

func (w *WaypointString) IsContainer() bool

func (*WaypointString) Time

func (w *WaypointString) Time() time.Time

func (*WaypointString) Voyager

func (w *WaypointString) Voyager(parserArg ...*Parser) *Voyager

type WaypointStrings

type WaypointStrings []*WaypointString

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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