Skip to content

Commit 2e3e513

Browse files
JohannesHellevelockels
authored andcommitted
Implement toZonedDateTime in PlainDate (#192)
Related to #148 First time contributer, not sure if is correct. Tought this might be a good place to get some feedback while showcasing the code. Struggling to choose type for the item and how to check if it is an object. Thanks for the feedback! [Link to method ](https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.tozoneddatetime) In kahoots with @brageh01
1 parent 2fc0cac commit 2e3e513

File tree

2 files changed

+85
-4
lines changed

2 files changed

+85
-4
lines changed

src/builtins/compiled/date.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use crate::{builtins::TZ_PROVIDER, TemporalError, TemporalResult, PlainDate, PlainTime};
2+
3+
impl PlainDate {
4+
5+
/// Converts a `Date` to a `ZonedDateTime` in the UTC time zone.
6+
pub fn to_zoned_date_time(
7+
&self,
8+
time_zone: TimeZone,
9+
plain_time: Option<PlainTime>
10+
) -> TemporalResult<crate::ZonedDateTime> {
11+
let provider = TZ_PROVIDER
12+
.lock()
13+
.map_err(|_| TemporalError::general("Unable to acquire lock"))?;
14+
self.to_zoned_date_time_with_provider(time_zone, plain_time, &*provider)
15+
}
16+
}

src/builtins/core/date.rs

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33
use crate::{
44
builtins::core::{
55
calendar::Calendar, duration::DateDuration, Duration, PlainDateTime, PlainTime,
6+
ZonedDateTime,
67
},
78
iso::{IsoDate, IsoDateTime, IsoTime},
89
options::{
9-
ArithmeticOverflow, DifferenceOperation, DifferenceSettings, DisplayCalendar,
10-
ResolvedRoundingOptions, TemporalUnit, UnitGroup,
10+
ArithmeticOverflow, DifferenceOperation, DifferenceSettings, Disambiguation,
11+
DisplayCalendar, ResolvedRoundingOptions, TemporalUnit, UnitGroup,
1112
},
1213
parsers::{parse_date_time, IxdtfStringBuilder},
1314
primitive::FiniteF64,
14-
provider::NeverProvider,
15+
provider::{NeverProvider, TimeZoneProvider},
1516
MonthCode, TemporalError, TemporalResult, TemporalUnwrap, TimeZone,
1617
};
1718
use alloc::{format, string::String};
@@ -666,8 +667,50 @@ impl PlainDate {
666667
.with_calendar(self.calendar.identifier(), display_calendar)
667668
.build()
668669
}
669-
}
670670

671+
#[inline]
672+
pub fn to_zoned_date_time_with_provider(
673+
&self,
674+
tz: TimeZone,
675+
plain_time: Option<PlainTime>,
676+
provider: &impl TimeZoneProvider,
677+
) -> TemporalResult<ZonedDateTime> {
678+
// 1. Let temporalDate be the this value.
679+
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
680+
// 3. If item is an Object, then
681+
// a. Let timeZoneLike be ? Get(item, "timeZone").
682+
// b. If timeZoneLike is undefined, then
683+
// i. Let timeZone be ? ToTemporalTimeZoneIdentifier(item).
684+
// ii. Let temporalTime be undefined.
685+
// c. Else,
686+
// i. Let timeZone be ? ToTemporalTimeZoneIdentifier(timeZoneLike).
687+
// ii. Let temporalTime be ? Get(item, "plainTime").
688+
// 4. Else,
689+
// a. Let timeZone be ? ToTemporalTimeZoneIdentifier(item).
690+
// b. Let temporalTime be undefined.
691+
692+
// 5. If temporalTime is undefined, then
693+
// a. Let epochNs be ? GetStartOfDay(timeZone, temporalDate.[[ISODate]]).
694+
// 6. Else,
695+
// a. Set temporalTime to ? ToTemporalTime(temporalTime).
696+
// b. Let isoDateTime be CombineISODateAndTimeRecord(temporalDate.[[ISODate]], temporalTime.[[Time]]).
697+
// c. If ISODateTimeWithinLimits(isoDateTime) is false, throw a RangeError exception.
698+
// d. Let epochNs be ? GetEpochNanosecondsFor(timeZone, isoDateTime, compatible).
699+
let epoch_ns = if let Some(time) = plain_time {
700+
let result_iso = IsoDateTime::new(self.iso, time.iso);
701+
702+
tz.get_epoch_nanoseconds_for(
703+
result_iso.unwrap_or_default(),
704+
Disambiguation::Compatible,
705+
provider,
706+
)?
707+
} else {
708+
tz.get_start_of_day(&self.iso, provider)?
709+
};
710+
// 7. Return ! CreateTemporalZonedDateTime(epochNs, timeZone, temporalDate.[[Calendar]]).
711+
ZonedDateTime::try_new(epoch_ns.0, self.calendar.clone(), tz)
712+
}
713+
}
671714
// ==== Trait impls ====
672715

673716
impl From<PlainDateTime> for PlainDate {
@@ -950,6 +993,28 @@ mod tests {
950993
assert_eq!(with_day.day(), 17);
951994
}
952995

996+
// test toZonedDateTime
997+
#[cfg(feature = "tzdb")]
998+
#[test]
999+
fn to_zoned_date_time() {
1000+
use crate::tzdb::FsTzdbProvider;
1001+
let date = PlainDate::from_str("2020-01-01").unwrap();
1002+
let tz = TimeZone::try_from_str("UTC").unwrap();
1003+
let provider = &FsTzdbProvider::default();
1004+
let zdt = date
1005+
.to_zoned_date_time_with_provider(tz, None, provider)
1006+
.unwrap();
1007+
assert_eq!(zdt.year_with_provider(provider).unwrap(), 2020);
1008+
assert_eq!(zdt.month_with_provider(provider).unwrap(), 1);
1009+
assert_eq!(zdt.day_with_provider(provider).unwrap(), 1);
1010+
assert_eq!(zdt.hour_with_provider(provider).unwrap(), 0);
1011+
assert_eq!(zdt.minute_with_provider(provider).unwrap(), 0);
1012+
assert_eq!(zdt.second_with_provider(provider).unwrap(), 0);
1013+
assert_eq!(zdt.millisecond_with_provider(provider).unwrap(), 0);
1014+
assert_eq!(zdt.microsecond_with_provider(provider).unwrap(), 0);
1015+
assert_eq!(zdt.nanosecond_with_provider(provider).unwrap(), 0);
1016+
}
1017+
9531018
// test262/test/built-ins/Temporal/Calendar/prototype/month/argument-string-invalid.js
9541019
#[test]
9551020
fn invalid_strings() {

0 commit comments

Comments
 (0)