Events, day status and working days

What a calendar has on a day, when an institution is closed, and the arithmetic that counts the days it opens. No event data ships with the library by design; a consumer plugs in a NepaliEventProvider.

Events

A named day in the Bikram Sambat calendar, and the spans built from one.

class NepaliEventKind(value)[source]

Bases: Enum

What kind of thing an event is, which is what a calendar colors by.

Deliberately narrow, because adding a case is a breaking change. An app with its own taxonomy picks the closest kind and keeps its own category in NepaliCalendarEvent.payload; whether the day is worked is NepaliCalendarEvent.closes_offices’s job either way.

GOVERNMENT_PUBLIC = 'government_public'

Bank / government office is closed. Sarkari bida.

RELIGIOUS = 'religious'

Dashain, Tihar, Holi, Id, Christmas, and the like.

Type:

Religious or cultural

REGIONAL = 'regional'

Province- or district-level, not nationally observed.

OBSERVANCE = 'observance'

World Health Day, a school programme, a meeting.

Type:

Recognized but ordinarily worked

property priority: int

How strongly a kind describes a day when several land on it, lowest first.

A day the offices close is called that before anything else it also happens to be, and an observance yields to everything. Sorting and color resolution both read this, so the two never disagree.

property closes_offices_by_default: bool

Whether an event of this kind usually shuts the institution.

This is what NepaliCalendarEvent.closes_offices falls back to. Only a starting point: the event itself has the final say, because the same kind closes one place and not another.

class NepaliCalendarEvent(date, name, kind, closes_offices=None, id=None, payload=None)[source]

Bases: object

Something that happens on one Bikram Sambat day.

A public holiday, a festival, a school programme, a deadline, a birthday. A holiday is an event like any other; what separates them is closes_offices, not the name: two days can both be called Dashain and only one of them close the school. That is why the flag lives here rather than on kind, where a taxonomy would have to decide for every institution at once.

Parameters:
date

Bikram Sambat date the event falls on. One day per entry, so a ten-day festival is ten entries, which spanning_days() and spanning_through() build from one.

Type:

SimpleDate

name

Display name, for example “Dashain - Vijaya Dashami” or “Sprint review”.

Type:

str

kind

Category, which is what a calendar colors by.

Type:

NepaliEventKind

closes_offices

Whether the institution is shut for this. Leave it unset (None) to take what kind usually means, so a GOVERNMENT_PUBLIC entry closes the day and an OBSERVANCE does not; the attribute is a plain bool once constructed. Either default can be overridden: a regional holiday closes one district and not the next, and a school programme closes nothing at all.

Type:

bool

id

An identifier the app can correlate back to its own record, carried through untouched. The library never reads it.

Type:

str

payload

Anything else the app wants back when the day is tapped, as an opaque string: JSON, a URL, an identifier list. The library never parses it.

Type:

str

spanning_days(days)[source]

This event on each of days consecutive days, starting on its own date.

A NepaliCalendarEvent covers a single day, so a span is a list of entries rather than a range. Every entry keeps this event’s name, kind, closes_offices, id and payload, and differs only in its date. A span running out of Chaitra into Baisakh yields entries in both years, so each one is reported by the year a provider is asked for.

Give the event an id first when the days have to be recognized as one thing again. Nepal’s published holiday lists name each day separately, so prefer real per-day names where they exist and keep this for a span an app owns.

The event’s own date is taken as stated, the way every other call here treats it, so only the length of the span is checked.

Parameters:

days (int) – How many days the span covers, counting the first. 1 returns this event alone.

Returns:

One entry per day, in date order.

Return type:

list[NepaliCalendarEvent]

Raises:

ValueError – if days is below 1, or if the span runs past NepaliCalendarDefaults.NepaliYearRange.

spanning_through(end)[source]

This event on each day from its own date through end, both included.

That is the shape published holiday lists and leave requests usually state a span in. Expands the same way spanning_days() does: an entry per day, carrying everything but the date unchanged. An end equal to this event’s date returns the event alone.

Parameters:

end (SimpleDate) – Last day of the span, included.

Returns:

One entry per day, in date order.

Return type:

list[NepaliCalendarEvent]

Raises:

ValueError – if end falls before this event’s date, if end is not a day its month has, or if the span runs past NepaliCalendarDefaults.NepaliYearRange. A day the month does not reach is refused rather than rolled into the next one, since rolling would silently return a span of the wrong length.

