wallclock_timer/timers/
chrono.rs1use crate::timers::WallClockTimer;
2use snafu::prelude::*;
3use std::time::Duration;
4
5#[derive(Debug, Snafu)]
7pub enum ChronoTimerError<E>
8where
9 E: std::error::Error + Send + Sync + 'static,
10{
11 #[snafu(display("chrono::DateTime is out of range for SystemTime"))]
13 ChronoOutOfRange,
14 #[snafu(display("timer error while scheduling chrono DateTime: {source}"))]
16 ImplementationSpecific { source: E },
17}
18
19pub trait ChronoTimer: WallClockTimer {
21 fn schedule_at_datetime<Tz>(
26 &mut self,
27 deadline: chrono::DateTime<Tz>,
28 state: Self::State,
29 ) -> Result<(), ChronoTimerError<Self::Error>>
30 where
31 Tz: chrono::TimeZone,
32 Tz::Offset: Send + Sync;
33}
34
35impl<T> ChronoTimer for T
36where
37 T: WallClockTimer,
38{
39 fn schedule_at_datetime<Tz>(
40 &mut self,
41 deadline: chrono::DateTime<Tz>,
42 state: Self::State,
43 ) -> Result<(), ChronoTimerError<Self::Error>>
44 where
45 Tz: chrono::TimeZone,
46 Tz::Offset: Send + Sync,
47 {
48 let delta = deadline
49 .with_timezone(&chrono::Utc)
50 .signed_duration_since(chrono::DateTime::<chrono::Utc>::UNIX_EPOCH);
51 let duration = delta.to_std().ok().context(ChronoOutOfRangeSnafu)?;
53 let deadline = std::time::UNIX_EPOCH
54 .checked_add(Duration::from_secs(duration.as_secs()))
55 .and_then(|t| t.checked_add(Duration::from_nanos(duration.subsec_nanos() as u64)))
56 .context(ChronoOutOfRangeSnafu)?;
57 self.schedule_at(deadline, state)
58 .context(ImplementationSpecificSnafu)
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65 use crate::timers::{State, WallClockTimer};
66
67 #[derive(Debug)]
68 struct TestState {
69 id: u64,
70 }
71
72 impl State for TestState {
73 type Id = u64;
74
75 fn id(&self) -> &Self::Id {
76 &self.id
77 }
78
79 fn trigger(self) {}
80 }
81
82 #[derive(Debug)]
83 struct StubTimer {
84 last_deadline: Option<std::time::SystemTime>,
85 }
86
87 #[derive(Debug)]
88 struct StubError;
89
90 impl std::fmt::Display for StubError {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 write!(f, "stub")
93 }
94 }
95
96 impl std::error::Error for StubError {}
97
98 impl WallClockTimer for StubTimer {
99 type Id = u64;
100 type State = TestState;
101 type Error = StubError;
102
103 fn schedule_at(
104 &mut self,
105 deadline: std::time::SystemTime,
106 _state: Self::State,
107 ) -> Result<(), Self::Error> {
108 self.last_deadline = Some(deadline);
109 Ok(())
110 }
111
112 fn cancel(&mut self, _id: Self::Id) -> Result<(), Self::Error> {
113 Ok(())
114 }
115 }
116
117 #[test]
118 fn chrono_datetime_converts_to_systemtime() {
119 let mut timer = StubTimer {
120 last_deadline: None,
121 };
122 let dt = chrono::DateTime::<chrono::Utc>::from_timestamp(1, 0).expect("timestamp");
123 timer
124 .schedule_at_datetime(dt, TestState { id: 1 })
125 .expect("schedule");
126 let expected = std::time::UNIX_EPOCH + std::time::Duration::from_secs(1);
127 assert_eq!(timer.last_deadline, Some(expected));
128 }
129}