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((self.nanos / NANOS_PER_SECOND) as i64)
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 pub fn normalized(&self) -> Self {
77 let mut result = *self;
78
79 result.normalize();
80
81 result
82 }
83}
84
85impl Name for Duration {
86 const PACKAGE: &'static str = PACKAGE_PREFIX;
87
88 const NAME: &'static str = "Duration";
89
90 fn type_url() -> String {
91 type_url_for::<Self>()
92 }
93}
94
95impl TryFrom<time::Duration> for Duration {
96 type Error = DurationError;
97
98 fn try_from(duration: time::Duration) -> Result<Duration, DurationError> {
100 let seconds = i64::try_from(duration.as_secs()).map_err(|_| DurationError::OutOfRange)?;
101
102 let nanos = duration.subsec_nanos() as i32;
103
104 let duration = Duration { seconds, nanos };
105
106 Ok(duration.normalized())
107 }
108}
109
110impl TryFrom<Duration> for time::Duration {
111 type Error = DurationError;
112
113 fn try_from(mut duration: Duration) -> Result<time::Duration, DurationError> {
115 duration.normalize();
116
117 if duration.seconds >= 0 && duration.nanos >= 0 {
118 Ok(time::Duration::new(
119 duration.seconds as u64,
120 duration.nanos as u32,
121 ))
122 } else {
123 Err(DurationError::NegativeDuration(time::Duration::new(
124 (-duration.seconds) as u64,
125 (-duration.nanos) as u32,
126 )))
127 }
128 }
129}
130
131#[derive(Debug, PartialEq, Eq, Clone)]
133#[non_exhaustive]
134pub enum DurationError {
135 ParseFailure,
141
142 NegativeDuration(time::Duration),
146
147 OutOfRange,
152}
153
154impl fmt::Display for DurationError {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 match self {
157 DurationError::ParseFailure => write!(f, "failed to parse duration"),
158
159 DurationError::NegativeDuration(duration) => {
160 write!(f, "failed to convert negative duration: {:?}", duration)
161 }
162
163 DurationError::OutOfRange => {
164 write!(f, "failed to convert duration out of range")
165 }
166 }
167 }
168}
169
170impl std::error::Error for DurationError {}
171
172impl FromStr for Duration {
173 type Err = DurationError;
174
175 fn from_str(s: &str) -> Result<Duration, DurationError> {
176 datetime_internal::parse_duration(s).ok_or(DurationError::ParseFailure)
177 }
178}
179
180#[cfg(feature = "chrono")]
181mod chrono {
182 use chrono::TimeDelta;
183
184 use crate::{duration::DurationError, Duration};
185
186 impl From<::chrono::TimeDelta> for Duration {
187 fn from(value: ::chrono::TimeDelta) -> Self {
188 let mut result = Self {
189 seconds: value.num_seconds(),
190
191 nanos: value.subsec_nanos(),
192 };
193
194 result.normalize();
195
196 result
197 }
198 }
199
200 impl TryFrom<Duration> for ::chrono::TimeDelta {
201 type Error = DurationError;
202
203 fn try_from(mut value: Duration) -> Result<TimeDelta, DurationError> {
204 value.normalize();
205
206 let seconds = TimeDelta::try_seconds(value.seconds).ok_or(DurationError::OutOfRange)?;
207
208 let nanos = TimeDelta::nanoseconds(value.nanos.into());
209
210 seconds.checked_add(&nanos).ok_or(DurationError::OutOfRange)
211 }
212 }
213}