prost_wkt_types/pbtime/
timestamp.rs1use super::*;
2
3impl Timestamp {
8 pub fn normalize(&mut self) {
14 if self.nanos <= -NANOS_PER_SECOND || self.nanos >= NANOS_PER_SECOND {
16 if let Some(seconds) = self
17 .seconds
18 .checked_add((self.nanos / NANOS_PER_SECOND) as i64)
19 {
20 self.seconds = seconds;
21 self.nanos %= NANOS_PER_SECOND;
22 } else if self.nanos < 0 {
23 self.seconds = i64::MIN;
25 self.nanos = 0;
26 } else {
27 self.seconds = i64::MAX;
29 self.nanos = 999_999_999;
30 }
31 }
32
33 if self.nanos < 0 {
35 if let Some(seconds) = self.seconds.checked_sub(1) {
36 self.seconds = seconds;
37 self.nanos += NANOS_PER_SECOND;
38 } else {
39 debug_assert_eq!(self.seconds, i64::MIN);
41 self.nanos = 0;
42 }
43 }
44
45 }
49
50 pub fn try_normalize(mut self) -> Result<Timestamp, Timestamp> {
57 let before = self;
58 self.normalize();
59 if (self.seconds == i64::MAX || self.seconds == i64::MIN) && self.seconds != before.seconds
62 {
63 Err(before)
64 } else {
65 Ok(self)
66 }
67 }
68
69 pub fn date(year: i64, month: u8, day: u8) -> Result<Timestamp, TimestampError> {
71 Timestamp::date_time_nanos(year, month, day, 0, 0, 0, 0)
72 }
73
74 pub fn date_time(
76 year: i64,
77 month: u8,
78 day: u8,
79 hour: u8,
80 minute: u8,
81 second: u8,
82 ) -> Result<Timestamp, TimestampError> {
83 Timestamp::date_time_nanos(year, month, day, hour, minute, second, 0)
84 }
85
86 pub fn date_time_nanos(
88 year: i64,
89 month: u8,
90 day: u8,
91 hour: u8,
92 minute: u8,
93 second: u8,
94 nanos: u32,
95 ) -> Result<Timestamp, TimestampError> {
96 let date_time = datetime::DateTime {
97 year,
98 month,
99 day,
100 hour,
101 minute,
102 second,
103 nanos,
104 };
105
106 Timestamp::try_from(date_time)
107 }
108}
109
110#[cfg(feature = "std")]
122impl Eq for Timestamp {}
123
124#[cfg(feature = "std")]
125impl std::hash::Hash for Timestamp {
126 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
127 self.seconds.hash(state);
128 self.nanos.hash(state);
129 }
130}
131
132#[cfg(feature = "std")]
133impl From<std::time::SystemTime> for Timestamp {
134 fn from(system_time: std::time::SystemTime) -> Timestamp {
135 let (seconds, nanos) = match system_time.duration_since(std::time::UNIX_EPOCH) {
136 Ok(duration) => {
137 let seconds = i64::try_from(duration.as_secs()).unwrap();
138 (seconds, duration.subsec_nanos() as i32)
139 }
140 Err(error) => {
141 let duration = error.duration();
142 let seconds = i64::try_from(duration.as_secs()).unwrap();
143 let nanos = duration.subsec_nanos() as i32;
144 if nanos == 0 {
145 (-seconds, 0)
146 } else {
147 (-seconds - 1, 1_000_000_000 - nanos)
148 }
149 }
150 };
151 Timestamp { seconds, nanos }
152 }
153}
154
155#[allow(clippy::derive_partial_eq_without_eq)]
157#[derive(Debug, PartialEq)]
158#[non_exhaustive]
159pub enum TimestampError {
160 OutOfSystemRange(Timestamp),
168
169 ParseFailure,
171
172 InvalidDateTime,
174}
175
176impl fmt::Display for TimestampError {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 match self {
179 TimestampError::OutOfSystemRange(timestamp) => {
180 write!(
181 f,
182 "{} is not representable as a `SystemTime` because it is out of range",
183 timestamp
184 )
185 }
186 TimestampError::ParseFailure => {
187 write!(f, "failed to parse RFC-3339 formatted timestamp")
188 }
189 TimestampError::InvalidDateTime => {
190 write!(f, "invalid date or time")
191 }
192 }
193 }
194}
195
196#[cfg(feature = "std")]
197impl std::error::Error for TimestampError {}
198
199#[cfg(feature = "std")]
200impl TryFrom<Timestamp> for std::time::SystemTime {
201 type Error = TimestampError;
202
203 fn try_from(mut timestamp: Timestamp) -> Result<std::time::SystemTime, Self::Error> {
204 let orig_timestamp = timestamp;
205 timestamp.normalize();
206
207 let system_time = if timestamp.seconds >= 0 {
208 std::time::UNIX_EPOCH.checked_add(time::Duration::from_secs(timestamp.seconds as u64))
209 } else {
210 std::time::UNIX_EPOCH.checked_sub(time::Duration::from_secs(
211 timestamp
212 .seconds
213 .checked_neg()
214 .ok_or(TimestampError::OutOfSystemRange(timestamp))? as u64,
215 ))
216 };
217
218 let system_time = system_time.and_then(|system_time| {
219 system_time.checked_add(time::Duration::from_nanos(timestamp.nanos as u64))
220 });
221
222 system_time.ok_or(TimestampError::OutOfSystemRange(orig_timestamp))
223 }
224}
225
226impl FromStr for Timestamp {
227 type Err = TimestampError;
228
229 fn from_str(s: &str) -> Result<Timestamp, TimestampError> {
230 datetime::parse_timestamp(s).ok_or(TimestampError::ParseFailure)
231 }
232}
233
234impl fmt::Display for Timestamp {
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 datetime::DateTime::from(*self).fmt(f)
237 }
238}
239
240impl From<NaiveDateTime> for Timestamp {
246 fn from(dt: NaiveDateTime) -> Self {
247 Timestamp {
248 seconds: dt.and_utc().timestamp(),
249 nanos: dt.and_utc().timestamp_subsec_nanos() as i32,
250 }
251 }
252}
253
254impl From<DateTime<Utc>> for Timestamp {
256 fn from(dt: DateTime<Utc>) -> Self {
257 Timestamp {
258 seconds: dt.timestamp(),
259 nanos: dt.timestamp_subsec_nanos() as i32,
260 }
261 }
262}
263
264impl From<Timestamp> for DateTime<Utc> {
266 fn from(val: Timestamp) -> Self {
267 let mut value = val;
268 value.normalize();
273 DateTime::from_timestamp(value.seconds, value.nanos as u32)
274 .expect("invalid or out-of-range datetime")
275 }
276}
277
278impl Serialize for Timestamp {
279 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
280 where
281 S: Serializer,
282 {
283 let mut ts = Timestamp {
284 seconds: self.seconds,
285 nanos: self.nanos,
286 };
287 ts.normalize();
288 let dt: DateTime<Utc> = ts.try_into().map_err(serde::ser::Error::custom)?;
289 serializer.serialize_str(format!("{dt:?}").as_str())
290 }
291}
292
293impl<'de> Deserialize<'de> for Timestamp {
294 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
295 where
296 D: Deserializer<'de>,
297 {
298 struct TimestampVisitor;
299
300 impl<'de> Visitor<'de> for TimestampVisitor {
301 type Value = Timestamp;
302
303 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
304 formatter.write_str("Timestamp in RFC3339 format")
305 }
306
307 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
308 where
309 E: de::Error,
310 {
311 let utc: DateTime<Utc> = chrono::DateTime::from_str(value).map_err(|err| {
312 serde::de::Error::custom(format!(
313 "Failed to parse {value} as datetime: {err:?}"
314 ))
315 })?;
316 let ts = Timestamp::from(utc);
317 Ok(ts)
318 }
319 }
320 deserializer.deserialize_str(TimestampVisitor)
321 }
322}
323