Arithmetic & ranges

Adding/subtracting days and months, comparing spans, and finding month/year boundaries.

Days

func (d Date) AddDays(n int) (Date, error)

Returns the date n calendar days after d (or before, if n is negative). Returns an error wrapping ErrInvalidYear/ErrInvalidMonth/ErrInvalidDay if d itself is invalid, or ErrOutOfRange if the result falls outside the supported range.

func (d Date) SubDays(n int) (Date, error)

Returns the date n calendar days before d. Same errors as AddDays.

func (d Date) NextDay() (Date, error)

The day after d — a thin wrapper over AddDays(1).

func (d Date) PreviousDay() (Date, error)

The day before d — a thin wrapper over SubDays(1).

func (d Date) DayOfWeek() (time.Weekday, error)

The day of the week d falls on, derived through its equivalent Gregorian date.

func DaysBetween(a, b Date) (int, error)

The number of calendar days from a to b: positive if b is after a, negative if b is before a, zero if equal.

d, _ := bs.NewDate(2083, 6, 6)
 
next, _ := d.AddDays(10)  // 2083-06-16
prev, _ := d.SubDays(10)  // 2083-05-27
 
nd, _ := d.NextDay()      // 2083-06-07
pd, _ := d.PreviousDay()  // 2083-06-05
 
weekday, _ := d.DayOfWeek() // Tuesday
 
other, _ := bs.NewDate(2083, 6, 16)
diff, _ := bs.DaysBetween(d, other) // 10

Months

func (d Date) NextMonth() (Date, error)

The date one Bikram Sambat month after d, in the same day-of-month — clamped to the target month's last day if it's shorter, rather than rolling over into the month after. Returns the usual validation errors for an invalid receiver.

func (d Date) PreviousMonth() (Date, error)

The date one Bikram Sambat month before d, with the same clamping behavior as NextMonth.

This clamping is deliberate. Go's own time.Time.AddDate(0, 1, 0) has a well-known gotcha where Jan 31 + one month rolls into March, since February doesn't have 31 days. NextMonth/PreviousMonth avoid that: day 32 in a 32-day month becomes day 31 in a 31-day target month, not day 1 of the month after.

d, _ := bs.NewDate(2083, 6, 6)
 
next, _ := d.NextMonth()     // 2083-07-06
prev, _ := d.PreviousMonth() // 2083-05-06
 
// Ashadh (month 3) 2083 has 32 days; Shrawan (month 4) has only 31.
last, _ := bs.NewDate(2083, 3, 32)
clamped, _ := last.NextMonth() // 2083-04-31, not 2083-05-01

Month and year boundaries

func (d Date) StartOfMonth() (Date, error)

Day 1 of d's month.

func (d Date) EndOfMonth() (Date, error)

The last day of d's month.

func (d Date) StartOfYear() (Date, error)

Baisakh 1 of d's year.

func (d Date) EndOfYear() (Date, error)

The last day of Chaitra of d's year.

func (d Date) DayOfYear() (int, error)

d's 1-based ordinal day within its year (1 for Baisakh 1, up to 365 or 366 for the last day of Chaitra).

d, _ := bs.NewDate(2083, 6, 6)
 
som, _ := d.StartOfMonth() // 2083-06-01
eom, _ := d.EndOfMonth()   // 2083-06-31
 
soy, _ := d.StartOfYear()  // 2083-01-01
eoy, _ := d.EndOfYear()    // 2083-12-30 (Chaitra 2083 has 30 days)
 
day, _ := d.DayOfYear()    // 162

Age

func Age(birthBS, todayBS Date) (years, months, days int, err error)

Computes calendar age from birthBS to todayBS (see TodayBS) — real Y/M/D calendar math with month/day borrowing, not just a raw day count divided by 365. Returns an error wrapping ErrInvalidDateOrder if birthBS is after todayBS.

birth := bs.MustParse("2060-06-15")
today := bs.MustParse("2083-06-06") // 9 days before the 23rd birthday
 
years, months, days, err := bs.Age(birth, today)
// years == 22, months == 11, days == 22