sc_offchain/api/timestamp.rs
1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Helper methods dedicated to timestamps.
20
21use sp_core::offchain::Timestamp;
22use std::time::{Duration, SystemTime};
23
24/// Returns the current time as a `Timestamp`.
25pub fn now() -> Timestamp {
26 let now = SystemTime::now();
27 let epoch_duration = now.duration_since(SystemTime::UNIX_EPOCH);
28 match epoch_duration {
29 Err(_) => {
30 // Current time is earlier than UNIX_EPOCH.
31 Timestamp::from_unix_millis(0)
32 },
33 Ok(d) => {
34 let duration = d.as_millis();
35 // Assuming overflow won't happen for a few hundred years.
36 Timestamp::from_unix_millis(
37 duration
38 .try_into()
39 .expect("epoch milliseconds won't overflow u64 for hundreds of years; qed"),
40 )
41 },
42 }
43}
44
45/// Returns how a `Timestamp` compares to "now".
46///
47/// In other words, returns `timestamp - now()`.
48pub fn timestamp_from_now(timestamp: Timestamp) -> Duration {
49 Duration::from_millis(timestamp.diff(&now()).millis())
50}
51
52/// Converts the deadline into a `Future` that resolves when the deadline is reached.
53///
54/// If `None`, returns a never-ending `Future`.
55pub fn deadline_to_future(
56 deadline: Option<Timestamp>,
57) -> futures::future::MaybeDone<impl futures::Future<Output = ()>> {
58 use futures::future::{self, Either};
59
60 future::maybe_done(match deadline.map(timestamp_from_now) {
61 None => Either::Left(future::pending()),
62 // Only apply delay if we need to wait a non-zero duration
63 Some(duration) if duration <= Duration::from_secs(0) =>
64 Either::Right(Either::Left(future::ready(()))),
65 Some(duration) => Either::Right(Either::Right(futures_timer::Delay::new(duration))),
66 })
67}