Provider SPI

Service-provider interface for supplying a calendar’s events.

This library ships no data by design: Nepali holiday lists change year to year and every institution keeps its own, so nothing here could be right for long. Implement NepaliEventProvider with your own source (a static map, a CMS, an HR API) and hand it to NepaliCalendarPolicy.

class NepaliEventProvider[source]

Bases: object

Interface for supplying the events a calendar names: holidays, festivals, programmes, anything an app wants written on a day.

Implementations must:
  • return a stable set for a given year (calling twice yields the same contents),

  • not raise for years outside the supported Nepali year range, returning an empty set instead.

Example:

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())
events(year)[source]

Everything named in year (BS). An empty set is a valid answer.

Parameters:

year (int)

Return type:

Set[NepaliCalendarEvent]

closes_on(date)[source]

Whether the institution is shut on date, which is what the working-day helpers count by.

The default answers from events(), counting only the entries whose closes_offices is set, so a day carrying nothing but an observance is still a working day. Override with a memoized implementation if you call this in tight loops.

Parameters:

date (SimpleDate)

Return type:

bool

filtered(predicate)[source]

A view of this provider carrying only the entries predicate accepts.

For narrowing a shared list to what one screen cares about:

provider.filtered(lambda e: e.kind is NepaliEventKind.GOVERNMENT_PUBLIC)

predicate runs on every entry of a year each time that year is asked for, so keep it a test rather than a lookup.

A day stays closed while a closure behind it survives the filter, so narrowing a list narrows the closures with it. A day the source shuts without naming a closing event stays shut, since there is nothing to narrow. filtered(lambda e: True) therefore answers exactly what the source does.

Parameters:

predicate (Callable[[NepaliCalendarEvent], bool])

Return type:

NepaliEventProvider

NoOpEventProvider = <nepali_calendar_utils.event.nepali_event_provider._NoOpEventProvider object>

No-op provider singleton. Use when you want the event-aware APIs but have not wired a data source yet.

class NepaliWeekend[source]

Bases: object

Day-of-week conventions.

The library uses a 1-based-Sunday convention everywhere: Sunday = 1, …, Saturday = 7.

Default = frozenset({7})

Saturday only. Nepal observes a single-day weekend, so working-day arithmetic that uses this matches what a Nepali office counts as “5 working days from today”. Pass frozenset({6, 7}) for a Friday-and- Saturday weekend.

Type:

Default weekend in Nepal

Day status

What one day is under a calendar policy.

class NepaliDayStatus(is_weekly_off, events=())[source]

Bases: object

Whether the week makes a day off, and what is named on it.

The two are kept apart because they answer different questions. A weekly off day repeats fifty-two times a year and carries no name worth drawing; an event is a fact about that date alone. A day can be both, and a day that is both is still one day off, so is_non_working is a single answer rather than a count.

Parameters:
is_weekly_off

Whether the day falls on one of the policy’s weekly off days.

Type:

bool

events

Everything named on the day, strongest NepaliEventKind first. Empty when the day carries nothing, which is the usual case even for a weekly off day. Any sequence may be passed in; it is kept as a tuple.

Type:

tuple[NepaliCalendarEvent, …]

property is_non_working: bool

True when the institution is shut, whether the week says so or an event does.

Only an event that closes offices counts, so a working day carrying a programme or a meeting stays a working day, and the working-day helpers agree.

property primary_kind: NepaliEventKind | None

The kind that describes the day best, or None when nothing is named on it.

A day that is only a weekly off day has no kind, which is what separates “it is Saturday” from “it is Dashain, which happens to be a Saturday”.

property names: List[str]

The names of the day’s events, strongest first, for whatever the app draws or announces.

property closures: List[NepaliCalendarEvent]

The events that actually shut the institution, the subset the arithmetic counts.

Calendar policy

When one institution is closed, and what its calendar has on it.

class NepaliCalendarPolicy(weekly_off_days=frozenset({7}), provider=<nepali_calendar_utils.event.nepali_event_provider._NoOpEventProvider object>)[source]

Bases: object

The days of the week an institution never opens, plus the events it keeps.

