@@ -6726,6 +6726,77 @@ def rotator(source, dest):
67266726 rh .close ()
67276727
67286728class TimedRotatingFileHandlerTest (BaseFileTest ):
6729+ def assert_rollover_contents (self , when , timestamps , expected , ** kwargs ):
6730+ with tempfile .TemporaryDirectory () as dirname :
6731+ filename = os .path .join (dirname , 'app.log' )
6732+ now = [timestamps [0 ]]
6733+ with patch ('time.time' , lambda : now [0 ]):
6734+ handler = logging .handlers .TimedRotatingFileHandler (
6735+ filename , when = when , delay = True , encoding = 'utf-8' , ** kwargs )
6736+ try :
6737+ for i , timestamp in enumerate (timestamps ):
6738+ now [0 ] = timestamp
6739+ handler .emit (logging .makeLogRecord ({'msg' : str (i )}))
6740+ finally :
6741+ handler .close ()
6742+ actual = {}
6743+ for name in os .listdir (dirname ):
6744+ with open (os .path .join (dirname , name ), encoding = 'utf-8' ) as f :
6745+ actual [name .removeprefix ('app.log' )] = f .read ().splitlines ()
6746+ self .assertEqual (actual , expected )
6747+
6748+ @support .run_with_tz ('EST+05EDT,M3.2.0,M11.1.0' )
6749+ def test_rollover_fixed_interval_dst (self ):
6750+ DT = datetime .datetime
6751+ cases = (
6752+ ('H' , DT (2026 , 3 , 8 , 1 , 30 ), 3 ),
6753+ ('H' , DT (2026 , 11 , 1 , 1 , 30 ), 2 ),
6754+ ('D' , DT (2026 , 3 , 7 , 23 , 30 ), 3 ),
6755+ ('D' , DT (2026 , 11 , 1 , 0 , 30 ), 2 ),
6756+ )
6757+ for when , start , count in cases :
6758+ interval = 3600 if when == 'H' else 86400
6759+ timestamps = [start .timestamp () + i * interval for i in range (count )]
6760+ suffix = '.%Y-%m-%d_%H' if when == 'H' else '.%Y-%m-%d'
6761+ for utc in (False , True ):
6762+ with self .subTest (when = when , start = start , utc = utc ):
6763+ convert = time .gmtime if utc else time .localtime
6764+ expected = {
6765+ time .strftime (suffix , convert (timestamp )): [str (i )]
6766+ for i , timestamp in enumerate (timestamps [:- 1 ])
6767+ }
6768+ expected ['' ] = [str (count - 1 )]
6769+ self .assert_rollover_contents (
6770+ when , timestamps , expected , utc = utc )
6771+
6772+ @support .run_with_tz ('EST+05EDT,M3.2.0,M11.1.0' )
6773+ def test_rollover_delayed_dst (self ):
6774+ DT = datetime .datetime
6775+ cases = (
6776+ ('MIDNIGHT' , None ,
6777+ [DT (2026 , 10 , day , hour ).timestamp ()
6778+ for day in (30 , 31 ) for hour in (9 , 18 )] +
6779+ [DT (2026 , 11 , day , hour ).timestamp ()
6780+ for day in (1 , 2 , 3 ) for hour in (9 , 18 )],
6781+ {'.2026-10-30' : ['0' , '1' ], '.2026-10-31' : ['2' , '3' ],
6782+ '.2026-11-01' : ['4' , '5' ], '.2026-11-02' : ['6' , '7' ],
6783+ '' : ['8' , '9' ]}),
6784+ ('W6' , None ,
6785+ [DT (2026 , 10 , 23 , 9 ).timestamp (),
6786+ DT (2026 , 11 , 1 , 9 ).timestamp (),
6787+ DT (2026 , 11 , 2 , 9 ).timestamp ()],
6788+ {'.2026-10-19' : ['0' ], '.2026-10-26' : ['1' ], '' : ['2' ]}),
6789+ ('MIDNIGHT' , datetime .time (23 , 30 ),
6790+ [DT (2026 , month , 1 , 12 ).timestamp () for month in (3 , 4 , 10 , 12 )],
6791+ {'.2026-02-28' : ['0' ], '.2026-03-31' : ['1' ],
6792+ '.2026-09-30' : ['2' ], '' : ['3' ]}),
6793+ )
6794+ for when , at_time , timestamps , expected in cases :
6795+ for utc in (False , True ):
6796+ with self .subTest (when = when , atTime = at_time , utc = utc ):
6797+ self .assert_rollover_contents (
6798+ when , timestamps , expected , atTime = at_time , utc = utc )
6799+
67296800 @unittest .skipIf (support .is_wasi , "WASI does not have /dev/null." )
67306801 def test_should_not_rollover (self ):
67316802 # See bpo-45401. Should only ever rollover regular files
0 commit comments