proto_types/duration/
base.rs1use super::super::*;
3
4impl Duration {
5 pub fn normalize(&mut self) {
11 if self.nanos <= -NANOS_PER_SECOND || self.nanos >= NANOS_PER_SECOND {
13 if let Some(seconds) = self
14 .seconds
15 .checked_add((self.nanos / NANOS_PER_SECOND) as i64)
16 {
17 self.seconds = seconds;
18
19 self.nanos %= NANOS_PER_SECOND;
20 } else if self.nanos < 0 {
21 self.seconds = i64::MIN;
24
25 self.nanos = -NANOS_MAX;
26 } else {
27 self.seconds = i64::MAX;
30
31 self.nanos = NANOS_MAX;
32 }
33 }
34
35 if self.seconds < 0 && self.nanos > 0 {
38 if let Some(seconds) = self.seconds.checked_add(1) {
39 self.seconds = seconds;
40
41 self.nanos -= NANOS_PER_SECOND;
42 } else {
43 debug_assert_eq!(self.seconds, i64::MAX);
46
47 self.nanos = NANOS_MAX;
48 }
49 } else if self.seconds > 0 && self.nanos < 0 {
50 if let Some(seconds) = self.seconds.checked_sub(1) {
51 self.seconds = seconds;
52
53 self.nanos += NANOS_PER_SECOND;
54 } else {
55 debug_assert_eq!(self.seconds, i64::MIN);
58
59 self.nanos = -NANOS_MAX;
60 }
61 }
62
63 }
69
70 pub fn normalized(&self) -> Self {
76 let mut result = *self;
77
78 result.normalize();
79
80 result
81 }
82}
83
84impl Name for Duration {
85 const PACKAGE: &'static str = PACKAGE_PREFIX;
86
87 const NAME: &'static str = "Duration";
88
89 fn type_url() -> String {
90 type_url_for::<Self>()
91 }
92}
93
94impl TryFrom<time::Duration> for Duration {
95 type Error = DurationError;
96
97 fn try_from(duration: time::Duration) -> Result<Duration, DurationError> {
99 let seconds = i64::try_from(duration.as_secs()).map_err(|_| DurationError::OutOfRange)?;
100
101 let nanos = duration.subsec_nanos() as i32;
102
103 let duration = Duration { seconds, nanos };
104
105 Ok(duration.normalized())
106 }
107}
108
109impl TryFrom<Duration> for time::Duration {
110 type Error = DurationError;
111
112 fn try_from(mut duration: Duration) -> Result<time::Duration, DurationError> {
114 duration.normalize();
115
116 if duration.seconds >= 0 && duration.nanos >= 0 {
117 Ok(time::Duration::new(
118 duration.seconds as u64,
119 duration.nanos as u32,
120 ))
121 } else {
122 Err(DurationError::NegativeDuration(time::Duration::new(
123 (-duration.seconds) as u64,
124 (-duration.nanos) as u32,
125 )))
126 }
127 }
128}
129
130#[derive(Debug, PartialEq)]
132#[non_exhaustive]
133pub enum DurationError {
134 ParseFailure,
140
141 NegativeDuration(time::Duration),
145
146 OutOfRange,
151}
152
153impl fmt::Display for DurationError {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 match self {
156 DurationError::ParseFailure => write!(f, "failed to parse duration"),
157
158 DurationError::NegativeDuration(duration) => {
159 write!(f, "failed to convert negative duration: {:?}", duration)
160 }
161
162 DurationError::OutOfRange => {
163 write!(f, "failed to convert duration out of range")
164 }
165 }
166 }
167}
168
169impl std::error::Error for DurationError {}
170
171impl FromStr for Duration {
172 type Err = DurationError;
173
174 fn from_str(s: &str) -> Result<Duration, DurationError> {
175 datetime::parse_duration(s).ok_or(DurationError::ParseFailure)
176 }
177}
178
179#[cfg(feature = "chrono")]
180mod chrono {
181 use chrono::TimeDelta;
182
183 use crate::{Duration, DurationError};
184
185 impl From<::chrono::TimeDelta> for Duration {
186 fn from(value: ::chrono::TimeDelta) -> Self {
187 let mut result = Self {
188 seconds: value.num_seconds(),
189
190 nanos: value.subsec_nanos(),
191 };
192
193 result.normalize();
194
195 result
196 }
197 }
198
199 impl TryFrom<Duration> for ::chrono::TimeDelta {
200 type Error = DurationError;
201
202 fn try_from(mut value: Duration) -> Result<TimeDelta, DurationError> {
203 value.normalize();
204
205 let seconds = TimeDelta::try_seconds(value.seconds).ok_or(DurationError::OutOfRange)?;
206
207 let nanos = TimeDelta::nanoseconds(value.nanos.into());
208
209 seconds.checked_add(&nanos).ok_or(DurationError::OutOfRange)
210 }
211 }
212}