Skip to main content

PyCalendar

Struct PyCalendar 

pub(crate) struct PyCalendar {
    inner: Arc<dyn Calendar>,
    expression: String,
}
Expand description

A business-day calendar, resolved from a lookup expression.

The expression is a business-centre code ("GBLO") or a comma-separated list joined into the union of its members’ holidays ("GBLO,EUTA"). "EVERYDAY" names the calendar on which every day is a business day and "WEEKDAYS" the one on which only weekends are not. A value date that must also be good on a second calendar is stated with [Calendar.settle]’s settlement.

Calendars are immutable. No operation amends the calendar in hand: there is no add_holiday, no remove_holiday, and nothing a caller does here changes what any other holder resolves for a centre. The one operation that rebinds a code is dates.ingest_calendars, which never alters a calendar anyone holds. Two calendars claiming one centre and disagreeing about its holidays would make two prices differ for a reason no ledger records, and that is what the registry exists to prevent.

Construction is a different thing from amendment, and is permitted. [Calendar.from_holidays] builds a calendar from an explicit holiday set, and [Calendar.amended] derives one from a calendar in hand by closing and opening dates. Both return a new value the caller passes explicitly, both register nothing, and both leave every other holder’s resolution exactly as it was. What keeps the guarantee is the narrower rule that a built or derived calendar never takes a registered centre’s name: from_holidays refuses one, and a derived calendar’s name is one the lookup does not resolve.

Fields§

§inner: Arc<dyn Calendar>§expression: String

Implementations§

§

impl PyCalendar

pub(crate) fn handle(&self) -> &Arc<dyn Calendar>

pub(crate) fn of(inner: Arc<dyn Calendar>, label: &str) -> Self

Wraps a calendar the core already resolved.

The handle is shared, not rebuilt: a caller who takes a calendar off a template holds the same object the template’s own schedules are built on. Its expression is its name wherever the parser resolves that name — a centre, or a holiday join spelled A,B — so Calendar(...) rebuilds it; label describes where it came from otherwise, for a calendar no expression spells (a business-day join, an amended one).

§

impl PyCalendar

fn new(expression: &str) -> PyResult<Self>

fn from_holidays( name: &str, holidays: Vec<NaiveDate>, weekend: Option<Vec<String>>, ) -> PyResult<Self>

Builds a calendar from an explicit holiday set, for a market the shipped registry does not carry.

Registers nothing and mutates nothing. The result is a value the caller passes explicitly; every registry calendar resolves afterwards to exactly what it resolved to before, and no other holder can observe that this was built. That is the distinction the calendar layer actually draws: shared mutable state is refused, construction is not.

weekend names the days that are not business days, parsed by the core’s own day-of-week vocabulary; it defaults to Saturday and Sunday.

A registered business centre’s name is refused. Two calendars claiming the same centre and disagreeing about its holidays is the failure the registry exists to prevent, and building one under a name the lookup already answers would reintroduce it.

fn amended( &self, closing: Option<Vec<NaiveDate>>, opening: Option<Vec<NaiveDate>>, ) -> PyResult<Self>

Derives a new calendar from this one, closing closing and opening opening.

This is how a caller says “GBLO, plus the closure announced after the shipped table was generated” without enumerating a century of UK bank holidays. from_holidays takes a whole holiday set; this takes a difference from a calendar already in hand.

The receiver is unchanged. Nothing is registered, nothing is amended in place, and every other holder — including a later Calendar("GBLO") — resolves exactly what it resolved before. The result is a value you pass explicitly.

The derived calendar takes a name of its own that Calendar(...) does not resolve, so it can never be a second calendar claiming a registered centre.

Both sets are taken in one call. Each derivation rebuilds the calendar’s grid, so deriving three times to close three dates pays that cost three times; and taking both together makes the result independent of the order they are supplied in. A date named in both is refused.

This rebuilds the calendar — it belongs in setup, not on a pricing path.

fn __repr__(&self) -> String

