bs

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 22, 2026 License: MIT Imports: 6 Imported by: 0

README

go-bs

test Go Reference Go Report Card

A small, dependency-free Go library for converting dates between Gregorian (AD) and Bikram Sambat (BS), the calendar used in Nepal.

✓ 122 BS years covered (1979-2100)
✓ 1,464 BS months represented
✓ Every supported BS date tested
✓ AD → BS → AD round-trip tested
✓ BS → AD → BS round-trip tested
✓ Zero runtime dependencies
✓ Zero network requests

Features

  • ADToBS / BSToAD conversion
  • Supports BS years 1979–2100 inclusive
  • Strict date validation against real BS month lengths (not just shape)
  • Zero runtime dependencies
  • Timezone-safe: conversion is based on calendar date, not time-of-day
  • Table-driven calendar data, verified against a live source where possible (see docs/calendar-data.md)
  • Exhaustively tested: every one of the 44,562 supported days round-trips in both directions

Installation

go get github.com/suprimkhatri77/go-bs

Usage

Convert AD to BS:

package main

import (
	"fmt"
	"log"
	"time"

	bs "github.com/suprimkhatri77/go-bs"
)

func main() {
	ad := time.Date(2026, time.September, 22, 0, 0, 0, 0, time.UTC)

	d, err := bs.ADToBS(ad)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(d) // 2083-06-06
}

Convert BS to AD:

d, err := bs.NewDate(2083, 6, 6)
if err != nil {
	log.Fatal(err)
}

ad, err := bs.BSToAD(d)
if err != nil {
	log.Fatal(err)
}

fmt.Println(ad.Format("2006-01-02")) // 2026-09-22

Validate a BS date without converting it:

if !bs.IsValid(2080, 2, 32) {
	fmt.Println("not a real Bikram Sambat date")
}

Date arithmetic and comparison:

d, _ := bs.NewDate(2083, 6, 15)

next, _ := d.AddDays(10)
fmt.Println(next) // 2083-06-25

fmt.Println(d.Before(next)) // true

weekday, _ := d.DayOfWeek()
fmt.Println(weekday) // Thursday

name, _ := d.MonthName()
fmt.Println(name) // Ashwin

diff, _ := bs.DaysBetween(d, next)
fmt.Println(diff) // 10

start, _ := d.StartOfMonth()
end, _ := d.EndOfMonth()
fmt.Println(start, end) // 2083-06-01 2083-06-31

Parsing:

d, err := bs.Parse("2083-06-15")
if err != nil {
	log.Fatal(err)
}

Formatting and Nepali digits:

d, _ := bs.NewDate(2083, 6, 6)

s, _ := d.Format("dddd, MMMM D, YYYY")
fmt.Println(s) // Tuesday, Ashwin 6, 2083

fmt.Println(bs.ToNepaliDigits(d.String())) // २०८३-०६-०६

name, _ := d.MonthNameNepali()
fmt.Println(name) // असोज

API overview

const (
	MinBSYear = 1979
	MaxBSYear = 2100
)

type Date struct {
	Year, Month, Day int
}

func NewDate(year, month, day int) (Date, error)
func Parse(s string) (Date, error) // "YYYY-MM-DD"

func (d Date) Valid() bool
func (d Date) String() string // "YYYY-MM-DD"
func (d Date) MonthName() (string, error)
func (d Date) MonthNameNepali() (string, error)
func (d Date) Format(layout string) (string, error) // e.g. "YYYY-MM-DD"

func (d Date) Before(other Date) bool
func (d Date) After(other Date) bool
func (d Date) Equal(other Date) bool
func Compare(a, b Date) int // -1, 0, 1

func (d Date) AddDays(n int) (Date, error)
func (d Date) SubDays(n int) (Date, error)
func (d Date) DayOfWeek() (time.Weekday, error)
func DaysBetween(a, b Date) (int, error)

