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}