calendar_types/
duration.rs1use thiserror::Error;
4
5use crate::{
6 primitive::Sign,
7 time::{FractionalSecond, InvalidFractionalSecondError},
8};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub enum Duration {
13 Nominal(NominalDuration),
14 Exact(ExactDuration),
15}
16
17#[derive(Debug, Clone, Copy, Error, PartialEq, Eq)]
19pub enum InvalidDurationError {
20 #[error("invalid fractional second: {0}")]
22 FractionalSecond(#[from] InvalidFractionalSecondError),
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct SignedDuration {
28 pub sign: Sign,
30 pub duration: Duration,
32}
33
34impl From<Duration> for SignedDuration {
35 fn from(value: Duration) -> Self {
36 Self {
37 sign: Default::default(),
38 duration: value,
39 }
40 }
41}
42
43#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
46pub struct NominalDuration {
47 pub weeks: u32,
49 pub days: u32,
51 pub exact: Option<ExactDuration>,
53}
54
55#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
57pub struct ExactDuration {
58 pub hours: u32,
60 pub minutes: u32,
62 pub seconds: u32,
64 pub frac: Option<FractionalSecond>,
66}
67
68impl std::fmt::Display for ExactDuration {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 if self.hours > 0 {
71 write!(f, "{}H", self.hours)?;
72 }
73 if self.minutes > 0 || (self.hours > 0 && (self.seconds > 0 || self.frac.is_some())) {
74 write!(f, "{}M", self.minutes)?;
75 }
76 if self.seconds > 0 || self.frac.is_some() {
77 write!(f, "{}", self.seconds)?;
78 if let Some(frac) = self.frac {
79 let nanos = frac.get().get();
80 let mut s = format!("{nanos:09}");
81 let trimmed = s.trim_end_matches('0');
82 s.truncate(trimmed.len());
83 write!(f, ".{s}")?;
84 }
85 write!(f, "S")?;
86 }
87 if self.hours == 0 && self.minutes == 0 && self.seconds == 0 && self.frac.is_none() {
89 write!(f, "0S")?;
90 }
91 Ok(())
92 }
93}
94
95impl std::fmt::Display for NominalDuration {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 if self.weeks > 0 {
98 write!(f, "{}W", self.weeks)?;
99 }
100 if self.days > 0 {
101 write!(f, "{}D", self.days)?;
102 }
103 if let Some(exact) = &self.exact {
104 write!(f, "T{exact}")?;
105 }
106 if self.weeks == 0 && self.days == 0 && self.exact.is_none() {
108 write!(f, "0D")?;
109 }
110 Ok(())
111 }
112}
113
114impl std::fmt::Display for Duration {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 write!(f, "P")?;
117 match self {
118 Duration::Nominal(n) => write!(f, "{n}"),
119 Duration::Exact(e) => write!(f, "T{e}"),
120 }
121 }
122}
123
124impl std::fmt::Display for SignedDuration {
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 match self.sign {
127 Sign::Neg => write!(f, "-{}", self.duration),
128 Sign::Pos => write!(f, "{}", self.duration),
129 }
130 }
131}