The two travel together because they answer the same question, and an app that states them once gets both a calendar that paints the right days and arithmetic that counts the right ones. An office in Nepal is NepaliCalendarPolicy(provider=my_events), closed Saturdays. A school closed Saturday and Sunday is NepaliCalendarPolicy(frozenset({7, 1}), my_events). Two institutions with different lists are two policies over two providers, and a national list shared by both is national + own_list.

Nothing here blocks a date on its own. as_selectable_dates() turns the policy into a picker rule when that is what you want, so marking a day and refusing it stay separate decisions.

weekly_off_days

Days of the week the institution is closed, 1 for Sunday through 7 for Saturday. Defaults to NepaliWeekend.Default, the single Saturday weekend Nepal observes. An empty set is an institution that never closes for the week alone. Any iterable may be passed in; it is kept as a frozenset.

Type:

frozenset[int]

provider

The events the institution keeps. Defaults to NoOpEventProvider, which is the right value while an app has only its weekly rule wired.

Type:

NepaliEventProvider

Raises:

ValueError – if weekly_off_days holds a number that is not a day of the week. Other calendars number Sunday 0, and a set written to that convention would quietly close nothing, so it is rejected at construction rather than at the end of a month of wrong answers.

Parameters:
is_weekly_off(day_of_week)[source]

Whether day_of_week (1 for Sunday through 7 for Saturday) is one of the institution’s weekly off days.

Takes the day of the week rather than a date because a calendar grid already knows it, and resolving it again would cost a conversion per cell.

Parameters:

day_of_week (int)

Return type:

bool

events_on(date)[source]

The events on date, strongest kind first, or an empty list when the day carries none. A weekly off day with nothing named on it answers empty.

Parameters:

date (SimpleDate)

Return type:

List[NepaliCalendarEvent]

events_in(year, month)[source]

Every event in the Bikram Sambat month month of year, in date order and, within a date, strongest kind first.

Empty for a month with none, and for a year the provider does not cover.

Parameters:
Return type:

List[NepaliCalendarEvent]

status_of(date)[source]

What date is under this policy: whether the week makes it a day off, and what is named on it.

Resolves the day of the week through the conversion table, so prefer month_status() when laying out a whole month.

Parameters:

date (SimpleDate)

Return type:

NepaliDayStatus

month_status(year, month)[source]

Every day of one Bikram Sambat month, in day order: index 0 is day 1.

The month’s first weekday is resolved once and the week walked forward from it, so a grid or an agenda costs one conversion rather than one per day.

Parameters:
Return type:

List[NepaliDayStatus]

is_non_working_day(date)[source]

Whether the institution is closed on date, for either reason.

A day that is both a weekly off day and a holiday is closed once, and a day carrying only an observance is still worked. The provider is asked directly rather than through status_of(), so one that answers closes_on without listing the event behind it is still heard, and this agrees with what the working-day helpers count.

Parameters:

date (SimpleDate)

Return type:

bool

as_selectable_dates()[source]

The policy as a picker rule: every closed day becomes unselectable.

Opt in to this when a screen should refuse the days it marks, and leave it out when the days are only to be seen. An event that does not close the institution, a school programme or a meeting, leaves its day selectable, since the day is still worked.

Return type:

NepaliSelectableDates

Working-day helpers

Selectable-date wrappers and working-day arithmetic over a calendar’s events.

The arithmetic helpers are also surfaced as static methods on NepaliDateConverter. Each of them takes either a provider and a weekend set, or a NepaliCalendarPolicy, which already states both.

MAX_FORWARD_SCAN_DAYS = 366

A year of scanning, the point at which a forward search gives up rather than looping on a policy that closes every day.

MAX_WORKING_DAY_SCAN_DAYS = 732

About two years of slack, the bound on a working-day count in either direction.

excluding_closures(base, provider)[source]

Wrap base so it additionally rejects any date provider says the institution is shut for.

An event that does not close, a programme or a meeting, leaves its day selectable: the day is still worked, and refusing it would surprise anyone booking on it.

Year-level rejection still defers to the wrapped predicate, since event data is per-date.

Parameters:
Return type:

NepaliSelectableDates

excluding_weekends(base, weekend=frozenset({7}))[source]

Wrap base so it additionally rejects weekend days.

weekend is a set of 1-based-Sunday day-of-week numbers (Sunday = 1, …, Saturday = 7). Defaults to Saturday only. Pass frozenset({6, 7}) for a Friday-and-Saturday weekend.

Parameters:

