///|
/// A Temporal unit, from `Table 21: Temporal units by descending magnitude`.
///
/// The declaration order is significant: comparison operators use it, so a
/// larger unit compares greater. `Auto` sorts below every real unit, matching
/// the discriminant order `temporal_rs` relies on.
///
/// Spec:
pub(all) enum DateTimeUnit {
/// The `"auto"` sentinel, resolved to a concrete unit before use.
Auto
/// Nanoseconds.
Nanosecond
/// Microseconds.
Microsecond
/// Milliseconds.
Millisecond
/// Seconds.
Second
/// Minutes.
Minute
/// Hours.
Hour
/// Days.
Day
/// Weeks.
Week
/// Months.
Month
/// Years.
Year
} derive(Eq, Compare, Hash, Debug)
///|
pub impl Show for DateTimeUnit with fn output(self, logger) {
logger.write_string(self.to_singular())
}
///|
/// Returns the singular option string for this unit, as accepted and produced
/// by the Temporal API.
pub fn DateTimeUnit::to_singular(self : DateTimeUnit) -> String {
match self {
Auto => "auto"
Year => "year"
Month => "month"
Week => "week"
Day => "day"
Hour => "hour"
Minute => "minute"
Second => "second"
Millisecond => "millisecond"
Microsecond => "microsecond"
Nanosecond => "nanosecond"
}
}
///|
/// Parses a unit from its option string. Both singular and plural spellings
/// are accepted, as in `GetTemporalUnitValuedOption`.
///
/// ```mbt check
/// test {
/// inspect(@temporal.DateTimeUnit::of_string("days"), content="day")
/// inspect(@temporal.DateTimeUnit::of_string("nanosecond"), content="nanosecond")
/// }
/// ```
pub fn DateTimeUnit::of_string(s : String) -> DateTimeUnit raise TemporalError {
match s {
"auto" => Auto
"year" | "years" => Year
"month" | "months" => Month
"week" | "weeks" => Week
"day" | "days" => Day
"hour" | "hours" => Hour
"minute" | "minutes" => Minute
"second" | "seconds" => Second
"millisecond" | "milliseconds" => Millisecond
"microsecond" | "microseconds" => Microsecond
"nanosecond" | "nanoseconds" => Nanosecond
_ => raise RangeError("'\{s}' is not a valid Temporal unit")
}
}
///|
/// Returns whether this is one of the calendar units: year, month or week.
pub fn DateTimeUnit::is_calendar_unit(self : DateTimeUnit) -> Bool {
self is (Year | Month | Week)
}
///|
/// Returns whether this unit belongs to the date group: year, month, week or
/// day.
pub fn DateTimeUnit::is_date_unit(self : DateTimeUnit) -> Bool {
self is (Year | Month | Week | Day)
}
///|
/// Returns whether this unit belongs to the time group: hour through
/// nanosecond.
pub fn DateTimeUnit::is_time_unit(self : DateTimeUnit) -> Bool {
self is (Hour | Minute | Second | Millisecond | Microsecond | Nanosecond)
}
///|
/// Returns the length of this unit in nanoseconds, or `None` for units whose
/// length is not fixed (year, month, week) and for `Auto`.
pub fn DateTimeUnit::as_nanoseconds(self : DateTimeUnit) -> Int64? {
match self {
Year | Month | Week | Auto => None
Day => Some(NS_PER_DAY)
Hour => Some(NS_PER_HOUR)
Minute => Some(NS_PER_MINUTE)
Second => Some(NS_PER_SECOND)
Millisecond => Some(1_000_000L)
Microsecond => Some(1_000L)
Nanosecond => Some(1L)
}
}
///|
/// `MaximumTemporalDurationRoundingIncrement`: the largest rounding increment
/// permitted for this unit, or `None` when unbounded.
pub fn DateTimeUnit::to_maximum_rounding_increment(self : DateTimeUnit) -> Int? {
match self {
Year | Month | Week | Day | Auto => None
Hour => Some(24)
Minute | Second => Some(60)
Millisecond | Microsecond | Nanosecond => Some(1000)
}
}
///|
/// `LargerOfTwoTemporalUnits`.
pub fn DateTimeUnit::larger(
self : DateTimeUnit,
other : DateTimeUnit,
) -> DateTimeUnit {
if self >= other {
self
} else {
other
}
}
///|
/// Returns the ordinal of this unit in `Table 21`, counting down from year.
///
/// Used by `BubbleRelativeDuration` to step between adjacent units.
fn DateTimeUnit::table_index(self : DateTimeUnit) -> Int raise TemporalError {
match self {
Year => 0
Month => 1
Week => 2
Day => 3
Hour => 4
Minute => 5
Second => 6
Millisecond => 7
Microsecond => 8
Nanosecond => 9
Auto => raise AssertError("'auto' units are not allowed during comparison")
}
}
///|
/// Inverse of [`DateTimeUnit::table_index`].
fn unit_from_table_index(index : Int) -> DateTimeUnit {
match index {
0 => Year
1 => Month
2 => Week
3 => Day
4 => Hour
5 => Minute
6 => Second
7 => Millisecond
8 => Microsecond
9 => Nanosecond
_ => Auto
}
}
///|
/// Which units an option is allowed to name.
///
/// Spec:
pub(all) enum UnitGroup {
/// Date units only.
Date
/// Time units only.
Time
/// Any unit.
DateTime
} derive(Eq, Debug)
///|
/// Validates that `unit` is permitted in this group.
///
/// `extra_unit` names an additional unit that is allowed even when it is not
/// part of the group, which is how `auto` is threaded through.
fn UnitGroup::validate_unit(
self : UnitGroup,
unit : DateTimeUnit?,
extra_unit : DateTimeUnit?,
) -> DateTimeUnit? raise TemporalError {
if unit == extra_unit {
return unit
}
match self {
Date =>
match unit {
Some(u) if u.is_date_unit() => unit
None => unit
_ => raise RangeError("unit was not part of the date unit group")
}
Time =>
match unit {
Some(u) if u.is_time_unit() => unit
None => unit
_ => raise RangeError("unit was not part of the time unit group")
}
DateTime =>
if unit != Some(Auto) {
unit
} else {
raise RangeError("'auto' units are not allowed during comparison")
}
}
}
///|
/// Validates a unit option that must be present.
fn UnitGroup::validate_required_unit(
self : UnitGroup,
unit : DateTimeUnit?,
extra_unit : DateTimeUnit?,
) -> DateTimeUnit raise TemporalError {
match unit {
None => raise RangeError("unit is required")
Some(u) => {
self.validate_unit(Some(u), extra_unit) |> ignore
u
}
}
}
///|
/// How to handle a field that is out of range for its calendar.
pub(all) enum Overflow {
/// Clamp the field into range. This is the default.
Constrain
/// Raise a `RangeError`.
Reject
} derive(Eq, Debug)
///|
pub impl Default for Overflow with fn default() {
Constrain
}
///|
pub impl Show for Overflow with fn output(self, logger) {
logger.write_string(
match self {
Constrain => "constrain"
Reject => "reject"
},
)
}
///|
/// Parses an `overflow` option value.
pub fn Overflow::of_string(s : String) -> Overflow raise TemporalError {
match s {
"constrain" => Constrain
"reject" => Reject
_ => raise RangeError("'\{s}' is not a valid overflow value")
}
}
///|
/// How to resolve a wall-clock time that is ambiguous or nonexistent in a time
/// zone, as happens around a DST transition.
pub(all) enum Disambiguation {
/// Pick the earlier instant for ambiguous times and the later one for gaps.
/// This is the default.
Compatible
/// Always pick the earlier instant.
Earlier
/// Always pick the later instant.
Later
/// Raise a `RangeError`.
Reject
} derive(Eq, Debug)
///|
pub impl Default for Disambiguation with fn default() {
Compatible
}
///|
pub impl Show for Disambiguation with fn output(self, logger) {
logger.write_string(
match self {
Compatible => "compatible"
Earlier => "earlier"
Later => "later"
Reject => "reject"
},
)
}
///|
/// Parses a `disambiguation` option value.
pub fn Disambiguation::of_string(
s : String,
) -> Disambiguation raise TemporalError {
match s {
"compatible" => Compatible
"earlier" => Earlier
"later" => Later
"reject" => Reject
_ => raise RangeError("'\{s}' is not a valid disambiguation value")
}
}
///|
/// How to treat a UTC offset that disagrees with the named time zone.
pub(all) enum OffsetDisambiguation {
/// Trust the offset and ignore the time zone's own rules.
Use
/// Use the offset when it matches a possible instant, otherwise fall back to
/// disambiguation.
Prefer
/// Ignore the offset entirely.
Ignore
/// Raise a `RangeError` on any mismatch.
Reject
} derive(Eq, Debug)
///|
pub impl Show for OffsetDisambiguation with fn output(self, logger) {
logger.write_string(
match self {
Use => "use"
Prefer => "prefer"
Ignore => "ignore"
Reject => "reject"
},
)
}
///|
/// Parses an `offset` option value.
pub fn OffsetDisambiguation::of_string(
s : String,
) -> OffsetDisambiguation raise TemporalError {
match s {
"use" => Use
"prefer" => Prefer
"ignore" => Ignore
"reject" => Reject
_ => raise RangeError("'\{s}' is not a valid offset option value")
}
}
///|
/// The rounding mode applied when a value falls between two increments.
pub(all) enum RoundingMode {
/// Toward positive infinity.
Ceil
/// Toward negative infinity.
Floor
/// Away from zero.
Expand
/// Toward zero.
Trunc
/// To nearest, ties toward positive infinity.
HalfCeil
/// To nearest, ties toward negative infinity.
HalfFloor
/// To nearest, ties away from zero. This is the default for rounding.
HalfExpand
/// To nearest, ties toward zero.
HalfTrunc
/// To nearest, ties to even.
HalfEven
} derive(Eq, Debug)
///|
pub impl Default for RoundingMode with fn default() {
HalfExpand
}
///|
pub impl Show for RoundingMode with fn output(self, logger) {
logger.write_string(
match self {
Ceil => "ceil"
Floor => "floor"
Expand => "expand"
Trunc => "trunc"
HalfCeil => "halfCeil"
HalfFloor => "halfFloor"
HalfExpand => "halfExpand"
HalfTrunc => "halfTrunc"
HalfEven => "halfEven"
},
)
}
///|
/// Parses a `roundingMode` option value.
pub fn RoundingMode::of_string(s : String) -> RoundingMode raise TemporalError {
match s {
"ceil" => Ceil
"floor" => Floor
"expand" => Expand
"trunc" => Trunc
"halfCeil" => HalfCeil
"halfFloor" => HalfFloor
"halfExpand" => HalfExpand
"halfTrunc" => HalfTrunc
"halfEven" => HalfEven
_ => raise RangeError("'\{s}' is not a valid roundingMode value")
}
}
///|
/// `NegateRoundingMode`: mirrors the mode about zero, used to turn an `until`
/// rounding mode into the `since` one.
pub fn RoundingMode::negate(self : RoundingMode) -> RoundingMode {
match self {
Ceil => Floor
Floor => Ceil
HalfCeil => HalfFloor
HalfFloor => HalfCeil
Trunc => Trunc
Expand => Expand
HalfTrunc => HalfTrunc
HalfExpand => HalfExpand
HalfEven => HalfEven
}
}
///|
/// `GetUnsignedRoundingMode`: resolves a signed mode against the sign of the
/// value being rounded.
pub fn RoundingMode::to_unsigned(
self : RoundingMode,
is_positive : Bool,
) -> UnsignedRoundingMode {
match self {
Ceil => if is_positive { Infinity } else { Zero }
Trunc => Zero
Floor => if is_positive { Zero } else { Infinity }
Expand => Infinity
HalfCeil => if is_positive { HalfInfinity } else { HalfZero }
HalfTrunc => HalfZero
HalfFloor => if is_positive { HalfZero } else { HalfInfinity }
HalfExpand => HalfInfinity
HalfEven => HalfEven
}
}
///|
/// A rounding mode with the sign of the operand already folded in.
pub(all) enum UnsignedRoundingMode {
/// Round away from zero.
Infinity
/// Round toward zero.
Zero
/// To nearest, ties away from zero.
HalfInfinity
/// To nearest, ties toward zero.
HalfZero
/// To nearest, ties to even.
HalfEven
} derive(Eq, Debug)
///|
/// Whether `toString` should include the calendar annotation.
pub(all) enum DisplayCalendar {
/// Include it only for a non-ISO calendar. This is the default.
Auto
/// Always include it.
Always
/// Never include it.
Never
/// Always include it, flagged critical with a `!`.
Critical
} derive(Eq, Debug)
///|
pub impl Default for DisplayCalendar with fn default() {
Auto
}
///|
pub impl Show for DisplayCalendar with fn output(self, logger) {
logger.write_string(
match self {
Auto => "auto"
Always => "always"
Never => "never"
Critical => "critical"
},
)
}
///|
/// Parses a `calendarName` option value.
pub fn DisplayCalendar::of_string(
s : String,
) -> DisplayCalendar raise TemporalError {
match s {
"auto" => Auto
"always" => Always
"never" => Never
"critical" => Critical
_ => raise RangeError("'\{s}' is not a valid calendarName value")
}
}
///|
/// Whether `toString` should include the UTC offset.
pub(all) enum DisplayOffset {
/// Include it. This is the default.
Auto
/// Omit it.
Never
} derive(Eq, Debug)
///|
pub impl Default for DisplayOffset with fn default() {
Auto
}
///|
pub impl Show for DisplayOffset with fn output(self, logger) {
logger.write_string(
match self {
Auto => "auto"
Never => "never"
},
)
}
///|
/// Parses an `offset` display option value.
pub fn DisplayOffset::of_string(
s : String,
) -> DisplayOffset raise TemporalError {
match s {
"auto" => Auto
"never" => Never
_ => raise RangeError("'\{s}' is not a valid offset display value")
}
}
///|
/// Whether `toString` should include the time zone annotation.
pub(all) enum DisplayTimeZone {
/// Include it. This is the default.
Auto
/// Omit it.
Never
/// Include it, flagged critical with a `!`.
Critical
} derive(Eq, Debug)
///|
pub impl Default for DisplayTimeZone with fn default() {
Auto
}
///|
pub impl Show for DisplayTimeZone with fn output(self, logger) {
logger.write_string(
match self {
Auto => "auto"
Never => "never"
Critical => "critical"
},
)
}
///|
/// Parses a `timeZoneName` option value.
pub fn DisplayTimeZone::of_string(
s : String,
) -> DisplayTimeZone raise TemporalError {
match s {
"auto" => Auto
"never" => Never
"critical" => Critical
_ => raise RangeError("'\{s}' is not a valid timeZoneName value")
}
}
///|
/// The number of fractional-second digits `toString` should emit.
pub(all) enum Precision {
/// Emit as many digits as needed, and none when the sub-second part is zero.
Auto
/// Emit exactly this many digits, from 0 to 9.
Digit(Int)
/// Stop at minutes, emitting no seconds at all.
Minute
} derive(Eq, Debug)
///|
pub impl Default for Precision with fn default() {
Auto
}
///|
/// A validated rounding increment, in `1 ..= 10^9`.
pub struct RoundingIncrement(Int) derive(Eq, Compare, Debug)
///|
pub impl Show for RoundingIncrement with fn output(self, logger) {
logger.write_string(self.get().to_string())
}
///|
pub impl Default for RoundingIncrement with fn default() {
RoundingIncrement(1)
}
///|
/// A rounding increment of one, meaning "round to the unit itself".
pub let rounding_increment_one : RoundingIncrement = RoundingIncrement(1)
///|
/// Creates a rounding increment, rejecting values outside `1 ..= 10^9`.
///
/// ```mbt check
/// test {
/// inspect(@temporal.RoundingIncrement::try_new(30), content="30")
/// }
/// ```
pub fn RoundingIncrement::try_new(
increment : Int,
) -> RoundingIncrement raise TemporalError {
if increment < 1 || increment > 1_000_000_000 {
raise RangeError("roundingIncrement must be between 1 and 10**9")
}
RoundingIncrement(increment)
}
///|
/// Returns the numeric value of the increment.
pub fn RoundingIncrement::get(self : RoundingIncrement) -> Int {
self.0
}
///|
/// `ValidateTemporalRoundingIncrement`: checks the increment evenly divides
/// `dividend` and does not exceed the permitted maximum.
fn RoundingIncrement::validate(
self : RoundingIncrement,
dividend : Int64,
inclusive : Bool,
) -> Unit raise TemporalError {
let max = if inclusive { dividend } else { dividend - 1L }
let increment = self.get().to_int64()
if increment > max {
raise RangeError("roundingIncrement exceeds its maximum")
}
if rem_euclid(dividend, increment) != 0L {
raise RangeError("dividend is not divisible by roundingIncrement")
}
}
///|
/// The unit-and-rounding options accepted by the `until` and `since` methods.
pub struct DifferenceSettings {
/// The largest unit the resulting duration may use.
largest_unit : DateTimeUnit?
/// The unit the result is rounded to.
smallest_unit : DateTimeUnit?
/// How to round.
rounding_mode : RoundingMode?
/// The rounding increment.
increment : RoundingIncrement?
} derive(Default, Debug)
///|
/// Builds difference settings; every field defaults to unset.
pub fn DifferenceSettings::new(
largest_unit? : DateTimeUnit,
smallest_unit? : DateTimeUnit,
rounding_mode? : RoundingMode,
increment? : RoundingIncrement,
) -> DifferenceSettings {
{ largest_unit, smallest_unit, rounding_mode, increment }
}
///|
/// The options accepted by the `round` methods.
pub struct RoundingOptions {
/// The largest unit the result may use.
largest_unit : DateTimeUnit?
/// The unit to round to.
smallest_unit : DateTimeUnit?
/// How to round.
rounding_mode : RoundingMode?
/// The rounding increment.
increment : RoundingIncrement?
} derive(Debug)
///|
pub impl Default for RoundingOptions with fn default() {
{
largest_unit: Some(Auto),
smallest_unit: None,
rounding_mode: None,
increment: None,
}
}
///|
/// Builds rounding options; every field defaults to unset.
pub fn RoundingOptions::new(
largest_unit? : DateTimeUnit,
smallest_unit? : DateTimeUnit,
rounding_mode? : RoundingMode,
increment? : RoundingIncrement,
) -> RoundingOptions {
{ largest_unit, smallest_unit, rounding_mode, increment }
}
///|
/// The options controlling how `toString` renders sub-second precision.
pub struct ToStringRoundingOptions {
/// How many fractional digits to emit.
precision : Precision
/// The unit to round to, which overrides `precision` when set.
smallest_unit : DateTimeUnit?
/// How to round; defaults to truncation.
rounding_mode : RoundingMode?
} derive(Default, Debug)
///|
/// Builds `toString` rounding options.
pub fn ToStringRoundingOptions::new(
precision? : Precision = Auto,
smallest_unit? : DateTimeUnit,
rounding_mode? : RoundingMode,
) -> ToStringRoundingOptions {
{ precision, smallest_unit, rounding_mode }
}
///|
/// `toString` rounding options with every choice made.
priv struct ResolvedToStringRoundingOptions {
precision : Precision
smallest_unit : DateTimeUnit
rounding_mode : RoundingMode
increment : RoundingIncrement
}
///|
/// Resolves the `smallestUnit` and `fractionalSecondDigits` options against
/// each other, as `ToSecondsStringPrecisionRecord` does.
fn ToStringRoundingOptions::resolve(
self : ToStringRoundingOptions,
) -> ResolvedToStringRoundingOptions raise TemporalError {
let rounding_mode = self.rounding_mode.unwrap_or(Trunc)
match self.smallest_unit {
Some(Minute) =>
{
precision: Minute,
smallest_unit: Minute,
rounding_mode,
increment: rounding_increment_one,
}
Some(Second) =>
{
precision: Digit(0),
smallest_unit: Second,
rounding_mode,
increment: rounding_increment_one,
}
Some(Millisecond) =>
{
precision: Digit(3),
smallest_unit: Millisecond,
rounding_mode,
increment: rounding_increment_one,
}
Some(Microsecond) =>
{
precision: Digit(6),
smallest_unit: Microsecond,
rounding_mode,
increment: rounding_increment_one,
}
Some(Nanosecond) =>
{
precision: Digit(9),
smallest_unit: Nanosecond,
rounding_mode,
increment: rounding_increment_one,
}
Some(_) => raise RangeError("smallestUnit must be a valid time unit")
None =>
match self.precision {
Auto =>
{
precision: Auto,
smallest_unit: Nanosecond,
rounding_mode,
increment: rounding_increment_one,
}
Digit(0) =>
{
precision: Digit(0),
smallest_unit: Second,
rounding_mode,
increment: rounding_increment_one,
}
// Fewer digits than a unit provides means rounding by a power of ten.
Digit(d) if d >= 1 && d <= 3 =>
{
precision: Digit(d),
smallest_unit: Millisecond,
rounding_mode,
increment: RoundingIncrement::try_new(pow10(3 - d)),
}
Digit(d) if d >= 4 && d <= 6 =>
{
precision: Digit(d),
smallest_unit: Microsecond,
rounding_mode,
increment: RoundingIncrement::try_new(pow10(6 - d)),
}
Digit(d) if d >= 7 && d <= 9 =>
{
precision: Digit(d),
smallest_unit: Nanosecond,
rounding_mode,
increment: RoundingIncrement::try_new(pow10(9 - d)),
}
// `Minute` precision only has a meaning when paired with an explicit
// `smallestUnit`, which the arm above handles.
_ => raise RangeError("invalid fractionalDigits precision value")
}
}
}
///|
/// Returns `10^exp` for small non-negative exponents.
fn pow10(exp : Int) -> Int {
let mut result = 1
for _ in 0.. ResolvedRoundingOptions {
{
largest_unit: Auto,
smallest_unit: options.smallest_unit,
increment: options.increment,
rounding_mode: options.rounding_mode,
}
}
///|
/// `GetDifferenceSettings`: resolves the options for `until` and `since`.
fn ResolvedRoundingOptions::from_diff_settings(
options : DifferenceSettings,
operation : DifferenceOperation,
unit_group : UnitGroup,
fallback_largest : DateTimeUnit,
fallback_smallest : DateTimeUnit,
) -> ResolvedRoundingOptions raise TemporalError {
unit_group.validate_unit(options.largest_unit, Some(Auto)) |> ignore
let increment = options.increment.unwrap_or(rounding_increment_one)
let rounding_mode = match operation {
Since => options.rounding_mode.unwrap_or(Trunc).negate()
Until => options.rounding_mode.unwrap_or(Trunc)
}
let smallest_unit = options.smallest_unit.unwrap_or(fallback_smallest)
unit_group.validate_unit(options.smallest_unit, None) |> ignore
let default_largest_unit = smallest_unit.larger(fallback_largest)
let largest_unit = match options.largest_unit {
None | Some(Auto) => default_largest_unit
Some(u) => u
}
if largest_unit < smallest_unit {
raise RangeError("smallestUnit must be smaller than largestUnit")
}
if smallest_unit.to_maximum_rounding_increment() is Some(max) {
increment.validate(max.to_int64(), false)
}
{ largest_unit, smallest_unit, increment, rounding_mode }
}
///|
/// Resolves the options for `PlainDateTime.round`.
fn ResolvedRoundingOptions::from_datetime_options(
options : RoundingOptions,
) -> ResolvedRoundingOptions raise TemporalError {
let increment = options.increment.unwrap_or(rounding_increment_one)
let rounding_mode = options.rounding_mode.unwrap_or(HalfExpand)
let smallest_unit = UnitGroup::Time.validate_required_unit(
options.smallest_unit,
Some(Day),
)
// Rounding to whole days only permits an increment of one, and that
// increment is inclusive of the dividend.
let (maximum, inclusive) = if smallest_unit == Day {
(1L, true)
} else {
match smallest_unit.to_maximum_rounding_increment() {
Some(max) => (max.to_int64(), false)
None => raise RangeError("smallestUnit must be a valid time unit")
}
}
increment.validate(maximum, inclusive)
{ largest_unit: Auto, smallest_unit, increment, rounding_mode }
}
///|
/// Resolves the options for `PlainTime.round`.
fn ResolvedRoundingOptions::from_time_options(
options : RoundingOptions,
) -> ResolvedRoundingOptions raise TemporalError {
let smallest_unit = match options.smallest_unit {
None => raise RangeError("smallestUnit is required")
Some(u) => u
}
let increment = options.increment.unwrap_or(rounding_increment_one)
let rounding_mode = options.rounding_mode.unwrap_or(HalfExpand)
let max = match smallest_unit.to_maximum_rounding_increment() {
Some(max) => max
None => raise RangeError("smallestUnit must be a valid time unit")
}
increment.validate(max.to_int64(), false)
{ largest_unit: Auto, smallest_unit, increment, rounding_mode }
}
///|
/// Resolves the options for `Instant.round`.
fn ResolvedRoundingOptions::from_instant_options(
options : RoundingOptions,
) -> ResolvedRoundingOptions raise TemporalError {
let increment = options.increment.unwrap_or(rounding_increment_one)
let rounding_mode = options.rounding_mode.unwrap_or(HalfExpand)
let smallest_unit = UnitGroup::Time.validate_required_unit(
options.smallest_unit,
None,
)
// An Instant has no calendar, so every time unit is bounded by the day.
let maximum = match smallest_unit {
Hour => 24L
Minute => 24L * 60L
Second => 24L * 3600L
Millisecond => MS_PER_DAY
Microsecond => MS_PER_DAY * 1000L
Nanosecond => NS_PER_DAY
_ => raise RangeError("invalid roundTo unit provided")
}
increment.validate(maximum, true)
{ largest_unit: Auto, smallest_unit, increment, rounding_mode }
}
///|
/// Returns whether applying these options would leave a value unchanged.
fn ResolvedRoundingOptions::is_noop(self : ResolvedRoundingOptions) -> Bool {
self.smallest_unit == Nanosecond && self.increment == rounding_increment_one
}