11use jiff:: { civil:: DateTime , tz:: TimeZone , Span , Timestamp , Zoned } ;
22
3- use std:: time:: SystemTime ;
3+ use std:: time:: { Duration , SystemTime , UNIX_EPOCH } ;
44
55/// Filter based on time ranges.
66#[ derive( Debug , PartialEq , Eq ) ]
@@ -9,40 +9,49 @@ pub enum TimeFilter {
99 After ( SystemTime ) ,
1010}
1111
12+ #[ cfg( not( test) ) ]
13+ fn now ( ) -> Zoned {
14+ Zoned :: now ( )
15+ }
16+
17+ #[ cfg( test) ]
18+ thread_local ! {
19+ static TESTTIME : std:: cell:: RefCell <Option <Zoned >> = None . into( ) ;
20+ }
21+
22+ /// This allows us to set a specific time when running tests
23+ #[ cfg( test) ]
24+ fn now ( ) -> Zoned {
25+ TESTTIME . with_borrow ( |reftime| reftime. as_ref ( ) . cloned ( ) . unwrap_or_else ( Zoned :: now) )
26+ }
27+
1228impl TimeFilter {
13- fn from_str ( ref_time : & SystemTime , s : & str ) -> Option < SystemTime > {
14- s. parse :: < Span > ( )
15- . and_then ( |duration| {
16- Zoned :: try_from ( * ref_time) . and_then ( |zdt| zdt. checked_sub ( duration) )
17- } )
18- . ok ( )
19- . or_else ( || {
20- let local_tz = TimeZone :: system ( ) ;
21- s. parse :: < Timestamp > ( )
22- . map ( |ts| ts. to_zoned ( TimeZone :: UTC ) )
23- . ok ( )
24- . or_else ( || {
25- s. parse :: < DateTime > ( )
26- . map ( |dt| local_tz. to_ambiguous_zoned ( dt) )
27- . and_then ( |zdt| zdt. later ( ) )
28- . ok ( )
29- } )
30- . or_else ( || {
31- let timestamp_secs = s. strip_prefix ( '@' ) ?. parse ( ) . ok ( ) ?;
32- Timestamp :: from_second ( timestamp_secs)
33- . map ( |ts| ts. to_zoned ( TimeZone :: UTC ) )
34- . ok ( )
35- } )
36- } )
37- . map ( SystemTime :: from)
29+ fn from_str ( s : & str ) -> Option < SystemTime > {
30+ if let Ok ( span) = s. parse :: < Span > ( ) {
31+ let datetime = now ( ) . checked_sub ( span) . ok ( ) ?;
32+ Some ( datetime. into ( ) )
33+ } else if let Ok ( timestamp) = s. parse :: < Timestamp > ( ) {
34+ Some ( timestamp. into ( ) )
35+ } else if let Ok ( datetime) = s. parse :: < DateTime > ( ) {
36+ Some (
37+ TimeZone :: system ( )
38+ . to_ambiguous_zoned ( datetime)
39+ . later ( )
40+ . ok ( ) ?
41+ . into ( ) ,
42+ )
43+ } else {
44+ let timestamp_secs: u64 = s. strip_prefix ( '@' ) ?. parse ( ) . ok ( ) ?;
45+ Some ( UNIX_EPOCH + Duration :: from_secs ( timestamp_secs) )
46+ }
3847 }
3948
40- pub fn before ( ref_time : & SystemTime , s : & str ) -> Option < TimeFilter > {
41- TimeFilter :: from_str ( ref_time , s) . map ( TimeFilter :: Before )
49+ pub fn before ( s : & str ) -> Option < TimeFilter > {
50+ TimeFilter :: from_str ( s) . map ( TimeFilter :: Before )
4251 }
4352
44- pub fn after ( ref_time : & SystemTime , s : & str ) -> Option < TimeFilter > {
45- TimeFilter :: from_str ( ref_time , s) . map ( TimeFilter :: After )
53+ pub fn after ( s : & str ) -> Option < TimeFilter > {
54+ TimeFilter :: from_str ( s) . map ( TimeFilter :: After )
4655 }
4756
4857 pub fn applies_to ( & self , t : & SystemTime ) -> bool {
@@ -58,106 +67,115 @@ mod tests {
5867 use super :: * ;
5968 use std:: time:: Duration ;
6069
70+ struct TestTime ( SystemTime ) ;
71+
72+ impl TestTime {
73+ fn new ( time : Zoned ) -> Self {
74+ TESTTIME . with_borrow_mut ( |t| * t = Some ( time. clone ( ) ) ) ;
75+ TestTime ( time. into ( ) )
76+ }
77+
78+ fn set ( & mut self , time : Zoned ) {
79+ TESTTIME . with_borrow_mut ( |t| * t = Some ( time. clone ( ) ) ) ;
80+ self . 0 = time. into ( ) ;
81+ }
82+
83+ fn timestamp ( & self ) -> SystemTime {
84+ self . 0
85+ }
86+ }
87+
88+ impl Drop for TestTime {
89+ fn drop ( & mut self ) {
90+ // Stop using manually set times
91+ TESTTIME . with_borrow_mut ( |t| * t = None ) ;
92+ }
93+ }
94+
6195 #[ test]
6296 fn is_time_filter_applicable ( ) {
6397 let local_tz = TimeZone :: system ( ) ;
64- let ref_time = local_tz
65- . to_ambiguous_zoned ( "2010-10-10 10:10:10" . parse :: < DateTime > ( ) . unwrap ( ) )
66- . later ( )
67- . unwrap ( )
68- . into ( ) ;
98+ let mut test_time = TestTime :: new (
99+ local_tz
100+ . to_ambiguous_zoned ( "2010-10-10 10:10:10" . parse :: < DateTime > ( ) . unwrap ( ) )
101+ . later ( )
102+ . unwrap ( ) ,
103+ ) ;
104+ let mut ref_time = test_time. timestamp ( ) ;
69105
70- assert ! ( TimeFilter :: after( & ref_time, "1min" )
71- . unwrap( )
72- . applies_to( & ref_time) ) ;
73- assert ! ( !TimeFilter :: before( & ref_time, "1min" )
74- . unwrap( )
75- . applies_to( & ref_time) ) ;
106+ assert ! ( TimeFilter :: after( "1min" ) . unwrap( ) . applies_to( & ref_time) ) ;
107+ assert ! ( !TimeFilter :: before( "1min" ) . unwrap( ) . applies_to( & ref_time) ) ;
76108
77109 let t1m_ago = ref_time - Duration :: from_secs ( 60 ) ;
78- assert ! ( !TimeFilter :: after( & ref_time, "30sec" )
79- . unwrap( )
80- . applies_to( & t1m_ago) ) ;
81- assert ! ( TimeFilter :: after( & ref_time, "2min" )
82- . unwrap( )
83- . applies_to( & t1m_ago) ) ;
110+ assert ! ( !TimeFilter :: after( "30sec" ) . unwrap( ) . applies_to( & t1m_ago) ) ;
111+ assert ! ( TimeFilter :: after( "2min" ) . unwrap( ) . applies_to( & t1m_ago) ) ;
84112
85- assert ! ( TimeFilter :: before( & ref_time, "30sec" )
86- . unwrap( )
87- . applies_to( & t1m_ago) ) ;
88- assert ! ( !TimeFilter :: before( & ref_time, "2min" )
89- . unwrap( )
90- . applies_to( & t1m_ago) ) ;
113+ assert ! ( TimeFilter :: before( "30sec" ) . unwrap( ) . applies_to( & t1m_ago) ) ;
114+ assert ! ( !TimeFilter :: before( "2min" ) . unwrap( ) . applies_to( & t1m_ago) ) ;
91115
92116 let t10s_before = "2010-10-10 10:10:00" ;
93- assert ! ( !TimeFilter :: before( & ref_time , t10s_before)
117+ assert ! ( !TimeFilter :: before( t10s_before)
94118 . unwrap( )
95119 . applies_to( & ref_time) ) ;
96- assert ! ( TimeFilter :: before( & ref_time , t10s_before)
120+ assert ! ( TimeFilter :: before( t10s_before)
97121 . unwrap( )
98122 . applies_to( & t1m_ago) ) ;
99123
100- assert ! ( TimeFilter :: after( & ref_time , t10s_before)
124+ assert ! ( TimeFilter :: after( t10s_before)
101125 . unwrap( )
102126 . applies_to( & ref_time) ) ;
103- assert ! ( !TimeFilter :: after( & ref_time, t10s_before)
104- . unwrap( )
105- . applies_to( & t1m_ago) ) ;
127+ assert ! ( !TimeFilter :: after( t10s_before) . unwrap( ) . applies_to( & t1m_ago) ) ;
106128
107129 let same_day = "2010-10-10" ;
108- assert ! ( !TimeFilter :: before( & ref_time, same_day)
109- . unwrap( )
110- . applies_to( & ref_time) ) ;
111- assert ! ( !TimeFilter :: before( & ref_time, same_day)
112- . unwrap( )
113- . applies_to( & t1m_ago) ) ;
114-
115- assert ! ( TimeFilter :: after( & ref_time, same_day)
116- . unwrap( )
117- . applies_to( & ref_time) ) ;
118- assert ! ( TimeFilter :: after( & ref_time, same_day)
119- . unwrap( )
120- . applies_to( & t1m_ago) ) ;
121-
122- let ref_time = "2010-10-10T10:10:10+00:00"
123- . parse :: < Timestamp > ( )
124- . unwrap ( )
125- . into ( ) ;
130+ assert ! ( !TimeFilter :: before( same_day) . unwrap( ) . applies_to( & ref_time) ) ;
131+ assert ! ( !TimeFilter :: before( same_day) . unwrap( ) . applies_to( & t1m_ago) ) ;
132+
133+ assert ! ( TimeFilter :: after( same_day) . unwrap( ) . applies_to( & ref_time) ) ;
134+ assert ! ( TimeFilter :: after( same_day) . unwrap( ) . applies_to( & t1m_ago) ) ;
135+
136+ test_time. set (
137+ "2010-10-10T10:10:10+00:00"
138+ . parse :: < Timestamp > ( )
139+ . unwrap ( )
140+ . to_zoned ( local_tz. clone ( ) ) ,
141+ ) ;
142+ ref_time = test_time. timestamp ( ) ;
126143 let t1m_ago = ref_time - Duration :: from_secs ( 60 ) ;
127144 let t10s_before = "2010-10-10T10:10:00+00:00" ;
128- assert ! ( !TimeFilter :: before( & ref_time , t10s_before)
145+ assert ! ( !TimeFilter :: before( t10s_before)
129146 . unwrap( )
130147 . applies_to( & ref_time) ) ;
131- assert ! ( TimeFilter :: before( & ref_time , t10s_before)
148+ assert ! ( TimeFilter :: before( t10s_before)
132149 . unwrap( )
133150 . applies_to( & t1m_ago) ) ;
134151
135- assert ! ( TimeFilter :: after( & ref_time , t10s_before)
152+ assert ! ( TimeFilter :: after( t10s_before)
136153 . unwrap( )
137154 . applies_to( & ref_time) ) ;
138- assert ! ( !TimeFilter :: after( & ref_time, t10s_before)
139- . unwrap( )
140- . applies_to( & t1m_ago) ) ;
155+ assert ! ( !TimeFilter :: after( t10s_before) . unwrap( ) . applies_to( & t1m_ago) ) ;
141156
142157 let ref_timestamp = 1707723412u64 ; // Mon Feb 12 07:36:52 UTC 2024
143- let ref_time = "2024-02-12T07:36:52+00:00"
144- . parse :: < Timestamp > ( )
145- . unwrap ( )
146- . into ( ) ;
158+ test_time. set (
159+ "2024-02-12T07:36:52+00:00"
160+ . parse :: < Timestamp > ( )
161+ . unwrap ( )
162+ . to_zoned ( local_tz) ,
163+ ) ;
164+ ref_time = test_time. timestamp ( ) ;
147165 let t1m_ago = ref_time - Duration :: from_secs ( 60 ) ;
148166 let t1s_later = ref_time + Duration :: from_secs ( 1 ) ;
149167 // Timestamp only supported via '@' prefix
150- assert ! ( TimeFilter :: before( & ref_time , & ref_timestamp. to_string( ) ) . is_none( ) ) ;
151- assert ! ( TimeFilter :: before( & ref_time , & format!( "@{ref_timestamp}" ) )
168+ assert ! ( TimeFilter :: before( & ref_timestamp. to_string( ) ) . is_none( ) ) ;
169+ assert ! ( TimeFilter :: before( & format!( "@{ref_timestamp}" ) )
152170 . unwrap( )
153171 . applies_to( & t1m_ago) ) ;
154- assert ! ( !TimeFilter :: before( & ref_time , & format!( "@{ref_timestamp}" ) )
172+ assert ! ( !TimeFilter :: before( & format!( "@{ref_timestamp}" ) )
155173 . unwrap( )
156174 . applies_to( & t1s_later) ) ;
157- assert ! ( !TimeFilter :: after( & ref_time , & format!( "@{ref_timestamp}" ) )
175+ assert ! ( !TimeFilter :: after( & format!( "@{ref_timestamp}" ) )
158176 . unwrap( )
159177 . applies_to( & t1m_ago) ) ;
160- assert ! ( TimeFilter :: after( & ref_time , & format!( "@{ref_timestamp}" ) )
178+ assert ! ( TimeFilter :: after( & format!( "@{ref_timestamp}" ) )
161179 . unwrap( )
162180 . applies_to( & t1s_later) ) ;
163181 }
0 commit comments