1use decanter::prelude::Hashable;
7use serde::{Deserialize, Serialize};
8use std::time::Duration;
9
10pub enum EpochPerspective {
11 Before,
12 During,
13 After,
14}
15
16#[derive(
18 Clone, Copy, Debug, Deserialize, Eq, Hash, Hashable, Ord, PartialEq, PartialOrd, Serialize,
19)]
20pub struct Epoch {
21 pub duration: Duration,
22 pub start: i64,
23}
24
25impl Epoch {
26 pub fn new(duration: Option<Duration>, start: impl Into<i64>) -> Self {
27 Self {
28 duration: duration.unwrap_or_else(|| Duration::from_secs(1)),
29 start: start.into(),
30 }
31 }
32 pub fn duration(&self) -> Duration {
33 self.duration
34 }
35 pub fn elapsed(&self) -> Duration {
37 let now: i64 = chrono::Utc::now().timestamp();
38 Duration::from_secs((now - self.start) as u64)
39 }
40 pub fn end(&self) -> i64 {
42 self.start + self.duration.as_secs() as i64
43 }
44 pub fn is_expired(&self) -> bool {
46 self.elapsed() > self.duration
47 }
48 pub fn start(&self) -> i64 {
50 self.start
51 }
52 pub fn tstep(&self, n: usize) -> Vec<i64> {
53 let step_size = self.duration.div_f64(n as f64).as_secs() as i64;
54 (0..n)
55 .map(|i| self.start + (i as i64 * step_size))
56 .collect()
57 }
58}
59
60impl Default for Epoch {
61 fn default() -> Self {
62 Self::new(Some(Duration::new(1, 0)), chrono::Utc::now().timestamp())
63 }
64}
65
66impl std::fmt::Display for Epoch {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 write!(f, "{}", serde_json::to_string(&self).unwrap())
69 }
70}
71
72impl From<Duration> for Epoch {
73 fn from(duration: Duration) -> Self {
74 Self::new(Some(duration), chrono::Utc::now().timestamp())
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn test_epoch() {
84 let epoch = Epoch::from(Duration::new(2, 0));
85 assert_eq!(epoch.duration(), Duration::new(2, 0));
86 assert_eq!(
87 epoch.end(),
88 epoch.start() + epoch.duration().as_secs() as i64
89 );
90 assert_eq!(epoch.is_expired(), false);
91 assert_eq!(epoch.tstep(1), vec![epoch.start()]);
92 assert_eq!(
93 epoch.tstep(2),
94 vec![
95 epoch.start(),
96 epoch.start() + epoch.duration().div_f64(2.0).as_secs() as i64
97 ]
98 );
99 }
100}