mirror of https://github.com/boa-dev/boa.git
Kevin
10 months ago
committed by
GitHub
7 changed files with 1285 additions and 971 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,461 @@ |
|||||||
|
//! Implementation of a `DateDuration`
|
||||||
|
|
||||||
|
use crate::{ |
||||||
|
components::{ |
||||||
|
calendar::CalendarProtocol, duration::TimeDuration, tz::TzProtocol, Date, DateTime, |
||||||
|
Duration, ZonedDateTime, |
||||||
|
}, |
||||||
|
options::{ArithmeticOverflow, TemporalRoundingMode, TemporalUnit}, |
||||||
|
utils, TemporalError, TemporalResult, NS_PER_DAY, |
||||||
|
}; |
||||||
|
|
||||||
|
use std::any::Any; |
||||||
|
|
||||||
|
/// `DateDuration` represents the [date duration record][spec] of the `Duration.`
|
||||||
|
///
|
||||||
|
/// These fields are laid out in the [Temporal Proposal][field spec] as 64-bit floating point numbers.
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-date-duration-records
|
||||||
|
/// [field spec]: https://tc39.es/proposal-temporal/#sec-properties-of-temporal-duration-instances
|
||||||
|
#[derive(Debug, Default, Clone, Copy)] |
||||||
|
pub struct DateDuration { |
||||||
|
pub(crate) years: f64, |
||||||
|
pub(crate) months: f64, |
||||||
|
pub(crate) weeks: f64, |
||||||
|
pub(crate) days: f64, |
||||||
|
} |
||||||
|
|
||||||
|
impl DateDuration { |
||||||
|
/// Creates a new, non-validated `DateDuration`.
|
||||||
|
#[inline] |
||||||
|
#[must_use] |
||||||
|
pub(crate) const fn new_unchecked(years: f64, months: f64, weeks: f64, days: f64) -> Self { |
||||||
|
Self { |
||||||
|
years, |
||||||
|
months, |
||||||
|
weeks, |
||||||
|
days, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl DateDuration { |
||||||
|
/// Creates a new `DateDuration` with provided values.
|
||||||
|
pub fn new(years: f64, months: f64, weeks: f64, days: f64) -> TemporalResult<Self> { |
||||||
|
let result = Self::new_unchecked(years, months, weeks, days); |
||||||
|
if !super::is_valid_duration(&result.into_iter().collect()) { |
||||||
|
return Err(TemporalError::range().with_message("Invalid DateDuration.")); |
||||||
|
} |
||||||
|
Ok(result) |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a `PartialDateDuration` with all fields set to `NaN`.
|
||||||
|
#[must_use] |
||||||
|
pub const fn partial() -> Self { |
||||||
|
Self { |
||||||
|
years: f64::NAN, |
||||||
|
months: f64::NAN, |
||||||
|
weeks: f64::NAN, |
||||||
|
days: f64::NAN, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a new `DateDuration` representing the absolute value of the current.
|
||||||
|
#[inline] |
||||||
|
#[must_use] |
||||||
|
pub fn abs(&self) -> Self { |
||||||
|
Self { |
||||||
|
years: self.years.abs(), |
||||||
|
months: self.months.abs(), |
||||||
|
weeks: self.weeks.abs(), |
||||||
|
days: self.days.abs(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the `[[years]]` value.
|
||||||
|
#[must_use] |
||||||
|
pub const fn years(&self) -> f64 { |
||||||
|
self.years |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the `[[months]]` value.
|
||||||
|
#[must_use] |
||||||
|
pub const fn months(&self) -> f64 { |
||||||
|
self.months |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the `[[weeks]]` value.
|
||||||
|
#[must_use] |
||||||
|
pub const fn weeks(&self) -> f64 { |
||||||
|
self.weeks |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the `[[days]]` value.
|
||||||
|
#[must_use] |
||||||
|
pub const fn days(&self) -> f64 { |
||||||
|
self.days |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the iterator for `DateDuration`
|
||||||
|
#[must_use] |
||||||
|
pub fn iter(&self) -> DateIter<'_> { |
||||||
|
<&Self as IntoIterator>::into_iter(self) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ==== DateDuration Operations ====
|
||||||
|
|
||||||
|
impl DateDuration { |
||||||
|
/// Rounds the current `DateDuration` returning a tuple of the rounded `DateDuration` and
|
||||||
|
/// the `total` value of the smallest unit prior to rounding.
|
||||||
|
#[allow(clippy::type_complexity, clippy::let_and_return)] |
||||||
|
pub fn round<C: CalendarProtocol, Z: TzProtocol>( |
||||||
|
&self, |
||||||
|
additional_time: Option<TimeDuration>, |
||||||
|
increment: f64, |
||||||
|
unit: TemporalUnit, |
||||||
|
rounding_mode: TemporalRoundingMode, |
||||||
|
relative_targets: ( |
||||||
|
Option<&Date<C>>, |
||||||
|
Option<&ZonedDateTime<C, Z>>, |
||||||
|
Option<&DateTime<C>>, |
||||||
|
), |
||||||
|
context: &mut dyn Any, |
||||||
|
) -> TemporalResult<(Self, f64)> { |
||||||
|
// 1. If plainRelativeTo is not present, set plainRelativeTo to undefined.
|
||||||
|
let plain_relative_to = relative_targets.0; |
||||||
|
// 2. If zonedRelativeTo is not present, set zonedRelativeTo to undefined.
|
||||||
|
let zoned_relative_to = relative_targets.1; |
||||||
|
// 3. If precalculatedPlainDateTime is not present, set precalculatedPlainDateTime to undefined.
|
||||||
|
let _ = relative_targets.2; |
||||||
|
|
||||||
|
let mut fractional_days = match unit { |
||||||
|
// 4. If unit is "year", "month", or "week", and plainRelativeTo is undefined, then
|
||||||
|
TemporalUnit::Year | TemporalUnit::Month | TemporalUnit::Week |
||||||
|
if plain_relative_to.is_none() => |
||||||
|
{ |
||||||
|
// a. Throw a RangeError exception.
|
||||||
|
return Err(TemporalError::range() |
||||||
|
.with_message("plainRelativeTo canot be undefined with given TemporalUnit")); |
||||||
|
} |
||||||
|
// 5. If unit is one of "year", "month", "week", or "day", then
|
||||||
|
TemporalUnit::Year | TemporalUnit::Month | TemporalUnit::Week | TemporalUnit::Day => { |
||||||
|
// a. Let nanoseconds be TotalDurationNanoseconds(hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
|
||||||
|
let nanoseconds = additional_time.unwrap_or_default().as_nanos(); |
||||||
|
|
||||||
|
// b. If zonedRelativeTo is not undefined, then
|
||||||
|
// i. Let intermediate be ? MoveRelativeZonedDateTime(zonedRelativeTo, years, months, weeks, days, precalculatedPlainDateTime).
|
||||||
|
// ii. Let result be ? NanosecondsToDays(nanoseconds, intermediate).
|
||||||
|
// iii. Let fractionalDays be days + result.[[Days]] + result.[[Nanoseconds]] / result.[[DayLength]].
|
||||||
|
// c. Else,
|
||||||
|
// i. Let fractionalDays be days + nanoseconds / nsPerDay.
|
||||||
|
// d. Set days, hours, minutes, seconds, milliseconds, microseconds, and nanoseconds to 0.
|
||||||
|
// e. Assert: fractionalSeconds is not used below.
|
||||||
|
if zoned_relative_to.is_none() { |
||||||
|
self.days + nanoseconds / NS_PER_DAY as f64 |
||||||
|
} else { |
||||||
|
// implementation of b: i-iii needed.
|
||||||
|
return Err(TemporalError::range().with_message("Not yet implemented.")); |
||||||
|
} |
||||||
|
} |
||||||
|
_ => { |
||||||
|
return Err(TemporalError::range() |
||||||
|
.with_message("Invalid TemporalUnit provided to DateDuration.round")) |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// 7. let total be unset.
|
||||||
|
// We begin matching against unit and return the remainder value.
|
||||||
|
match unit { |
||||||
|
// 8. If unit is "year", then
|
||||||
|
TemporalUnit::Year => { |
||||||
|
let plain_relative_to = plain_relative_to.expect("this must exist."); |
||||||
|
// a. Let calendar be plainRelativeTo.[[Calendar]].
|
||||||
|
let calendar = plain_relative_to.calendar(); |
||||||
|
|
||||||
|
// b. Let yearsDuration be ! CreateTemporalDuration(years, 0, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||||
|
let years = DateDuration::new_unchecked(self.years, 0.0, 0.0, 0.0); |
||||||
|
let years_duration = Duration::new_unchecked(years, TimeDuration::default()); |
||||||
|
|
||||||
|
// c. If calendar is an Object, then
|
||||||
|
// i. Let dateAdd be ? GetMethod(calendar, "dateAdd").
|
||||||
|
// d. Else,
|
||||||
|
// i. Let dateAdd be unused.
|
||||||
|
|
||||||
|
// e. Let yearsLater be ? AddDate(calendar, plainRelativeTo, yearsDuration, undefined, dateAdd).
|
||||||
|
let years_later = plain_relative_to.contextual_add_date( |
||||||
|
&years_duration, |
||||||
|
ArithmeticOverflow::Constrain, |
||||||
|
context, |
||||||
|
)?; |
||||||
|
|
||||||
|
// f. Let yearsMonthsWeeks be ! CreateTemporalDuration(years, months, weeks, 0, 0, 0, 0, 0, 0, 0).
|
||||||
|
let years_months_weeks = Duration::new_unchecked( |
||||||
|
Self::new_unchecked(self.years, self.months, self.weeks, 0.0), |
||||||
|
TimeDuration::default(), |
||||||
|
); |
||||||
|
|
||||||
|
// g. Let yearsMonthsWeeksLater be ? AddDate(calendar, plainRelativeTo, yearsMonthsWeeks, undefined, dateAdd).
|
||||||
|
let years_months_weeks_later = plain_relative_to.contextual_add_date( |
||||||
|
&years_months_weeks, |
||||||
|
ArithmeticOverflow::Constrain, |
||||||
|
context, |
||||||
|
)?; |
||||||
|
|
||||||
|
// h. Let monthsWeeksInDays be DaysUntil(yearsLater, yearsMonthsWeeksLater).
|
||||||
|
let months_weeks_in_days = years_later.days_until(&years_months_weeks_later); |
||||||
|
|
||||||
|
// i. Set plainRelativeTo to yearsLater.
|
||||||
|
let plain_relative_to = years_later; |
||||||
|
|
||||||
|
// j. Set fractionalDays to fractionalDays + monthsWeeksInDays.
|
||||||
|
fractional_days += f64::from(months_weeks_in_days); |
||||||
|
|
||||||
|
// k. Let isoResult be ! AddISODate(plainRelativeTo.[[ISOYear]]. plainRelativeTo.[[ISOMonth]], plainRelativeTo.[[ISODay]], 0, 0, 0, truncate(fractionalDays), "constrain").
|
||||||
|
let iso_result = plain_relative_to.iso_date().add_iso_date( |
||||||
|
&DateDuration::new_unchecked(0.0, 0.0, 0.0, fractional_days.trunc()), |
||||||
|
ArithmeticOverflow::Constrain, |
||||||
|
)?; |
||||||
|
|
||||||
|
// l. Let wholeDaysLater be ? CreateDate(isoResult.[[Year]], isoResult.[[Month]], isoResult.[[Day]], calendar).
|
||||||
|
let whole_days_later = Date::new_unchecked(iso_result, calendar.clone()); |
||||||
|
|
||||||
|
// m. Let untilOptions be OrdinaryObjectCreate(null).
|
||||||
|
// n. Perform ! CreateDataPropertyOrThrow(untilOptions, "largestUnit", "year").
|
||||||
|
// o. Let timePassed be ? DifferenceDate(calendar, plainRelativeTo, wholeDaysLater, untilOptions).
|
||||||
|
let time_passed = plain_relative_to.contextual_difference_date( |
||||||
|
&whole_days_later, |
||||||
|
TemporalUnit::Year, |
||||||
|
context, |
||||||
|
)?; |
||||||
|
|
||||||
|
// p. Let yearsPassed be timePassed.[[Years]].
|
||||||
|
let years_passed = time_passed.date.years(); |
||||||
|
|
||||||
|
// q. Set years to years + yearsPassed.
|
||||||
|
let years = self.years() + years_passed; |
||||||
|
|
||||||
|
// r. Let yearsDuration be ! CreateTemporalDuration(yearsPassed, 0, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||||
|
let years_duration = Duration::one_year(years_passed); |
||||||
|
|
||||||
|
// s. Let moveResult be ? MoveRelativeDate(calendar, plainRelativeTo, yearsDuration, dateAdd).
|
||||||
|
// t. Set plainRelativeTo to moveResult.[[RelativeTo]].
|
||||||
|
// u. Let daysPassed be moveResult.[[Days]].
|
||||||
|
let (plain_relative_to, days_passed) = |
||||||
|
plain_relative_to.move_relative_date(&years_duration, context)?; |
||||||
|
|
||||||
|
// v. Set fractionalDays to fractionalDays - daysPassed.
|
||||||
|
fractional_days -= days_passed; |
||||||
|
|
||||||
|
// w. If fractionalDays < 0, let sign be -1; else, let sign be 1.
|
||||||
|
let sign = if fractional_days < 0.0 { -1 } else { 1 }; |
||||||
|
|
||||||
|
// x. Let oneYear be ! CreateTemporalDuration(sign, 0, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||||
|
let one_year = Duration::one_year(f64::from(sign)); |
||||||
|
|
||||||
|
// y. Set moveResult to ? MoveRelativeDate(calendar, plainRelativeTo, oneYear, dateAdd).
|
||||||
|
// z. Let oneYearDays be moveResult.[[Days]].
|
||||||
|
let (_, one_year_days) = |
||||||
|
plain_relative_to.move_relative_date(&one_year, context)?; |
||||||
|
|
||||||
|
// aa. Let fractionalYears be years + fractionalDays / abs(oneYearDays).
|
||||||
|
let frac_years = years + (fractional_days / one_year_days.abs()); |
||||||
|
|
||||||
|
// ab. Set years to RoundNumberToIncrement(fractionalYears, increment, roundingMode).
|
||||||
|
let rounded_years = |
||||||
|
utils::round_number_to_increment(frac_years, increment, rounding_mode); |
||||||
|
|
||||||
|
// ac. Set total to fractionalYears.
|
||||||
|
// ad. Set months and weeks to 0.
|
||||||
|
let result = Self::new(rounded_years, 0f64, 0f64, 0f64)?; |
||||||
|
Ok((result, frac_years)) |
||||||
|
} |
||||||
|
// 9. Else if unit is "month", then
|
||||||
|
TemporalUnit::Month => { |
||||||
|
// a. Let calendar be plainRelativeTo.[[Calendar]].
|
||||||
|
let plain_relative_to = plain_relative_to.expect("this must exist."); |
||||||
|
|
||||||
|
// b. Let yearsMonths be ! CreateTemporalDuration(years, months, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||||
|
let years_months = Duration::from_date_duration(DateDuration::new_unchecked( |
||||||
|
self.years(), |
||||||
|
self.months(), |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
)); |
||||||
|
|
||||||
|
// c. If calendar is an Object, then
|
||||||
|
// i. Let dateAdd be ? GetMethod(calendar, "dateAdd").
|
||||||
|
// d. Else,
|
||||||
|
// i. Let dateAdd be unused.
|
||||||
|
|
||||||
|
// e. Let yearsMonthsLater be ? AddDate(calendar, plainRelativeTo, yearsMonths, undefined, dateAdd).
|
||||||
|
let years_months_later = plain_relative_to.contextual_add_date( |
||||||
|
&years_months, |
||||||
|
ArithmeticOverflow::Constrain, |
||||||
|
context, |
||||||
|
)?; |
||||||
|
|
||||||
|
// f. Let yearsMonthsWeeks be ! CreateTemporalDuration(years, months, weeks, 0, 0, 0, 0, 0, 0, 0).
|
||||||
|
let years_months_weeks = Duration::from_date_duration(DateDuration::new_unchecked( |
||||||
|
self.years(), |
||||||
|
self.months(), |
||||||
|
self.weeks(), |
||||||
|
0.0, |
||||||
|
)); |
||||||
|
|
||||||
|
// g. Let yearsMonthsWeeksLater be ? AddDate(calendar, plainRelativeTo, yearsMonthsWeeks, undefined, dateAdd).
|
||||||
|
let years_months_weeks_later = plain_relative_to.contextual_add_date( |
||||||
|
&years_months_weeks, |
||||||
|
ArithmeticOverflow::Constrain, |
||||||
|
context, |
||||||
|
)?; |
||||||
|
|
||||||
|
// h. Let weeksInDays be DaysUntil(yearsMonthsLater, yearsMonthsWeeksLater).
|
||||||
|
let weeks_in_days = years_months_later.days_until(&years_months_weeks_later); |
||||||
|
|
||||||
|
// i. Set plainRelativeTo to yearsMonthsLater.
|
||||||
|
let plain_relative_to = years_months_later; |
||||||
|
|
||||||
|
// j. Set fractionalDays to fractionalDays + weeksInDays.
|
||||||
|
fractional_days += f64::from(weeks_in_days); |
||||||
|
|
||||||
|
// k. If fractionalDays < 0, let sign be -1; else, let sign be 1.
|
||||||
|
let sign = if fractional_days < 0.0 { -1f64 } else { 1f64 }; |
||||||
|
|
||||||
|
// l. Let oneMonth be ! CreateTemporalDuration(0, sign, 0, 0, 0, 0, 0, 0, 0, 0).
|
||||||
|
let one_month = Duration::one_month(sign); |
||||||
|
|
||||||
|
// m. Let moveResult be ? MoveRelativeDate(calendar, plainRelativeTo, oneMonth, dateAdd).
|
||||||
|
// n. Set plainRelativeTo to moveResult.[[RelativeTo]].
|
||||||
|
// o. Let oneMonthDays be moveResult.[[Days]].
|
||||||
|
let (mut plain_relative_to, mut one_month_days) = |
||||||
|
plain_relative_to.move_relative_date(&one_month, context)?; |
||||||
|
|
||||||
|
let mut months = self.months; |
||||||
|
// p. Repeat, while abs(fractionalDays) ≥ abs(oneMonthDays),
|
||||||
|
while fractional_days.abs() >= one_month_days.abs() { |
||||||
|
// i. Set months to months + sign.
|
||||||
|
months += sign; |
||||||
|
|
||||||
|
// ii. Set fractionalDays to fractionalDays - oneMonthDays.
|
||||||
|
fractional_days -= one_month_days; |
||||||
|
|
||||||
|
// iii. Set moveResult to ? MoveRelativeDate(calendar, plainRelativeTo, oneMonth, dateAdd).
|
||||||
|
let move_result = plain_relative_to.move_relative_date(&one_month, context)?; |
||||||
|
|
||||||
|
// iv. Set plainRelativeTo to moveResult.[[RelativeTo]].
|
||||||
|
plain_relative_to = move_result.0; |
||||||
|
// v. Set oneMonthDays to moveResult.[[Days]].
|
||||||
|
one_month_days = move_result.1; |
||||||
|
} |
||||||
|
|
||||||
|
// q. Let fractionalMonths be months + fractionalDays / abs(oneMonthDays).
|
||||||
|
let frac_months = months + fractional_days / one_month_days.abs(); |
||||||
|
|
||||||
|
// r. Set months to RoundNumberToIncrement(fractionalMonths, increment, roundingMode).
|
||||||
|
let rounded_months = |
||||||
|
utils::round_number_to_increment(frac_months, increment, rounding_mode); |
||||||
|
|
||||||
|
// s. Set total to fractionalMonths.
|
||||||
|
// t. Set weeks to 0.
|
||||||
|
let result = Self::new(self.years, rounded_months, 0f64, 0f64)?; |
||||||
|
Ok((result, frac_months)) |
||||||
|
} |
||||||
|
// 10. Else if unit is "week", then
|
||||||
|
TemporalUnit::Week => { |
||||||
|
// a. Let calendar be plainRelativeTo.[[Calendar]].
|
||||||
|
let plain_relative_to = plain_relative_to.expect("date must exist given Week"); |
||||||
|
|
||||||
|
// b. If fractionalDays < 0, let sign be -1; else, let sign be 1.
|
||||||
|
let sign = if fractional_days < 0.0 { -1f64 } else { 1f64 }; |
||||||
|
|
||||||
|
// c. Let oneWeek be ! CreateTemporalDuration(0, 0, sign, 0, 0, 0, 0, 0, 0, 0).
|
||||||
|
let one_week = Duration::one_week(sign); |
||||||
|
|
||||||
|
// d. If calendar is an Object, then
|
||||||
|
// i. Let dateAdd be ? GetMethod(calendar, "dateAdd").
|
||||||
|
// e. Else,
|
||||||
|
// i. Let dateAdd be unused.
|
||||||
|
|
||||||
|
// f. Let moveResult be ? MoveRelativeDate(calendar, plainRelativeTo, oneWeek, dateAdd).
|
||||||
|
// g. Set plainRelativeTo to moveResult.[[RelativeTo]].
|
||||||
|
// h. Let oneWeekDays be moveResult.[[Days]].
|
||||||
|
let (mut plain_relative_to, mut one_week_days) = |
||||||
|
plain_relative_to.move_relative_date(&one_week, context)?; |
||||||
|
|
||||||
|
let mut weeks = self.weeks; |
||||||
|
// i. Repeat, while abs(fractionalDays) ≥ abs(oneWeekDays),
|
||||||
|
while fractional_days.abs() >= one_week_days.abs() { |
||||||
|
// i. Set weeks to weeks + sign.
|
||||||
|
weeks += sign; |
||||||
|
|
||||||
|
// ii. Set fractionalDays to fractionalDays - oneWeekDays.
|
||||||
|
fractional_days -= one_week_days; |
||||||
|
|
||||||
|
// iii. Set moveResult to ? MoveRelativeDate(calendar, plainRelativeTo, oneWeek, dateAdd).
|
||||||
|
let move_result = plain_relative_to.move_relative_date(&one_week, context)?; |
||||||
|
|
||||||
|
// iv. Set plainRelativeTo to moveResult.[[RelativeTo]].
|
||||||
|
plain_relative_to = move_result.0; |
||||||
|
// v. Set oneWeekDays to moveResult.[[Days]].
|
||||||
|
one_week_days = move_result.1; |
||||||
|
} |
||||||
|
|
||||||
|
// j. Let fractionalWeeks be weeks + fractionalDays / abs(oneWeekDays).
|
||||||
|
let frac_weeks = weeks + fractional_days / one_week_days.abs(); |
||||||
|
|
||||||
|
// k. Set weeks to RoundNumberToIncrement(fractionalWeeks, increment, roundingMode).
|
||||||
|
let rounded_weeks = |
||||||
|
utils::round_number_to_increment(frac_weeks, increment, rounding_mode); |
||||||
|
// l. Set total to fractionalWeeks.
|
||||||
|
let result = Self::new(self.years, self.months, rounded_weeks, 0f64)?; |
||||||
|
Ok((result, frac_weeks)) |
||||||
|
} |
||||||
|
// 11. Else if unit is "day", then
|
||||||
|
TemporalUnit::Day => { |
||||||
|
// a. Set days to RoundNumberToIncrement(fractionalDays, increment, roundingMode).
|
||||||
|
let rounded_days = |
||||||
|
utils::round_number_to_increment(fractional_days, increment, rounding_mode); |
||||||
|
// b. Set total to fractionalDays.
|
||||||
|
let result = Self::new(self.years, self.months, self.weeks, rounded_days)?; |
||||||
|
Ok((result, fractional_days)) |
||||||
|
} |
||||||
|
_ => unreachable!("All other TemporalUnits were returned early as invalid."), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a DateDuration { |
||||||
|
type Item = f64; |
||||||
|
type IntoIter = DateIter<'a>; |
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter { |
||||||
|
DateIter { |
||||||
|
date: self, |
||||||
|
index: 0, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// An iterator over the `DateDuration`
|
||||||
|
#[derive(Debug)] |
||||||
|
pub struct DateIter<'a> { |
||||||
|
date: &'a DateDuration, |
||||||
|
index: usize, |
||||||
|
} |
||||||
|
|
||||||
|
impl Iterator for DateIter<'_> { |
||||||
|
type Item = f64; |
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> { |
||||||
|
let result = match self.index { |
||||||
|
0 => Some(self.date.years), |
||||||
|
1 => Some(self.date.months), |
||||||
|
2 => Some(self.date.weeks), |
||||||
|
3 => Some(self.date.days), |
||||||
|
_ => None, |
||||||
|
}; |
||||||
|
self.index += 1; |
||||||
|
result |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,545 @@ |
|||||||
|
//! An implementation of `TimeDuration` and it's methods.
|
||||||
|
|
||||||
|
use crate::{ |
||||||
|
options::{TemporalRoundingMode, TemporalUnit}, |
||||||
|
utils, TemporalError, TemporalResult, |
||||||
|
}; |
||||||
|
|
||||||
|
use super::is_valid_duration; |
||||||
|
|
||||||
|
/// `TimeDuration` represents the [Time Duration record][spec] of the `Duration.`
|
||||||
|
///
|
||||||
|
/// These fields are laid out in the [Temporal Proposal][field spec] as 64-bit floating point numbers.
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-time-duration-records
|
||||||
|
/// [field spec]: https://tc39.es/proposal-temporal/#sec-properties-of-temporal-duration-instances
|
||||||
|
#[derive(Debug, Default, Clone, Copy)] |
||||||
|
pub struct TimeDuration { |
||||||
|
pub(crate) hours: f64, |
||||||
|
pub(crate) minutes: f64, |
||||||
|
pub(crate) seconds: f64, |
||||||
|
pub(crate) milliseconds: f64, |
||||||
|
pub(crate) microseconds: f64, |
||||||
|
pub(crate) nanoseconds: f64, |
||||||
|
} |
||||||
|
// ==== TimeDuration Private API ====
|
||||||
|
|
||||||
|
impl TimeDuration { |
||||||
|
/// Creates a new `TimeDuration`.
|
||||||
|
#[must_use] |
||||||
|
pub(crate) const fn new_unchecked( |
||||||
|
hours: f64, |
||||||
|
minutes: f64, |
||||||
|
seconds: f64, |
||||||
|
milliseconds: f64, |
||||||
|
microseconds: f64, |
||||||
|
nanoseconds: f64, |
||||||
|
) -> Self { |
||||||
|
Self { |
||||||
|
hours, |
||||||
|
minutes, |
||||||
|
seconds, |
||||||
|
milliseconds, |
||||||
|
microseconds, |
||||||
|
nanoseconds, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the current `TimeDuration` as nanoseconds.
|
||||||
|
#[inline] |
||||||
|
pub(crate) fn as_nanos(&self) -> f64 { |
||||||
|
self.hours |
||||||
|
.mul_add(60_f64, self.minutes) |
||||||
|
.mul_add(60_f64, self.seconds) |
||||||
|
.mul_add(1_000_f64, self.milliseconds) |
||||||
|
.mul_add(1_000_f64, self.microseconds) |
||||||
|
.mul_add(1_000_f64, self.nanoseconds) |
||||||
|
} |
||||||
|
|
||||||
|
/// Abstract Operation 7.5.18 `BalancePossiblyInfiniteDuration ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, largestUnit )`
|
||||||
|
///
|
||||||
|
/// This function will balance the current `TimeDuration`. It returns the balanced `day` and `TimeDuration` value.
|
||||||
|
#[allow(clippy::too_many_arguments)] |
||||||
|
pub(crate) fn balance_possibly_infinite_time_duration( |
||||||
|
days: f64, |
||||||
|
hours: f64, |
||||||
|
minutes: f64, |
||||||
|
seconds: f64, |
||||||
|
milliseconds: f64, |
||||||
|
microseconds: f64, |
||||||
|
nanoseconds: f64, |
||||||
|
largest_unit: TemporalUnit, |
||||||
|
) -> TemporalResult<(f64, Option<Self>)> { |
||||||
|
// 1. Set hours to hours + days × 24.
|
||||||
|
let hours = hours + (days * 24f64); |
||||||
|
|
||||||
|
// 2. Set nanoseconds to TotalDurationNanoseconds(hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
|
||||||
|
let mut nanoseconds = Self::new_unchecked( |
||||||
|
hours, |
||||||
|
minutes, |
||||||
|
seconds, |
||||||
|
milliseconds, |
||||||
|
microseconds, |
||||||
|
nanoseconds, |
||||||
|
) |
||||||
|
.as_nanos(); |
||||||
|
|
||||||
|
// 3. Set days, hours, minutes, seconds, milliseconds, and microseconds to 0.
|
||||||
|
let mut days = 0f64; |
||||||
|
let mut hours = 0f64; |
||||||
|
let mut minutes = 0f64; |
||||||
|
let mut seconds = 0f64; |
||||||
|
let mut milliseconds = 0f64; |
||||||
|
let mut microseconds = 0f64; |
||||||
|
|
||||||
|
// 4. If nanoseconds < 0, let sign be -1; else, let sign be 1.
|
||||||
|
let sign = if nanoseconds < 0f64 { -1 } else { 1 }; |
||||||
|
// 5. Set nanoseconds to abs(nanoseconds).
|
||||||
|
nanoseconds = nanoseconds.abs(); |
||||||
|
|
||||||
|
match largest_unit { |
||||||
|
// 9. If largestUnit is "year", "month", "week", "day", or "hour", then
|
||||||
|
TemporalUnit::Year |
||||||
|
| TemporalUnit::Month |
||||||
|
| TemporalUnit::Week |
||||||
|
| TemporalUnit::Day |
||||||
|
| TemporalUnit::Hour => { |
||||||
|
// a. Set microseconds to floor(nanoseconds / 1000).
|
||||||
|
microseconds = (nanoseconds / 1000f64).floor(); |
||||||
|
// b. Set nanoseconds to nanoseconds modulo 1000.
|
||||||
|
nanoseconds %= 1000f64; |
||||||
|
|
||||||
|
// c. Set milliseconds to floor(microseconds / 1000).
|
||||||
|
milliseconds = (microseconds / 1000f64).floor(); |
||||||
|
// d. Set microseconds to microseconds modulo 1000.
|
||||||
|
microseconds %= 1000f64; |
||||||
|
|
||||||
|
// e. Set seconds to floor(milliseconds / 1000).
|
||||||
|
seconds = (milliseconds / 1000f64).floor(); |
||||||
|
// f. Set milliseconds to milliseconds modulo 1000.
|
||||||
|
milliseconds %= 1000f64; |
||||||
|
|
||||||
|
// g. Set minutes to floor(seconds / 60).
|
||||||
|
minutes = (seconds / 60f64).floor(); |
||||||
|
// h. Set seconds to seconds modulo 60.
|
||||||
|
seconds %= 60f64; |
||||||
|
|
||||||
|
// i. Set hours to floor(minutes / 60).
|
||||||
|
hours = (minutes / 60f64).floor(); |
||||||
|
// j. Set minutes to minutes modulo 60.
|
||||||
|
minutes %= 60f64; |
||||||
|
|
||||||
|
// k. Set days to floor(hours / 24).
|
||||||
|
days = (hours / 24f64).floor(); |
||||||
|
// l. Set hours to hours modulo 24.
|
||||||
|
hours %= 24f64; |
||||||
|
} |
||||||
|
// 10. Else if largestUnit is "minute", then
|
||||||
|
TemporalUnit::Minute => { |
||||||
|
// a. Set microseconds to floor(nanoseconds / 1000).
|
||||||
|
// b. Set nanoseconds to nanoseconds modulo 1000.
|
||||||
|
microseconds = (nanoseconds / 1000f64).floor(); |
||||||
|
nanoseconds %= 1000f64; |
||||||
|
|
||||||
|
// c. Set milliseconds to floor(microseconds / 1000).
|
||||||
|
// d. Set microseconds to microseconds modulo 1000.
|
||||||
|
milliseconds = (microseconds / 1000f64).floor(); |
||||||
|
microseconds %= 1000f64; |
||||||
|
|
||||||
|
// e. Set seconds to floor(milliseconds / 1000).
|
||||||
|
// f. Set milliseconds to milliseconds modulo 1000.
|
||||||
|
seconds = (milliseconds / 1000f64).floor(); |
||||||
|
milliseconds %= 1000f64; |
||||||
|
|
||||||
|
// g. Set minutes to floor(seconds / 60).
|
||||||
|
// h. Set seconds to seconds modulo 60.
|
||||||
|
minutes = (seconds / 60f64).floor(); |
||||||
|
seconds %= 60f64; |
||||||
|
} |
||||||
|
// 11. Else if largestUnit is "second", then
|
||||||
|
TemporalUnit::Second => { |
||||||
|
// a. Set microseconds to floor(nanoseconds / 1000).
|
||||||
|
// b. Set nanoseconds to nanoseconds modulo 1000.
|
||||||
|
microseconds = (nanoseconds / 1000f64).floor(); |
||||||
|
nanoseconds %= 1000f64; |
||||||
|
|
||||||
|
// c. Set milliseconds to floor(microseconds / 1000).
|
||||||
|
// d. Set microseconds to microseconds modulo 1000.
|
||||||
|
milliseconds = (microseconds / 1000f64).floor(); |
||||||
|
microseconds %= 1000f64; |
||||||
|
|
||||||
|
// e. Set seconds to floor(milliseconds / 1000).
|
||||||
|
// f. Set milliseconds to milliseconds modulo 1000.
|
||||||
|
seconds = (milliseconds / 1000f64).floor(); |
||||||
|
milliseconds %= 1000f64; |
||||||
|
} |
||||||
|
// 12. Else if largestUnit is "millisecond", then
|
||||||
|
TemporalUnit::Millisecond => { |
||||||
|
// a. Set microseconds to floor(nanoseconds / 1000).
|
||||||
|
// b. Set nanoseconds to nanoseconds modulo 1000.
|
||||||
|
microseconds = (nanoseconds / 1000f64).floor(); |
||||||
|
nanoseconds %= 1000f64; |
||||||
|
|
||||||
|
// c. Set milliseconds to floor(microseconds / 1000).
|
||||||
|
// d. Set microseconds to microseconds modulo 1000.
|
||||||
|
milliseconds = (microseconds / 1000f64).floor(); |
||||||
|
microseconds %= 1000f64; |
||||||
|
} |
||||||
|
// 13. Else if largestUnit is "microsecond", then
|
||||||
|
TemporalUnit::Microsecond => { |
||||||
|
// a. Set microseconds to floor(nanoseconds / 1000).
|
||||||
|
// b. Set nanoseconds to nanoseconds modulo 1000.
|
||||||
|
microseconds = (nanoseconds / 1000f64).floor(); |
||||||
|
nanoseconds %= 1000f64; |
||||||
|
} |
||||||
|
// 14. Else,
|
||||||
|
// a. Assert: largestUnit is "nanosecond".
|
||||||
|
_ => debug_assert!(largest_unit == TemporalUnit::Nanosecond), |
||||||
|
} |
||||||
|
|
||||||
|
let result_values = Vec::from(&[ |
||||||
|
days, |
||||||
|
hours, |
||||||
|
minutes, |
||||||
|
seconds, |
||||||
|
milliseconds, |
||||||
|
microseconds, |
||||||
|
nanoseconds, |
||||||
|
]); |
||||||
|
// 15. For each value v of « days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds », do
|
||||||
|
for value in result_values { |
||||||
|
// a. If 𝔽(v) is not finite, then
|
||||||
|
if !value.is_finite() { |
||||||
|
// i. If sign = 1, then
|
||||||
|
if sign == 1 { |
||||||
|
// 1. Return positive overflow.
|
||||||
|
return Ok((f64::INFINITY, None)); |
||||||
|
} |
||||||
|
// ii. Else if sign = -1, then
|
||||||
|
// 1. Return negative overflow.
|
||||||
|
return Ok((f64::NEG_INFINITY, None)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let sign = f64::from(sign); |
||||||
|
|
||||||
|
// 16. Return ? CreateTimeDurationRecord(days, hours × sign, minutes × sign, seconds × sign, milliseconds × sign, microseconds × sign, nanoseconds × sign).
|
||||||
|
let result = Self::new( |
||||||
|
hours * sign, |
||||||
|
minutes * sign, |
||||||
|
seconds * sign, |
||||||
|
milliseconds * sign, |
||||||
|
microseconds * sign, |
||||||
|
nanoseconds * sign, |
||||||
|
)?; |
||||||
|
|
||||||
|
Ok((days, Some(result))) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ==== TimeDuration's public API ====
|
||||||
|
|
||||||
|
impl TimeDuration { |
||||||
|
/// Creates a new validated `TimeDuration`.
|
||||||
|
pub fn new( |
||||||
|
hours: f64, |
||||||
|
minutes: f64, |
||||||
|
seconds: f64, |
||||||
|
milliseconds: f64, |
||||||
|
microseconds: f64, |
||||||
|
nanoseconds: f64, |
||||||
|
) -> TemporalResult<Self> { |
||||||
|
let result = Self::new_unchecked( |
||||||
|
hours, |
||||||
|
minutes, |
||||||
|
seconds, |
||||||
|
milliseconds, |
||||||
|
microseconds, |
||||||
|
nanoseconds, |
||||||
|
); |
||||||
|
if !is_valid_duration(&result.into_iter().collect()) { |
||||||
|
return Err( |
||||||
|
TemporalError::range().with_message("Attempted to create an invalid TimeDuration.") |
||||||
|
); |
||||||
|
} |
||||||
|
Ok(result) |
||||||
|
} |
||||||
|
|
||||||
|
/// Creates a partial `TimeDuration` with all values set to `NaN`.
|
||||||
|
#[must_use] |
||||||
|
pub const fn partial() -> Self { |
||||||
|
Self { |
||||||
|
hours: f64::NAN, |
||||||
|
minutes: f64::NAN, |
||||||
|
seconds: f64::NAN, |
||||||
|
milliseconds: f64::NAN, |
||||||
|
microseconds: f64::NAN, |
||||||
|
nanoseconds: f64::NAN, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a new `TimeDuration` representing the absolute value of the current.
|
||||||
|
#[inline] |
||||||
|
#[must_use] |
||||||
|
pub fn abs(&self) -> Self { |
||||||
|
Self { |
||||||
|
hours: self.hours.abs(), |
||||||
|
minutes: self.minutes.abs(), |
||||||
|
seconds: self.seconds.abs(), |
||||||
|
milliseconds: self.milliseconds.abs(), |
||||||
|
microseconds: self.microseconds.abs(), |
||||||
|
nanoseconds: self.nanoseconds.abs(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Balances a `TimeDuration` given a day value and the largest unit. `balance` will return
|
||||||
|
/// the balanced `day` and `TimeDuration`.
|
||||||
|
///
|
||||||
|
/// # Errors:
|
||||||
|
/// - Will error if provided duration is invalid
|
||||||
|
pub fn balance(&self, days: f64, largest_unit: TemporalUnit) -> TemporalResult<(f64, Self)> { |
||||||
|
let result = Self::balance_possibly_infinite_time_duration( |
||||||
|
days, |
||||||
|
self.hours, |
||||||
|
self.minutes, |
||||||
|
self.seconds, |
||||||
|
self.milliseconds, |
||||||
|
self.microseconds, |
||||||
|
self.nanoseconds, |
||||||
|
largest_unit, |
||||||
|
)?; |
||||||
|
let Some(time_duration) = result.1 else { |
||||||
|
return Err(TemporalError::range().with_message("Invalid balance TimeDuration.")); |
||||||
|
}; |
||||||
|
Ok((result.0, time_duration)) |
||||||
|
} |
||||||
|
|
||||||
|
/// Utility function for returning if values in a valid range.
|
||||||
|
#[inline] |
||||||
|
#[must_use] |
||||||
|
pub fn is_within_range(&self) -> bool { |
||||||
|
self.hours.abs() < 24f64 |
||||||
|
&& self.minutes.abs() < 60f64 |
||||||
|
&& self.seconds.abs() < 60f64 |
||||||
|
&& self.milliseconds.abs() < 1000f64 |
||||||
|
&& self.milliseconds.abs() < 1000f64 |
||||||
|
&& self.milliseconds.abs() < 1000f64 |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the `[[hours]]` value.
|
||||||
|
#[must_use] |
||||||
|
pub const fn hours(&self) -> f64 { |
||||||
|
self.hours |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the `[[minutes]]` value.
|
||||||
|
#[must_use] |
||||||
|
pub const fn minutes(&self) -> f64 { |
||||||
|
self.minutes |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the `[[seconds]]` value.
|
||||||
|
#[must_use] |
||||||
|
pub const fn seconds(&self) -> f64 { |
||||||
|
self.seconds |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the `[[milliseconds]]` value.
|
||||||
|
#[must_use] |
||||||
|
pub const fn milliseconds(&self) -> f64 { |
||||||
|
self.milliseconds |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the `[[microseconds]]` value.
|
||||||
|
#[must_use] |
||||||
|
pub const fn microseconds(&self) -> f64 { |
||||||
|
self.microseconds |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the `[[nanoseconds]]` value.
|
||||||
|
#[must_use] |
||||||
|
pub const fn nanoseconds(&self) -> f64 { |
||||||
|
self.nanoseconds |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the `TimeDuration`'s iterator.
|
||||||
|
#[must_use] |
||||||
|
pub fn iter(&self) -> TimeIter<'_> { |
||||||
|
<&Self as IntoIterator>::into_iter(self) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ==== TimeDuration method impls ====
|
||||||
|
|
||||||
|
impl TimeDuration { |
||||||
|
/// Rounds the current `TimeDuration` given a rounding increment, unit and rounding mode. `round` will return a tuple of the rounded `TimeDuration` and
|
||||||
|
/// the `total` value of the smallest unit prior to rounding.
|
||||||
|
#[inline] |
||||||
|
pub fn round( |
||||||
|
&self, |
||||||
|
increment: f64, |
||||||
|
unit: TemporalUnit, |
||||||
|
rounding_mode: TemporalRoundingMode, |
||||||
|
) -> TemporalResult<(Self, f64)> { |
||||||
|
let fraction_seconds = match unit { |
||||||
|
TemporalUnit::Year |
||||||
|
| TemporalUnit::Month |
||||||
|
| TemporalUnit::Week |
||||||
|
| TemporalUnit::Day |
||||||
|
| TemporalUnit::Auto => { |
||||||
|
return Err(TemporalError::r#type() |
||||||
|
.with_message("Invalid unit provided to for TimeDuration to round.")) |
||||||
|
} |
||||||
|
_ => self.nanoseconds().mul_add( |
||||||
|
1_000_000_000f64, |
||||||
|
self.microseconds().mul_add( |
||||||
|
1_000_000f64, |
||||||
|
self.milliseconds().mul_add(1000f64, self.seconds()), |
||||||
|
), |
||||||
|
), |
||||||
|
}; |
||||||
|
|
||||||
|
match unit { |
||||||
|
// 12. Else if unit is "hour", then
|
||||||
|
TemporalUnit::Hour => { |
||||||
|
// a. Let fractionalHours be (fractionalSeconds / 60 + minutes) / 60 + hours.
|
||||||
|
let frac_hours = (fraction_seconds / 60f64 + self.minutes) / 60f64 + self.hours; |
||||||
|
// b. Set hours to RoundNumberToIncrement(fractionalHours, increment, roundingMode).
|
||||||
|
let rounded_hours = |
||||||
|
utils::round_number_to_increment(frac_hours, increment, rounding_mode); |
||||||
|
// c. Set total to fractionalHours.
|
||||||
|
// d. Set minutes, seconds, milliseconds, microseconds, and nanoseconds to 0.
|
||||||
|
let result = Self::new(rounded_hours, 0f64, 0f64, 0f64, 0f64, 0f64)?; |
||||||
|
Ok((result, frac_hours)) |
||||||
|
} |
||||||
|
// 13. Else if unit is "minute", then
|
||||||
|
TemporalUnit::Minute => { |
||||||
|
// a. Let fractionalMinutes be fractionalSeconds / 60 + minutes.
|
||||||
|
let frac_minutes = fraction_seconds / 60f64 + self.minutes; |
||||||
|
// b. Set minutes to RoundNumberToIncrement(fractionalMinutes, increment, roundingMode).
|
||||||
|
let rounded_minutes = |
||||||
|
utils::round_number_to_increment(frac_minutes, increment, rounding_mode); |
||||||
|
// c. Set total to fractionalMinutes.
|
||||||
|
// d. Set seconds, milliseconds, microseconds, and nanoseconds to 0.
|
||||||
|
let result = Self::new(self.hours, rounded_minutes, 0f64, 0f64, 0f64, 0f64)?; |
||||||
|
|
||||||
|
Ok((result, frac_minutes)) |
||||||
|
} |
||||||
|
// 14. Else if unit is "second", then
|
||||||
|
TemporalUnit::Second => { |
||||||
|
// a. Set seconds to RoundNumberToIncrement(fractionalSeconds, increment, roundingMode).
|
||||||
|
let rounded_seconds = |
||||||
|
utils::round_number_to_increment(fraction_seconds, increment, rounding_mode); |
||||||
|
// b. Set total to fractionalSeconds.
|
||||||
|
// c. Set milliseconds, microseconds, and nanoseconds to 0.
|
||||||
|
let result = |
||||||
|
Self::new(self.hours, self.minutes, rounded_seconds, 0f64, 0f64, 0f64)?; |
||||||
|
|
||||||
|
Ok((result, fraction_seconds)) |
||||||
|
} |
||||||
|
// 15. Else if unit is "millisecond", then
|
||||||
|
TemporalUnit::Millisecond => { |
||||||
|
// a. Let fractionalMilliseconds be nanoseconds × 10-6 + microseconds × 10-3 + milliseconds.
|
||||||
|
let fraction_millis = self.nanoseconds.mul_add( |
||||||
|
1_000_000f64, |
||||||
|
self.microseconds.mul_add(1_000f64, self.milliseconds), |
||||||
|
); |
||||||
|
|
||||||
|
// b. Set milliseconds to RoundNumberToIncrement(fractionalMilliseconds, increment, roundingMode).
|
||||||
|
let rounded_millis = |
||||||
|
utils::round_number_to_increment(fraction_millis, increment, rounding_mode); |
||||||
|
|
||||||
|
// c. Set total to fractionalMilliseconds.
|
||||||
|
// d. Set microseconds and nanoseconds to 0.
|
||||||
|
let result = Self::new( |
||||||
|
self.hours, |
||||||
|
self.minutes, |
||||||
|
self.seconds, |
||||||
|
rounded_millis, |
||||||
|
0f64, |
||||||
|
0f64, |
||||||
|
)?; |
||||||
|
Ok((result, fraction_millis)) |
||||||
|
} |
||||||
|
// 16. Else if unit is "microsecond", then
|
||||||
|
TemporalUnit::Microsecond => { |
||||||
|
// a. Let fractionalMicroseconds be nanoseconds × 10-3 + microseconds.
|
||||||
|
let frac_micros = self.nanoseconds.mul_add(1_000f64, self.microseconds); |
||||||
|
|
||||||
|
// b. Set microseconds to RoundNumberToIncrement(fractionalMicroseconds, increment, roundingMode).
|
||||||
|
let rounded_micros = |
||||||
|
utils::round_number_to_increment(frac_micros, increment, rounding_mode); |
||||||
|
|
||||||
|
// c. Set total to fractionalMicroseconds.
|
||||||
|
// d. Set nanoseconds to 0.
|
||||||
|
let result = Self::new( |
||||||
|
self.hours, |
||||||
|
self.minutes, |
||||||
|
self.seconds, |
||||||
|
self.milliseconds, |
||||||
|
rounded_micros, |
||||||
|
0f64, |
||||||
|
)?; |
||||||
|
Ok((result, frac_micros)) |
||||||
|
} |
||||||
|
// 17. Else,
|
||||||
|
TemporalUnit::Nanosecond => { |
||||||
|
// a. Assert: unit is "nanosecond".
|
||||||
|
// b. Set total to nanoseconds.
|
||||||
|
let total = self.nanoseconds; |
||||||
|
// c. Set nanoseconds to RoundNumberToIncrement(nanoseconds, increment, roundingMode).
|
||||||
|
let rounded_nanos = |
||||||
|
utils::round_number_to_increment(self.nanoseconds, increment, rounding_mode); |
||||||
|
|
||||||
|
let result = Self::new( |
||||||
|
self.hours, |
||||||
|
self.minutes, |
||||||
|
self.seconds, |
||||||
|
self.milliseconds, |
||||||
|
self.microseconds, |
||||||
|
rounded_nanos, |
||||||
|
)?; |
||||||
|
|
||||||
|
Ok((result, total)) |
||||||
|
} |
||||||
|
_ => unreachable!("All other units early return error."), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a TimeDuration { |
||||||
|
type Item = f64; |
||||||
|
type IntoIter = TimeIter<'a>; |
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter { |
||||||
|
TimeIter { |
||||||
|
time: self, |
||||||
|
index: 0, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// An iterator over a `TimeDuration`.
|
||||||
|
#[derive(Debug, Clone)] |
||||||
|
pub struct TimeIter<'a> { |
||||||
|
time: &'a TimeDuration, |
||||||
|
index: usize, |
||||||
|
} |
||||||
|
|
||||||
|
impl Iterator for TimeIter<'_> { |
||||||
|
type Item = f64; |
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> { |
||||||
|
let result = match self.index { |
||||||
|
0 => Some(self.time.hours), |
||||||
|
1 => Some(self.time.minutes), |
||||||
|
2 => Some(self.time.seconds), |
||||||
|
3 => Some(self.time.milliseconds), |
||||||
|
4 => Some(self.time.microseconds), |
||||||
|
5 => Some(self.time.nanoseconds), |
||||||
|
_ => None, |
||||||
|
}; |
||||||
|
self.index += 1; |
||||||
|
result |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue