massa_time/lib.rs
1// Copyright (c) 2022 MASSA LABS <info@massa.net>
2//! Unsigned time management
3#![warn(missing_docs)]
4#![warn(unused_crate_dependencies)]
5
6mod error;
7mod mapping_grpc;
8pub use error::TimeError;
9use massa_serialization::{Deserializer, Serializer, U64VarIntDeserializer, U64VarIntSerializer};
10use nom::error::{context, ContextError, ParseError};
11use nom::IResult;
12use serde::{Deserialize, Serialize};
13use std::convert::{TryFrom, TryInto};
14use std::fmt;
15use std::ops::Bound;
16use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
17use time::format_description::well_known::Rfc3339;
18use time::{Date, OffsetDateTime};
19
20/// Time structure used everywhere.
21/// milliseconds since 01/01/1970.
22#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
23pub struct MassaTime(u64);
24
25/// Serializer for `MassaTime`
26pub struct MassaTimeSerializer {
27 u64_serializer: U64VarIntSerializer,
28}
29
30impl MassaTimeSerializer {
31 /// Creates a `MassaTimeSerializer`
32 pub fn new() -> Self {
33 Self {
34 u64_serializer: U64VarIntSerializer::new(),
35 }
36 }
37}
38
39impl Default for MassaTimeSerializer {
40 fn default() -> Self {
41 Self::new()
42 }
43}
44
45impl Serializer<MassaTime> for MassaTimeSerializer {
46 /// ```
47 /// use std::ops::Bound::Included;
48 /// use massa_serialization::Serializer;
49 /// use massa_time::{MassaTime, MassaTimeSerializer};
50 ///
51 /// let time: MassaTime = MassaTime::from_millis(30);
52 /// let mut serialized = Vec::new();
53 /// let serializer = MassaTimeSerializer::new();
54 /// serializer.serialize(&time, &mut serialized).unwrap();
55 /// ```
56 fn serialize(
57 &self,
58 value: &MassaTime,
59 buffer: &mut Vec<u8>,
60 ) -> Result<(), massa_serialization::SerializeError> {
61 self.u64_serializer.serialize(&value.as_millis(), buffer)
62 }
63}
64
65/// Deserializer for `MassaTime`
66pub struct MassaTimeDeserializer {
67 u64_deserializer: U64VarIntDeserializer,
68}
69
70impl MassaTimeDeserializer {
71 /// Creates a `MassaTimeDeserializer`
72 ///
73 /// Arguments:
74 /// * range: minimum value for the time to deserialize
75 pub fn new(range: (Bound<MassaTime>, Bound<MassaTime>)) -> Self {
76 let min = match range.0 {
77 Bound::Included(x) => Bound::Included(x.as_millis()),
78 Bound::Excluded(x) => Bound::Excluded(x.as_millis()),
79 Bound::Unbounded => Bound::Included(0),
80 };
81
82 let max = match range.1 {
83 Bound::Included(x) => Bound::Included(x.as_millis()),
84 Bound::Excluded(x) => Bound::Excluded(x.as_millis()),
85 Bound::Unbounded => Bound::Included(MassaTime::max().as_millis()),
86 };
87
88 Self {
89 u64_deserializer: U64VarIntDeserializer::new(min, max),
90 }
91 }
92}
93
94impl Deserializer<MassaTime> for MassaTimeDeserializer {
95 /// ```
96 /// use std::ops::Bound::Included;
97 /// use massa_serialization::{Serializer, Deserializer, DeserializeError};
98 /// use massa_time::{MassaTime, MassaTimeSerializer, MassaTimeDeserializer};
99 ///
100 /// let time: MassaTime = MassaTime::from_millis(30);
101 /// let mut serialized = Vec::new();
102 /// let serializer = MassaTimeSerializer::new();
103 /// let deserializer = MassaTimeDeserializer::new((Included(MassaTime::from_millis(0)), Included(MassaTime::from_millis(u64::MAX))));
104 /// serializer.serialize(&time, &mut serialized).unwrap();
105 /// let (rest, time_deser) = deserializer.deserialize::<DeserializeError>(&serialized).unwrap();
106 /// assert!(rest.is_empty());
107 /// assert_eq!(time, time_deser);
108 /// ```
109 fn deserialize<'a, E: ParseError<&'a [u8]> + ContextError<&'a [u8]>>(
110 &self,
111 buffer: &'a [u8],
112 ) -> IResult<&'a [u8], MassaTime, E> {
113 context("Failed MassaTime deserialization", |input| {
114 self.u64_deserializer
115 .deserialize(input)
116 .map(|(rest, res)| (rest, MassaTime::from_millis(res)))
117 })(buffer)
118 }
119}
120
121impl fmt::Display for MassaTime {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 write!(f, "{}", self.as_millis())
124 }
125}
126
127impl From<MassaTime> for Duration {
128 /// Conversion from `massa_time` to duration, representing timestamp in milliseconds.
129 /// ```
130 /// # use std::time::Duration;
131 /// # use massa_time::*;
132 /// # use std::convert::Into;
133 /// let duration: Duration = Duration::from_millis(42);
134 /// let time : MassaTime = MassaTime::from_millis(42);
135 /// let res: Duration = time.into();
136 /// assert_eq!(res, duration);
137 /// ```
138 fn from(value: MassaTime) -> Self {
139 value.to_duration()
140 }
141}
142
143impl MassaTime {
144 /// Conversion from `u64`, representing timestamp in milliseconds.
145 /// ```
146 /// # use massa_time::*;
147 /// let time : MassaTime = MassaTime::from_millis(42);
148 /// ```
149 pub const fn from_millis(value: u64) -> Self {
150 MassaTime(value)
151 }
152
153 /// Smallest time interval
154 pub const EPSILON: MassaTime = MassaTime(1);
155
156 /// Gets current UNIX timestamp (resolution: milliseconds).
157 ///
158 /// ```
159 /// # use std::time::{Duration, SystemTime, UNIX_EPOCH};
160 /// # use massa_time::*;
161 /// # use std::convert::TryFrom;
162 /// # use std::cmp::max;
163 /// let now_duration : Duration = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
164 /// let now_massa_time : MassaTime = MassaTime::now();
165 /// let converted:MassaTime = MassaTime::from_millis(now_duration.as_millis() as u64);
166 /// assert!(max(now_massa_time.saturating_sub(converted), converted.saturating_sub(now_massa_time)) < MassaTime::from_millis(100))
167 /// ```
168 pub fn now() -> Self {
169 let now_millis = SystemTime::now()
170 .duration_since(UNIX_EPOCH)
171 .expect("could not get duration since UNIX_EPOCH")
172 .as_millis()
173 .try_into()
174 .expect("could fit current time into its underlying representation");
175 MassaTime::from_millis(now_millis)
176 }
177
178 /// Conversion to `std::time::Duration`.
179 /// ```
180 /// # use std::time::Duration;
181 /// # use massa_time::*;
182 /// let duration: Duration = Duration::from_millis(42);
183 /// let time : MassaTime = MassaTime::from_millis(42);
184 /// let res: Duration = time.to_duration();
185 /// assert_eq!(res, duration);
186 /// ```
187 pub fn to_duration(&self) -> Duration {
188 Duration::from_millis(self.0)
189 }
190
191 /// Conversion to `u64`, representing milliseconds.
192 /// ```
193 /// # use massa_time::*;
194 /// let time : MassaTime = MassaTime::from_millis(42);
195 /// let res: u64 = time.as_millis();
196 /// assert_eq!(res, 42);
197 /// ```
198 pub const fn as_millis(&self) -> u64 {
199 self.0
200 }
201
202 /// ```
203 /// # use std::time::{Duration, SystemTime, UNIX_EPOCH};
204 /// # use massa_time::*;
205 /// # use std::convert::TryFrom;
206 /// # use std::cmp::max;
207 /// # use std::time::Instant;
208 /// let (cur_timestamp, cur_instant): (MassaTime, Instant) = (MassaTime::now(), Instant::now());
209 /// let massa_time_instant: Instant = cur_timestamp.estimate_instant().unwrap();
210 /// assert!(max(
211 /// massa_time_instant.saturating_duration_since(cur_instant),
212 /// cur_instant.saturating_duration_since(massa_time_instant)
213 /// ) < std::time::Duration::from_millis(10))
214 /// ```
215 pub fn estimate_instant(self) -> Result<Instant, TimeError> {
216 let (cur_timestamp, cur_instant) = (MassaTime::now(), Instant::now());
217 if self >= cur_timestamp {
218 cur_instant.checked_add(self.saturating_sub(cur_timestamp).to_duration())
219 } else {
220 cur_instant.checked_sub(cur_timestamp.saturating_sub(self).to_duration())
221 }
222 .ok_or(TimeError::TimeOverflowError)
223 }
224
225 /// ```
226 /// # use massa_time::*;
227 /// let time_1 : MassaTime = MassaTime::from_millis(42);
228 /// let time_2 : MassaTime = MassaTime::from_millis(7);
229 /// let res : MassaTime = time_1.saturating_sub(time_2);
230 /// assert_eq!(res, MassaTime::from_millis(42-7))
231 /// ```
232 #[must_use]
233 pub fn saturating_sub(self, t: MassaTime) -> Self {
234 MassaTime(self.0.saturating_sub(t.0))
235 }
236
237 /// ```
238 /// # use massa_time::*;
239 /// let time_1 : MassaTime = MassaTime::from_millis(42);
240 /// let time_2 : MassaTime = MassaTime::from_millis(7);
241 /// let res : MassaTime = time_1.saturating_add(time_2);
242 /// assert_eq!(res, MassaTime::from_millis(42+7))
243 /// ```
244 #[must_use]
245 pub fn saturating_add(self, t: MassaTime) -> Self {
246 MassaTime(self.0.saturating_add(t.0))
247 }
248
249 /// ```
250 /// # use massa_time::*;
251 /// let time_1 : MassaTime = MassaTime::from_millis(42);
252 /// let time_2 : MassaTime = MassaTime::from_millis(7);
253 /// let res : MassaTime = time_1.checked_sub(time_2).unwrap();
254 /// assert_eq!(res, MassaTime::from_millis(42-7))
255 /// ```
256 pub fn checked_sub(self, t: MassaTime) -> Result<Self, TimeError> {
257 self.0
258 .checked_sub(t.0)
259 .ok_or_else(|| TimeError::CheckedOperationError("subtraction error".to_string()))
260 .map(MassaTime)
261 }
262
263 /// ```
264 /// # use massa_time::*;
265 /// let time_1 : MassaTime = MassaTime::from_millis(42);
266 /// let time_2 : MassaTime = MassaTime::from_millis(7);
267 /// let res : MassaTime = time_1.checked_add(time_2).unwrap();
268 /// assert_eq!(res, MassaTime::from_millis(42+7))
269 /// ```
270 pub fn checked_add(self, t: MassaTime) -> Result<Self, TimeError> {
271 self.0
272 .checked_add(t.0)
273 .ok_or_else(|| TimeError::CheckedOperationError("addition error".to_string()))
274 .map(MassaTime)
275 }
276
277 /// ```
278 /// # use massa_time::*;
279 /// let time_1 : MassaTime = MassaTime::from_millis(42);
280 /// let time_2 : MassaTime = MassaTime::from_millis(7);
281 /// let res : u64 = time_1.checked_div_time(time_2).unwrap();
282 /// assert_eq!(res,42/7)
283 /// ```
284 pub fn checked_div_time(self, t: MassaTime) -> Result<u64, TimeError> {
285 self.0
286 .checked_div(t.0)
287 .ok_or_else(|| TimeError::CheckedOperationError("division error".to_string()))
288 }
289
290 /// ```
291 /// # use massa_time::*;
292 /// let time_1 : MassaTime = MassaTime::from_millis(42);
293 /// let res : MassaTime = time_1.checked_div_u64(7).unwrap();
294 /// assert_eq!(res,MassaTime::from_millis(42/7))
295 /// ```
296 pub fn checked_div_u64(self, n: u64) -> Result<MassaTime, TimeError> {
297 self.0
298 .checked_div(n)
299 .ok_or_else(|| TimeError::CheckedOperationError("division error".to_string()))
300 .map(MassaTime)
301 }
302
303 /// ```
304 /// # use massa_time::*;
305 /// let time_1 : MassaTime = MassaTime::from_millis(42);
306 /// let res : MassaTime = time_1.saturating_mul(7);
307 /// assert_eq!(res,MassaTime::from_millis(42*7))
308 /// ```
309 #[must_use]
310 pub const fn saturating_mul(self, n: u64) -> MassaTime {
311 MassaTime(self.0.saturating_mul(n))
312 }
313
314 /// ```
315 /// # use massa_time::*;
316 /// let time_1 : MassaTime = MassaTime::from_millis(42);
317 /// let res : MassaTime = time_1.checked_mul(7).unwrap();
318 /// assert_eq!(res,MassaTime::from_millis(42*7))
319 /// ```
320 pub fn checked_mul(self, n: u64) -> Result<Self, TimeError> {
321 self.0
322 .checked_mul(n)
323 .ok_or_else(|| TimeError::CheckedOperationError("multiplication error".to_string()))
324 .map(MassaTime)
325 }
326
327 /// ```
328 /// # use massa_time::*;
329 /// let time_1 : MassaTime = MassaTime::from_millis(42);
330 /// let time_2 : MassaTime = MassaTime::from_millis(7);
331 /// let res : MassaTime = time_1.checked_rem_time(time_2).unwrap();
332 /// assert_eq!(res,MassaTime::from_millis(42%7))
333 /// ```
334 pub fn checked_rem_time(self, t: MassaTime) -> Result<Self, TimeError> {
335 self.0
336 .checked_rem(t.0)
337 .ok_or_else(|| TimeError::CheckedOperationError("remainder error".to_string()))
338 .map(MassaTime)
339 }
340
341 /// ```
342 /// # use massa_time::*;
343 /// let time_1 : MassaTime = MassaTime::from_millis(42);
344 /// let res : MassaTime = time_1.checked_rem_u64(7).unwrap();
345 /// assert_eq!(res,MassaTime::from_millis(42%7))
346 /// ```
347 pub fn checked_rem_u64(self, n: u64) -> Result<Self, TimeError> {
348 self.0
349 .checked_rem(n)
350 .ok_or_else(|| TimeError::CheckedOperationError("remainder error".to_string()))
351 .map(MassaTime)
352 }
353
354 /// ```
355 /// # use massa_time::*;
356 ///
357 /// let time1 = MassaTime::from_millis(42);
358 /// let time2 = MassaTime::from_millis(84);
359 ///
360 /// assert_eq!(time1.abs_diff(time2), MassaTime::from_millis(42));
361 /// assert_eq!(time2.abs_diff(time1), MassaTime::from_millis(42));
362 /// ```
363 pub fn abs_diff(&self, t: MassaTime) -> MassaTime {
364 MassaTime(self.0.abs_diff(t.0))
365 }
366
367 /// ```
368 /// # use massa_time::*;
369 /// let massa_time : MassaTime = MassaTime::from_millis(1_640_995_200_000);
370 /// assert_eq!(massa_time.format_instant(), String::from("2022-01-01T00:00:00Z"))
371 /// ```
372 pub fn format_instant(&self) -> String {
373 OffsetDateTime::from_unix_timestamp((self.as_millis() / 1000) as i64)
374 .map(|time| time.format(&Rfc3339).unwrap_or_default())
375 .unwrap_or_default()
376 }
377
378 /// ```
379 /// # use massa_time::*;
380 /// let massa_time : MassaTime = MassaTime::from_millis(1000*( 8 * 24*60*60 + 1 * 60*60 + 3 * 60 + 6 ));
381 /// assert_eq!(massa_time.format_duration().unwrap(), String::from("8 days, 1 hours, 3 minutes, 6 seconds"))
382 /// ```
383 pub fn format_duration(&self) -> Result<String, TimeError> {
384 let (days, hours, mins, secs) = self.days_hours_mins_secs()?;
385 Ok(format!(
386 "{} days, {} hours, {} minutes, {} seconds",
387 days, hours, mins, secs
388 ))
389 }
390
391 /// ```
392 /// # use massa_time::*;
393 /// let massa_time : MassaTime = MassaTime::from_utc_ymd_hms(2022, 2, 5, 22, 50, 40).unwrap();
394 /// assert_eq!(massa_time.format_instant(), String::from("2022-02-05T22:50:40Z"))
395 /// ```
396 pub fn from_utc_ymd_hms(
397 year: i32,
398 month: u8,
399 day: u8,
400 hour: u8,
401 minute: u8,
402 second: u8,
403 ) -> Result<MassaTime, TimeError> {
404 let month = month.try_into().map_err(|_| TimeError::ConversionError)?;
405
406 let date =
407 Date::from_calendar_date(year, month, day).map_err(|_| TimeError::ConversionError)?;
408
409 let date_time = date
410 .with_hms(hour, minute, second)
411 .map_err(|_| TimeError::ConversionError)?
412 .assume_utc();
413
414 Ok(MassaTime::from_millis(
415 date_time
416 .unix_timestamp_nanos()
417 .checked_div(1_000_000)
418 .ok_or(TimeError::ConversionError)? as u64,
419 ))
420 }
421
422 /// ```
423 /// # use massa_time::*;
424 /// let massa_time = MassaTime::from_millis(1000 * ( 8 * 24*60*60 + 1 * 60*60 + 3 * 60 + 6 ));
425 /// let (days, hours, mins, secs) = massa_time.days_hours_mins_secs().unwrap();
426 /// assert_eq!(days, 8);
427 /// assert_eq!(hours, 1);
428 /// assert_eq!(mins, 3);
429 /// assert_eq!(secs, 6);
430 /// ```
431 pub fn days_hours_mins_secs(&self) -> Result<(i64, i64, i64, i64), TimeError> {
432 let time: time::Duration = time::Duration::try_from(self.to_duration())
433 .map_err(|_| TimeError::TimeOverflowError)?;
434 let days = time.whole_days();
435 let hours = (time - time::Duration::days(days)).whole_hours();
436 let mins =
437 (time - time::Duration::days(days) - time::Duration::hours(hours)).whole_minutes();
438 let secs = (time
439 - time::Duration::days(days)
440 - time::Duration::hours(hours)
441 - time::Duration::minutes(mins))
442 .whole_seconds();
443 Ok((days, hours, mins, secs))
444 }
445
446 /// Get max MassaTime value
447 pub fn max() -> MassaTime {
448 MassaTime::from_millis(u64::MAX)
449 }
450}