From 9baaaa4de5e490c34b3d7a10301fbbc0cfecdf4a Mon Sep 17 00:00:00 2001 From: Kevin Ness <46825870+nekevss@users.noreply.github.com> Date: Wed, 27 Nov 2024 23:22:02 -0600 Subject: [PATCH] Add accessor methods to ZonedDateTime + organization --- src/components/duration/normalized.rs | 2 +- src/components/zoneddatetime.rs | 234 +++++++++++++++++++++++++- 2 files changed, 229 insertions(+), 7 deletions(-) diff --git a/src/components/duration/normalized.rs b/src/components/duration/normalized.rs index 1d4d7ebb..0b6799d5 100644 --- a/src/components/duration/normalized.rs +++ b/src/components/duration/normalized.rs @@ -74,7 +74,7 @@ impl NormalizedTimeDuration { // TODO: Potentially, update divisor to u64? /// `Divide the NormalizedTimeDuraiton` by a divisor. - pub(super) fn divide(&self, divisor: i64) -> i128 { + pub(crate) fn divide(&self, divisor: i64) -> i128 { // TODO: Validate. self.0 / i128::from(divisor) } diff --git a/src/components/zoneddatetime.rs b/src/components/zoneddatetime.rs index f6feed4d..ac78dde8 100644 --- a/src/components/zoneddatetime.rs +++ b/src/components/zoneddatetime.rs @@ -3,8 +3,8 @@ use tinystr::TinyAsciiStr; use crate::{ - components::{calendar::Calendar, tz::TimeZone, Instant}, - iso::IsoDateTime, + components::{calendar::Calendar, duration::normalized::NormalizedTimeDuration, tz::TimeZone, Instant}, + iso::{IsoDate, IsoDateTime}, options::{ArithmeticOverflow, Disambiguation}, Sign, TemporalError, TemporalResult, }; @@ -78,7 +78,7 @@ impl ZonedDateTime { .with_message("Intermediate ISO datetime was not within a valid range.")); } // 6. Let intermediateNs be ! GetEpochNanosecondsFor(timeZone, intermediateDateTime, compatible). - let intermediate_ns = self.tz().get_epoch_nanoseconds_for( + let intermediate_ns = self.timezone().get_epoch_nanoseconds_for( intermediate, Disambiguation::Compatible, provider, @@ -113,7 +113,7 @@ impl ZonedDateTime { .add_as_instant(duration, overflow, provider)? .epoch_nanos; // 9. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar). - Self::try_new(epoch_ns, self.calendar().clone(), self.tz().clone()) + Self::try_new(epoch_ns, self.calendar().clone(), self.timezone().clone()) } } @@ -137,7 +137,7 @@ impl ZonedDateTime { /// Returns `ZonedDateTime`'s `TimeZone` slot. #[inline] #[must_use] - pub fn tz(&self) -> &TimeZone { + pub fn timezone(&self) -> &TimeZone { &self.tz } @@ -166,7 +166,7 @@ impl ZonedDateTime { } } -// ===== TzProvider APIs for ZonedDateTime ===== +// ===== Experimental TZ_PROVIDER accessor implementations ===== #[cfg(feature = "experimental")] impl ZonedDateTime { @@ -240,7 +240,103 @@ impl ZonedDateTime { self.millisecond_with_provider(provider.deref()) } +} + +// ==== Experimental TZ_PROVIDER calendar method implementations ==== + +#[cfg(feature = "experimental")] +impl ZonedDateTime { + pub fn era(&self) -> TemporalResult>> { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.era_with_provider(provider.deref()) + } + + pub fn era_year(&self) -> TemporalResult> { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.era_year_with_provider(provider.deref()) + } + + /// Returns the calendar day of week value. + pub fn day_of_week(&self) -> TemporalResult { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.day_of_week_with_provider(provider.deref()) + } + + /// Returns the calendar day of year value. + pub fn day_of_year(&self) -> TemporalResult { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.day_of_year_with_provider(provider.deref()) + } + + /// Returns the calendar week of year value. + pub fn week_of_year(&self) -> TemporalResult> { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.week_of_year_with_provider(provider.deref()) + } + + /// Returns the calendar year of week value. + pub fn year_of_week(&self) -> TemporalResult> { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.year_of_week_with_provider(provider.deref()) + } + + /// Returns the calendar days in week value. + pub fn days_in_week(&self) -> TemporalResult { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.days_in_week_with_provider(provider.deref()) + } + + /// Returns the calendar days in month value. + pub fn days_in_month(&self) -> TemporalResult { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.days_in_month_with_provider(provider.deref()) + } + + /// Returns the calendar days in year value. + pub fn days_in_year(&self) -> TemporalResult { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.days_in_year_with_provider(provider.deref()) + } + + /// Returns the calendar months in year value. + pub fn months_in_year(&self) -> TemporalResult { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.months_in_year_with_provider(provider.deref()) + } + /// Returns returns whether the date in a leap year for the given calendar. + pub fn in_leap_year(&self) -> TemporalResult { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.in_leap_year_with_provider(provider.deref()) + } +} + +// ==== Experimental TZ_PROVIDER method implementations ==== + +#[cfg(feature = "experimental")] +impl ZonedDateTime { pub fn add( &self, duration: &Duration, @@ -270,9 +366,44 @@ impl ZonedDateTime { overflow.unwrap_or(ArithmeticOverflow::Constrain), provider.deref(), ) + } } +// ==== HoursInDay accessor method implementation ==== + +impl ZonedDateTime { + #[cfg(feature = "experimental")] + pub fn hours_in_day(&self) -> TemporalResult { + let provider = TZ_PROVIDER + .lock() + .map_err(|_| TemporalError::general("Unable to acquire lock"))?; + self.hours_in_day_with_provider(provider.deref()) + } + + pub fn hours_in_day_with_provider(&self, provider: &impl TzProvider) -> TemporalResult { + // 1-3. Is engine specific steps + // 4. Let isoDateTime be GetISODateTimeFor(timeZone, zonedDateTime.[[EpochNanoseconds]]). + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + // 5. Let today be isoDateTime.[[ISODate]]. + let today = iso.date; + // 6. Let tomorrow be BalanceISODate(today.[[Year]], today.[[Month]], today.[[Day]] + 1). + let tomorrow = IsoDate::balance(today.year, today.month.into(), i32::from(today.day + 1)); + // 7. Let todayNs be ? GetStartOfDay(timeZone, today). + let today_ns = self.tz.get_start_of_day(&today, provider)?; + // 8. Let tomorrowNs be ? GetStartOfDay(timeZone, tomorrow). + let tomorrow_ns = self.tz.get_start_of_day(&tomorrow, provider)?; + // 9. Let diff be TimeDurationFromEpochNanosecondsDifference(tomorrowNs, todayNs). + let diff = NormalizedTimeDuration::from_nanosecond_difference(tomorrow_ns, today_ns)?; + // NOTE: The below should be safe as today_ns and tomorrow_ns should be at most 25 hours. + // TODO: Tests for the below cast. + // 10. Return 𝔽(TotalTimeDuration(diff, hour)). + Ok(diff.divide(60_000_000_000) as u8) + } +} + +// ==== Core accessor methods ==== + impl ZonedDateTime { /// Returns the `year` value for this `ZonedDateTime`. #[inline] @@ -341,7 +472,98 @@ impl ZonedDateTime { let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; Ok(iso.time.nanosecond) } +} + +// ==== Core calendar method implementations ==== + +impl ZonedDateTime { + pub fn era_with_provider(&self, provider: &impl TzProvider) -> TemporalResult>> { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + let pdt = PlainDateTime::new_unchecked(iso, self.calendar.clone()); + self.calendar.era(&CalendarDateLike::DateTime(&pdt)) + } + + pub fn era_year_with_provider(&self, provider: &impl TzProvider) -> TemporalResult> { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + let pdt = PlainDateTime::new_unchecked(iso, self.calendar.clone()); + self.calendar.era_year(&CalendarDateLike::DateTime(&pdt)) + } + + /// Returns the calendar day of week value. + pub fn day_of_week_with_provider(&self, provider: &impl TzProvider) -> TemporalResult { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + let pdt = PlainDateTime::new_unchecked(iso, self.calendar.clone()); + self.calendar.day_of_week(&CalendarDateLike::DateTime(&pdt)) + + } + + /// Returns the calendar day of year value. + pub fn day_of_year_with_provider(&self, provider: &impl TzProvider) -> TemporalResult { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + let pdt = PlainDateTime::new_unchecked(iso, self.calendar.clone()); + self.calendar.day_of_year(&CalendarDateLike::DateTime(&pdt)) + } + + /// Returns the calendar week of year value. + pub fn week_of_year_with_provider(&self, provider: &impl TzProvider) -> TemporalResult> { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + let pdt = PlainDateTime::new_unchecked(iso, self.calendar.clone()); + self.calendar.week_of_year(&CalendarDateLike::DateTime(&pdt)) + + } + + /// Returns the calendar year of week value. + pub fn year_of_week_with_provider(&self, provider: &impl TzProvider) -> TemporalResult> { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + let pdt = PlainDateTime::new_unchecked(iso, self.calendar.clone()); + self.calendar.year_of_week(&CalendarDateLike::DateTime(&pdt)) + + } + + /// Returns the calendar days in week value. + pub fn days_in_week_with_provider(&self, provider: &impl TzProvider) -> TemporalResult { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + let pdt = PlainDateTime::new_unchecked(iso, self.calendar.clone()); + self.calendar.days_in_week(&CalendarDateLike::DateTime(&pdt)) + + } + + /// Returns the calendar days in month value. + pub fn days_in_month_with_provider(&self, provider: &impl TzProvider) -> TemporalResult { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + let pdt = PlainDateTime::new_unchecked(iso, self.calendar.clone()); + self.calendar.days_in_month(&CalendarDateLike::DateTime(&pdt)) + + } + + /// Returns the calendar days in year value. + pub fn days_in_year_with_provider(&self, provider: &impl TzProvider) -> TemporalResult { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + let pdt = PlainDateTime::new_unchecked(iso, self.calendar.clone()); + self.calendar.days_in_year(&CalendarDateLike::DateTime(&pdt)) + } + + /// Returns the calendar months in year value. + pub fn months_in_year_with_provider(&self, provider: &impl TzProvider) -> TemporalResult { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + let pdt = PlainDateTime::new_unchecked(iso, self.calendar.clone()); + self.calendar.months_in_year(&CalendarDateLike::DateTime(&pdt)) + + } + + /// Returns returns whether the date in a leap year for the given calendar. + pub fn in_leap_year_with_provider(&self, provider: &impl TzProvider) -> TemporalResult { + let iso = self.tz.get_iso_datetime_for(&self.instant, provider)?; + let pdt = PlainDateTime::new_unchecked(iso, self.calendar.clone()); + self.calendar.in_leap_year(&CalendarDateLike::DateTime(&pdt)) + } + +} + +// ==== Core method implementations ==== + +impl ZonedDateTime { pub fn add_with_provider( &self, duration: &Duration,