I can't recall the exact moment it began. Perhaps it stemmed from a dissatisfaction with
time.Timeand existing time libraries—I developed a near-absurd obsession: Why not write a Go time library myself?In ancient philosophy, Aeon represents "eternity" and "layered dimensions."
I chose this name because I want to express a different logic of time—time is not a slender straight line; it is a flowing universe that can be nested and penetrated.
Aeon is a Zero-Allocation time navigation library for Go based on Time Container theory. It replaces linear calculation with structured navigation, expressing complex time intentions in a way that aligns with human intuition.
Aeon achieves True Zero Allocation (Zero Alloc) and leverages a Cascading Addressing architecture. Whether you span multiple dimensions (from Millennium to Nanosecond), Aeon completes the operation in a single atomic step. The more complex the logic, the more staggering Aeon's lead becomes.
Note
The following baseline data were obtained under single-atom operations without using cascade parameters.
Benchmark | ns/op | allocs/op x B/op | up
New |
Aeon | 18.6 | 0 | x74
Carbon | ████████████████████████████████████████ 1376 | 13x1600
Now |
Aeon | 7.8 | 0 | x177
Carbon | ████████████████████████████████████████ 1384 | 13x1600
From Unix |
Aeon | 3.6 | 0 | x383
Carbon | ████████████████████████████████████████ 1380 | 13x1600
From Std |
Aeon | 5.0 | 0 | x323
Carbon | ████████████████████████████████████████ 1619 | 13x1600
Parse (Compact) |
Aeon | 23.3 | 0 | x195
Carbon | ████████████████████████████████████████ 4561 | 85x3922
Parse (ISO) |
Aeon | 19.6 | 0 | x91
Carbon | ████████████████████████████████████████ 1794 | 15x1697
Start/End |
Aeon | █ 56.4 | 0 | x20
Carbon | ████████████████████ 1141 | 7x1440
Add (Offset) |
Aeon | █ 56.5 | 0 | x2.5
Carbon | ██ 142 | 2x128
Set (Position) |
Aeon | █ 58.7 | 0 | x2.6
Carbon | ███ 156 | 2x128go get github.com/baagod/aeonThe core of Aeon is Container Offset. All navigation is essentially indexing within the Parent Container of the current unit (starting from 0). For example:
GoYear(5): Not going to the year 5 AD, but indexing to the 5th year within the current Decade (the parent container) ➜···5.GoDecade(2): Indexing to the 2nd Decade of the current Century ➜··2·.GoCentury(0): Indexing to the 0th Century of the current Millennium ➜·0··.
[Millennium]
└─ [0...9 Century]
└─ [0...9 Decade]
└─ [0...9 Year]
└─ [1...12 Month]
Example: GoYear(5) Addressing Logic
[-9] [-8] [-5] [-4] [-1]
2020 ─┬─ 2021 ──┬── 2022 ··· ──┬── [2025] ──┬── 2026 ─┬─ ··· ─┬─ 2029
[0] [1] [2] [5] [6] [9]
Aeon's API design is completely Orthogonal. You only need to remember 4 Actions:
Go.. [·]Absolute Positioning:GoYear(5, 1)➜ 5th Year, 1st Month of current decade.By.. [➜]Relative Offset:ByYear(1, 5)➜ Offset by 1 Year and 5 Months.At.. [·, ➜]Position then Offset:AtYear(5, 1)➜ Locate 5th Year, then offset 1 Month.In.. [➜, ·]Offset then Position:InYear(1, 5)➜ Next Year (Offset 1), then 5th Month.
Important
Bymethods default to1. Others default to0.- Invalid
0time (e.g., 0th Month) remains unchanged in Positioning Mode (but works in Offset mode). Gopositions only the target unit and preserves original time details as much as possible** (e.g.,GoWeekautomatically retains the weekday).
t := Parse("2021-07-21 07:14:15") // Wed
t.GoMonth(1) // 2021-01-21 07:14:15 (Set to Jan, time preserved)
t.GoWeek(1) // 2021-06-30 07:14:15 (1st Week, Weekday preserved as Wed)Combined with Start/End prefixes to hit time boundaries:
StartYear(): Start of this year (01-01 00:00:00...)EndYear(): End of this year (12-31 23:59:59...)
6 Top-Level methods allow the first parameter to enter Absolute Year mode:
Go(2025, 2)➜ 2025-02At(2025, 2)➜ Position at 2025, then offset 2 months.Start(2025, 2)➜ 2025-02-01 00:00:00StartAt(2025, 1)➜ Position at 2025, offset 1 month, then Start of Month.End(2025, 2)➜ 2025-02-28 23:59:59...EndAt(2025, 1)➜ Position at 2025, offset 1 month, then End of Month.
Method chaining? No, this is Atomic Operation! All methods support Variadic Parameters that cascade downwards. Parameters flow like water, completing complex positioning in one line of code.
Aeon automatically switches between 4 cascading sequences based on the Entry Unit:
-
Year Sequence
Default:Century ➜ Decade ➜ Year ➜ Month ➜ Day ➜ Hour.. ➜ Nanosecond -
Quarter Flow
Quarter:Quarter ➜ Month (in Quarter) ➜ Day ➜ Hour.. ➜ Nanosecond -
Week Sequence
Week🦬:Week (Smart Context) ➜ Weekday ➜ Hour.. ➜ NanosecondThis is a Transformer! It automatically shifts shape based on the passed Flags:
ISO: ISO Week. Starts from the 1st ISO week of the year.Full: Full Week. Starts from the 1st Monday of the month.Ord: Ordinal Week. Starts from the 1st day of the month.Qtr: Quarter Week. Starts from the 1st day of the quarter's first month.Default: Calendar/Natural Week. Follows the calendar visual row.
-
Weekday Flow
Weekday:Weekday ➜ Hour.. ➜ Nanosecond
// Relative offset: 1 Year, 3 Months, 5 Days
ByYear(1, 3, 5)
// 2nd Tuesday of the current Quarter
GoWeek(aeon.Qtr|aeon.Ord, 2, 2)
// Last Friday of the current Quarter
GoWeek(aeon.Qtr|aeon.Ord, -1, 5)
// 2025, Feb, Last Day, 23:00
Go(2025).StartMonth(2, -1, 23)
// End of the 3rd Quarter, minus 1 month, minus 2 days
EndQuarter(3, -1, -2)
// 10th ISO Monday of 2025
Go(2025).StartWeek(aeon.ISO, 10, 1)
// 3rd Friday of this month (Ordinal week starting from 1st)
StartWeek(aeon.Ord, 3, 5)
// Last Friday of this month
GoWeek(aeon.Ord, -1, 5)
// End of previous Quarter
EndByQuarter(-1)
// 1st day of the last month of this Quarter
StartQuarter(0, -1, 1)
// This Friday at 18:00 (Happy Hour)
StartWeekday(5, 18)
// 3rd to last day of this month
StartDay(-3)
// Next Wednesday at 2 PM
StartInWeek(1, 3, 14)
// Yearly Archive: Start/End boundaries
StartYear() / EndYear()
// Last day of next month
EndInMonth(1, -1)Negative numbers are not just subtraction; they are Reverse Indexing, representing the "N-th from last" item in the container.
Aeon's core philosophy is Intention First. By default, navigation protects against day overflow for units "Month and above".
base := NewDate(2025, 1, 31)
base.GoMonth(2) // 2025-02-28 (Protected)
base.ByMonth(Overflow, 1) // 2025-03-03 (Overflow allowed)
base.ByMonth(1, 2) // 🛡️🦬 2025-03-02 (Protect to 2-28, then add 2 days)
// Leap Year Handling
leap := NewDate(2024, 2, 29)
leap.ByYear(1) // 2025-02-28 (Protected)
leap.ByYear(Overflow, 1) // 2025-03-01 (Overflow: Crosses month boundary)
leap.ByYear(4) // 2028-02-29 (Next Leap Year)// Create time from optional time.Time(s)
Aeon(t ...time.Time) Time
// Create current time with optional location
Now(loc ...*time.Location) Time
// Precisely create time (y, m, d, h, mm, s), with optional nanosecond/location
New(y, m, d, h, mm, int, add ...any) Time
// Create from timestamp (auto-detects second/milli/micro/nano)
Unix(int64, ...*time.Location) Time
// Parse time string (ignores error)
Parse(string, ...*time.Location) Time
// Parse time string (returns error)
ParseE(string, ...*time.Location) (Time, error)
// Parse with layout (ignores error)
ParseBy(layout, value string, loc ...*time.Location) Time
// Parse with layout (returns error)
ParseByE(layout, value string, loc ...*time.Location) (Time, error)t.Year() int // Year
t.Month() int // Month (1-12)
t.Day() int // Day (1-31)
t.Hour() int // Hour (0-23)
t.Minute() int // Minute (0-59)
t.Second() int // Second (0-59)
t.Milli() int // Millisecond (0-999)
t.Micro() int // Microsecond (0-999999)
t.Nano() int // Nanosecond (0-999999999)
t.Clock() (h, mm, s int) // Hour, Minute, Second
t.Weekday() time.Weekday // Weekday
t.ISOWeek() (year, week int) // ISO Week
t.YearDay() int // Day of Year (1-365/366)
t.YearDays() int // Total Days in Year
t.Days() int // Total Days in Month
t.Unix(n ...int) int64 // Timestamp, n specifies precision
t.Time() time.Time // Convert to Std Lib
t.Location() *time.Location // Location
t.Zone() (name string, offset int) // Zone Name and Offset (Seconds)t.Lt(u Time) bool // t < u
t.Gt(u Time) bool // t > u
t.Eq(u Time) bool // t == u
t.Compare(u Time) int // -1 / 0 / 1
t.IsSame(u Unit, target Time) bool // Check if same unit (Year/Month/Day etc.)
t.Between(start, end Time, bound ...byte) bool // Check if within interval
// bound: '=' inclusive, '!' exclusive, '[' left inclusive, ']' right inclusive
t.Diff(u Time, unit string, abs ...bool) float64
// unit: "y"Year, "M"Month, "d"Day, "h"Hour, "m"Minute, "s"Second
// abs: true returns absolute value
t.Sub(u Time) time.Duration // t - u
t.Until() time.Duration // t - Now()Pick(op byte, times ...Time) Time
// op: '>' Max, '<' Min, '-' Near, '+' Fart.UTC() Time // UTC Time
t.Local() Time // Local Time
t.To(loc *time.Location) Time // Specific Location
t.WithWeekStarts(w time.Weekday) Time // Set Week Start DayIsLeapYear(y int) bool // Is Leap Year
IsLongYear(y int) bool // Is ISO Long Year (53 Weeks)
DaysIn(y int, m ...int) int // Days in y Year m Month, returns year days if m omitted
t.IsLeapYear() bool // Is Leap Year
t.IsLongYear() bool // Is ISO Long Year
t.IsWeekend() bool // Is Weekend
t.IsAM() bool // Is AM
t.IsDST() bool // Is DST
t.IsZero() bool // Is Zero
t.ZeroOr(u Time) Time // Return u if Zerot.String() string // Default Format
t.Format(layout string) string // Custom Format
t.ToString(f ...string) string // Custom Format (Short)
t.AppendFormat(b []byte, layout string) []byte// Constant Zones (70+ IANA Zones)
const (
UTC, Local, CET, EET, EST, GMT, MET, MST, WET
Shanghai, Tokyo, NewYork, London, Paris, Berlin, Moscow
// ...
)
// Return Zone by name and offset
Zone(string, ...int) *time.Location