22//!
33//! Requires the `time_trigger` feature.
44
5- #[ cfg( test) ]
6- use chrono:: NaiveDateTime ;
75use chrono:: { DateTime , Datelike , Duration , Local , TimeZone , Timelike } ;
86#[ cfg( test) ]
97use mock_instant:: { SystemTime , UNIX_EPOCH } ;
@@ -13,6 +11,7 @@ use serde::de;
1311#[ cfg( feature = "config_parsing" ) ]
1412use std:: fmt;
1513use std:: sync:: { Once , RwLock } ;
14+ use thiserror:: Error ;
1615
1716use crate :: append:: rolling_file:: { policy:: compound:: trigger:: Trigger , LogFile } ;
1817#[ cfg( feature = "config_parsing" ) ]
@@ -73,6 +72,12 @@ impl Default for TimeTriggerInterval {
7372 }
7473}
7574
75+ #[ derive( Debug , Error ) ]
76+ enum TimeTrigerError {
77+ #[ error( "too large interval in time trigger {0:?}" ) ]
78+ TooLargeInterval ( TimeTriggerInterval ) ,
79+ }
80+
7681#[ cfg( feature = "config_parsing" ) ]
7782impl < ' de > serde:: Deserialize < ' de > for TimeTriggerInterval {
7883 fn deserialize < D > ( d : D ) -> Result < Self , D :: Error >
@@ -193,13 +198,14 @@ impl TimeTrigger {
193198 current : DateTime < Local > ,
194199 interval : TimeTriggerInterval ,
195200 modulate : bool ,
196- ) -> DateTime < Local > {
201+ ) -> anyhow :: Result < DateTime < Local > > {
197202 let year = current. year ( ) ;
198203 if let TimeTriggerInterval :: Year ( n) = interval {
199204 let n = n as i32 ;
200205 let increment = if modulate { n - year % n } else { n } ;
201206 let year_new = year + increment;
202- return Local . with_ymd_and_hms ( year_new, 1 , 1 , 0 , 0 , 0 ) . unwrap ( ) ;
207+ let result = Local . with_ymd_and_hms ( year_new, 1 , 1 , 0 , 0 , 0 ) . unwrap ( ) ;
208+ return Ok ( result) ;
203209 }
204210
205211 if let TimeTriggerInterval :: Month ( n) = interval {
@@ -210,9 +216,10 @@ impl TimeTrigger {
210216 let num_months_new = num_months + increment;
211217 let year_new = ( num_months_new / 12 ) as i32 ;
212218 let month_new = ( num_months_new) % 12 + 1 ;
213- return Local
219+ let result = Local
214220 . with_ymd_and_hms ( year_new, month_new, 1 , 0 , 0 , 0 )
215221 . unwrap ( ) ;
222+ return Ok ( result) ;
216223 }
217224
218225 let month = current. month ( ) ;
@@ -222,14 +229,21 @@ impl TimeTrigger {
222229 let weekday = current. weekday ( ) . num_days_from_monday ( ) as i64 ; // Monday is the first day of the week
223230 let time = Local . with_ymd_and_hms ( year, month, day, 0 , 0 , 0 ) . unwrap ( ) ;
224231 let increment = if modulate { n - week0 % n } else { n } ;
225- return time + Duration :: weeks ( increment) - Duration :: days ( weekday) ;
232+ let result = time
233+ + Duration :: try_weeks ( increment)
234+ . ok_or ( TimeTrigerError :: TooLargeInterval ( interval) ) ?
235+ - Duration :: try_days ( weekday) . unwrap ( ) ;
236+ return Ok ( result) ;
226237 }
227238
228239 if let TimeTriggerInterval :: Day ( n) = interval {
229240 let ordinal0 = current. ordinal0 ( ) as i64 ;
230241 let time = Local . with_ymd_and_hms ( year, month, day, 0 , 0 , 0 ) . unwrap ( ) ;
231242 let increment = if modulate { n - ordinal0 % n } else { n } ;
232- return time + Duration :: days ( increment) ;
243+ let result = time
244+ + Duration :: try_days ( increment)
245+ . ok_or ( TimeTrigerError :: TooLargeInterval ( interval) ) ?;
246+ return Ok ( result) ;
233247 }
234248
235249 let hour = current. hour ( ) ;
@@ -238,7 +252,10 @@ impl TimeTrigger {
238252 . with_ymd_and_hms ( year, month, day, hour, 0 , 0 )
239253 . unwrap ( ) ;
240254 let increment = if modulate { n - ( hour as i64 ) % n } else { n } ;
241- return time + Duration :: hours ( increment) ;
255+ let result = time
256+ + Duration :: try_hours ( increment)
257+ . ok_or ( TimeTrigerError :: TooLargeInterval ( interval) ) ?;
258+ return Ok ( result) ;
242259 }
243260
244261 let min = current. minute ( ) ;
@@ -247,7 +264,10 @@ impl TimeTrigger {
247264 . with_ymd_and_hms ( year, month, day, hour, min, 0 )
248265 . unwrap ( ) ;
249266 let increment = if modulate { n - ( min as i64 ) % n } else { n } ;
250- return time + Duration :: minutes ( increment) ;
267+ let result = time
268+ + Duration :: try_minutes ( increment)
269+ . ok_or ( TimeTrigerError :: TooLargeInterval ( interval) ) ?;
270+ return Ok ( result) ;
251271 }
252272
253273 let sec = current. second ( ) ;
@@ -256,48 +276,56 @@ impl TimeTrigger {
256276 . with_ymd_and_hms ( year, month, day, hour, min, sec)
257277 . unwrap ( ) ;
258278 let increment = if modulate { n - ( sec as i64 ) % n } else { n } ;
259- return time + Duration :: seconds ( increment) ;
279+ let result = time
280+ + Duration :: try_seconds ( increment)
281+ . ok_or ( TimeTrigerError :: TooLargeInterval ( interval) ) ?;
282+ return Ok ( result) ;
260283 }
261284 panic ! ( "Should not reach here!" ) ;
262285 }
263286
264- fn refresh_time ( & self ) {
287+ fn refresh_time ( & self ) -> anyhow :: Result < ( ) > {
265288 #[ cfg( test) ]
266289 let current = {
267290 let now: std:: time:: Duration = SystemTime :: now ( )
268291 . duration_since ( UNIX_EPOCH )
269292 . expect ( "system time before Unix epoch" ) ;
270- NaiveDateTime :: from_timestamp_opt ( now. as_secs ( ) as i64 , now. subsec_nanos ( ) )
293+ DateTime :: from_timestamp ( now. as_secs ( ) as i64 , now. subsec_nanos ( ) )
271294 . unwrap ( )
295+ . naive_local ( )
272296 . and_local_timezone ( Local )
273297 . unwrap ( )
274298 } ;
275299
276300 #[ cfg( not( test) ) ]
277301 let current = Local :: now ( ) ;
278- let next_time = TimeTrigger :: get_next_time ( current, self . interval , self . modulate ) ;
302+ let next_time = TimeTrigger :: get_next_time ( current, self . interval , self . modulate ) ? ;
279303 let next_roll_time = if self . max_random_delay > 0 {
280304 let random_delay = rand:: thread_rng ( ) . gen_range ( 0 ..self . max_random_delay ) ;
281- next_time + Duration :: seconds ( random_delay as i64 )
305+ next_time
306+ + Duration :: try_seconds ( random_delay as i64 )
307+ . unwrap_or ( Duration :: try_milliseconds ( i64:: MAX ) . unwrap ( ) )
282308 } else {
283309 next_time
284310 } ;
285311 * self . next_roll_time . write ( ) . unwrap ( ) = next_roll_time;
312+ Ok ( ( ) )
286313 }
287314}
288315
289316impl Trigger for TimeTrigger {
290317 fn trigger ( & self , _file : & LogFile ) -> anyhow:: Result < bool > {
291- self . initial . call_once ( || {
292- self . refresh_time ( ) ;
293- } ) ;
318+ let mut result = anyhow :: Result :: Ok ( ( ) ) ;
319+ self . initial . call_once ( || result = self . refresh_time ( ) ) ;
320+ result? ;
294321 #[ cfg( test) ]
295322 let current = {
296323 let now = SystemTime :: now ( )
297324 . duration_since ( UNIX_EPOCH )
298325 . expect ( "system time before Unix epoch" ) ;
299- NaiveDateTime :: from_timestamp_opt ( now. as_secs ( ) as i64 , now. subsec_nanos ( ) )
326+ DateTime :: from_timestamp ( now. as_secs ( ) as i64 , now. subsec_nanos ( ) )
300327 . unwrap ( )
328+ . naive_local ( )
301329 . and_local_timezone ( Local )
302330 . unwrap ( )
303331 } ;
@@ -308,7 +336,7 @@ impl Trigger for TimeTrigger {
308336 let is_trigger = current >= * next_roll_time;
309337 drop ( next_roll_time) ;
310338 if is_trigger {
311- self . refresh_time ( ) ;
339+ self . refresh_time ( ) ? ;
312340 }
313341 Ok ( is_trigger)
314342 }
@@ -494,6 +522,20 @@ mod test {
494522 assert_eq ! ( interval, TimeTriggerInterval :: Second ( 1 ) ) ;
495523 }
496524
525+ #[ test]
526+ fn trigger_large_interval ( ) {
527+ let interval = TimeTriggerInterval :: Second ( i64:: MAX ) ;
528+ let file = tempfile:: tempdir ( ) . unwrap ( ) ;
529+ let logfile = LogFile {
530+ writer : & mut None ,
531+ path : file. path ( ) ,
532+ len : 0 ,
533+ } ;
534+
535+ let trigger = TimeTrigger :: new ( interval, false , 0 ) ;
536+ trigger. trigger ( & logfile) . unwrap_err ( ) ;
537+ }
538+
497539 #[ test]
498540 fn pre_process ( ) {
499541 let trigger = TimeTrigger :: new ( TimeTriggerInterval :: Minute ( 2 ) , true , 0 ) ;
0 commit comments