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.
π¨π³ δΈζ | πΊπΈ English
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 | 2x128
go get github.com/baagod/aeon
The 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, '+' Far
t.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 Day
IsLeapYear(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 Zero
t.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