///|
/// Matplotlib YearLocator with exact aligned year bounds and a live wrapper.
/// Invalid aligned month/day pairs raise instead of skipping candidate years.
pub struct YearLocator {
priv locator : RRuleLocator
}
///|
/// Capture the display zone after constructing the underlying yearly rule.
/// An explicit parser context provides deterministic host-local now fields.
///
/// ```mbt check
/// test {
/// let context = @date.DateParserContext::utc(clock=fn() { 1781524800123L })
/// let locator = @date.YearLocator::new(
/// base=5,
/// month=7,
/// day=4,
/// tz="UTC",
/// context~,
/// )
/// let values = locator.tick_values(
/// @date.RRuleDateTime::naive(@date.datetime(2021, 5, 1)),
/// @date.RRuleDateTime::naive(@date.datetime(2028, 1, 1)),
/// )
/// assert_eq(values.map(value => @date.from_ordinal(value).year()), [
/// 2020, 2025, 2030,
/// ])
/// }
/// ```
pub fn YearLocator::new(
base~ : Int32 = 1,
month~ : Int32 = 1,
day~ : Int32 = 1,
tz? : String,
timezone? : DateTimeZone,
context? : DateParserContext,
) -> YearLocator raise DateError {
let rule = RRuleWrapper::new(
Yearly,
interval=base,
bymonth_value=month,
bymonthday_value=day,
byhour=[0],
byminute=[0],
bysecond=[0],
context?,
)
{
locator: {
..RRuleLocator::new(rule, tz?, timezone?),
year_base: Some(base),
},
}
}
///|
pub fn YearLocator::base(self : YearLocator) -> Int32 {
self.locator.year_base.unwrap()
}
///|
pub fn YearLocator::rule(self : YearLocator) -> RRuleWrapper {
self.locator.rule
}
///|
pub fn YearLocator::timezone(self : YearLocator) -> DateTimeZone {
self.locator.timezone
}
///|
pub fn YearLocator::interval(self : YearLocator) -> Int32 {
self.locator.interval()
}
///|
pub fn YearLocator::unit_scale(self : YearLocator) -> Double {
self.locator.unit_scale()
}
///|
pub fn YearLocator::with_timezone(
self : YearLocator,
timezone : DateTimeZone,
) -> YearLocator {
{ locator: self.locator.with_timezone(timezone), }
}
///|
pub fn YearLocator::with_offset(
self : YearLocator,
offset_minutes : Int32,
) -> YearLocator raise DateError {
{ locator: self.locator.with_offset(offset_minutes), }
}
///|
/// Retain yearly bound alignment when adapting to the common locator object.
pub fn YearLocator::as_rrule_locator(self : YearLocator) -> RRuleLocator {
self.locator
}
///|
pub fn YearLocator::as_locator(self : YearLocator) -> DateLocator {
self.locator.as_locator()
}
///|
pub fn YearLocator::tick_values(
self : YearLocator,
first : RRuleDateTime,
last : RRuleDateTime,
) -> Array[Double] raise DateError {
self.locator.tick_values(first, last)
}
///|
fn RRuleDateTime::replace_year_date(
self : RRuleDateTime,
year : Int32,
month : Int32,
day : Int32,
) -> RRuleDateTime raise DateError {
let wall = datetime(year, month, day, microsecond=self.value.microsecond)
match self.timezone {
Some(zone) => RRuleDateTime::from_local(wall, zone, fold=self.fold)
None => RRuleDateTime::naive(wall)
}
}
///|
fn RRuleLocator::year_bounds(
self : RRuleLocator,
base : Int32,
first : RRuleDateTime,
last : RRuleDateTime,
) -> (RRuleDateTime, RRuleDateTime) raise DateError {
let step = Int64::from_int(base)
let low = Int64::max(1L, Int64::from_int(first.value.year) / step * step).to_int()
let high = Int64::min(
9999L,
locator_ceil_div64(Int64::from_int(last.value.year), step) * step,
).to_int()
let month = self.rule.construct.bymonth_form.date_field()
let day = self.rule.construct.bymonthday_form.date_field()
let start = first.replace_year_date(low, month, day)
let stop = start.replace_year_date(high, month, day)
(start, stop)
}