base (NepaliSelectableDates)

Return type:

NepaliSelectableDates

working_days_between(start, end, provider, weekend=None)[source]

Number of working days in the half-open range [start, end).

Skips both the weekend days and the dates the institution is shut for. end is exclusive, matching get_nepali_days_in_between; to make it inclusive, add 1 when end itself is a working day. A day that is both a weekend and a closure is skipped once. Returns 0 when start == end.

Parameters:
  • start (SimpleDate) – First day of the range, included.

  • end (SimpleDate) – Day the range stops before.

  • provider – A NepaliEventProvider, or a NepaliCalendarPolicy that carries its own weekly off days.

  • weekend – Day-of-week numbers treated as the weekend. Defaults to Saturday only, and must be left unset when a policy is passed.

Raises:

ValueError – if start > end, or if both a policy and a weekend are given.

Return type:

int

next_working_day(from_date, provider, weekend=None)[source]

First working day at or after from_date.

If from_date is itself a working day, returns it unchanged. Otherwise scans forward day by day, bounded: gives up after a year rather than looping forever on a policy that closes every day.

Parameters:
  • from_date (SimpleDate) – Day to start looking from, included.

  • provider – A NepaliEventProvider, or a NepaliCalendarPolicy that carries its own weekly off days.

  • weekend – Day-of-week numbers treated as the weekend. Defaults to Saturday only, and must be left unset when a policy is passed.

Raises:
  • ValueError – if both a policy and a weekend are given.

  • RuntimeError – if no working day is found within 366 days.

Return type:

SimpleDate

add_working_days(from_date, days, provider, weekend=None)[source]

Date that is days working days from from_date (Excel WORKDAY semantics).

  • days == 0 returns from_date unchanged.

  • days > 0 returns the days-th working day strictly after from_date.

  • days < 0 returns the abs(days)-th working day strictly before it.

Note this means add_working_days(from_date, 0) is not the same as next_working_day() - use the latter for adjustment.

Parameters:
  • from_date (SimpleDate) – Day the count starts from, itself never the answer unless days is 0.

  • days (int) – How many working days to move, forward when positive.

  • provider – A NepaliEventProvider, or a NepaliCalendarPolicy that carries its own weekly off days.

  • weekend – Day-of-week numbers treated as the weekend. Defaults to Saturday only, and must be left unset when a policy is passed.

Raises:
  • ValueError – if both a policy and a weekend are given.

  • RuntimeError – if more than about two years of scanning fails, which guards against a policy that closes every day.

Return type:

SimpleDate

Former holiday names

Code written against the 3.0.0 holiday package keeps working, implementations of NepaliHolidayProvider included. Importing from here raises a DeprecationWarning.

The holiday provider SPI under its former names.

A holiday turned out to be one kind of event rather than the whole subject: the package also names festivals, school programmes, deadlines and birthdays, and whether a day is worked is now NepaliCalendarEvent.closes_offices rather than something the taxonomy decides. Everything here still works and resolves to the types in nepali_calendar_utils.event.

HolidayKind[source]

Former name of NepaliEventKind. The members and their values are unchanged.

HolidayEntry[source]

Former name of NepaliCalendarEvent, which takes the same three positional arguments and adds closes_offices, id and payload.

class NepaliHolidayProvider[source]

Bases: NepaliEventProvider

Former name of NepaliEventProvider, kept as a class of its own so an implementation written against it keeps working.

Subclasses implement holidays() and optionally is_holiday(), and the policy, the picker wrappers and the working-day helpers reach them through events and closes_on. A subclass keeps the older, blunter rule that every entry closes the day: closes_offices is not consulted, so an observance returned from holidays() still blocks its date and still stops a working-day count. Implement NepaliEventProvider directly for the finer answer.

holidays(year)[source]

All holidays for year (BS). An empty set is a valid answer.

Parameters:

year (int)

Return type:

Set[NepaliCalendarEvent]

is_holiday(date)[source]

True if any entry from holidays() for date.year falls on date.

Default implementation re-queries holidays() on every call. Override with a memoized implementation if you call this in tight loops.

Parameters:

date (SimpleDate)

Return type:

bool

events(year)[source]

Everything named in year (BS). An empty set is a valid answer.

Parameters:

year (int)

Return type:

Set[NepaliCalendarEvent]

closes_on(date)[source]

