Skip to content

Implement ZonedDateTime::since and ZonedDateTime::until #170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/components/calendar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::{
},
iso::IsoDate,
options::{ArithmeticOverflow, TemporalUnit},
parsers::parse_allowed_calendar_formats,
TemporalError, TemporalResult,
};

Expand Down Expand Up @@ -200,8 +201,13 @@ impl Calendar {
impl FromStr for Calendar {
type Err = TemporalError;

// 13.39 ParseTemporalCalendarString ( string )
// 13.34 ParseTemporalCalendarString ( string )
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(s) = parse_allowed_calendar_formats(s) {
return s
.map(Calendar::from_utf8)
.unwrap_or(Ok(Calendar::default()));
}
Calendar::from_utf8(s.as_bytes())
}
}
Expand Down
13 changes: 7 additions & 6 deletions src/components/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ use crate::{
iso::{IsoDate, IsoDateTime, IsoTime},
options::{
ArithmeticOverflow, DifferenceOperation, DifferenceSettings, DisplayCalendar,
ResolvedRoundingOptions, TemporalUnit,
ResolvedRoundingOptions, TemporalUnit, UnitGroup,
},
parsers::{parse_date_time, IxdtfStringBuilder},
primitive::FiniteF64,
Sign, TemporalError, TemporalResult, TemporalUnwrap, TimeZone,
TemporalError, TemporalResult, TemporalUnwrap, TimeZone,
};
use alloc::{format, string::String};
use core::str::FromStr;
Expand Down Expand Up @@ -260,9 +260,10 @@ impl PlainDate {

// 4. Let resolvedOptions be ? SnapshotOwnProperties(? GetOptionsObject(options), null).
// 5. Let settings be ? GetDifferenceSettings(operation, resolvedOptions, DATE, « », "day", "day").
let (sign, resolved) = ResolvedRoundingOptions::from_diff_settings(
let resolved = ResolvedRoundingOptions::from_diff_settings(
settings,
op,
UnitGroup::Date,
TemporalUnit::Day,
TemporalUnit::Day,
)?;
Expand Down Expand Up @@ -303,9 +304,9 @@ impl PlainDate {
}
let result = Duration::from_normalized(duration, TemporalUnit::Day)?;
// 13. Return ! CreateTemporalDuration(sign × duration.[[Years]], sign × duration.[[Months]], sign × duration.[[Weeks]], sign × duration.[[Days]], 0, 0, 0, 0, 0, 0).
match sign {
Sign::Positive | Sign::Zero => Ok(result),
Sign::Negative => Ok(result.negated()),
match op {
DifferenceOperation::Until => Ok(result),
DifferenceOperation::Since => Ok(result.negated()),
}
}
}
Expand Down
13 changes: 7 additions & 6 deletions src/components/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ use crate::{
iso::{IsoDate, IsoDateTime, IsoTime},
options::{
ArithmeticOverflow, DifferenceOperation, DifferenceSettings, DisplayCalendar,
ResolvedRoundingOptions, RoundingOptions, TemporalUnit, ToStringRoundingOptions,
ResolvedRoundingOptions, RoundingOptions, TemporalUnit, ToStringRoundingOptions, UnitGroup,
},
parsers::{parse_date_time, IxdtfStringBuilder},
temporal_assert, Sign, TemporalError, TemporalResult, TemporalUnwrap, TimeZone,
temporal_assert, TemporalError, TemporalResult, TemporalUnwrap, TimeZone,
};
use alloc::string::String;
use core::{cmp::Ordering, str::FromStr};
Expand Down Expand Up @@ -131,9 +131,10 @@ impl PlainDateTime {
}

// 5. Let settings be ? GetDifferenceSettings(operation, resolvedOptions, datetime, « », "nanosecond", "day").
let (sign, options) = ResolvedRoundingOptions::from_diff_settings(
let options = ResolvedRoundingOptions::from_diff_settings(
settings,
op,
UnitGroup::DateTime,
TemporalUnit::Day,
TemporalUnit::Nanosecond,
)?;
Expand All @@ -149,9 +150,9 @@ impl PlainDateTime {
let result = Duration::from_normalized(norm_record, options.largest_unit)?;

// Step 12
match sign {
Sign::Positive | Sign::Zero => Ok(result),
Sign::Negative => Ok(result.negated()),
match op {
DifferenceOperation::Until => Ok(result),
DifferenceOperation::Since => Ok(result.negated()),
}
}

Expand Down
17 changes: 9 additions & 8 deletions src/components/duration/normalized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,9 @@ impl NormalizedDurationRecord {
/// Equivalent: `CreateNormalizedDurationRecord` & `CombineDateAndNormalizedTimeDuration`.
pub(crate) fn new(date: DateDuration, norm: NormalizedTimeDuration) -> TemporalResult<Self> {
if date.sign() != Sign::Zero && norm.sign() != Sign::Zero && date.sign() != norm.sign() {
return Err(TemporalError::range()
.with_message("DateDuration and NormalizedTimeDuration must agree."));
return Err(TemporalError::range().with_message(
"DateDuration and NormalizedTimeDuration must agree if both are not zero.",
));
}
Ok(Self { date, norm })
}
Expand Down Expand Up @@ -778,18 +779,18 @@ impl NormalizedDurationRecord {
// 4. Let largestUnitIndex be the ordinal index of the row of Table 22 whose "Singular" column contains largestUnit.
// 5. Let smallestUnitIndex be the ordinal index of the row of Table 22 whose "Singular" column contains smallestUnit.
// 6. Let unitIndex be smallestUnitIndex - 1.
let mut unit = smallest_unit + 1;
let mut smallest_unit = smallest_unit + 1;
// 7. Let done be false.
// 8. Repeat, while unitIndex ≤ largestUnitIndex and done is false,
while unit != TemporalUnit::Auto && unit <= largest_unit {
while smallest_unit != TemporalUnit::Auto && largest_unit < smallest_unit {
// a. Let unit be the value in the "Singular" column of Table 22 in the row whose ordinal index is unitIndex.
// b. If unit is not "week", or largestUnit is "week", then
if unit == TemporalUnit::Week || largest_unit != TemporalUnit::Week {
unit = unit + 1;
if smallest_unit == TemporalUnit::Week || largest_unit != TemporalUnit::Week {
smallest_unit = smallest_unit + 1;
continue;
}

let end_duration = match unit {
let end_duration = match smallest_unit {
// i. If unit is "year", then
TemporalUnit::Year => {
// 1. Let years be duration.[[Years]] + sign.
Expand Down Expand Up @@ -887,7 +888,7 @@ impl NormalizedDurationRecord {
break;
}
// c. Set unitIndex to unitIndex - 1.
unit = unit + 1;
smallest_unit = smallest_unit + 1;
}

Ok(duration)
Expand Down
48 changes: 24 additions & 24 deletions src/components/instant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ use crate::{
iso::{IsoDate, IsoDateTime, IsoTime},
options::{
ArithmeticOverflow, DifferenceOperation, DifferenceSettings, DisplayOffset,
ResolvedRoundingOptions, RoundingOptions, TemporalUnit, ToStringRoundingOptions,
ResolvedRoundingOptions, RoundingOptions, TemporalUnit, ToStringRoundingOptions, UnitGroup,
},
parsers::{parse_instant, IxdtfStringBuilder},
primitive::FiniteF64,
rounding::{IncrementRounder, Round},
Sign, TemporalError, TemporalResult, TemporalUnwrap, TimeZone, NS_MAX_INSTANT,
TemporalError, TemporalResult, TemporalUnwrap, TimeZone, NS_MAX_INSTANT,
};

use ixdtf::parsers::records::UtcOffsetRecordOrZ;
Expand All @@ -24,11 +24,12 @@ use num_traits::FromPrimitive;
use super::{
duration::normalized::{NormalizedDurationRecord, NormalizedTimeDuration},
timezone::TimeZoneProvider,
DateDuration,
};

const NANOSECONDS_PER_SECOND: f64 = 1e9;
const NANOSECONDS_PER_MINUTE: f64 = 60f64 * NANOSECONDS_PER_SECOND;
const NANOSECONDS_PER_HOUR: f64 = 60f64 * NANOSECONDS_PER_MINUTE;
const NANOSECONDS_PER_SECOND: i128 = 1_000_000_000;
const NANOSECONDS_PER_MINUTE: i128 = 60 * NANOSECONDS_PER_SECOND;
const NANOSECONDS_PER_HOUR: i128 = 60 * NANOSECONDS_PER_MINUTE;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct EpochNanoseconds(pub(crate) i128);
Expand Down Expand Up @@ -83,19 +84,14 @@ impl Instant {
// TODO: Update to `i128`?
/// Adds a `TimeDuration` to the current `Instant`.
///
/// Temporal-Proposal equivalent: `AddDurationToOrSubtractDurationFrom`.
/// Temporal-Proposal equivalent: `AddInstant`.
pub(crate) fn add_to_instant(&self, duration: &TimeDuration) -> TemporalResult<Self> {
let current_nanos = self.epoch_nanoseconds() as f64;
let result = current_nanos
+ duration.nanoseconds.0
+ (duration.microseconds.0 * 1000f64)
+ (duration.milliseconds.0 * 1_000_000f64)
+ (duration.seconds.0 * NANOSECONDS_PER_SECOND)
+ (duration.minutes.0 * NANOSECONDS_PER_MINUTE)
+ (duration.hours.0 * NANOSECONDS_PER_HOUR);
let norm = NormalizedTimeDuration::from_time_duration(duration);
let result = self.epoch_nanoseconds() + norm.0;
Ok(Self::from(EpochNanoseconds::try_from(result)?))
}

/// `temporal_rs` equivalent of `DifferenceInstant`
pub(crate) fn diff_instant_internal(
&self,
other: &Self,
Expand All @@ -104,7 +100,10 @@ impl Instant {
let diff =
NormalizedTimeDuration::from_nanosecond_difference(other.as_i128(), self.as_i128())?;
let (round_record, _) = diff.round(FiniteF64::default(), resolved_options)?;
Ok(round_record)
NormalizedDurationRecord::new(
DateDuration::default(),
round_record.normalized_time_duration(),
)
}

// TODO: Add test for `diff_instant`.
Expand All @@ -121,9 +120,10 @@ impl Instant {
// 2. Set other to ? ToTemporalInstant(other).
// 3. Let resolvedOptions be ? SnapshotOwnProperties(? GetOptionsObject(options), null).
// 4. Let settings be ? GetDifferenceSettings(operation, resolvedOptions, time, « », "nanosecond", "second").
let (sign, resolved_options) = ResolvedRoundingOptions::from_diff_settings(
let resolved_options = ResolvedRoundingOptions::from_diff_settings(
options,
op,
UnitGroup::Time,
TemporalUnit::Second,
TemporalUnit::Nanosecond,
)?;
Expand All @@ -138,9 +138,9 @@ impl Instant {
// 6. Let norm be diffRecord.[[NormalizedTimeDuration]].
// 7. Let result be ! BalanceTimeDuration(norm, settings.[[LargestUnit]]).
// 8. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
match sign {
Sign::Positive | Sign::Zero => Ok(result),
Sign::Negative => Ok(result.negated()),
match op {
DifferenceOperation::Until => Ok(result),
DifferenceOperation::Since => Ok(result.negated()),
}
}

Expand Down Expand Up @@ -338,12 +338,12 @@ impl FromStr for Instant {
// Find the offset
let offset = match ixdtf_record.offset {
UtcOffsetRecordOrZ::Offset(offset) => {
f64::from(offset.hour) * NANOSECONDS_PER_HOUR
+ f64::from(offset.minute) * NANOSECONDS_PER_MINUTE
+ f64::from(offset.second) * NANOSECONDS_PER_SECOND
+ f64::from(offset.nanosecond)
offset.hour as i128 * NANOSECONDS_PER_HOUR
+ i128::from(offset.minute) * NANOSECONDS_PER_MINUTE
+ i128::from(offset.second) * NANOSECONDS_PER_SECOND
+ i128::from(offset.nanosecond)
}
UtcOffsetRecordOrZ::Z => 0.0,
UtcOffsetRecordOrZ::Z => 0,
};
let nanoseconds = IsoDateTime::new_unchecked(iso_date, iso_time)
.as_nanoseconds()
Expand Down
13 changes: 7 additions & 6 deletions src/components/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ use crate::{
iso::IsoTime,
options::{
ArithmeticOverflow, DifferenceOperation, DifferenceSettings, ResolvedRoundingOptions,
RoundingIncrement, TemporalRoundingMode, TemporalUnit, ToStringRoundingOptions,
RoundingIncrement, TemporalRoundingMode, TemporalUnit, ToStringRoundingOptions, UnitGroup,
},
parsers::{parse_time, IxdtfStringBuilder},
primitive::FiniteF64,
Sign, TemporalError, TemporalResult,
TemporalError, TemporalResult,
};
use alloc::string::String;
use core::str::FromStr;
Expand Down Expand Up @@ -124,9 +124,10 @@ impl PlainTime {
// 2. Set other to ? ToTemporalTime(other).
// 3. Let resolvedOptions be ? SnapshotOwnProperties(? GetOptionsObject(options), null).
// 4. Let settings be ? GetDifferenceSettings(operation, resolvedOptions, TIME, « », "nanosecond", "hour").
let (sign, resolved) = ResolvedRoundingOptions::from_diff_settings(
let resolved = ResolvedRoundingOptions::from_diff_settings(
settings,
op,
UnitGroup::Time,
TemporalUnit::Hour,
TemporalUnit::Nanosecond,
)?;
Expand All @@ -151,9 +152,9 @@ impl PlainTime {
let result = TimeDuration::from_normalized(normalized_time, resolved.largest_unit)?.1;

// 8. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
match sign {
Sign::Positive | Sign::Zero => Ok(Duration::from(result)),
Sign::Negative => Ok(Duration::from(result.negated())),
match op {
DifferenceOperation::Until => Ok(Duration::from(result)),
DifferenceOperation::Since => Ok(Duration::from(result.negated())),
}
}
}
Expand Down
Loading
Loading