proto_types/timestamp/
mod.rs1#[cfg(feature = "serde")]
5mod serde;
6
7mod timestamp_conversions;
8mod timestamp_impls;
9mod timestamp_operations;
10
11use super::*;
12use crate::{
13 Timestamp,
14 constants::{NANOS_PER_SECOND, PACKAGE_PREFIX},
15 datetime_internal::DateTime,
16};
17
18impl Timestamp {
19 pub fn normalize(&mut self) {
25 if self.nanos <= -NANOS_PER_SECOND || self.nanos >= NANOS_PER_SECOND {
27 if let Some(seconds) = self
28 .seconds
29 .checked_add(i64::from(self.nanos / NANOS_PER_SECOND))
30 {
31 self.seconds = seconds;
32
33 self.nanos %= NANOS_PER_SECOND;
34 } else if self.nanos < 0 {
35 self.seconds = i64::MIN;
38
39 self.nanos = 0;
40 } else {
41 self.seconds = i64::MAX;
44
45 self.nanos = 999_999_999;
46 }
47 }
48
49 if self.nanos < 0 {
52 if let Some(seconds) = self.seconds.checked_sub(1) {
53 self.seconds = seconds;
54
55 self.nanos += NANOS_PER_SECOND;
56 } else {
57 debug_assert_eq!(self.seconds, i64::MIN);
60
61 self.nanos = 0;
62 }
63 }
64
65 }
71
72 pub fn try_normalize(mut self) -> Result<Self, Self> {
79 let before = self;
80
81 self.normalize();
82
83 if (self.seconds == i64::MAX || self.seconds == i64::MIN) && self.seconds != before.seconds
88 {
89 Err(before)
90 } else {
91 Ok(self)
92 }
93 }
94
95 #[must_use]
101 #[inline]
102 pub fn normalized(&self) -> Self {
103 let mut result = *self;
104
105 result.normalize();
106
107 result
108 }
109
110 #[inline]
111 pub fn date(year: i64, month: u8, day: u8) -> Result<Self, TimestampError> {
113 Self::date_time_nanos(year, month, day, 0, 0, 0, 0)
114 }
115
116 #[inline]
117 pub fn date_time(
119 year: i64,
120
121 month: u8,
122
123 day: u8,
124
125 hour: u8,
126
127 minute: u8,
128
129 second: u8,
130 ) -> Result<Self, TimestampError> {
131 Self::date_time_nanos(year, month, day, hour, minute, second, 0)
132 }
133
134 #[inline]
135 pub fn date_time_nanos(
137 year: i64,
138
139 month: u8,
140
141 day: u8,
142
143 hour: u8,
144
145 minute: u8,
146
147 second: u8,
148
149 nanos: u32,
150 ) -> Result<Self, TimestampError> {
151 let date_time = DateTime {
152 year,
153
154 month,
155
156 day,
157
158 hour,
159
160 minute,
161
162 second,
163
164 nanos,
165 };
166
167 Self::try_from(date_time)
168 }
169}
170
171impl Name for Timestamp {
172 const PACKAGE: &'static str = PACKAGE_PREFIX;
173
174 const NAME: &'static str = "Timestamp";
175
176 fn type_url() -> String {
177 type_url_for::<Self>()
178 }
179}
180
181#[cfg(feature = "std")]
182impl From<std::time::SystemTime> for Timestamp {
183 fn from(system_time: std::time::SystemTime) -> Self {
184 let (seconds, nanos) = match system_time.duration_since(std::time::UNIX_EPOCH) {
185 Ok(duration) => {
186 let seconds = i64::try_from(duration.as_secs()).unwrap_or_default();
187
188 (seconds, duration.subsec_nanos().cast_signed())
190 }
191
192 Err(error) => {
193 let duration = error.duration();
194
195 let seconds = i64::try_from(duration.as_secs()).unwrap_or_default();
196
197 let nanos = duration.subsec_nanos().cast_signed();
199
200 if nanos == 0 {
201 (-seconds, 0)
202 } else {
203 (-seconds - 1, 1_000_000_000 - nanos)
204 }
205 }
206 };
207
208 Self { seconds, nanos }
209 }
210}
211
212#[derive(Debug, PartialEq, Eq, Clone)]
214#[non_exhaustive]
215pub enum TimestampError {
216 OutOfSystemRange(Timestamp),
224 ParseFailure,
226 InvalidDateTime,
228}
229
230impl fmt::Display for TimestampError {
231 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232 match self {
233 Self::OutOfSystemRange(timestamp) => {
234 write!(
235 f,
236 "{timestamp} is not representable as a `SystemTime` because it is out of range"
237 )
238 }
239
240 Self::ParseFailure => {
241 write!(f, "failed to parse RFC-3339 formatted timestamp")
242 }
243
244 Self::InvalidDateTime => {
245 write!(f, "invalid date or time")
246 }
247 }
248 }
249}
250
251impl core::error::Error for TimestampError {}
252
253#[cfg(feature = "std")]
254impl TryFrom<Timestamp> for std::time::SystemTime {
255 type Error = TimestampError;
256
257 fn try_from(mut timestamp: Timestamp) -> Result<Self, Self::Error> {
258 let orig_timestamp = timestamp;
259
260 timestamp.normalize();
261
262 let system_time = if timestamp.seconds >= 0 {
263 std::time::UNIX_EPOCH.checked_add(time::Duration::from_secs(
264 timestamp
265 .seconds
266 .try_into()
267 .map_err(|_| TimestampError::OutOfSystemRange(timestamp))?,
268 ))
269 } else {
270 std::time::UNIX_EPOCH.checked_sub(time::Duration::from_secs(
271 timestamp
272 .seconds
273 .checked_neg()
274 .and_then(|s| s.try_into().ok())
275 .ok_or(TimestampError::OutOfSystemRange(timestamp))?,
276 ))
277 };
278
279 let system_time = system_time
280 .map(|time| {
281 let nanos = u64::try_from(timestamp.nanos)
282 .map_err(|_| TimestampError::OutOfSystemRange(timestamp))?;
283
284 time.checked_add(core::time::Duration::from_nanos(nanos))
285 .ok_or(TimestampError::OutOfSystemRange(timestamp))
286 })
287 .transpose()?;
288
289 system_time.ok_or(TimestampError::OutOfSystemRange(orig_timestamp))
290 }
291}
292
293impl FromStr for Timestamp {
294 type Err = TimestampError;
295
296 fn from_str(s: &str) -> Result<Self, TimestampError> {
297 datetime_internal::parse_timestamp(s).ok_or(TimestampError::ParseFailure)
298 }
299}
300
301impl fmt::Display for Timestamp {
302 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
303 core::fmt::Display::fmt(&DateTime::from(*self), f)
304 }
305}