use crate::parsers::Precision;
use crate::TemporalUnwrap;
use crate::{error::ErrorMessage, TemporalError, TemporalResult, MS_PER_DAY, NS_PER_DAY};
use core::cmp::Ordering;
use core::num::NonZeroU128;
use core::ops::Add;
use core::{fmt, str::FromStr};
mod increment;
mod relative_to;
pub use increment::RoundingIncrement;
pub use relative_to::RelativeTo;
#[derive(Debug, Clone, Copy)]
pub(crate) enum DifferenceOperation {
Until,
Since,
}
#[derive(Debug, Default)]
pub struct ToStringRoundingOptions {
pub precision: Precision,
pub smallest_unit: Option<Unit>,
pub rounding_mode: Option<RoundingMode>,
}
#[derive(Debug)]
pub(crate) struct ResolvedToStringRoundingOptions {
pub(crate) precision: Precision,
pub(crate) smallest_unit: Unit,
pub(crate) rounding_mode: RoundingMode,
pub(crate) increment: RoundingIncrement,
}
impl ToStringRoundingOptions {
pub(crate) fn resolve(&self) -> TemporalResult<ResolvedToStringRoundingOptions> {
let rounding_mode = self.rounding_mode.unwrap_or(RoundingMode::Trunc);
match self.smallest_unit {
Some(Unit::Minute) => Ok(ResolvedToStringRoundingOptions {
precision: Precision::Minute,
smallest_unit: Unit::Minute,
rounding_mode,
increment: RoundingIncrement::ONE,
}),
Some(Unit::Second) => Ok(ResolvedToStringRoundingOptions {
precision: Precision::Digit(0),
smallest_unit: Unit::Second,
rounding_mode,
increment: RoundingIncrement::ONE,
}),
Some(Unit::Millisecond) => Ok(ResolvedToStringRoundingOptions {
precision: Precision::Digit(3),
smallest_unit: Unit::Millisecond,
rounding_mode,
increment: RoundingIncrement::ONE,
}),
Some(Unit::Microsecond) => Ok(ResolvedToStringRoundingOptions {
precision: Precision::Digit(6),
smallest_unit: Unit::Microsecond,
rounding_mode,
increment: RoundingIncrement::ONE,
}),
Some(Unit::Nanosecond) => Ok(ResolvedToStringRoundingOptions {
precision: Precision::Digit(9),
smallest_unit: Unit::Nanosecond,
rounding_mode,
increment: RoundingIncrement::ONE,
}),
None => match self.precision {
Precision::Auto => Ok(ResolvedToStringRoundingOptions {
precision: Precision::Auto,
smallest_unit: Unit::Nanosecond,
rounding_mode,
increment: RoundingIncrement::ONE,
}),
Precision::Digit(0) => Ok(ResolvedToStringRoundingOptions {
precision: Precision::Digit(0),
smallest_unit: Unit::Second,
rounding_mode,
increment: RoundingIncrement::ONE,
}),
Precision::Digit(d) if (1..=3).contains(&d) => {
Ok(ResolvedToStringRoundingOptions {
precision: Precision::Digit(d),
smallest_unit: Unit::Millisecond,
rounding_mode,
increment: RoundingIncrement::try_new(10_u32.pow(3 - d as u32))
.temporal_unwrap()?,
})
}
Precision::Digit(d) if (4..=6).contains(&d) => {
Ok(ResolvedToStringRoundingOptions {
precision: Precision::Digit(d),
smallest_unit: Unit::Microsecond,
rounding_mode,
increment: RoundingIncrement::try_new(10_u32.pow(6 - d as u32))
.temporal_unwrap()?,
})
}
Precision::Digit(d) if (7..=9).contains(&d) => {
Ok(ResolvedToStringRoundingOptions {
precision: Precision::Digit(d),
smallest_unit: Unit::Nanosecond,
rounding_mode,
increment: RoundingIncrement::try_new(10_u32.pow(9 - d as u32))
.temporal_unwrap()?,
})
}
_ => Err(TemporalError::range()
.with_enum(ErrorMessage::FractionalDigitsPrecisionInvalid)),
},
_ => Err(TemporalError::range().with_enum(ErrorMessage::SmallestUnitNotTimeUnit)),
}
}
}
#[non_exhaustive]
#[derive(Debug, Default, Clone, Copy)]
pub struct DifferenceSettings {
pub largest_unit: Option<Unit>,
pub smallest_unit: Option<Unit>,
pub rounding_mode: Option<RoundingMode>,
pub increment: Option<RoundingIncrement>,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy)]
pub struct RoundingOptions {
pub largest_unit: Option<Unit>,
pub smallest_unit: Option<Unit>,
pub rounding_mode: Option<RoundingMode>,
pub increment: Option<RoundingIncrement>,
}
impl Default for RoundingOptions {
fn default() -> Self {
Self {
largest_unit: Some(Unit::Auto),
smallest_unit: None,
rounding_mode: None,
increment: None,
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct ResolvedRoundingOptions {
pub(crate) largest_unit: Unit,
pub(crate) smallest_unit: Unit,
pub(crate) increment: RoundingIncrement,
pub(crate) rounding_mode: RoundingMode,
}
impl ResolvedRoundingOptions {
pub(crate) fn from_to_string_options(options: &ResolvedToStringRoundingOptions) -> Self {
Self {
largest_unit: Unit::Auto,
smallest_unit: options.smallest_unit,
increment: options.increment,
rounding_mode: options.rounding_mode,
}
}
pub(crate) fn from_diff_settings(
options: DifferenceSettings,
operation: DifferenceOperation,
unit_group: UnitGroup,
fallback_largest: Unit,
fallback_smallest: Unit,
) -> TemporalResult<Self> {
unit_group.validate_unit(options.largest_unit, Some(Unit::Auto))?;
let increment = options.increment.unwrap_or_default();
let rounding_mode = match operation {
DifferenceOperation::Since => options
.rounding_mode
.unwrap_or(RoundingMode::Trunc)
.negate(),
DifferenceOperation::Until => options.rounding_mode.unwrap_or(RoundingMode::Trunc),
};
let smallest_unit = options.smallest_unit.unwrap_or(fallback_smallest);
unit_group.validate_unit(options.smallest_unit, None)?;
let default_largest_unit = smallest_unit.max(fallback_largest);
let mut largest_unit = options.largest_unit.unwrap_unit_or(default_largest_unit);
if largest_unit == Unit::Auto {
largest_unit = default_largest_unit;
}
if largest_unit < smallest_unit {
return Err(
TemporalError::range().with_enum(ErrorMessage::SmallestUnitLargerThanLargestUnit)
);
}
let maximum = smallest_unit.to_maximum_rounding_increment();
if let Some(max) = maximum {
increment.validate(max.into(), false)?;
}
Ok(ResolvedRoundingOptions {
largest_unit,
smallest_unit,
increment,
rounding_mode,
})
}
pub(crate) fn from_datetime_options(options: RoundingOptions) -> TemporalResult<Self> {
let increment = options.increment.unwrap_or_default();
let rounding_mode = options.rounding_mode.unwrap_or_default();
let smallest_unit =
UnitGroup::Time.validate_required_unit(options.smallest_unit, Some(Unit::Day))?;
let (maximum, inclusive) = if smallest_unit == Unit::Day {
(1, true)
} else {
let maximum = smallest_unit
.to_maximum_rounding_increment()
.ok_or(TemporalError::range().with_enum(ErrorMessage::SmallestUnitNotTimeUnit))?;
(maximum, false)
};
increment.validate(maximum.into(), inclusive)?;
Ok(Self {
largest_unit: Unit::Auto,
smallest_unit,
increment,
rounding_mode,
})
}
pub(crate) fn from_time_options(options: RoundingOptions) -> TemporalResult<Self> {
let Some(smallest_unit) = options.smallest_unit else {
return Err(TemporalError::range().with_enum(ErrorMessage::SmallestUnitIsRequired));
};
let increment = options.increment.unwrap_or(RoundingIncrement::ONE);
let rounding_mode = options.rounding_mode.unwrap_or(RoundingMode::HalfExpand);
let max = smallest_unit
.to_maximum_rounding_increment()
.ok_or_else(|| {
TemporalError::range().with_enum(ErrorMessage::SmallestUnitNotTimeUnit)
})?;
increment.validate(u64::from(max), false)?;
Ok(ResolvedRoundingOptions {
largest_unit: Unit::Auto,
increment,
smallest_unit,
rounding_mode,
})
}
pub(crate) fn from_instant_options(options: RoundingOptions) -> TemporalResult<Self> {
let increment = options.increment.unwrap_or_default();
let rounding_mode = options.rounding_mode.unwrap_or_default();
let smallest_unit = UnitGroup::Time.validate_required_unit(options.smallest_unit, None)?;
let maximum = match smallest_unit {
Unit::Hour => 24u64,
Unit::Minute => 24 * 60,
Unit::Second => 24 * 3600,
Unit::Millisecond => MS_PER_DAY as u64,
Unit::Microsecond => MS_PER_DAY as u64 * 1000,
Unit::Nanosecond => NS_PER_DAY,
_ => return Err(TemporalError::range().with_enum(ErrorMessage::RoundToUnitInvalid)),
};
increment.validate(maximum, true)?;
Ok(Self {
largest_unit: Unit::Auto,
smallest_unit,
increment,
rounding_mode,
})
}
pub(crate) fn is_noop(&self) -> bool {
self.smallest_unit == Unit::Nanosecond && self.increment == RoundingIncrement::ONE
}
}
#[derive(Debug, Clone, Copy)]
pub enum UnitGroup {
Date,
Time,
DateTime,
}
impl UnitGroup {
pub fn validate_required_unit(
self,
unit: Option<Unit>,
extra_unit: Option<Unit>,
) -> TemporalResult<Unit> {
let Some(unit) = unit else {
return Err(TemporalError::range().with_enum(ErrorMessage::UnitRequired));
};
self.validate_unit(Some(unit), extra_unit)?;
Ok(unit)
}
pub fn validate_unit(self, unit: Option<Unit>, extra_unit: Option<Unit>) -> TemporalResult<()> {
match self {
_ if unit == extra_unit => Ok(()),
UnitGroup::Date => match unit {
Some(unit) if unit.is_date_unit() => Ok(()),
None => Ok(()),
_ => Err(TemporalError::range().with_enum(ErrorMessage::UnitNotDate)),
},
UnitGroup::Time => match unit {
Some(unit) if unit.is_time_unit() => Ok(()),
None => Ok(()),
_ => Err(TemporalError::range().with_enum(ErrorMessage::UnitNotTime)),
},
UnitGroup::DateTime if unit != Some(Unit::Auto) => Ok(()),
_ => Err(TemporalError::range().with_enum(ErrorMessage::UnitNoAutoDuringComparison)),
}
}
}
pub(crate) const UNIT_VALUE_TABLE: [Unit; 10] = [
Unit::Year,
Unit::Month,
Unit::Week,
Unit::Day,
Unit::Hour,
Unit::Minute,
Unit::Second,
Unit::Millisecond,
Unit::Microsecond,
Unit::Nanosecond,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Unit {
Auto = 0,
Nanosecond,
Microsecond,
Millisecond,
Second,
Minute,
Hour,
Day,
Week,
Month,
Year,
}
impl Unit {
#[inline]
#[must_use]
pub fn to_maximum_rounding_increment(self) -> Option<u32> {
use Unit::{
Auto, Day, Hour, Microsecond, Millisecond, Minute, Month, Nanosecond, Second, Week,
Year,
};
let max = match self {
Year | Month | Week | Day => return None,
Hour => 24,
Minute | Second => 60,
Millisecond | Microsecond | Nanosecond => 1000,
Auto => {
debug_assert!(false, "Auto units should be resolved by this point");
return None;
}
};
Some(max)
}
#[must_use]
pub const fn as_nanoseconds(&self) -> Option<NonZeroU128> {
use Unit::{
Auto, Day, Hour, Microsecond, Millisecond, Minute, Month, Nanosecond, Second, Week,
Year,
};
match self {
Year | Month | Week | Auto => None,
Day => NonZeroU128::new(NS_PER_DAY as u128),
Hour => NonZeroU128::new(3_600_000_000_000),
Minute => NonZeroU128::new(60_000_000_000),
Second => NonZeroU128::new(1_000_000_000),
Millisecond => NonZeroU128::new(1_000_000),
Microsecond => NonZeroU128::new(1_000),
Nanosecond => NonZeroU128::new(1),
}
}
#[inline]
#[must_use]
pub fn is_calendar_unit(&self) -> bool {
use Unit::{Month, Week, Year};
matches!(self, Year | Month | Week)
}
#[inline]
#[must_use]
pub fn is_date_unit(&self) -> bool {
use Unit::{Day, Month, Week, Year};
matches!(self, Day | Year | Month | Week)
}
#[inline]
#[must_use]
pub fn is_time_unit(&self) -> bool {
use Unit::{Hour, Microsecond, Millisecond, Minute, Nanosecond, Second};
matches!(
self,
Hour | Minute | Second | Millisecond | Microsecond | Nanosecond
)
}
#[inline]
pub fn larger(u1: Unit, u2: Unit) -> TemporalResult<Unit> {
for unit in UNIT_VALUE_TABLE {
if u1 == unit {
return Ok(unit);
}
if u2 == unit {
return Ok(unit);
}
}
Err(TemporalError::assert().with_enum(ErrorMessage::UnitNoAutoDuringComparison))
}
pub(crate) fn table_index(&self) -> TemporalResult<usize> {
for (i, unit) in UNIT_VALUE_TABLE.iter().enumerate() {
if self == unit {
return Ok(i);
}
}
Err(TemporalError::assert().with_enum(ErrorMessage::UnitNoAutoDuringComparison))
}
}
trait UnwrapUnit {
type Result;
fn unwrap_unit_or(self, unit: Unit) -> Self::Result;
}
impl UnwrapUnit for Option<Unit> {
type Result = Unit;
fn unwrap_unit_or(self, unit: Unit) -> Self::Result {
if self == Some(Unit::Auto) {
return unit;
}
self.unwrap_or(unit)
}
}
impl From<usize> for Unit {
fn from(value: usize) -> Self {
match value {
10 => Self::Year,
9 => Self::Month,
8 => Self::Week,
7 => Self::Day,
6 => Self::Hour,
5 => Self::Minute,
4 => Self::Second,
3 => Self::Millisecond,
2 => Self::Microsecond,
1 => Self::Nanosecond,
_ => Self::Auto,
}
}
}
impl Add<usize> for Unit {
type Output = Unit;
fn add(self, rhs: usize) -> Self::Output {
Unit::from(self as usize + rhs)
}
}
#[derive(Debug, Clone, Copy)]
pub struct ParseUnitError;
impl fmt::Display for ParseUnitError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("provided string was not a valid Unit")
}
}
impl FromStr for Unit {
type Err = ParseUnitError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"auto" => Ok(Self::Auto),
"year" | "years" => Ok(Self::Year),
"month" | "months" => Ok(Self::Month),
"week" | "weeks" => Ok(Self::Week),
"day" | "days" => Ok(Self::Day),
"hour" | "hours" => Ok(Self::Hour),
"minute" | "minutes" => Ok(Self::Minute),
"second" | "seconds" => Ok(Self::Second),
"millisecond" | "milliseconds" => Ok(Self::Millisecond),
"microsecond" | "microseconds" => Ok(Self::Microsecond),
"nanosecond" | "nanoseconds" => Ok(Self::Nanosecond),
_ => Err(ParseUnitError),
}
}
}
impl fmt::Display for Unit {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Auto => "auto",
Self::Year => "year",
Self::Month => "month",
Self::Week => "week",
Self::Day => "day",
Self::Hour => "hour",
Self::Minute => "minute",
Self::Second => "second",
Self::Millisecond => "millsecond",
Self::Microsecond => "microsecond",
Self::Nanosecond => "nanosecond",
}
.fmt(f)
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum Overflow {
#[default]
Constrain,
Reject,
}
#[derive(Debug, Clone, Copy)]
pub struct ParseOverflowError;
impl fmt::Display for ParseOverflowError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("provided string was not a valid overflow value")
}
}
impl FromStr for Overflow {
type Err = ParseOverflowError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"constrain" => Ok(Self::Constrain),
"reject" => Ok(Self::Reject),
_ => Err(ParseOverflowError),
}
}
}
impl fmt::Display for Overflow {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Constrain => "constrain",
Self::Reject => "reject",
}
.fmt(f)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Disambiguation {
#[default]
Compatible,
Earlier,
Later,
Reject,
}
#[derive(Debug, Clone, Copy)]
pub struct ParseDisambiguationError;
impl fmt::Display for ParseDisambiguationError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("provided string was not a valid instant disambiguation value")
}
}
impl FromStr for Disambiguation {
type Err = ParseDisambiguationError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"compatible" => Ok(Self::Compatible),
"earlier" => Ok(Self::Earlier),
"later" => Ok(Self::Later),
"reject" => Ok(Self::Reject),
_ => Err(ParseDisambiguationError),
}
}
}
impl fmt::Display for Disambiguation {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Compatible => "compatible",
Self::Earlier => "earlier",
Self::Later => "later",
Self::Reject => "reject",
}
.fmt(f)
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum OffsetDisambiguation {
Use,
Prefer,
Ignore,
Reject,
}
#[derive(Debug, Clone, Copy)]
pub struct ParseOffsetDisambiguationError;
impl fmt::Display for ParseOffsetDisambiguationError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("provided string was not a valid offset disambiguation value")
}
}
impl FromStr for OffsetDisambiguation {
type Err = ParseOffsetDisambiguationError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"use" => Ok(Self::Use),
"prefer" => Ok(Self::Prefer),
"ignore" => Ok(Self::Ignore),
"reject" => Ok(Self::Reject),
_ => Err(ParseOffsetDisambiguationError),
}
}
}
impl fmt::Display for OffsetDisambiguation {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Use => "use",
Self::Prefer => "prefer",
Self::Ignore => "ignore",
Self::Reject => "reject",
}
.fmt(f)
}
}
#[derive(Debug, Copy, Clone, Default)]
pub enum RoundingMode {
Ceil,
Floor,
Expand,
Trunc,
HalfCeil,
HalfFloor,
#[default]
HalfExpand,
HalfTrunc,
HalfEven,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnsignedRoundingMode {
Infinity,
Zero,
HalfInfinity,
HalfZero,
HalfEven,
}
impl RoundingMode {
#[inline]
#[must_use]
pub const fn negate(self) -> Self {
use RoundingMode::{
Ceil, Expand, Floor, HalfCeil, HalfEven, HalfExpand, HalfFloor, HalfTrunc, Trunc,
};
match self {
Ceil => Self::Floor,
Floor => Self::Ceil,
HalfCeil => Self::HalfFloor,
HalfFloor => Self::HalfCeil,
Trunc => Self::Trunc,
Expand => Self::Expand,
HalfTrunc => Self::HalfTrunc,
HalfExpand => Self::HalfExpand,
HalfEven => Self::HalfEven,
}
}
#[inline]
#[must_use]
pub const fn get_unsigned_round_mode(self, is_positive: bool) -> UnsignedRoundingMode {
use RoundingMode::{
Ceil, Expand, Floor, HalfCeil, HalfEven, HalfExpand, HalfFloor, HalfTrunc, Trunc,
};
match self {
Ceil if is_positive => UnsignedRoundingMode::Infinity,
Ceil | Trunc => UnsignedRoundingMode::Zero,
Floor if is_positive => UnsignedRoundingMode::Zero,
Floor | Expand => UnsignedRoundingMode::Infinity,
HalfCeil if is_positive => UnsignedRoundingMode::HalfInfinity,
HalfCeil | HalfTrunc => UnsignedRoundingMode::HalfZero,
HalfFloor if is_positive => UnsignedRoundingMode::HalfZero,
HalfFloor | HalfExpand => UnsignedRoundingMode::HalfInfinity,
HalfEven => UnsignedRoundingMode::HalfEven,
}
}
}
impl UnsignedRoundingMode {
pub(crate) fn apply(self, dividend: u128, divisor: u128, r1: i128, r2: i128) -> i128 {
if dividend as i128 == r1 * divisor as i128 {
return r1;
}
if self == UnsignedRoundingMode::Zero {
return r1;
} else if self == UnsignedRoundingMode::Infinity {
return r2;
}
let d1_times_divisor = dividend as i128 - r1 * divisor as i128;
let d2_times_divisor = r2 * divisor as i128 - dividend as i128;
match d1_times_divisor.cmp(&d2_times_divisor) {
Ordering::Less => r1,
Ordering::Greater => r2,
Ordering::Equal => {
match self {
UnsignedRoundingMode::HalfZero => r1,
UnsignedRoundingMode::HalfInfinity => r2,
_ => {
let diff = r2 - r1;
let cardinality = r1.div_euclid(diff).rem_euclid(2);
if cardinality == 0 {
r1
} else {
r2
}
}
}
}
}
}
}
impl FromStr for RoundingMode {
type Err = TemporalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"ceil" => Ok(Self::Ceil),
"floor" => Ok(Self::Floor),
"expand" => Ok(Self::Expand),
"trunc" => Ok(Self::Trunc),
"halfCeil" => Ok(Self::HalfCeil),
"halfFloor" => Ok(Self::HalfFloor),
"halfExpand" => Ok(Self::HalfExpand),
"halfTrunc" => Ok(Self::HalfTrunc),
"halfEven" => Ok(Self::HalfEven),
_ => Err(TemporalError::range().with_enum(ErrorMessage::RoundingModeInvalid)),
}
}
}
impl fmt::Display for RoundingMode {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Ceil => "ceil",
Self::Floor => "floor",
Self::Expand => "expand",
Self::Trunc => "trunc",
Self::HalfCeil => "halfCeil",
Self::HalfFloor => "halfFloor",
Self::HalfExpand => "halfExpand",
Self::HalfTrunc => "halfTrunc",
Self::HalfEven => "halfEven",
}
.fmt(f)
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DisplayCalendar {
#[default]
Auto,
Always,
Never,
Critical,
}
impl fmt::Display for DisplayCalendar {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
DisplayCalendar::Auto => "auto",
DisplayCalendar::Always => "always",
DisplayCalendar::Never => "never",
DisplayCalendar::Critical => "critical",
}
.fmt(f)
}
}
impl FromStr for DisplayCalendar {
type Err = TemporalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"auto" => Ok(Self::Auto),
"always" => Ok(Self::Always),
"never" => Ok(Self::Never),
"critical" => Ok(Self::Critical),
_ => Err(TemporalError::range().with_enum(ErrorMessage::CalendarNameInvalid)),
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DisplayOffset {
#[default]
Auto,
Never,
}
impl fmt::Display for DisplayOffset {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
DisplayOffset::Auto => "auto",
DisplayOffset::Never => "never",
}
.fmt(f)
}
}
impl FromStr for DisplayOffset {
type Err = TemporalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"auto" => Ok(Self::Auto),
"never" => Ok(Self::Never),
_ => Err(TemporalError::range().with_enum(ErrorMessage::OffsetOptionInvalid)),
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DisplayTimeZone {
#[default]
Auto,
Never,
Critical,
}
impl fmt::Display for DisplayTimeZone {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
DisplayTimeZone::Auto => "auto",
DisplayTimeZone::Never => "never",
DisplayTimeZone::Critical => "critical",
}
.fmt(f)
}
}
impl FromStr for DisplayTimeZone {
type Err = TemporalError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"auto" => Ok(Self::Auto),
"never" => Ok(Self::Never),
"critical" => Ok(Self::Critical),
_ => Err(TemporalError::range().with_enum(ErrorMessage::TimeZoneNameInvalid)),
}
}
}