Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
23 changes: 22 additions & 1 deletion src/builtins/core/calendar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ impl Calendar {
return iso_date.month;
}
let calendar_date = self.0.from_iso(*iso_date.to_icu4x().inner());
self.0.month(&calendar_date).month_number()
self.0.month(&calendar_date).ordinal
}

/// `CalendarMonthCode`
Expand Down Expand Up @@ -563,6 +563,27 @@ impl Calendar {
_ => None,
}
}

pub(crate) fn calendar_has_eras(kind: AnyCalendarKind) -> bool {
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.

Note: would close #337

match kind {
AnyCalendarKind::Buddhist
| AnyCalendarKind::Coptic
| AnyCalendarKind::Ethiopian
| AnyCalendarKind::EthiopianAmeteAlem
| AnyCalendarKind::Gregorian
| AnyCalendarKind::Hebrew
| AnyCalendarKind::Indian
| AnyCalendarKind::HijriSimulatedMecca
| AnyCalendarKind::HijriTabularTypeIIFriday
| AnyCalendarKind::HijriTabularTypeIIThursday
| AnyCalendarKind::HijriUmmAlQura
| AnyCalendarKind::Japanese
| AnyCalendarKind::Persian
| AnyCalendarKind::Roc => true,
AnyCalendarKind::Chinese | AnyCalendarKind::Dangi | AnyCalendarKind::Iso => false,
_ => false,
}
}
}