func (d Date) StartOfMonth() (Date, error)
func (d Date) EndOfMonth() (Date, error)
func (d Date) StartOfYear() (Date, error)
func (d Date) EndOfYear() (Date, error)
func (d Date) DayOfYear() (int, error)

func ADToBS(t time.Time) (Date, error)
func BSToAD(d Date) (time.Time, error)

func IsValid(year, month, day int) bool
func IsSupportedBSYear(year int) bool
func DaysInMonth(year, month int) (int, error)
func DaysInYear(year int) (int, error)
func MonthName(month int) (string, error)
func MonthNameNepali(month int) (string, error)
func ToNepaliDigits(s string) string
func FromNepaliDigits(s string) string

var (
	ErrInvalidYear   error
	ErrInvalidMonth  error
	ErrInvalidDay    error
	ErrInvalidFormat error
	ErrOutOfRange    error
)

All errors support errors.Is, e.g. errors.Is(err, bs.ErrInvalidDay).

Date.Month is 1-based: 1 is Baisakh, 12 is Chaitra.

Calendar-grid helpers (month view, weeks-in-month, first-weekday-of-month) are still on the way in a later minor release — see CHANGELOG.md.

Supported range

  • Bikram Sambat: 1979–2100 (MinBSYear–MaxBSYear), inclusive.
  • The corresponding Gregorian range is 1922-04-13 to 2044-04-13, derived from the verified calendar data rather than assumed.

Most BS calendar libraries stop around this range because that's roughly the edge of what Nepal's calendar authorities have officially published in advance. MaxBSYear is expected to move out further (realistically not before around BS 2095) once more official data exists.

Accuracy

BS month lengths are not computed from a formula — they follow the officially published Nepali calendar and are stored as a static, table-driven dataset. That dataset was cross-checked against multiple existing open-source implementations and, where possible, against a live calendar source, rather than copied from a single upstream project as-is. See docs/calendar-data.md for the sources, the verification method, and a documented known limitation (BS years 1979–1999 could not be checked against a live source and rely on two library sources agreeing with each other).

Timezone behavior

ADToBS looks only at the Year, Month and Day of the given time.Time — its time-of-day and location are ignored, and the same Gregorian calendar date always converts to the same BS date, regardless of which timezone the time.Time is expressed in.

Testing

go test ./...
go test -race ./...
go vet ./...

TestExhaustiveBSRoundTrip and TestExhaustiveADRoundTrip convert every one of the 44,562 supported days in both directions and confirm the round trip is exact, rather than relying on a sample.

Contributing

See CONTRIBUTING.md.

License

MIT — see LICENSE.

Documentation

Overview

Code generated by tools/calendar-generator. DO NOT EDIT.

Regenerate with: go run ./tools/calendar-generator -out data.go See docs/calendar-data.md for sources and verification method.

Package bs converts calendar dates between the Gregorian (AD) calendar and the Bikram Sambat (BS) calendar used in Nepal, for BS years MinBSYear through MaxBSYear inclusive.

BS month lengths are not derived from a formula: they follow Nepal's officially published calendar and are stored as a verified, table-driven dataset (see docs/calendar-data.md for sources). The package has no runtime dependencies.

Conversion operates on calendar dates (year, month, day), not timestamps. ADToBS considers only the Year, Month and Day components of the given time.Time and ignores time-of-day and location.

Index

Constants

View Source
const (
	MinBSYear = 1979
	MaxBSYear = 2100
)

MinBSYear and MaxBSYear are the inclusive bounds of the BS years supported by this package.

Variables

View Source
var (
	// ErrInvalidYear is returned when a BS year is outside MinBSYear..MaxBSYear.
	ErrInvalidYear = errors.New("bs: invalid year")

	// ErrInvalidMonth is returned when a BS month is not in the range 1..12.
	ErrInvalidMonth = errors.New("bs: invalid month")

	// ErrInvalidDay is returned when a BS day is not a valid day of the given
	// month (either out of the generic 1..32 bound, or greater than the
	// actual number of days in that month/year).
	ErrInvalidDay = errors.New("bs: invalid day")

	// ErrOutOfRange is returned when an AD date falls outside the Gregorian
	// range corresponding to MinBSYear..MaxBSYear.
	ErrOutOfRange = errors.New("bs: date outside supported range")

	// ErrInvalidFormat is returned when a string passed to Parse is not
	// shaped like "YYYY-MM-DD".
	ErrInvalidFormat = errors.New("bs: invalid date format")
)

