date
Month and day boundaries, computed in UTC.
It exists because billing and reporting ask the same question in a dozen places - where does this month start, where does it end, how many months lie between these two dates - and every hand-rolled answer gets the edges slightly differently. An end-of-month computed as "the 30th" is wrong four times a year; one computed by adding a month and subtracting a nanosecond is right every time.
import "gitlab.internal/tuan.quang.tran/aioz-common/date"Usage
start, end := date.MonthBoundary(time.Now())
rows, err := db.UsageBetween(ctx, start, end)// A period identifier from a report or an invoice, back into a time.
t, err := date.PeriodToTime("2026-03")Everything here is UTC
UTCBeginOfMonth and UTCEndOfMonth say so in the name; MonthBoundary and DayBoundary do the same without saying it. That is deliberate and it is the trap worth naming: a boundary computed in a server's local zone moves when the server moves, so two nodes in different regions disagree about which month a usage record belongs to. Accounting periods are UTC or they are not reproducible.
API
MonthBoundary(t)andDayBoundary(t)return the inclusive start and end of the month or day containingt.UTCBeginOfMonth(t),UTCEndOfMonth(t)are the same edges, named.MonthsBetweenDates(from, to)andMonthsCountSince(from)count whole months.PeriodToTime("YYYY-MM")parses a period identifier. Seeperiodfor the value type that owns that format.TruncateToHourInNano(t)truncates to the hour and returns Unix nanoseconds, for bucketing time series.