tendermint_proto/google/protobuf/
timestamp.rs1use prost::Name;
5
6use crate::prelude::*;
7
8use super::type_url::type_url_for;
9use super::PACKAGE;
10
11#[derive(Copy, Clone, PartialEq, ::prost::Message, ::serde::Deserialize, ::serde::Serialize)]
25#[serde(
26 from = "crate::serializers::timestamp::Rfc3339",
27 into = "crate::serializers::timestamp::Rfc3339"
28)]
29#[cfg_attr(feature = "json-schema", derive(::schemars::JsonSchema))]
30pub struct Timestamp {
31 #[prost(int64, tag = "1")]
35 pub seconds: i64,
36 #[prost(int32, tag = "2")]
41 pub nanos: i32,
42}
43
44impl Name for Timestamp {
45 const PACKAGE: &'static str = PACKAGE;
46 const NAME: &'static str = "Timestamp";
47
48 fn type_url() -> String {
49 type_url_for::<Self>()
50 }
51}
52
53const NANOS_PER_SECOND: i32 = 1_000_000_000;
54
55impl Timestamp {
56 pub fn normalize(&mut self) {
58 if self.nanos <= -NANOS_PER_SECOND || self.nanos >= NANOS_PER_SECOND {
60 if let Some(seconds) = self
61 .seconds
62 .checked_add((self.nanos / NANOS_PER_SECOND) as i64)
63 {
64 self.seconds = seconds;
65 self.nanos %= NANOS_PER_SECOND;
66 } else if self.nanos < 0 {
67 self.seconds = i64::MIN;
69 self.nanos = 0;
70 } else {
71 self.seconds = i64::MAX;
73 self.nanos = 999_999_999;
74 }
75 }
76
77 if self.nanos < 0 {
79 if let Some(seconds) = self.seconds.checked_sub(1) {
80 self.seconds = seconds;
81 self.nanos += NANOS_PER_SECOND;
82 } else {
83 debug_assert_eq!(self.seconds, i64::MIN);
85 self.nanos = 0;
86 }
87 }
88 }
89}
90
91impl Eq for Timestamp {}
94
95#[allow(clippy::derived_hash_with_manual_eq)]
97impl core::hash::Hash for Timestamp {
98 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
99 self.seconds.hash(state);
100 self.nanos.hash(state);
101 }
102}
103
104#[cfg(feature = "std")]
105impl From<std::time::SystemTime> for Timestamp {
106 fn from(system_time: std::time::SystemTime) -> Timestamp {
107 let (seconds, nanos) = match system_time.duration_since(std::time::UNIX_EPOCH) {
108 Ok(duration) => {
109 let seconds = i64::try_from(duration.as_secs()).unwrap();
110 (seconds, duration.subsec_nanos() as i32)
111 },
112 Err(error) => {
113 let duration = error.duration();
114 let seconds = i64::try_from(duration.as_secs()).unwrap();
115 let nanos = duration.subsec_nanos() as i32;
116 if nanos == 0 {
117 (-seconds, 0)
118 } else {
119 (-seconds - 1, 1_000_000_000 - nanos)
120 }
121 },
122 };
123 Timestamp { seconds, nanos }
124 }
125}
126
127#[cfg(feature = "std")]
135#[derive(Debug)]
136#[non_exhaustive]
137pub struct TimestampOutOfSystemRangeError {
138 pub timestamp: Timestamp,
139}
140
141#[cfg(feature = "std")]
142impl core::fmt::Display for TimestampOutOfSystemRangeError {
143 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
144 write!(
145 f,
146 "{self:?} is not representable as a `SystemTime` because it is out of range"
147 )
148 }
149}
150
151#[cfg(feature = "std")]
152impl std::error::Error for TimestampOutOfSystemRangeError {}
153
154#[cfg(feature = "std")]
155impl TryFrom<Timestamp> for std::time::SystemTime {
156 type Error = TimestampOutOfSystemRangeError;
157
158 fn try_from(mut timestamp: Timestamp) -> Result<std::time::SystemTime, Self::Error> {
159 let orig_timestamp = timestamp;
160
161 timestamp.normalize();
162
163 let system_time = if timestamp.seconds >= 0 {
164 std::time::UNIX_EPOCH
165 .checked_add(core::time::Duration::from_secs(timestamp.seconds as u64))
166 } else {
167 std::time::UNIX_EPOCH
168 .checked_sub(core::time::Duration::from_secs((-timestamp.seconds) as u64))
169 };
170
171 let system_time = system_time.and_then(|system_time| {
172 system_time.checked_add(core::time::Duration::from_nanos(timestamp.nanos as u64))
173 });
174
175 system_time.ok_or(TimestampOutOfSystemRangeError {
176 timestamp: orig_timestamp,
177 })
178 }
179}