Sentinel errors returned by this package. Use errors.Is to check for them; wrapped errors include additional context via fmt.Errorf's %w verb.

Functions

func BSToAD

func BSToAD(d Date) (time.Time, error)

BSToAD converts a Bikram Sambat date to the corresponding Gregorian calendar date, returned as a time.Time at UTC midnight. It returns an error wrapping ErrInvalidYear, ErrInvalidMonth or ErrInvalidDay if d is not a real, supported Bikram Sambat date.

func Compare added in v0.2.0

func Compare(a, b Date) int

Compare compares two dates and returns -1 if a is before b, 0 if they're equal, and 1 if a is after b. It compares fields directly and does not require either date to be valid.

func DaysBetween added in v0.2.0

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

DaysBetween returns the number of calendar days from a to b: positive if b is after a, negative if b is before a, zero if they're equal. It returns an error wrapping ErrInvalidYear, ErrInvalidMonth or ErrInvalidDay if either date is invalid.

func DaysInMonth

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

DaysInMonth returns the number of days in the given Bikram Sambat month. It returns ErrInvalidYear or ErrInvalidMonth if year or month is out of range.

func DaysInYear

func DaysInYear(year int) (int, error)

DaysInYear returns the total number of days in the given Bikram Sambat year. It returns ErrInvalidYear if year is out of range.

func FromNepaliDigits added in v0.3.0

func FromNepaliDigits(s string) string

FromNepaliDigits replaces every Devanagari digit (०-९) in s with its ASCII equivalent (e.g. "२०८३" becomes "2083"). Other characters, including existing ASCII digits, are copied through unchanged.

func IsSupportedBSYear

func IsSupportedBSYear(year int) bool

IsSupportedBSYear reports whether year is within MinBSYear..MaxBSYear.

func IsValid

func IsValid(year, month, day int) bool

IsValid reports whether year, month and day form a real Bikram Sambat calendar date within the supported range (MinBSYear..MaxBSYear).

func MonthName

func MonthName(month int) (string, error)

MonthName returns the English name of the given 1-based Bikram Sambat month (1 is Baisakh, 12 is Chaitra).

func MonthNameNepali added in v0.3.0

func MonthNameNepali(month int) (string, error)

MonthNameNepali returns the Devanagari name of the given 1-based Bikram Sambat month (1 is Baisakh, 12 is Chaitra).

func ToNepaliDigits added in v0.3.0

func ToNepaliDigits(s string) string

ToNepaliDigits replaces every ASCII digit (0-9) in s with its Devanagari equivalent (e.g. "2083" becomes "२०८३"). Non-digit characters, including punctuation and existing Devanagari digits, are copied through unchanged.

Types

type Date

type Date struct {
	Year  int
	Month int
	Day   int
}

Date represents a Bikram Sambat calendar date. Month is 1-based, where 1 is Baisakh and 12 is Chaitra. A Date is not guaranteed to be valid unless it was constructed with NewDate or returned by this package.

func ADToBS

func ADToBS(t time.Time) (Date, error)

ADToBS converts a Gregorian calendar date to Bikram Sambat. Only the Year, Month and Day components of t are used; time-of-day and location are ignored. It returns an error wrapping ErrOutOfRange if t falls outside the Gregorian range corresponding to MinBSYear..MaxBSYear.

func NewDate

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

NewDate constructs a Date and validates it. It returns an error wrapping ErrInvalidYear, ErrInvalidMonth or ErrInvalidDay if the given components do not form a real Bikram Sambat calendar date in the supported range.

func Parse

