proto_types/duration/
base.rs1use super::super::*;
3use crate::constants::{NANOS_PER_SECOND, PACKAGE_PREFIX, TIME_NANOS_MAX};
4
5impl Duration {
6 pub fn normalize(&mut self) {
12 if self.nanos <= -NANOS_PER_SECOND || self.nanos >= NANOS_PER_SECOND {
14 if let Some(seconds) = self
15 .seconds
16 .checked_add(i64::from(self.nanos / NANOS_PER_SECOND))
17 {
18 self.seconds = seconds;
19
20 self.nanos %= NANOS_PER_SECOND;
21 } else if self.nanos < 0 {
22 self.seconds = i64::MIN;
25
26 self.nanos = -TIME_NANOS_MAX;
27 } else {
28 self.seconds = i64::MAX;
31
32 self.nanos = TIME_NANOS_MAX;
33 }
34 }
35
36 if self.seconds < 0 && self.nanos > 0 {
39 if let Some(seconds) = self.seconds.checked_add(1) {
40 self.seconds = seconds;
41
42 self.nanos -= NANOS_PER_SECOND;
43 } else {
44 debug_assert_eq!(self.seconds, i64::MAX);
47
48 self.nanos = TIME_NANOS_MAX;
49 }
50 } else if self.seconds > 0 && self.nanos < 0 {
51 if let Some(seconds) = self.seconds.checked_sub(1) {
52 self.seconds = seconds;
53
54 self.nanos += NANOS_PER_SECOND;
55 } else {
56 debug_assert_eq!(self.seconds, i64::MIN);
59
60 self.nanos = -TIME_NANOS_MAX;
61 }
62 }
63
64 }
70
71 #[must_use]
77 pub fn normalized(&self) -> Self {
78 let mut result = *self;
79
80 result.normalize();
81
82 result
83 }
84}
85
86impl Name for Duration {
87 const PACKAGE: &'static str = PACKAGE_PREFIX;
88
89 const NAME: &'static str = "Duration";
90
91 fn type_url() -> String {
92 type_url_for::<Self>()
93 }
94}
95
96impl TryFrom<time::Duration> for Duration {
97 type Error = DurationError;
98
99 fn try_from(duration: time::Duration) -> Result<Self, DurationError> {
101 let seconds = i64::try_from(duration.as_secs()).map_err(|_| DurationError::OutOfRange)?;
102
103 let nanos: i32 = duration
104 .subsec_nanos()
105 .try_into()
106 .map_err(|_| DurationError::OutOfRange)?;
107
108 let duration = Self { seconds, nanos };
109
110 Ok(duration.normalized())
111 }
112}
113
114impl TryFrom<Duration> for time::Duration {
115 type Error = DurationError;
116
117 fn try_from(mut duration: Duration) -> Result<Self, DurationError> {
119 duration.normalize();
120
121 if duration.seconds >= 0 && duration.nanos >= 0 {
122 Ok(Self::new(
123 duration
124 .seconds
125 .try_into()
126 .map_err(|_| DurationError::OutOfRange)?,
127 duration
128 .nanos
129 .try_into()
130 .map_err(|_| DurationError::OutOfRange)?,
131 ))
132 } else {
133 Err(DurationError::NegativeDuration(Self::new(
134 (-duration.seconds)
135 .try_into()
136 .map_err(|_| DurationError::OutOfRange)?,
137 (-duration.nanos)
138 .try_into()
139 .map_err(|_| DurationError::OutOfRange)?,
140 )))
141 }
142 }
143}
144
145#[derive(Debug, PartialEq, Eq, Clone)]
147#[non_exhaustive]
148pub enum DurationError {
149 ParseFailure,
155
156 NegativeDuration(time::Duration),
160
161 OutOfRange,
166}
167
168impl fmt::Display for DurationError {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 match self {
171 Self::ParseFailure => write!(f, "failed to parse duration"),
172
173 Self::NegativeDuration(duration) => {
174 write!(f, "failed to convert negative duration: {duration:?}")
175 }
176
177 Self::OutOfRange => {
178 write!(f, "failed to convert duration out of range")
179 }
180 }
181 }
182}
183
184impl core::error::Error for DurationError {}
185
186impl FromStr for Duration {
187 type Err = DurationError;
188
189 fn from_str(s: &str) -> Result<Self, DurationError> {
190 datetime_internal::parse_duration(s).ok_or(DurationError::ParseFailure)
191 }
192}
193
194#[cfg(feature = "chrono")]
195mod chrono {
196
197 use crate::{Duration, duration::DurationError};
198
199 impl From<::chrono::TimeDelta> for Duration {
200 #[inline]
201 fn from(value: ::chrono::TimeDelta) -> Self {
202 let mut result = Self {
203 seconds: value.num_seconds(),
204
205 nanos: value.subsec_nanos(),
206 };
207
208 result.normalize();
209
210 result
211 }
212 }
213
214 impl TryFrom<Duration> for ::chrono::TimeDelta {
215 type Error = DurationError;
216
217 fn try_from(mut value: Duration) -> Result<Self, DurationError> {
218 value.normalize();
219
220 let seconds = Self::try_seconds(value.seconds).ok_or(DurationError::OutOfRange)?;
221
222 let nanos = Self::nanoseconds(value.nanos.into());
223
224 seconds
225 .checked_add(&nanos)
226 .ok_or(DurationError::OutOfRange)
227 }
228 }
229}