Date, parsing & validation

The Date type, constructing dates, getting today's date, and validating year/month/day combinations.

type Date struct { Year, Month, Day int }

A Bikram Sambat calendar date. Month is 1-based — 1 is Baisakh, 12 is Chaitra. A Date zero value or one built directly from a struct literal isn't guaranteed valid unless constructed with NewDate or returned by this package.

const MinBSYear = 1979
const MaxBSYear = 2100

The inclusive bounds of the BS years this package supports, corresponding to the Gregorian range 1922-04-13 to 2044-04-13. Referenced throughout this reference as MinBSYear..MaxBSYear.

func NewDate(year, month, day int) (Date, error)

Constructs and validates a Date. Returns an error wrapping ErrInvalidYear, ErrInvalidMonth or ErrInvalidDay if the components don't form a real Bikram Sambat calendar date in the supported range.

func Parse(s string) (Date, error)

Parses a date in "YYYY-MM-DD" format — the same format Date.String produces. Returns an error wrapping ErrInvalidFormat if s isn't shaped like a date, or the usual validation errors if it's shaped correctly but not a real date.

func MustParse(s string) Date

Like Parse, but panics instead of returning an error. Meant for cases like package-level variable initialization with a literal, known-good date string — most callers should use Parse.

func TodayBS() (Date, error)

Returns today's date in Nepal, converted to Bikram Sambat. Returns an error wrapping ErrOutOfRange if today's date falls outside the supported range — i.e. this code running before MinBSYear or after MaxBSYear's corresponding Gregorian date.

TodayBS always means today in Nepal (UTC+05:45), not wherever the calling process happens to be running — it ignores the server's configured timezone entirely. This matters because cloud VMs and containers commonly default to UTC: for roughly 5h45m of every day, Nepal has already moved into the next calendar day while a UTC server hasn't, so a naive time.Now()-based implementation would report yesterday's BS date as "today" for part of every day. TodayBS accounts for this so callers don't have to.

func (d Date) Valid() bool

Reports whether d is a real Bikram Sambat calendar date in the supported range.

func (d Date) String() string // "YYYY-MM-DD"

Formats d as an ISO-like string, zero-padded.

Validation

func IsValid(year, month, day int) bool

Reports whether year, month and day form a real Bikram Sambat calendar date within the supported range — checked against the actual number of days in that month, not just a generic 1–31 shape check.

func IsSupportedBSYear(year int) bool

Reports whether year is within MinBSYear..MaxBSYear.

func DaysInMonth(year, month int) (int, error)

Returns the number of days in the given Bikram Sambat month (29, 30, 31 or 32, depending on the year and month). Returns an error wrapping ErrInvalidYear or ErrInvalidMonth if out of range.

func DaysInYear(year int) (int, error)

Returns the total number of days in the given Bikram Sambat year (364–367, depending on the year). Returns an error wrapping ErrInvalidYear if out of range.

Example

d, err := bs.NewDate(2083, 6, 6)
 
d2, err := bs.Parse("2083-06-06")
// d == d2
 
birthday := bs.MustParse("2060-06-15") // panics if malformed — use a literal only
 
today, err := bs.TodayBS()
 
fmt.Println(d.String()) // "2083-06-06"
fmt.Println(d.Valid())  // true
 
bs.IsValid(2083, 9, 32)      // false — Poush 2083 only has 30 days
bs.IsSupportedBSYear(2101)   // false — outside MinBSYear..MaxBSYear
 
days, _ := bs.DaysInMonth(2083, 6) // 31
total, _ := bs.DaysInYear(2083)    // 365

MustParse panics on invalid input — only use it with a compile-time-known literal string, never with user input or anything from an external source. Use Parse for that.