func Parse(s string) (Date, error)

Parse parses a Bikram Sambat date in "YYYY-MM-DD" format (the same format Date.String produces). It returns an error wrapping ErrInvalidFormat if s is not shaped like a date, or ErrInvalidYear, ErrInvalidMonth or ErrInvalidDay if it's shaped correctly but not a real supported date.

func (Date) AddDays

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

AddDays returns the date n calendar days after d (or before, if n is negative). It returns an error wrapping ErrInvalidYear, ErrInvalidMonth or ErrInvalidDay if d is not itself a valid date, or ErrOutOfRange if the result falls outside the supported range.

func (Date) After

func (d Date) After(other Date) bool

After reports whether d is chronologically after other. It compares fields directly and does not require either date to be valid.

func (Date) Before

func (d Date) Before(other Date) bool

Before reports whether d is chronologically before other. It compares fields directly and does not require either date to be valid.

func (Date) DayOfWeek

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

DayOfWeek returns the day of the week d falls on, derived through its equivalent Gregorian date. It returns an error wrapping ErrInvalidYear, ErrInvalidMonth or ErrInvalidDay if d is not a valid date.

func (Date) DayOfYear added in v0.2.0

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

DayOfYear returns d's 1-based ordinal day within its year (1 for Baisakh 1, up to 365 or 366 for the last day of Chaitra). It returns an error wrapping ErrInvalidYear, ErrInvalidMonth or ErrInvalidDay if d is not a valid date.

func (Date) EndOfMonth added in v0.2.0

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

EndOfMonth returns the last day of d's month. It returns an error wrapping ErrInvalidYear or ErrInvalidMonth if d's year or month is invalid.

func (Date) EndOfYear added in v0.2.0

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

EndOfYear returns the last day of Chaitra of d's year. It returns an error wrapping ErrInvalidYear if d's year is invalid.

func (Date) Equal

func (d Date) Equal(other Date) bool

Equal reports whether d and other represent the same calendar date.

func (Date) Format added in v0.3.0

func (d Date) Format(layout string) (string, error)

Format renders d according to layout, replacing recognized tokens:

YYYY  4-digit year, e.g. "2083"
YY    2-digit year, e.g. "83"
MMMM  full month name, e.g. "Ashwin"
MM    2-digit month, zero-padded
M     month, no leading zero
DD    2-digit day, zero-padded
D     day, no leading zero
dddd  full weekday name, e.g. "Thursday"
ddd   3-letter weekday abbreviation, e.g. "Thu"

Any other character in layout (including punctuation and spaces) is copied through unchanged. It returns an error wrapping ErrInvalidYear, ErrInvalidMonth or ErrInvalidDay if d is not a valid date.

func (Date) MonthName

func (d Date) MonthName() (string, error)

MonthName returns the English name of d's month (e.g. "Baisakh").

func (Date) MonthNameNepali added in v0.3.0

func (d Date) MonthNameNepali() (string, error)

MonthNameNepali returns the Devanagari name of d's month.

func (Date) StartOfMonth added in v0.2.0

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

StartOfMonth returns the first day (day 1) of d's month. It returns an error wrapping ErrInvalidYear or ErrInvalidMonth if d's year or month is invalid.

func (Date) StartOfYear added in v0.2.0

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

StartOfYear returns Baisakh 1 of d's year. It returns an error wrapping ErrInvalidYear if d's year is invalid.

func (Date) String

func (d Date) String() string

String returns d formatted as "YYYY-MM-DD".

func (Date) SubDays added in v0.2.0

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

SubDays returns the date n calendar days before d (or after, if n is negative). It returns the same errors as AddDays.

func (Date) Valid

func (d Date) Valid() bool

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

Directories

Path Synopsis
tools
calendar-generator command
Command calendar-generator produces the ../../data.go calendar dataset for package bs (github.com/suprimkhatri77/go-bs).
Command calendar-generator produces the ../../data.go calendar dataset for package bs (github.com/suprimkhatri77/go-bs).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL