Skip to content

Commit 547e882

Browse files
[3.12] gh-90535: Fix support of interval>1 in logging.TimedRotatingFileHandler (GH-116220) (GH-116892)
Fix support of interval values > 1 in logging.TimedRotatingFileHandler for when='MIDNIGHT' and when='Wx'. (cherry picked from commit 269051d) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 6383b14 commit 547e882

File tree

3 files changed

+173
-35
lines changed

3 files changed

+173
-35
lines changed

Lib/logging/handlers.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,10 @@ def computeRollover(self, currentTime):
334334
daysToWait = self.dayOfWeek - day
335335
else:
336336
daysToWait = 6 - day + self.dayOfWeek + 1
337-
result += daysToWait * (60 * 60 * 24)
337+
result += daysToWait * _MIDNIGHT
338+
result += self.interval - _MIDNIGHT * 7
339+
else:
340+
result += self.interval - _MIDNIGHT
338341
if not self.utc:
339342
dstNow = t[-1]
340343
dstAtRollover = time.localtime(result)[-1]

Lib/test/test_logging.py

Lines changed: 166 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6522,6 +6522,129 @@ def test(current, expected):
65226522

65236523
fh.close()
65246524

6525+
# Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
6526+
# March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
6527+
@support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
6528+
def test_compute_rollover_MIDNIGHT_local_interval(self):
6529+
# DST begins at 2012-3-11T02:00:00 and ends at 2012-11-4T02:00:00.
6530+
DT = datetime.datetime
6531+
def test(current, expected):
6532+
actual = fh.computeRollover(current.timestamp())
6533+
diff = actual - expected.timestamp()
6534+
if diff:
6535+
self.assertEqual(diff, 0, datetime.timedelta(seconds=diff))
6536+
6537+
fh = logging.handlers.TimedRotatingFileHandler(
6538+
self.fn, encoding="utf-8", when='MIDNIGHT', utc=False, interval=3)
6539+
6540+
test(DT(2012, 3, 8, 23, 59, 59), DT(2012, 3, 11, 0, 0))
6541+
test(DT(2012, 3, 9, 0, 0), DT(2012, 3, 12, 0, 0))
6542+
test(DT(2012, 3, 9, 1, 0), DT(2012, 3, 12, 0, 0))
6543+
test(DT(2012, 3, 10, 23, 59, 59), DT(2012, 3, 13, 0, 0))
6544+
test(DT(2012, 3, 11, 0, 0), DT(2012, 3, 14, 0, 0))
6545+
test(DT(2012, 3, 11, 1, 0), DT(2012, 3, 14, 0, 0))
6546+
6547+
test(DT(2012, 11, 1, 23, 59, 59), DT(2012, 11, 4, 0, 0))
6548+
test(DT(2012, 11, 2, 0, 0), DT(2012, 11, 5, 0, 0))
6549+
test(DT(2012, 11, 2, 1, 0), DT(2012, 11, 5, 0, 0))
6550+
test(DT(2012, 11, 3, 23, 59, 59), DT(2012, 11, 6, 0, 0))
6551+
test(DT(2012, 11, 4, 0, 0), DT(2012, 11, 7, 0, 0))
6552+
test(DT(2012, 11, 4, 1, 0), DT(2012, 11, 7, 0, 0))
6553+
6554+
fh.close()
6555+
6556+
fh = logging.handlers.TimedRotatingFileHandler(
6557+
self.fn, encoding="utf-8", when='MIDNIGHT', utc=False, interval=3,
6558+
atTime=datetime.time(12, 0, 0))
6559+
6560+
test(DT(2012, 3, 8, 11, 59, 59), DT(2012, 3, 10, 12, 0))
6561+
test(DT(2012, 3, 8, 12, 0), DT(2012, 3, 11, 12, 0))
6562+
test(DT(2012, 3, 8, 13, 0), DT(2012, 3, 11, 12, 0))
6563+
test(DT(2012, 3, 10, 11, 59, 59), DT(2012, 3, 12, 12, 0))
6564+
test(DT(2012, 3, 10, 12, 0), DT(2012, 3, 13, 12, 0))
6565+
test(DT(2012, 3, 10, 13, 0), DT(2012, 3, 13, 12, 0))
6566+
6567+
test(DT(2012, 11, 1, 11, 59, 59), DT(2012, 11, 3, 12, 0))
6568+
test(DT(2012, 11, 1, 12, 0), DT(2012, 11, 4, 12, 0))
6569+
test(DT(2012, 11, 1, 13, 0), DT(2012, 11, 4, 12, 0))
6570+
test(DT(2012, 11, 3, 11, 59, 59), DT(2012, 11, 5, 12, 0))
6571+
test(DT(2012, 11, 3, 12, 0), DT(2012, 11, 6, 12, 0))
6572+
test(DT(2012, 11, 3, 13, 0), DT(2012, 11, 6, 12, 0))
6573+
6574+
fh.close()
6575+
6576+
# Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
6577+
# March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
6578+
@support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
6579+
def test_compute_rollover_W6_local_interval(self):
6580+
# DST begins at 2012-3-11T02:00:00 and ends at 2012-11-4T02:00:00.
6581+
DT = datetime.datetime
6582+
def test(current, expected):
6583+
actual = fh.computeRollover(current.timestamp())
6584+
diff = actual - expected.timestamp()
6585+
if diff:
6586+
self.assertEqual(diff, 0, datetime.timedelta(seconds=diff))
6587+
6588+
fh = logging.handlers.TimedRotatingFileHandler(
6589+
self.fn, encoding="utf-8", when='W6', utc=False, interval=3)
6590+
6591+
test(DT(2012, 2, 19, 23, 59, 59), DT(2012, 3, 5, 0, 0))
6592+
test(DT(2012, 2, 20, 0, 0), DT(2012, 3, 12, 0, 0))
6593+
test(DT(2012, 2, 20, 1, 0), DT(2012, 3, 12, 0, 0))
6594+
test(DT(2012, 3, 4, 23, 59, 59), DT(2012, 3, 19, 0, 0))
6595+
test(DT(2012, 3, 5, 0, 0), DT(2012, 3, 26, 0, 0))
6596+
test(DT(2012, 3, 5, 1, 0), DT(2012, 3, 26, 0, 0))
6597+
6598+
test(DT(2012, 10, 14, 23, 59, 59), DT(2012, 10, 29, 0, 0))
6599+
test(DT(2012, 10, 15, 0, 0), DT(2012, 11, 5, 0, 0))
6600+
test(DT(2012, 10, 15, 1, 0), DT(2012, 11, 5, 0, 0))
6601+
test(DT(2012, 10, 28, 23, 59, 59), DT(2012, 11, 12, 0, 0))
6602+
test(DT(2012, 10, 29, 0, 0), DT(2012, 11, 19, 0, 0))
6603+
test(DT(2012, 10, 29, 1, 0), DT(2012, 11, 19, 0, 0))
6604+
6605+
fh.close()
6606+
6607+
fh = logging.handlers.TimedRotatingFileHandler(
6608+
self.fn, encoding="utf-8", when='W6', utc=False, interval=3,
6609+
atTime=datetime.time(0, 0, 0))
6610+
6611+
test(DT(2012, 2, 25, 23, 59, 59), DT(2012, 3, 11, 0, 0))
6612+
test(DT(2012, 2, 26, 0, 0), DT(2012, 3, 18, 0, 0))
6613+
test(DT(2012, 2, 26, 1, 0), DT(2012, 3, 18, 0, 0))
6614+
test(DT(2012, 3, 10, 23, 59, 59), DT(2012, 3, 25, 0, 0))
6615+
test(DT(2012, 3, 11, 0, 0), DT(2012, 4, 1, 0, 0))
6616+
test(DT(2012, 3, 11, 1, 0), DT(2012, 4, 1, 0, 0))
6617+
6618+
test(DT(2012, 10, 20, 23, 59, 59), DT(2012, 11, 4, 0, 0))
6619+
test(DT(2012, 10, 21, 0, 0), DT(2012, 11, 11, 0, 0))
6620+
test(DT(2012, 10, 21, 1, 0), DT(2012, 11, 11, 0, 0))
6621+
test(DT(2012, 11, 3, 23, 59, 59), DT(2012, 11, 18, 0, 0))
6622+
test(DT(2012, 11, 4, 0, 0), DT(2012, 11, 25, 0, 0))
6623+
test(DT(2012, 11, 4, 1, 0), DT(2012, 11, 25, 0, 0))
6624+
6625+
fh.close()
6626+
6627+
fh = logging.handlers.TimedRotatingFileHandler(
6628+
self.fn, encoding="utf-8", when='W6', utc=False, interval=3,
6629+
atTime=datetime.time(12, 0, 0))
6630+
6631+
test(DT(2012, 2, 18, 11, 59, 59), DT(2012, 3, 4, 12, 0))
6632+
test(DT(2012, 2, 19, 12, 0), DT(2012, 3, 11, 12, 0))
6633+
test(DT(2012, 2, 19, 13, 0), DT(2012, 3, 11, 12, 0))
6634+
test(DT(2012, 3, 4, 11, 59, 59), DT(2012, 3, 18, 12, 0))
6635+
test(DT(2012, 3, 4, 12, 0), DT(2012, 3, 25, 12, 0))
6636+
test(DT(2012, 3, 4, 13, 0), DT(2012, 3, 25, 12, 0))
6637+
6638+
test(DT(2012, 10, 14, 11, 59, 59), DT(2012, 10, 28, 12, 0))
6639+
test(DT(2012, 10, 14, 12, 0), DT(2012, 11, 4, 12, 0))
6640+
test(DT(2012, 10, 14, 13, 0), DT(2012, 11, 4, 12, 0))
6641+
test(DT(2012, 10, 28, 11, 59, 59), DT(2012, 11, 11, 12, 0))
6642+
test(DT(2012, 10, 28, 12, 0), DT(2012, 11, 18, 12, 0))
6643+
test(DT(2012, 10, 28, 13, 0), DT(2012, 11, 18, 12, 0))
6644+
6645+
fh.close()
6646+
6647+
65256648
def secs(**kw):
65266649
return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
65276650

@@ -6533,40 +6656,49 @@ def secs(**kw):
65336656
# current time (epoch start) is a Thursday, W0 means Monday
65346657
('W0', secs(days=4, hours=24)),
65356658
):
6536-
def test_compute_rollover(self, when=when, exp=exp):
6537-
rh = logging.handlers.TimedRotatingFileHandler(
6538-
self.fn, encoding="utf-8", when=when, interval=1, backupCount=0, utc=True)
6539-
currentTime = 0.0
6540-
actual = rh.computeRollover(currentTime)
6541-
if exp != actual:
6542-
# Failures occur on some systems for MIDNIGHT and W0.
6543-
# Print detailed calculation for MIDNIGHT so we can try to see
6544-
# what's going on
6545-
if when == 'MIDNIGHT':
6546-
try:
6547-
if rh.utc:
6548-
t = time.gmtime(currentTime)
6549-
else:
6550-
t = time.localtime(currentTime)
6551-
currentHour = t[3]
6552-
currentMinute = t[4]
6553-
currentSecond = t[5]
6554-
# r is the number of seconds left between now and midnight
6555-
r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
6556-
currentMinute) * 60 +
6557-
currentSecond)
6558-
result = currentTime + r
6559-
print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
6560-
print('currentHour: %s' % currentHour, file=sys.stderr)
6561-
print('currentMinute: %s' % currentMinute, file=sys.stderr)
6562-
print('currentSecond: %s' % currentSecond, file=sys.stderr)
6563-
print('r: %s' % r, file=sys.stderr)
6564-
print('result: %s' % result, file=sys.stderr)
6565-
except Exception as e:
6566-
print('exception in diagnostic code: %s' % e, file=sys.stderr)
6567-
self.assertEqual(exp, actual)
6568-
rh.close()
6569-
setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
6659+
for interval in 1, 3:
6660+
def test_compute_rollover(self, when=when, interval=interval, exp=exp):
6661+
rh = logging.handlers.TimedRotatingFileHandler(
6662+
self.fn, encoding="utf-8", when=when, interval=interval, backupCount=0, utc=True)
6663+
currentTime = 0.0
6664+
actual = rh.computeRollover(currentTime)
6665+
if when.startswith('W'):
6666+
exp += secs(days=7*(interval-1))
6667+
else:
6668+
exp *= interval
6669+
if exp != actual:
6670+
# Failures occur on some systems for MIDNIGHT and W0.
6671+
# Print detailed calculation for MIDNIGHT so we can try to see
6672+
# what's going on
6673+
if when == 'MIDNIGHT':
6674+
try:
6675+
if rh.utc:
6676+
t = time.gmtime(currentTime)
6677+
else:
6678+
t = time.localtime(currentTime)
6679+
currentHour = t[3]
6680+
currentMinute = t[4]
6681+
currentSecond = t[5]
6682+
# r is the number of seconds left between now and midnight
6683+
r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
6684+
currentMinute) * 60 +
6685+
currentSecond)
6686+
result = currentTime + r
6687+
print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
6688+
print('currentHour: %s' % currentHour, file=sys.stderr)
6689+
print('currentMinute: %s' % currentMinute, file=sys.stderr)
6690+
print('currentSecond: %s' % currentSecond, file=sys.stderr)
6691+
print('r: %s' % r, file=sys.stderr)
6692+
print('result: %s' % result, file=sys.stderr)
6693+
except Exception as e:
6694+
print('exception in diagnostic code: %s' % e, file=sys.stderr)
6695+
self.assertEqual(exp, actual)
6696+
rh.close()
6697+
name = "test_compute_rollover_%s" % when
6698+
if interval > 1:
6699+
name += "_interval"
6700+
test_compute_rollover.__name__ = name
6701+
setattr(TimedRotatingFileHandlerTest, name, test_compute_rollover)
65706702

65716703

65726704
@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix support of *interval* values > 1 in
2+
:class:`logging.TimedRotatingFileHandler` for ``when='MIDNIGHT'`` and
3+
``when='Wx'``.

0 commit comments

Comments
 (0)