Skip to content
Merged
30 changes: 17 additions & 13 deletions src/builtins/core/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,18 @@ impl PartialDate {
})
}

crate::impl_with_fallback_method!(with_fallback_date, PlainDate);
crate::impl_with_fallback_method!(with_fallback_datetime, PlainDateTime);
crate::impl_with_fallback_method!(with_fallback_year_month, () PlainYearMonth); // excludes day
crate::impl_with_fallback_method!(with_fallback_date, (with_day: day) PlainDate);
crate::impl_with_fallback_method!(with_fallback_datetime, (with_day:day) PlainDateTime);

// TODO: ZonedDateTime
}

// 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, $component_type:ty) => {
($method_name:ident, ( $(with_day: $day:ident)? ) $component_type:ty) => {
pub(crate) fn $method_name(&self, fallback: &$component_type) -> TemporalResult<Self> {
let era = if let Some(era) = self.era {
Some(era)
Expand All @@ -112,16 +114,18 @@ macro_rules! impl_with_fallback_method {
Some(fallback.month_code()),
),
};

Ok(Self {
year: Some(self.year.unwrap_or(fallback.year())),
month,
month_code,
day: Some(self.day.unwrap_or(fallback.day().into())),
era,
era_year,
calendar: fallback.calendar().clone(),
})
#[allow(clippy::needless_update)] {
Ok(Self {
year: Some(self.year.unwrap_or(fallback.year())),
month,
month_code,
$($day: Some(self.day.unwrap_or(fallback.day().into())),)?
era,
era_year,
calendar: fallback.calendar().clone(),
..Default::default()
})
}
}
};
}
Expand Down
92 changes: 89 additions & 3 deletions src/builtins/core/year_month.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,24 @@ impl PlainYearMonth {
/// Creates a `PlainYearMonth` using the fields provided from a [`PartialDate`]
pub fn with(
&self,
_partial: PartialDate,
_overflow: ArithmeticOverflow,
partial: PartialDate,
overflow: Option<ArithmeticOverflow>,
) -> TemporalResult<Self> {
Err(TemporalError::general("Not yet implemented."))
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
// 3. If ? IsPartialTemporalObject(temporalYearMonthLike) is false, throw a TypeError exception.
// 4. Let calendar be yearMonth.[[Calendar]].
// 5. Let fields be ISODateToFields(calendar, yearMonth.[[ISODate]], year-month).
// 6. Let partialYearMonth be ? PrepareCalendarFields(calendar, temporalYearMonthLike, « year, month, month-code », « », partial).
// 7. Set fields to CalendarMergeFields(calendar, fields, partialYearMonth).
// 8. Let resolvedOptions be ? GetOptionsObject(options).
// 9. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
// 10. Let isoDate be ? CalendarYearMonthFromFields(calendar, fields, overflow).
// 11. Return ! CreateTemporalYearMonth(isoDate, calendar).
self.calendar.year_month_from_partial(
&partial.with_fallback_year_month(self)?,
overflow.unwrap_or(ArithmeticOverflow::Constrain),
)
}

/// Compares one `PlainYearMonth` to another `PlainYearMonth` using their
Expand Down Expand Up @@ -298,6 +312,78 @@ mod tests {

use super::PlainYearMonth;

use tinystr::tinystr;

use super::*;

#[test]
fn test_plain_year_month_with() {
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.

praise: great tests!

let base = PlainYearMonth::new_with_overflow(
2025,
3,
None,
Calendar::default(),
ArithmeticOverflow::Reject,
)
.unwrap();

// Year
let partial = PartialDate {
year: Some(2001),
..Default::default()
};

let with_year = base.with(partial, None).unwrap();
assert_eq!(with_year.iso_year(), 2001); // year is changed
assert_eq!(with_year.iso_month(), 3); // month is not changed
assert_eq!(with_year.month_code(), MonthCode::from_str("M03").unwrap()); // assert month code has been initialized correctly

// Month
let partial = PartialDate {
month: Some(2),
..Default::default()
};
let with_month = base.with(partial, None).unwrap();
assert_eq!(with_month.iso_year(), 2025); // year is not changed
assert_eq!(with_month.iso_month(), 2); // month is changed
assert_eq!(with_month.month_code(), MonthCode::from_str("M02").unwrap()); // assert month code has changed as well as month

// Month Code
let partial = PartialDate {
month_code: Some(MonthCode(tinystr!(4, "M05"))), // change month to May (5)
..Default::default()
};
let with_month_code = base.with(partial, None).unwrap();
assert_eq!(with_month_code.iso_year(), 2025); // year is not changed
assert_eq!(
with_month_code.month_code(),
MonthCode::from_str("M05").unwrap()
); // assert month code has changed
assert_eq!(with_month_code.iso_month(), 5); // month is changed as well

// Day
let partial = PartialDate {
day: Some(15),
..Default::default()
};
let with_day = base.with(partial, None).unwrap();
assert_eq!(with_day.iso_year(), 2025); // year is not changed
assert_eq!(with_day.iso_month(), 3); // month is not changed
assert_eq!(with_day.iso.day, 1); // day is ignored

// All
let partial = PartialDate {
year: Some(2001),
month: Some(2),
day: Some(15),
..Default::default()
};
let with_all = base.with(partial, None).unwrap();
assert_eq!(with_all.iso_year(), 2001); // year is changed
assert_eq!(with_all.iso_month(), 2); // month is changed
assert_eq!(with_all.iso.day, 1); // day is ignored
}

#[test]
fn basic_from_str() {
let valid_strings = [
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions temporal_capi/bindings/cpp/temporal_rs/PlainYearMonth.hpp

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions temporal_capi/src/plain_year_month.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ pub mod ffi {
pub fn with(
&self,
partial: PartialDate,
overflow: ArithmeticOverflow,
overflow: Option<ArithmeticOverflow>,
) -> Result<Box<Self>, TemporalError> {
self.0
.with(partial.try_into()?, overflow.into())
.with(partial.try_into()?, overflow.map(Into::into))
.map(|x| Box::new(Self(x)))
.map_err(Into::into)
}
Expand Down