Skip to main content

proto_types/timestamp/
timestamp_impls.rs

1use crate::{Duration, Timestamp};
2
3#[cfg(not(feature = "chrono"))]
4impl crate::Timestamp {
5	/// Returns the timestamp in YYYY-MM-DD format.
6	/// The same method, with the `chrono` feature, allows for custom formatting.
7	pub fn format(&self) -> crate::String {
8		use crate::ToString;
9
10		self.to_string()
11	}
12}
13
14#[cfg(feature = "chrono")]
15mod chrono {
16	use chrono::Utc;
17
18	use crate::{String, Timestamp, ToString, timestamp::TimestampError};
19
20	impl Timestamp {
21		/// Converts this timestamp into a [`chrono::DateTime<Utc>`] struct and calls .format on it with the string argument being given.
22		pub fn format(&self, string: &str) -> Result<String, TimestampError> {
23			let chrono_timestamp: chrono::DateTime<Utc> = (*self).try_into()?;
24
25			Ok(chrono_timestamp.format(string).to_string())
26		}
27
28		/// Converts this [`Timestamp`] instance to chrono::[`DateTime`](::chrono::DateTime) with [`chrono::Utc`].
29		#[inline]
30		pub fn as_datetime_utc(&self) -> Result<chrono::DateTime<Utc>, TimestampError> {
31			(*self).try_into()
32		}
33	}
34}
35
36impl Timestamp {
37	/// Creates a new instance.
38	#[must_use]
39	#[inline]
40	pub const fn new(seconds: i64, nanos: i32) -> Self {
41		Self { seconds, nanos }
42	}
43}
44
45#[cfg(all(not(feature = "std"), feature = "chrono-wasm"))]
46impl Timestamp {
47	/// Returns the current timestamp.
48	#[must_use]
49	#[inline]
50	pub fn now() -> Self {
51		::chrono::Utc::now().into()
52	}
53}
54
55#[cfg(feature = "std")]
56impl Timestamp {
57	/// Returns the current timestamp.
58	#[must_use]
59	#[inline]
60	pub fn now() -> Self {
61		std::time::SystemTime::now().into()
62	}
63}
64
65#[cfg(any(feature = "std", feature = "chrono-wasm"))]
66impl Timestamp {
67	/// Checks whether the Timestamp instance is within the indicated range (positive or negative) from now.
68	#[must_use]
69	#[inline]
70	pub fn is_within_range_from_now(&self, range: Duration) -> bool {
71		let now = Self::now();
72
73		(now + range) >= *self && (now - range) <= *self
74	}
75
76	/// Checks whether the Timestamp instance is within the indicated range in the future.
77	#[must_use]
78	#[inline]
79	pub fn is_within_future_range(&self, range: Duration) -> bool {
80		let now = Self::now();
81		let max = now + range;
82
83		*self <= max && *self >= now
84	}
85
86	/// Checks whether the Timestamp instance is within the indicated range in the past.
87	#[must_use]
88	#[inline]
89	pub fn is_within_past_range(&self, range: Duration) -> bool {
90		let now = Self::now();
91		let min = now - range;
92
93		*self >= min && *self <= now
94	}
95
96	/// Returns `true` if the timestamp is in the future.
97	#[must_use]
98	#[inline]
99	pub fn is_future(&self) -> bool {
100		*self > Self::now()
101	}
102
103	/// Returns `true` if the timestamp is in the past.
104	#[must_use]
105	#[inline]
106	pub fn is_past(&self) -> bool {
107		*self < Self::now()
108	}
109}
110
111#[cfg(test)]
112mod tests {
113	use super::*;
114
115	fn offset_seconds(base: &Timestamp, s: i64) -> Timestamp {
116		Timestamp {
117			seconds: base.seconds + s,
118			nanos: base.nanos,
119		}
120	}
121
122	#[test]
123	fn test_is_future() {
124		let now = Timestamp::now();
125
126		let future_point = offset_seconds(&now, 5);
127		let past_point = offset_seconds(&now, -5);
128
129		assert!(future_point.is_future(), "T + 5s should be in the future");
130		assert!(
131			!past_point.is_future(),
132			"T - 5s should NOT be in the future"
133		);
134	}
135
136	#[test]
137	fn test_is_past() {
138		let now = Timestamp::now();
139
140		let future_point = offset_seconds(&now, 5);
141		let past_point = offset_seconds(&now, -5);
142
143		assert!(past_point.is_past(), "T - 5s should be in the past");
144		assert!(!future_point.is_past(), "T + 5s should NOT be in the past");
145	}
146
147	#[test]
148	fn test_is_within_future_range() {
149		let now = Timestamp::now();
150		let range = Duration::new(10, 0);
151
152		let inside = offset_seconds(&now, 5);
153		assert!(
154			inside.is_within_future_range(range),
155			"5s is within 10s range"
156		);
157
158		let outside_far = offset_seconds(&now, 15);
159		assert!(
160			!outside_far.is_within_future_range(range),
161			"15s is outside 10s range"
162		);
163
164		let outside_past = offset_seconds(&now, -1);
165		assert!(
166			!outside_past.is_within_future_range(range),
167			"Past value is not in future range"
168		);
169	}
170
171	#[test]
172	fn test_is_within_past_range() {
173		let now = Timestamp::now();
174		let range = Duration::new(10, 0);
175
176		let inside = offset_seconds(&now, -5);
177		assert!(
178			inside.is_within_past_range(range),
179			"-5s is within 10s past range"
180		);
181
182		let outside_old = offset_seconds(&now, -15);
183		assert!(
184			!outside_old.is_within_past_range(range),
185			"-15s is too old for 10s range"
186		);
187
188		let outside_future = offset_seconds(&now, 1);
189		assert!(
190			!outside_future.is_within_past_range(range),
191			"Future value is not in past range"
192		);
193	}
194}