Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions components/calendar/src/cal/east_asian_traditional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,12 @@ impl<R: Rules> DateFieldsResolver for EastAsianTraditional<R> {
fn min_months_from_inner(_start: Self::YearInfo, years: i64) -> i64 {
// A lunisolar leap month is inserted at least every 3 years. Reingold denies
// that the Chinese calendar determines leap years using the 19-year Metonic cycle.
//
// This still drifts from the actual month value so months calculations will still be
// linear time in the year diff being considered (just with a much smaller constant factor).
//
// TODO(#7077): Investigate the Chinese calendar and find a calculation
// here that can result in a bounded error bar.
12 * years + (years / 3)
}

Expand Down
25 changes: 24 additions & 1 deletion components/calendar/src/cal/hebrew.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,30 @@ impl DateFieldsResolver for Hebrew {

#[inline]
fn min_months_from_inner(_start: HebrewYear, years: i64) -> i64 {
// There are 7 leap years in every 19-year Metonic cycle.
// The Hebrew Metonic cycle is 7 leap years every 19 years,
// which comes out to 235 months per 19 years.
//
// We need to ensure that this is always *lower or equal to* the number of
// months in a given year span.
//
// Firstly, note that this math will produce exactly the number of months in any given cycle
// that is a multiple of 19 years. Note that we are only performing integer
// ops here, and our SAFE_YEAR_RANGE is well within the range of allowed values
// for multiplying by 235.
//
// So we only need to verify that this math produces the right results within a single cycle.
//
// The Hebrew Metonic cycle has leap years in year 3, 6, 8, 11, 14, 17, and 19,
// i.e., leap year gaps of +3, +3, +2, +3, +3, +3, +2.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

be explicit about the boundary, either add year 0 or year 22

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Year 19 is the boundary. This is every 19 years.

//
// 235 / 19 is 12.368 months per year, which leads to one leap month every three years plus 0.1
// month left over. So this is "in bounds" as long as it does not predict a leap month in 2 years
// where the Hebrew calendar expects one in 3.
//
// The longest sequence of "three year leap months" in the Hebrew calendar
// is 4: year 9->11->14->17. In that time the error will accumulate to about 0.3, which is not
// enough to create a "two year leap month" in our calculation. So this calculation cannot go past
// the actual cycle of the Hebrew calendar.
235 * years / 19
}

Expand Down
6 changes: 5 additions & 1 deletion components/calendar/src/calendar_arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,11 @@ pub(crate) trait DateFieldsResolver: Calendar {
12
}

/// The minimum number of months over `years` years, starting from the given year.
/// A minimum bound for the number of months over `years` years, starting from the given year.
///
/// This may be equal to the number of months, but should never be over. Ideally,
/// implementations should be close enough to the actual answer such that the error
/// is bounded by a constant.
///
/// The default impl is for non-lunisolar calendars with 12 months!
#[inline]
Expand Down