1use chrono::{DateTime, Local, NaiveDate, NaiveDateTime};
2
3use std::time::SystemTime;
4
5#[derive(Debug, PartialEq, Eq)]
7pub enum TimeFilter {
8 Before(SystemTime),
9 After(SystemTime),
10}
11
12impl TimeFilter {
13 fn from_str(ref_time: &SystemTime, s: &str) -> Option<SystemTime> {
14 humantime::parse_duration(s)
15 .map(|duration| *ref_time - duration)
16 .ok()
17 .or_else(|| {
18 DateTime::parse_from_rfc3339(s)
19 .map(|dt| dt.into())
20 .ok()
21 .or_else(|| {
22 NaiveDate::parse_from_str(s, "%F")
23 .ok()?
24 .and_hms_opt(0, 0, 0)?
25 .and_local_timezone(Local)
26 .latest()
27 })
28 .or_else(|| {
29 NaiveDateTime::parse_from_str(s, "%F %T")
30 .ok()?
31 .and_local_timezone(Local)
32 .latest()
33 })
34 .or_else(|| {
35 let timestamp_secs = s.strip_prefix('@')?.parse().ok()?;
36 DateTime::from_timestamp(timestamp_secs, 0).map(Into::into)
37 })
38 .map(|dt| dt.into())
39 })
40 }
41
42 pub fn before(ref_time: &SystemTime, s: &str) -> Option<TimeFilter> {
43 TimeFilter::from_str(ref_time, s).map(TimeFilter::Before)
44 }
45
46 pub fn after(ref_time: &SystemTime, s: &str) -> Option<TimeFilter> {
47 TimeFilter::from_str(ref_time, s).map(TimeFilter::After)
48 }
49
50 pub fn applies_to(&self, t: &SystemTime) -> bool {
51 match self {
52 TimeFilter::Before(limit) => t < limit,
53 TimeFilter::After(limit) => t > limit,
54 }
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use std::time::Duration;
62
63 #[test]
64 fn is_time_filter_applicable() {
65 let ref_time = NaiveDateTime::parse_from_str("2010-10-10 10:10:10", "%F %T")
66 .unwrap()
67 .and_local_timezone(Local)
68 .latest()
69 .unwrap()
70 .into();
71
72 assert!(TimeFilter::after(&ref_time, "1min")
73 .unwrap()
74 .applies_to(&ref_time));
75 assert!(!TimeFilter::before(&ref_time, "1min")
76 .unwrap()
77 .applies_to(&ref_time));
78
79 let t1m_ago = ref_time - Duration::from_secs(60);
80 assert!(!TimeFilter::after(&ref_time, "30sec")
81 .unwrap()
82 .applies_to(&t1m_ago));
83 assert!(TimeFilter::after(&ref_time, "2min")
84 .unwrap()
85 .applies_to(&t1m_ago));
86
87 assert!(TimeFilter::before(&ref_time, "30sec")
88 .unwrap()
89 .applies_to(&t1m_ago));
90 assert!(!TimeFilter::before(&ref_time, "2min")
91 .unwrap()
92 .applies_to(&t1m_ago));
93
94 let t10s_before = "2010-10-10 10:10:00";
95 assert!(!TimeFilter::before(&ref_time, t10s_before)
96 .unwrap()
97 .applies_to(&ref_time));
98 assert!(TimeFilter::before(&ref_time, t10s_before)
99 .unwrap()
100 .applies_to(&t1m_ago));
101
102 assert!(TimeFilter::after(&ref_time, t10s_before)
103 .unwrap()
104 .applies_to(&ref_time));
105 assert!(!TimeFilter::after(&ref_time, t10s_before)
106 .unwrap()
107 .applies_to(&t1m_ago));
108
109 let same_day = "2010-10-10";
110 assert!(!TimeFilter::before(&ref_time, same_day)
111 .unwrap()
112 .applies_to(&ref_time));
113 assert!(!TimeFilter::before(&ref_time, same_day)
114 .unwrap()
115 .applies_to(&t1m_ago));
116
117 assert!(TimeFilter::after(&ref_time, same_day)
118 .unwrap()
119 .applies_to(&ref_time));
120 assert!(TimeFilter::after(&ref_time, same_day)
121 .unwrap()
122 .applies_to(&t1m_ago));
123
124 let ref_time = DateTime::parse_from_rfc3339("2010-10-10T10:10:10+00:00")
125 .unwrap()
126 .into();
127 let t1m_ago = ref_time - Duration::from_secs(60);
128 let t10s_before = "2010-10-10T10:10:00+00:00";
129 assert!(!TimeFilter::before(&ref_time, t10s_before)
130 .unwrap()
131 .applies_to(&ref_time));
132 assert!(TimeFilter::before(&ref_time, t10s_before)
133 .unwrap()
134 .applies_to(&t1m_ago));
135
136 assert!(TimeFilter::after(&ref_time, t10s_before)
137 .unwrap()
138 .applies_to(&ref_time));
139 assert!(!TimeFilter::after(&ref_time, t10s_before)
140 .unwrap()
141 .applies_to(&t1m_ago));
142
143 let ref_timestamp = 1707723412u64; let ref_time = DateTime::parse_from_rfc3339("2024-02-12T07:36:52+00:00")
145 .unwrap()
146 .into();
147 let t1m_ago = ref_time - Duration::from_secs(60);
148 let t1s_later = ref_time + Duration::from_secs(1);
149 assert!(TimeFilter::before(&ref_time, &ref_timestamp.to_string()).is_none());
151 assert!(
152 TimeFilter::before(&ref_time, &format!("@{}", ref_timestamp))
153 .unwrap()
154 .applies_to(&t1m_ago)
155 );
156 assert!(
157 !TimeFilter::before(&ref_time, &format!("@{}", ref_timestamp))
158 .unwrap()
159 .applies_to(&t1s_later)
160 );
161 assert!(
162 !TimeFilter::after(&ref_time, &format!("@{}", ref_timestamp))
163 .unwrap()
164 .applies_to(&t1m_ago)
165 );
166 assert!(TimeFilter::after(&ref_time, &format!("@{}", ref_timestamp))
167 .unwrap()
168 .applies_to(&t1s_later));
169 }
170}