Repository navigation
Releases: shivathapaa/nepali_calendar_utils
Release list
v3.1.0
A holiday is one kind of calendar event. The package now names festivals, school programmes, deadlines and birthdays as well, marks whether a day is worked with a flag on the event rather than through its category, and states an institution's whole calendar, its week and its events, as a single policy.
This release also brings the package level with the Kotlin core it is ported from: whole-month reads, a calendar-tagged month, and the time half of the cross-platform wire format.
Every 3.0.0 name keeps working. No source change is required to upgrade.
Install
pip install nepali_calendar_utils==3.1.0Requires Python 3.11 or newer. Tested on 3.11, 3.12 and 3.13 across Linux, macOS and Windows. No runtime dependencies.
Highlights
NepaliCalendarEventreplacesHolidayEntry, addingcloses_offices,idandpayload.NepaliCalendarPolicyholds the week an institution keeps and the events it names, and answersstatus_of,month_status,events_on,events_inandas_selectable_datesfrom both.spanning_daysandspanning_throughexpand a festival or a stretch of leave into one entry per day.- Providers compose:
national + own_list, andprovider.filtered(predicate). get_english_calendarandis_english_date_convertibleread and test a Gregorian date directly.- Whole-month reads convert a month in one pass instead of one call per day.
CalendarSystemandMonthCalendarlet layout code stop caring which calendar produced a month.NepaliTimeFormattercompletes the cross-platform wire format.- Three correctness fixes, and a faster, lighter import.
Events
NepaliCalendarEvent(date, name, kind, closes_offices=None, id=None, payload=None) is one named day. Whether the institution shuts is closes_offices, not the category, so two days can both be called Dashain and only one of them close the school. Left unset it follows the kind: a GOVERNMENT_PUBLIC, RELIGIOUS or REGIONAL entry closes the day and an OBSERVANCE does not.
from nepali_calendar_utils import (
NepaliCalendarEvent, NepaliEventKind, NepaliEventProvider, SimpleDate,
)
class MyEvents(NepaliEventProvider):
_by_year = {
2082: {
NepaliCalendarEvent(SimpleDate(2082, 1, 1), "नयाँ वर्ष",
NepaliEventKind.GOVERNMENT_PUBLIC),
},
}
def events(self, year):
return self._by_year.get(year, set())idandpayloadare carried through untouched, for an app to correlate a day back to its own record.NepaliEventKind.priorityorders the kinds when several land on one day, strongest first.provider.closes_on(date)counts only the entries that close, so a day carrying nothing but a programme stays a working day.first + secondreports everything either side reports, keeping both entries on a day they both name. A day either side calls a closure stays a closure.provider.filtered(predicate)narrows a shared list to what one screen cares about, and narrows its closures with it.
No event data ships with the library, by design: Nepali holiday lists change year to year and every institution keeps its own.
Calendar policy
NepaliCalendarPolicy(weekly_off_days, provider) states an institution once. A number that is not a day of the week is rejected at construction.
from nepali_calendar_utils import NepaliCalendarPolicy, next_working_day
office = NepaliCalendarPolicy(provider=my_events) # closed Saturdays
school = NepaliCalendarPolicy(frozenset({7, 1}), my_events) # closed Saturday and Sunday
school.status_of(SimpleDate(2082, 1, 1)).names # ['नयाँ वर्ष']
school.month_status(2082, 1) # one status per day, index 0 is day 1
school.is_non_working_day(SimpleDate(2082, 1, 1)) # True
next_working_day(SimpleDate(2082, 1, 1), school) # the policy carries its own weekendstatus_of(date)returns aNepaliDayStatus: whether the week makes the day off, what is named on it strongest kind first, plusis_non_working,primary_kind,namesandclosures.month_status(year, month)answers for a whole month in one pass.as_selectable_dates()turns the policy into a picker rule. Marking a day and refusing it stay separate decisions, and an event that leaves the institution open leaves its day selectable.- The working-day helpers take a policy in place of a provider and a weekend. Passing both is refused.
Multi-day spans
An event covers exactly one day, so a span is a list of entries, each carrying the event's name, kind, closes_offices, id and payload unchanged.
event.spanning_days(10)
event.spanning_through(SimpleDate(2082, 6, 26))- A span running out of Chaitra into Baisakh yields entries in both years, so each is reported by the year a provider is asked for.
- Give the event an
idfirst when the days have to be recognized as one thing again. spanning_throughraises rather than rolling a day past the end of its month, which would return a span of the wrong length.
New in the converter
get_english_calendar(year, month, day)returns a fully populated GregorianCustomCalendarread straight from a Gregorian date. It needs no conversion anchor, so it answers for any year, and its derived fields follow the same definitions the Bikram Sambat calendars use.is_english_date_convertible(year, month, day)reports whether an English date has a Bikram Sambat equivalent.EnglishYearRangealone is not a sufficient check: the calendars start mid-year relative to each other, so 1913-01-01 through 1913-04-12 sit inside the year range yet cannot convert.- Whole-month reads:
get_english_calendars_in_month,get_nepali_calendars_in_english_monthandget_english_calendars_in_nepali_month. Each converts the month in one pass, so prefer them over calling a converter in a loop. A Gregorian day before the anchor reads asNone, never as a guess. NepaliCalendarDefaultsgainsminConvertibleEnglishDate,maxConvertibleEnglishDate,GregorianYearRangeandgregorian_year_range_for(...), so a Gregorian-first caller can bound itself without knowing where the conversion table starts.
Calendar systems and months
CalendarSystemnames the two calendars by theerathey carry, withopposite()andfrom_era, which answersNonefor an unrecognised era rather than raising.CustomCalendar.calendar_systemreads it. A calendar carries the system it was read in, so a Gregorian read stays Gregorian.MonthCalendarsays whatNepaliMonthCalendarsays, for a month of either calendar, tagged with its system.get_english_month_calendaranswers one for a Gregorian month,NepaliMonthCalendar.to_month_calendar()reads one from a Bikram Sambat month, andMonthCalendar.to_nepali_month_calendar()narrows one back. The narrowing copies the year and month verbatim, so calling it on a Gregorian month yields aNepaliMonthCalendarholding Gregorian numbers; convert first when that matters.
Times on the wire
NepaliTimeFormatter is the counterpart of NepaliDateFormatter, completing the wire format a Kotlin, Android, Swift or JavaScript client reads without translation.
NepaliTimeFormatter.format(SimpleTime(9, 30, 0, 0)) # "09:30:00"
NepaliTimeFormatter.format(SimpleTime(23, 59, 59, 123456789)) # "23:59:59.123456789"
NepaliTimeFormatter.parse("०९:३०:००") # SimpleTime(9, 30, 0, 0)
NepaliTimeFormatter.parse("24:00:00") # Noneformat writes HH:mm:ss, gaining a nine-digit fractional part only when the nanosecond is non-zero. parse returns None rather than raising. Field widths are not enforced and any Unicode decimal digit is accepted, so Devanagari input reads the same as Latin; whitespace, a sign and a digit separator are all refused.
Fixes
NepaliDateLocale.digit_scriptnow reachesformat_nepali_dateandformat_english_date. Both read the numeral script from the locale's language and ignored an explicit override, soNepaliDateLocale(language=NEPALI, digit_script=LATIN)still rendered Devanagari digits. A locale that leavesdigit_scriptunset is unaffected.get_english_date_nepali_time_from_iso_formatreads the Gregorian date straight from the timestamp instead of routing through Bikram Sambat and back, which had limited it toEnglishYearRangefor a result that never needed the conversion table. Any year now parses.get_nepali_date_time_from_iso_formatstill requires the table.get_nepali_calendarrejects a day below 1. A day of0or a negative day used to build a calendar whose derived fields described nothing.
Performance
- Month geometry is derived on demand instead of every supported month being built at import, so importing the package is faster and holds less memory.
- Unicode pattern formatting compiles its token matcher once rather than on every call.
Migrating from 3.0.0
The former names still resolve, so nothing has to change today. Importing from nepali_calendar_utils.holiday raises a DeprecationWarning naming the replacement.
| 3.0.0 | 3.1.0 |
|---|---|
HolidayEntry |
NepaliCalendarEvent |
HolidayKind |
NepaliEventKind |
NepaliHolidayProvider |
NepaliEventProvider |
NoOpHolidayProvider |
NoOpEventProvider |
provider.holidays(year) |
provider.events(year) |
provider.is_holiday(date) |
provider.closes_on(date) |
excluding_holidays(base, provider) |
excluding_closures(base, provider) |
nepali_calendar_utils.holiday |
nepali_calendar_utils.event |
A provider written against NepaliHolidayProvider keeps working as it is: it stays a real class, its holidays and is_holiday are what the policy and the helpers read, and it keeps the older rule that every en...
v3.0.0
Brings the Python package to feature parity with the :core module of the sibling Kotlin Multiplatform project, Nepali-Date-Picker (3.1.0). This is an additive release - every prior public symbol works unchanged and all new APIs are opt-in. The major version bump aligns the package with the Kotlin 3.x line.
The underlying Bikram Sambat data tables (day counts and reference anchors, years 1970–2100) are byte-for-byte identical to the Kotlin core, so conversions match exactly across both libraries.
Highlights
- Language-independent digit script (
DigitScript) with digit-localization helpers. - A
NepaliDateFormatterfor parsing/formatting short numeric date strings. - A holiday-provider SPI with working-day arithmetic (
WORKDAYsemantics). - Selectable-date predicates and before/after/range factories.
SimpleDateis now orderable (sorted,min,max,</>).- Correctness fixes: live "today", the 1913 anchor guard,
week_of_month,parse. - Dependency-free and portable: no IANA tz database needed; honest
requires-python. - Test suite more than doubled (117 → 257); a published Sphinx API reference.
New features
DigitScript: numeral script (LATIN / DEVANAGARI) decoupled from language, so any Devanagari-digit locale (Nepali, Hindi, Marathi, Maithili, Bhojpuri, Newari) reuses the same rendering. Helpers: default_digit_script, to_latin_digits, latin_digit_or_none, and NepaliDateConverter.localize_digits / .to_latin_digits.
from nepali_calendar_utils import NepaliDateConverter, DigitScript
NepaliDateConverter.localize_digits("2082/02/14", DigitScript.DEVANAGARI) # "२०८२/०२/१४"
NepaliDateConverter.to_latin_digits("२०८२/०२/१४") # "2082/02/14"NepaliDateFormatter: a parse/format primitive for short text-field input, with four DatePatterns (YYYY/MM/DD, YYYY-MM-DD, DD/MM/YYYY, DD-MM-YYYY). Accepts both Latin and Devanagari input; parse returns None on invalid input.
from nepali_calendar_utils import NepaliDateFormatter, DatePattern
NepaliDateFormatter.parse("२०८२/०२/१४", DatePattern.YYYY_SLASH_MM_SLASH_DD) # SimpleDate(2082, 2, 14)Holiday provider SPI: a new nepali_calendar_utils.holiday package: NepaliHolidayProvider, HolidayEntry, HolidayKind, NoOpHolidayProvider, and NepaliWeekend (Saturday-only default). No holiday data ships by design. Working-day arithmetic: working_days_between, next_working_day, add_working_days (Excel WORKDAY semantics), plus excluding_holidays / excluding_weekends wrappers. Also surfaced on the NepaliDateConverter facade.
NepaliSelectableDates: a predicate for enabling/disabling dates, with converter factories before_date_selectable, after_date_selectable, and date_range_selectable.
NepaliDateLocale.digit_script: an optional explicit numeral script with a resolved_digit_script property (None follows the language).
SimpleDate is ordered: supports <, >, sorted(), min(), and max() (chronological by year, then month, then day).
Correctness fixes
today_nepali_calendar/today_english_simple_date/today_english_calendarnow read the wall clock on each access. They were captured once when the model was constructed, so "today" never rolled over at midnight for a long-lived instance.- English dates before the earliest convertible anchor (1913-04-13) now raise
ValueError. They previously passed the year-only range check and silently returned Nepali 1970-01-01. - Out-of-table years raise a clear
ValueErrorinstead of leaking aKeyError, andget_total_days_in_nepali_monthvalidates the month range. week_of_monthuses the clamped day of month when a date is adjusted.NepaliCalendarModel.parse("YYYYMMDD")now returns a realCustomCalendar(it previously always returned an error stub because it passed adictwhere aSimpleDatewas expected). Logically invalid in-range dates return the documented stub withera=2and-1sentinel fields.
Dependencies and portability
requires-pythonis now>=3.11. The ISO 8601 conversion utilities rely ondatetime.fromisoformatfully parsing offsets-with-seconds and theZsuffix, which landed in Python 3.11. (The previous>=3.7floor was inaccurate - the package already usedzoneinfo, which is 3.9+.)- No third-party runtime dependencies. Time-zone handling switched from
ZoneInfo("Asia/Kathmandu")to a fixed+05:45offset (datetime.timezone). Nepal Standard Time has no DST or transitions in the supported range, so results are identical - but it no longer needs the IANA tz database, so it works on Windows and minimal containers whereZoneInfowould raiseZoneInfoNotFoundError. This mirrors the Kotlin core'sFixedOffsetTimeZone.
Performance
- Month-details lookups are memoized and the day offset is computed in O(1) via a cumulative-days prefix table, instead of re-summing every year on each call. Behavior-preserving.
Tests and docs
- Test suite expanded from 117 to 257 cases, mirroring the Kotlin
:coresuite and adding exhaustive invariants: every-year conversion round-trips, a cross-engine agreement check (offset table vs day-walk), a week-of-year suite with an independent Gregorian weekday oracle (stdlibdate), leap-year and month-length range checks, date arithmetic, comparison, parse/locale, and holiday working-day edge cases. - A Sphinx API reference is published at shivathapaa.github.io/nepali_calendar_utils, with source links back to GitHub.
Install
pip install nepali_calendar_utilsfrom nepali_calendar_utils import NepaliDateConverter
converter = NepaliDateConverter()
nepali = converter.convert_english_to_nepali(2025, 6, 15) # -> CustomCalendar (BS 2082-03-01)Compatibility / upgrade notes
- No public API was removed or renamed; existing 2.x code keeps working.
- The only upgrade action is the interpreter floor: the package now requires Python 3.11+. Installs on 3.7–3.10 (where the ISO utilities never fully worked anyway) are no longer supported.
v2.0.1
Overview
- Bug fixes (Fix Nepali dates of year 2082 according to Panchanga)
v2.0.0
Overview
- Added various utilities to format date and time using unicode pattern.
- Added iso to date time formatting. It converts ISO UTC format to CustomCalendar and SimpleTime. See examples
Breaking Changes
- Bumped minimum python version from 3.6 to 3.7 to support iso formatting.
v1.1.0
Overview
The version 1.1.0 has some changes in class naming. Now, some classes are name by their use cases.
New features
- Add today's SimpleDate property for Nepali date.
- Add today's CustomCalendar date property for both English date.
Bug Fixes/Improvements
- Rename classes for functional identification.
- Add documentation for core data classes.
v1.0.0
First stable release of nepali_calendar_utils package
- The packge is packed with various utilities for date conversions, and date and time representation.