impl From<PlainDate> for Calendar {
Expand Down
12 changes: 6 additions & 6 deletions src/builtins/core/calendar/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ impl MonthCode {
calendar,
..
} => {
// TODO(manishearth) this is incorrect,
// see https://github.com/unicode-org/icu4x/issues/6790
let month_code = month_to_month_code(*month)?;
month_code.validate(calendar)?;
Ok(month_code)
Expand Down Expand Up @@ -313,11 +315,9 @@ pub(crate) fn month_to_month_code(month: u8) -> TemporalResult<MonthCode> {
}

#[inline]
fn are_month_and_month_code_resolvable(month: u8, mc: &MonthCode) -> TemporalResult<()> {
if month != mc.to_month_integer() {
return Err(TemporalError::range()
.with_message("Month and monthCode values could not be resolved."));
}
fn are_month_and_month_code_resolvable(_month: u8, _mc: &MonthCode) -> TemporalResult<()> {
// TODO(Manishearth) month is an ordinal month, this check needs year/calendar info
// https://github.com/unicode-org/icu4x/issues/6790
Ok(())
}

Expand Down Expand Up @@ -481,7 +481,7 @@ mod tests {

// Get the full partial date.
let full_partial = PartialDate::default()
.with_fallback_date(&plain_date, ArithmeticOverflow::Constrain)
.with_fallback_date(&plain_date, *cal, ArithmeticOverflow::Constrain)
.expect(&expect_str);

let era_year =
Expand Down
107 changes: 88 additions & 19 deletions src/builtins/core/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,32 +68,57 @@ impl PartialDate {
}
crate::impl_with_fallback_method!(with_fallback_date, (with_day: day) PlainDate);
crate::impl_with_fallback_method!(with_fallback_datetime, (with_day:day) PlainDateTime);
crate::impl_field_keys_to_ignore!((with_day:day));
}

/// The return value of CalendarFieldKeysToIgnore
#[derive(Copy, Clone, Default)]
pub(crate) struct FieldKeysToIgnore {
/// Ignore all era fields (era-year, era)
pub era: bool,
/// Ignore arithmetical year
pub arithmetical_year: bool,
/// Ignore all month fields (month, month-code)
pub month: bool,
}

// Use macro to impl fallback methods to avoid having a trait method.
#[doc(hidden)]
#[macro_export]
macro_rules! impl_with_fallback_method {
($method_name:ident, ( $(with_day: $day:ident)? ) $component_type:ty) => {
pub(crate) fn $method_name(&self, fallback: &$component_type, overflow: ArithmeticOverflow) -> TemporalResult<Self> {
let era = if let Some(era) = self.era {
Some(era)
} else {
let era = fallback.era();
era.map(|e| {
TinyAsciiStr::<19>::try_from_utf8(e.as_bytes())
.map_err(|e| TemporalError::general(format!("{e}")))
})
.transpose()?
};
let era_year = self
.era_year
.map_or_else(|| fallback.era_year(), |ey| Some(ey));
pub(crate) fn $method_name(&self, fallback: &$component_type, calendar: AnyCalendarKind, overflow: ArithmeticOverflow) -> TemporalResult<Self> {
let keys_to_ignore = self.field_keys_to_ignore(calendar);
let mut era = self.era;

let mut era_year = self.era_year;
let mut year = self.year;

if !keys_to_ignore.era {
if era.is_none() {
era =
fallback.era().map(|e| {
TinyAsciiStr::<19>::try_from_utf8(e.as_bytes())
.map_err(|_| TemporalError::assert().with_message("Produced invalid era code"))
})
.transpose()?
}
if era_year.is_none() {
era_year = fallback.era_year();
}
}
if !keys_to_ignore.arithmetical_year {
if year.is_none() {
year = Some(fallback.year());
}
}

let (month, month_code) = match (self.month, self.month_code) {
(Some(month), Some(mc)) => (Some(month), Some(mc)),
(Some(month), None) => {
let month_maybe_clamped = if overflow == ArithmeticOverflow::Constrain {
// TODO (manishearth) this should be managed by ICU4X
// https://github.com/unicode-org/icu4x/issues/6790
month.clamp(1, 12)
} else {
month
Expand All @@ -102,14 +127,17 @@ macro_rules! impl_with_fallback_method {
(Some(month_maybe_clamped), Some(month_to_month_code(month_maybe_clamped)?))
}
(None, Some(mc)) => (Some(mc.to_month_integer()).map(Into::into), Some(mc)),
(None, None) => (
(None, None) if !keys_to_ignore.month => (
Some(fallback.month()).map(Into::into),
Some(fallback.month_code()),
),
// This should currently be unreachable, but it may change as CalendarFieldKeysToIgnore
// changes
(None, None) => (None, None)
};
#[allow(clippy::needless_update)] {
Ok(Self {
year: Some(self.year.unwrap_or(fallback.year())),
year,
month,
month_code,
$($day: Some(self.day.unwrap_or(fallback.day().into())),)?
Expand All @@ -122,6 +150,45 @@ macro_rules! impl_with_fallback_method {
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! impl_field_keys_to_ignore {
(( $(with_day: $day:ident)? )) => {
/// <https://tc39.es/proposal-temporal/#sec-temporal-calendarfieldkeystoignore>
fn field_keys_to_ignore(&self, calendar: AnyCalendarKind) -> $crate::builtins::core::date::FieldKeysToIgnore {
let mut keys = $crate::builtins::core::date::FieldKeysToIgnore::default();
// All calendars have months/month codes
if self.month.is_some() || self.month_code.is_some() {
keys.month = true;
}
if Calendar::calendar_has_eras(calendar) {
// We should clear years only if the calendar has eras
if self.year.is_some() || self.era_year.is_some() || self.era.is_some() {
keys.era = true;
keys.arithmetical_year = true;
}

// In a calendar such as "japanese" where eras do not start and end at year and/or month boundaries, note that the returned
// List should contain era and era-year if keys contains day, month, or month-code
// (not only if it contains era, era-year, or year, as in the example above) because it's possible for
// changing the day or month to cause a conflict with the era.
if calendar == AnyCalendarKind::Japanese {
if self.month.is_some() || self.month_code.is_some() {
keys.era = true;
}

$(
if self.$day.is_some() {
keys.era = true;
}
)?
}
}

keys
}
};
}

/// Convenience methods for building a `PartialDate`
impl PartialDate {
Expand Down Expand Up @@ -527,8 +594,10 @@ impl PlainDate {
// 9. Set fields to ? PrepareTemporalFields(fields, fieldsResult.[[FieldNames]], «»).
// 10. Return ? CalendarDateFromFields(calendarRec, fields, resolvedOptions).
let overflow = overflow.unwrap_or(ArithmeticOverflow::Constrain);
self.calendar
.date_from_partial(&partial.with_fallback_date(self, overflow)?, overflow)
self.calendar.date_from_partial(
&partial.with_fallback_date(self, self.calendar.kind(), overflow)?,
overflow,
)
}

/// Creates a new `Date` from the current `Date` and the provided calendar.
Expand Down Expand Up @@ -736,7 +805,7 @@ impl PlainDate {
pub fn to_plain_month_day(&self) -> TemporalResult<PlainMonthDay> {
let overflow = ArithmeticOverflow::Constrain;
self.calendar().month_day_from_partial(
&PartialDate::default().with_fallback_date(self, overflow)?,
&PartialDate::default().with_fallback_date(self, self.calendar.kind(), overflow)?,
overflow,
)
}
Expand Down
2 changes: 1 addition & 1 deletion src/builtins/core/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ impl PlainDateTime {
let result_date = self.calendar.date_from_partial(
&partial_datetime
.date
.with_fallback_datetime(self, overflow)?,
.with_fallback_datetime(self, self.calendar.kind(), overflow)?,
overflow,
)?;

Expand Down
16 changes: 11 additions & 5 deletions src/builtins/core/year_month.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ impl PartialYearMonth {
}

crate::impl_with_fallback_method!(with_fallback_year_month, () PlainYearMonth);
crate::impl_field_keys_to_ignore!(());
}

impl From<&PartialYearMonth> for PartialDate {
Expand Down Expand Up @@ -356,9 +357,12 @@ impl PlainYearMonth {
let added_date = calendar.date_add(&date, &duration_to_add, overflow)?;

// 14. Let addedDateFields be ISODateToFields(calendar, addedDate, year-month).
let added_date_fields = PartialYearMonth::from(
&PartialDate::default().with_fallback_date(&added_date, overflow)?,
);
let added_date_fields =
PartialYearMonth::from(&PartialDate::default().with_fallback_date(
&added_date,
self.calendar.kind(),
overflow,
)?);

// 15. Let isoDate be ? CalendarYearMonthFromFields(calendar, addedDateFields, overflow).
let iso_date = calendar.year_month_from_partial(&added_date_fields, overflow)?;
Expand Down Expand Up @@ -688,8 +692,10 @@ impl PlainYearMonth {
// 10. Let isoDate be ? CalendarYearMonthFromFields(calendar, fields, overflow).
// 11. Return ! CreateTemporalYearMonth(isoDate, calendar).
let overflow = overflow.unwrap_or(ArithmeticOverflow::Constrain);
self.calendar
.year_month_from_partial(&partial.with_fallback_year_month(self, overflow)?, overflow)
self.calendar.year_month_from_partial(
&partial.with_fallback_year_month(self, self.calendar.kind(), overflow)?,
overflow,
)
}

/// Compares one `PlainYearMonth` to another `PlainYearMonth` using their
Expand Down
8 changes: 5 additions & 3 deletions src/builtins/core/zoneddatetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,9 +725,11 @@ impl ZonedDateTime {

// 23. Let dateTimeResult be ? InterpretTemporalDateTimeFields(calendar, fields, overflow).
let result_date = self.calendar.date_from_partial(
&partial
.date
.with_fallback_datetime(&plain_date_time, overflow)?,
&partial.date.with_fallback_datetime(
&plain_date_time,
self.calendar.kind(),
overflow,
)?,
overflow,
)?;

Expand Down
Loading