Skip to content

Commit 5d55f0e

Browse files
authored
Implementation of ZonedDateTime.prototype.with (#267)
Fixes #260
1 parent 2db9b2a commit 5d55f0e

2 files changed

Lines changed: 133 additions & 5 deletions

File tree

src/builtins/core/date.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,6 @@ impl PartialDate {
8181
crate::impl_with_fallback_method!(with_fallback_year_month, () PlainYearMonth); // excludes day
8282
crate::impl_with_fallback_method!(with_fallback_date, (with_day: day) PlainDate);
8383
crate::impl_with_fallback_method!(with_fallback_datetime, (with_day:day) PlainDateTime);
84-
85-
// TODO: ZonedDateTime
8684
}
8785

8886
// Use macro to impl fallback methods to avoid having a trait method.

src/builtins/core/zoneddatetime.rs

Lines changed: 133 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -514,8 +514,55 @@ impl ZonedDateTime {
514514
self.instant
515515
}
516516

517-
pub fn with(&self, _partial: PartialZonedDateTime) -> TemporalResult<Self> {
518-
Err(TemporalError::general("Not yet implemented"))
517+
pub fn with(
518+
&self,
519+
partial: PartialZonedDateTime,
520+
disambiguation: Option<Disambiguation>,
521+
offset_option: Option<OffsetDisambiguation>,
522+
overflow: Option<ArithmeticOverflow>,
523+
provider: &impl TimeZoneProvider,
524+
) -> TemporalResult<Self> {
525+
let overflow = overflow.unwrap_or_default();
526+
let disambiguation = disambiguation.unwrap_or_default();
527+
let offset_option = offset_option.unwrap_or(OffsetDisambiguation::Reject);
528+
529+
let iso_date_time = self.tz.get_iso_datetime_for(&self.instant, provider)?;
530+
let plain_date_time = PlainDateTime::new_unchecked(iso_date_time, self.calendar.clone());
531+
532+
// 23. Let dateTimeResult be ? InterpretTemporalDateTimeFields(calendar, fields, overflow).
533+
let result_date = self.calendar.date_from_partial(
534+
&partial.date.with_fallback_datetime(&plain_date_time)?,
535+
overflow,
536+
)?;
537+
538+
let time = iso_date_time.time.with(partial.time, overflow)?;
539+
540+
// 24. Let newOffsetNanoseconds be ! ParseDateTimeUTCOffset(fields.[[OffsetString]]).
541+
let original_offset = self.offset_nanoseconds_with_provider(provider)?;
542+
let new_offset_nanos = partial
543+
.offset
544+
.map(|offset| i64::from(offset.0) * 60_000_000_000)
545+
.or(Some(original_offset));
546+
547+
// 25. Let epochNanoseconds be ? InterpretISODateTimeOffset(dateTimeResult.[[ISODate]], dateTimeResult.[[Time]], option, newOffsetNanoseconds, timeZone, disambiguation, offset, match-exactly).
548+
let epoch_nanos = interpret_isodatetime_offset(
549+
result_date.iso,
550+
Some(time),
551+
false,
552+
new_offset_nanos,
553+
&self.tz,
554+
disambiguation,
555+
offset_option,
556+
true,
557+
provider,
558+
)?;
559+
560+
// 26. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
561+
Ok(Self::new_unchecked(
562+
Instant::from(epoch_nanos),
563+
self.calendar.clone(),
564+
self.tz.clone(),
565+
))
519566
}
520567

521568
/// Creates a new `ZonedDateTime` from the current `ZonedDateTime`
@@ -1227,7 +1274,9 @@ pub(crate) fn nanoseconds_to_formattable_offset_minutes(
12271274
mod tests {
12281275
use super::ZonedDateTime;
12291276
use crate::{
1230-
options::{DifferenceSettings, Disambiguation, OffsetDisambiguation, Unit},
1277+
options::{
1278+
ArithmeticOverflow, DifferenceSettings, Disambiguation, OffsetDisambiguation, Unit,
1279+
},
12311280
partial::{PartialDate, PartialTime, PartialZonedDateTime},
12321281
time::EpochNanoseconds,
12331282
tzdb::FsTzdbProvider,
@@ -1366,4 +1415,85 @@ mod tests {
13661415
assert_eq!(diff.microseconds(), 0);
13671416
assert_eq!(diff.nanoseconds(), 0);
13681417
}
1418+
1419+
// overflow-reject-throws.js
1420+
#[test]
1421+
fn overflow_reject_throws() {
1422+
let provider = &FsTzdbProvider::default();
1423+
1424+
let zdt =
1425+
ZonedDateTime::try_new(217178610123456789, Calendar::default(), TimeZone::default())
1426+
.unwrap();
1427+
1428+
let overflow = ArithmeticOverflow::Reject;
1429+
1430+
let result_1 = zdt.with(
1431+
PartialZonedDateTime {
1432+
date: PartialDate {
1433+
month: Some(29),
1434+
..Default::default()
1435+
},
1436+
time: PartialTime::default(),
1437+
offset: None,
1438+
timezone: None,
1439+
},
1440+
None,
1441+
None,
1442+
Some(overflow),
1443+
provider,
1444+
);
1445+
1446+
let result_2 = zdt.with(
1447+
PartialZonedDateTime {
1448+
date: PartialDate {
1449+
day: Some(31),
1450+
..Default::default()
1451+
},
1452+
time: PartialTime::default(),
1453+
offset: None,
1454+
timezone: None,
1455+
},
1456+
None,
1457+
None,
1458+
Some(overflow),
1459+
provider,
1460+
);
1461+
1462+
let result_3 = zdt.with(
1463+
PartialZonedDateTime {
1464+
date: PartialDate::default(),
1465+
time: PartialTime {
1466+
hour: Some(29),
1467+
..Default::default()
1468+
},
1469+
offset: None,
1470+
timezone: None,
1471+
},
1472+
None,
1473+
None,
1474+
Some(overflow),
1475+
provider,
1476+
);
1477+
1478+
let result_4 = zdt.with(
1479+
PartialZonedDateTime {
1480+
date: PartialDate::default(),
1481+
time: PartialTime {
1482+
nanosecond: Some(9000),
1483+
..Default::default()
1484+
},
1485+
offset: None,
1486+
timezone: None,
1487+
},
1488+
None,
1489+
None,
1490+
Some(overflow),
1491+
provider,
1492+
);
1493+
1494+
assert!(result_1.is_err());
1495+
assert!(result_2.is_err());
1496+
assert!(result_3.is_err());
1497+
assert!(result_4.is_err());
1498+
}
13691499
}

0 commit comments

Comments
 (0)