diff --git a/src/components/instant.rs b/src/components/instant.rs index 0ba54a715..d715f4dca 100644 --- a/src/components/instant.rs +++ b/src/components/instant.rs @@ -62,7 +62,7 @@ impl TryFrom for EpochNanoseconds { /// The native Rust implementation of `Temporal.Instant` #[non_exhaustive] -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Instant(EpochNanoseconds); impl From for Instant { diff --git a/src/components/zoneddatetime.rs b/src/components/zoneddatetime.rs index 2ae16d27b..e6219dd22 100644 --- a/src/components/zoneddatetime.rs +++ b/src/components/zoneddatetime.rs @@ -26,6 +26,8 @@ use crate::components::tz::TZ_PROVIDER; #[cfg(feature = "experimental")] use std::ops::Deref; +use super::PlainTime; + /// A struct representing a partial `ZonedDateTime`. pub struct PartialZonedDateTime { /// The `PartialDate` portion of a `PartialZonedDateTime` @@ -228,6 +230,24 @@ impl ZonedDateTime { pub fn epoch_nanoseconds(&self) -> i128 { self.instant.epoch_nanoseconds() } + + /// Returns the current `ZonedDateTime` as an [`Instant`]. + #[must_use] + pub fn to_instant(&self) -> Instant { + self.instant + } + + /// Creates a new `ZonedDateTime` from the current `ZonedDateTime` + /// combined with the provided `TimeZone`. + pub fn with_timezone(&self, timezone: TimeZone) -> TemporalResult { + Self::try_new(self.epoch_nanoseconds(), self.calendar.clone(), timezone) + } + + /// Creates a new `ZonedDateTime` from the current `ZonedDateTime` + /// combined with the provided `Calendar`. + pub fn with_calendar(&self, calendar: Calendar) -> TemporalResult { + Self::try_new(self.epoch_nanoseconds(), calendar, self.tz.clone()) + } } // ===== Experimental TZ_PROVIDER accessor implementations ===== @@ -401,6 +421,15 @@ impl ZonedDateTime { #[cfg(feature = "experimental")] impl ZonedDateTime { + /// Creates a new `ZonedDateTime` from the current `ZonedDateTime` + /// combined with the provided `TimeZone`. + pub fn with_plain_time(&self, time: PlainTime) -> TemporalResult { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.with_plain_time_and_provider(time, provider.deref()) + } + pub fn add( &self, duration: &Duration, @@ -432,6 +461,34 @@ impl ZonedDateTime { ) } + pub fn start_of_day(&self) -> TemporalResult { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.start_of_day_with_provider(provider.deref()) + } + + pub fn to_plain_date(&self) -> TemporalResult { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.to_plain_date_with_provider(provider.deref()) + } + + pub fn to_plain_time(&self) -> TemporalResult { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.to_plain_time_with_provider(provider.deref()) + } + + pub fn to_plain_datetime(&self) -> TemporalResult { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.to_plain_datetime_with_provider(provider.deref()) + } + pub fn from_str( source: &str, disambiguation: Disambiguation, @@ -649,6 +706,22 @@ impl ZonedDateTime { // ==== Core method implementations ==== impl ZonedDateTime { + /// Creates a new `ZonedDateTime` from the current `ZonedDateTime` + /// combined with the provided `TimeZone`. + pub fn with_plain_time_and_provider( + &self, + time: PlainTime, + provider: &impl TzProvider, + ) -> TemporalResult { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + let result_iso = IsoDateTime::new_unchecked(iso.date, time.iso); + let epoch_ns = + self.tz + .get_epoch_nanoseconds_for(result_iso, Disambiguation::Compatible, provider)?; + Self::try_new(epoch_ns.0, self.calendar.clone(), self.tz.clone()) + } + + /// Add a duration to the current `ZonedDateTime` pub fn add_with_provider( &self, duration: &Duration, @@ -662,6 +735,7 @@ impl ZonedDateTime { ) } + /// Subtract a duration to the current `ZonedDateTime` pub fn subtract_with_provider( &self, duration: &Duration, @@ -675,6 +749,44 @@ impl ZonedDateTime { ) } + /// Return a `ZonedDateTime` representing the start of the day + /// for the current `ZonedDateTime`. + pub fn start_of_day_with_provider(&self, provider: &impl TzProvider) -> TemporalResult { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + let epoch_nanos = self.tz.get_start_of_day(&iso.date, provider)?; + Self::try_new(epoch_nanos.0, self.calendar.clone(), self.tz.clone()) + } + + /// Convert the current `ZonedDateTime` to a [`PlainDate`] with + /// a user defined time zone provider. + pub fn to_plain_date_with_provider( + &self, + provider: &impl TzProvider, + ) -> TemporalResult { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + Ok(PlainDate::new_unchecked(iso.date, self.calendar.clone())) + } + + /// Convert the current `ZonedDateTime` to a [`PlainTime`] with + /// a user defined time zone provider. + pub fn to_plain_time_with_provider( + &self, + provider: &impl TzProvider, + ) -> TemporalResult { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + Ok(PlainTime::new_unchecked(iso.time)) + } + + /// Convert the current `ZonedDateTime` to a [`PlainDateTime`] with + /// a user defined time zone provider. + pub fn to_plain_datetime_with_provider( + &self, + provider: &impl TzProvider, + ) -> TemporalResult { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + Ok(PlainDateTime::new_unchecked(iso, self.calendar.clone())) + } + // TODO: Should IANA Identifier be prechecked or allow potentially invalid IANA Identifer values here? pub fn from_str_with_provider( source: &str,