Whether the institution is shut on date, which is what the working-day helpers count by.

The default answers from events(), counting only the entries whose closes_offices is set, so a day carrying nothing but an observance is still a working day. Override with a memoized implementation if you call this in tight loops.

Parameters:

date (SimpleDate)

Return type:

bool

NoOpHolidayProvider = <nepali_calendar_utils.holiday.holiday_provider._NoOpHolidayProvider object>

Former name of NoOpEventProvider, answering to holidays and is_holiday as well.

class NepaliWeekend[source]

Bases: object

Day-of-week conventions.

The library uses a 1-based-Sunday convention everywhere: Sunday = 1, …, Saturday = 7.

Default = frozenset({7})

Saturday only. Nepal observes a single-day weekend, so working-day arithmetic that uses this matches what a Nepali office counts as “5 working days from today”. Pass frozenset({6, 7}) for a Friday-and- Saturday weekend.

Type:

Default weekend in Nepal

The picker wrappers and working-day arithmetic under their former names.

Each name here resolves to its counterpart in nepali_calendar_utils.event.event_helpers, which takes a calendar policy as well as a provider.

excluding_holidays(base, provider)[source]

Former name of excluding_closures(), which is what it does: an event that leaves the institution open no longer blocks its day.

A provider written against :class:` ~nepali_calendar_utils.holiday.holiday_provider.NepaliHolidayProvider` blocks every entry it lists, as it always did.

Parameters:
Return type:

NepaliSelectableDates

excluding_weekends(base, weekend=frozenset({7}))[source]

Wrap base so it additionally rejects weekend days.

weekend is a set of 1-based-Sunday day-of-week numbers (Sunday = 1, …, Saturday = 7). Defaults to Saturday only. Pass frozenset({6, 7}) for a Friday-and-Saturday weekend.

Parameters:

base (NepaliSelectableDates)

Return type:

NepaliSelectableDates

working_days_between(start, end, provider, weekend=None)[source]

Number of working days in the half-open range [start, end).

Skips both the weekend days and the dates the institution is shut for. end is exclusive, matching get_nepali_days_in_between; to make it inclusive, add 1 when end itself is a working day. A day that is both a weekend and a closure is skipped once. Returns 0 when start == end.

Parameters:
  • start (SimpleDate) – First day of the range, included.

  • end (SimpleDate) – Day the range stops before.

  • provider – A NepaliEventProvider, or a NepaliCalendarPolicy that carries its own weekly off days.

  • weekend – Day-of-week numbers treated as the weekend. Defaults to Saturday only, and must be left unset when a policy is passed.

Raises:

ValueError – if start > end, or if both a policy and a weekend are given.

Return type:

int

next_working_day(from_date, provider, weekend=None)[source]

First working day at or after from_date.

If from_date is itself a working day, returns it unchanged. Otherwise scans forward day by day, bounded: gives up after a year rather than looping forever on a policy that closes every day.

Parameters:
  • from_date (SimpleDate) – Day to start looking from, included.

  • provider – A NepaliEventProvider, or a NepaliCalendarPolicy that carries its own weekly off days.

  • weekend – Day-of-week numbers treated as the weekend. Defaults to Saturday only, and must be left unset when a policy is passed.

Raises:
  • ValueError – if both a policy and a weekend are given.

  • RuntimeError – if no working day is found within 366 days.

Return type:

SimpleDate

add_working_days(from_date, days, provider, weekend=None)[source]

Date that is days working days from from_date (Excel WORKDAY semantics).

  • days == 0 returns from_date unchanged.

  • days > 0 returns the days-th working day strictly after from_date.

  • days < 0 returns the abs(days)-th working day strictly before it.

Note this means add_working_days(from_date, 0) is not the same as next_working_day() - use the latter for adjustment.

Parameters:
  • from_date (SimpleDate) – Day the count starts from, itself never the answer unless days is 0.

  • days (int) – How many working days to move, forward when positive.

  • provider – A NepaliEventProvider, or a NepaliCalendarPolicy that carries its own weekly off days.

  • weekend – Day-of-week numbers treated as the weekend. Defaults to Saturday only, and must be left unset when a policy is passed.

Raises:
  • ValueError – if both a policy and a weekend are given.

  • RuntimeError – if more than about two years of scanning fails, which guards against a policy that closes every day.

Return type:

SimpleDate