fn join( calendars: Vec<PyRef<'_, PyCalendar>>, rule: Named<JointCalendarRule>, ) -> PyResult<Self>

calendars combined under rule: JoinHolidays (a holiday in any member is a holiday — what a comma in an expression means) or JoinBusinessDays (a business day in any member is a business day).

fn expression(&self) -> &str

The expression this calendar was resolved from.

fn name(&self) -> &str

The resolved calendar’s own name (diagnostic).

fn is_weekend(&self, date: NaiveDate) -> bool

Whether date falls on the weekend of this calendar’s week.

fn is_holiday(&self, date: NaiveDate) -> bool

Whether date is closed for a reason beyond the weekend — a listed closure, not a Saturday.

A weekend day carrying no listed closure is not a holiday, so is_weekend and is_holiday do not overlap and a caller can tell one from the other. The answer does not depend on the expression the calendar was resolved from, and it selects exactly the dates holidays returns when weekends are excluded.

fn is_business_day(&self, date: NaiveDate) -> bool

Whether date is a good business day.

fn adjust( &self, date: NaiveDate, business_day_convention: Named<BusinessDayConvention>, ) -> NaiveDate

Rolls date to a business day under business_day_convention.

fn settle( &self, date: NaiveDate, n: i32, unit: &str, settlement: Option<&PyCalendar>, business_day_convention: Named<BusinessDayConvention>, ) -> PyResult<NaiveDate>

Advances date by a settlement lag of n units and settles the result — the one primitive behind every value date: T+2 spot, a lagged payment, an FX delivery.

unit counts business_days on this calendar or calendar_days settled afterwards. business_day_convention rolls the result onto a business day of this calendar, or, when settlement is given, onto a day that is a business day of both — the way a value date is stated over two calendars. The count itself follows this calendar alone.

fn advance( &self, date: NaiveDate, tenor: CalendarTenor, business_day_convention: Named<BusinessDayConvention>, end_of_month: bool, roll_convention: Option<Named<RollConvention>>, ) -> PyResult<NaiveDate>

Advances date by tenor ("3M", "-1Y", "2W", "7D"), then adjusts the result under business_day_convention. "4B" is four good business days — add_business_days, so nothing is left to adjust.

end_of_month is the end-of-month roll rule: when the seed is a business month-end anchor and the tenor is in months or years, the result lands on the business month end of the target month rather than on the clamped day (28 Feb + 6M reaches 31 Aug, not 28 Aug).

roll_convention snaps the advanced date before it is adjusted: a day of the month ("31"), "EOM", or one of the named conventions. end_of_month supersedes it, as it does in a schedule.

fn end_of_month(&self, date: NaiveDate) -> NaiveDate

The last business day of date’s month. The calendar month end is the free function last_day_of_month, which is blind to holidays.

fn is_end_of_month(&self, date: NaiveDate) -> bool

Whether date is its month’s last business day. Strict: a trailing weekend day is not, though it does anchor the end-of-month roll.

fn is_month_end_anchor(&self, date: NaiveDate) -> bool

Whether date anchors the end-of-month roll — on or after its month’s last business day, so a month-end falling on a Saturday still fires.

fn next_business_day(&self, date: NaiveDate) -> PyResult<NaiveDate>

The first business day on or after date.

fn previous_business_day(&self, date: NaiveDate) -> PyResult<NaiveDate>

The first business day on or before date.

fn add_business_days(&self, date: NaiveDate, n: i64) -> PyResult<NaiveDate>

Steps date by n good business days. Negative looks backward; zero returns date unchanged even where it is itself a holiday. This is the primitive a settlement or fixing lag is built on.

fn business_days_between( &self, start: NaiveDate, end: NaiveDate, include_first: Option<bool>, include_last: Option<bool>, ) -> i64

The number of business days in the half-open interval [start, end), zero when start is not before end: what BUS/252 divides. With include_first or include_last stated, each endpoint’s inclusion is as stated (the other taking its half-open default), and reversing the dates negates the count with the flags swapped.

fn holidays( &self, start: NaiveDate, end: NaiveDate, include_weekends: bool, ) -> Vec<NaiveDate>

The non-business days in the inclusive range, ascending. Weekends are included only when asked for. Both endpoints are required: a calendar spans a century and an unbounded listing is not a useful answer.

fn business_days(&self, start: NaiveDate, end: NaiveDate) -> Vec<NaiveDate>

The business days in the inclusive range, ascending.

Trait Implementations§

§

impl DerefToPyAny for PyCalendar

§

impl IntoPy<Py<PyAny>> for PyCalendar

§

fn into_py(self, py: Python<'_>) -> PyObject

Performs the conversion.
§

impl PyClass for PyCalendar

§

type Frozen = True

Whether the pyclass is frozen. Read more
§

impl PyClassImpl for PyCalendar

§

const IS_BASETYPE: bool = false

#[pyclass(subclass)]
§

const IS_SUBCLASS: bool = false

#[pyclass(extends=…)]
§

const IS_MAPPING: bool = false

#[pyclass(mapping)]
§

const IS_SEQUENCE: bool = false

#[pyclass(sequence)]
§

type BaseType = PyAny

Base class
§

type ThreadChecker = SendablePyClass<PyCalendar>

This handles following two situations: Read more
§

type PyClassMutability = <<PyAny as PyClassBaseType>::PyClassMutability as PyClassMutability>::ImmutableChild

Immutable or mutable
§

type Dict = PyClassDummySlot

Specify this class has #[pyclass(dict)] or not.
§

type WeakRef = PyClassDummySlot

Specify this class has #[pyclass(weakref)] or not.
§

type BaseNativeType = PyAny

The closest native ancestor. This is PyAny by default, and when you declare #[pyclass(extends=PyDict)], it’s PyDict.
§

fn items_iter() -> PyClassItemsIter

§

fn doc(py: Python<'_>) -> PyResult<&'static CStr>

Rendered class doc
§

fn lazy_type_object() -> &'static LazyTypeObject<Self>

§

fn dict_offset() -> Option<isize>

§

fn weaklist_offset() -> Option<isize>

§

impl PyClassNewTextSignature<PyCalendar> for PyClassImplCollector<PyCalendar>

§

fn new_text_signature(self) -> Option<&'static str>

§

impl<'a, 'py> PyFunctionArgument<'a, 'py> for &'a PyCalendar

§

type Holder = Option<PyRef<'py, PyCalendar>>

§

fn extract( obj: &'a Bound<'py, PyAny>, holder: &'a mut Self::Holder, ) -> PyResult<Self>

§

impl PyMethods<PyCalendar> for PyClassImplCollector<PyCalendar>

§

fn py_methods(self) -> &'static PyClassItems

§

impl PyTypeInfo for PyCalendar

§

const NAME: &'static str = "Calendar"

Class name.
§

const MODULE: Option<&'static str>

Module name, if any.
§

fn type_object_raw(py: Python<'_>) -> *mut PyTypeObject

Returns the PyTypeObject instance for this type.
§

fn type_object_bound(py: Python<'_>) -> Bound<'_, PyType>

Returns the safe abstraction over the type object.
§

fn is_type_of_bound(object: &Bound<'_, PyAny>) -> bool

Checks if object is an instance of this type or a subclass of this type.
§

fn is_exact_type_of_bound(object: &Bound<'_, PyAny>) -> bool

Checks if object is an instance of this type.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> PyErrArguments for T
where T: IntoPy<Py<PyAny>> + Send + Sync,

§

fn arguments(self, py: Python<'_>) -> Py<PyAny>

Arguments for exception
§

impl<T> PyTypeCheck for T
where T: PyTypeInfo,

§

const NAME: &'static str = <T as PyTypeInfo>::NAME

Name of self. This is used in error messages, for example.
§

fn type_check(object: &Bound<'_, PyAny>) -> bool

Checks if object is an instance of Self, which may include a subtype. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> Ungil for T
where T: Send,