Skip to content

Commit c1feb41

Browse files
pythongh-90535: Fix support of interval>1 in logging.TimedRotatingFileHandler
Fix support of interval values > 1 in logging.TimedRotatingFileHandler for when='MIDNIGHT' and when='Wx'.
1 parent 936d461 commit c1feb41

File tree

3 files changed

+173
-34
lines changed

3 files changed

+173
-34
lines changed

Lib/logging/handlers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,10 @@ def computeRollover(self, currentTime):
329329
else:
330330
daysToWait = 6 - day + self.dayOfWeek + 1
331331
result += daysToWait * (60 * 60 * 24)
332+
if self.when == 'MIDNIGHT':
333+
result += self.interval - _MIDNIGHT
334+
else:
335+
result += self.interval - _MIDNIGHT * 7
332336
if not self.utc:
333337
dstNow = t[-1]
334338
dstAtRollover = time.localtime(result)[-1]

Lib/test/test_logging.py

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

65766576
fh.close()
65776577

6578+
# Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
6579+
# March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
6580+
@support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
6581+
def test_compute_rollover_MIDNIGHT_local_interval(self):
6582+
# DST begins at 2012-3-11T02:00:00 and ends at 2012-11-4T02:00:00.
6583+
DT = datetime.datetime
6584+
def test(current, expected):
6585+
actual = fh.computeRollover(current.timestamp())
6586+
diff = actual - expected.timestamp()
6587+
if diff:
6588+
self.assertEqual(diff, 0, datetime.timedelta(seconds=diff))
6589+
6590+
fh = logging.handlers.TimedRotatingFileHandler(
6591+
self.fn, encoding="utf-8", when='MIDNIGHT', utc=False, interval=3)
6592+
6593+
test(DT(2012, 3, 8, 23, 59, 59), DT(2012, 3, 11, 0, 0))
6594+
test(DT(2012, 3, 9, 0, 0), DT(2012, 3, 12, 0, 0))
6595+
test(DT(2012, 3, 9, 1, 0), DT(2012, 3, 12, 0, 0))
6596+
test(DT(2012, 3, 10, 23, 59, 59), DT(2012, 3, 13, 0, 0))
6597+
test(DT(2012, 3, 11, 0, 0), DT(2012, 3, 14, 0, 0))
6598+
test(DT(2012, 3, 11, 1, 0), DT(2012, 3, 14, 0, 0))
6599+
6600+
test(DT(2012, 11, 1, 23, 59, 59), DT(2012, 11, 4, 0, 0))
6601+
test(DT(2012, 11, 2, 0, 0), DT(2012, 11, 5, 0, 0))
6602+
test(DT(2012, 11, 2, 1, 0), DT(2012, 11, 5, 0, 0))
6603+
test(DT(2012, 11, 3, 23, 59, 59), DT(2012, 11, 6, 0, 0))
6604+
test(DT(2012, 11, 4, 0, 0), DT(2012, 11, 7, 0, 0))
6605+
test(DT(2012, 11, 4, 1, 0), DT(2012, 11, 7, 0, 0))
6606+
6607+
fh.close()
6608+
6609+
fh = logging.handlers.TimedRotatingFileHandler(
6610+
self.fn, encoding="utf-8", when='MIDNIGHT', utc=False, interval=3,
6611+
atTime=datetime.time(12, 0, 0))
6612+
6613+
test(DT(2012, 3, 8, 11, 59, 59), DT(2012, 3, 10, 12, 0))
6614+
test(DT(2012, 3, 8, 12, 0), DT(2012, 3, 11, 12, 0))
6615+
test(DT(2012, 3, 8, 13, 0), DT(2012, 3, 11, 12, 0))
6616+
test(DT(2012, 3, 10, 11, 59, 59), DT(2012, 3, 12, 12, 0))
6617+
test(DT(2012, 3, 10, 12, 0), DT(2012, 3, 13, 12, 0))
6618+
test(DT(2012, 3, 10, 13, 0), DT(2012, 3, 13, 12, 0))
6619+
6620+
test(DT(2012, 11, 1, 11, 59, 59), DT(2012, 11, 3, 12, 0))
6621+
test(DT(2012, 11, 1, 12, 0), DT(2012, 11, 4, 12, 0))
6622+
test(DT(2012, 11, 1, 13, 0), DT(2012, 11, 4, 12, 0))
6623+
test(DT(2012, 11, 3, 11, 59, 59), DT(2012, 11, 5, 12, 0))
6624+
test(DT(2012, 11, 3, 12, 0), DT(2012, 11, 6, 12, 0))
6625+
test(DT(2012, 11, 3, 13, 0), DT(2012, 11, 6, 12, 0))
6626+
6627+
fh.close()
6628+
6629+
# Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
6630+
# March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
6631+
@support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
6632+
def test_compute_rollover_W6_local_interval(self):
6633+
# DST begins at 2012-3-11T02:00:00 and ends at 2012-11-4T02:00:00.
6634+
DT = datetime.datetime
6635+
def test(current, expected):
6636+
actual = fh.computeRollover(current.timestamp())
6637+
diff = actual - expected.timestamp()
6638+
if diff:
6639+
self.assertEqual(diff, 0, datetime.timedelta(seconds=diff))
6640+
6641+
fh = logging.handlers.TimedRotatingFileHandler(
6642+
self.fn, encoding="utf-8", when='W6', utc=False, interval=3)
6643+
6644+
test(DT(2012, 2, 19, 23, 59, 59), DT(2012, 3, 5, 0, 0))
6645+
test(DT(2012, 2, 20, 0, 0), DT(2012, 3, 12, 0, 0))
6646+
test(DT(2012, 2, 20, 1, 0), DT(2012, 3, 12, 0, 0))
6647+
test(DT(2012, 3, 4, 23, 59, 59), DT(2012, 3, 19, 0, 0))
6648+
test(DT(2012, 3, 5, 0, 0), DT(2012, 3, 26, 0, 0))
6649+
test(DT(2012, 3, 5, 1, 0), DT(2012, 3, 26, 0, 0))
6650+
6651+
test(DT(2012, 10, 14, 23, 59, 59), DT(2012, 10, 29, 0, 0))
6652+
test(DT(2012, 10, 15, 0, 0), DT(2012, 11, 5, 0, 0))
6653+
test(DT(2012, 10, 15, 1, 0), DT(2012, 11, 5, 0, 0))
6654+
test(DT(2012, 10, 28, 23, 59, 59), DT(2012, 11, 12, 0, 0))
6655+
test(DT(2012, 10, 29, 0, 0), DT(2012, 11, 19, 0, 0))
6656+
test(DT(2012, 10, 29, 1, 0), DT(2012, 11, 19, 0, 0))
6657+
6658+
fh.close()
6659+
6660+
fh = logging.handlers.TimedRotatingFileHandler(
6661+
self.fn, encoding="utf-8", when='W6', utc=False, interval=3,
6662+
atTime=datetime.time(0, 0, 0))
6663+
6664+
test(DT(2012, 2, 25, 23, 59, 59), DT(2012, 3, 11, 0, 0))
6665+
test(DT(2012, 2, 26, 0, 0), DT(2012, 3, 18, 0, 0))
6666+
test(DT(2012, 2, 26, 1, 0), DT(2012, 3, 18, 0, 0))
6667+
test(DT(2012, 3, 10, 23, 59, 59), DT(2012, 3, 25, 0, 0))
6668+
test(DT(2012, 3, 11, 0, 0), DT(2012, 4, 1, 0, 0))
6669+
test(DT(2012, 3, 11, 1, 0), DT(2012, 4, 1, 0, 0))
6670+
6671+
test(DT(2012, 10, 20, 23, 59, 59), DT(2012, 11, 4, 0, 0))
6672+
test(DT(2012, 10, 21, 0, 0), DT(2012, 11, 11, 0, 0))
6673+
test(DT(2012, 10, 21, 1, 0), DT(2012, 11, 11, 0, 0))
6674+
test(DT(2012, 11, 3, 23, 59, 59), DT(2012, 11, 18, 0, 0))
6675+
test(DT(2012, 11, 4, 0, 0), DT(2012, 11, 25, 0, 0))
6676+
test(DT(2012, 11, 4, 1, 0), DT(2012, 11, 25, 0, 0))
6677+
6678+
fh.close()
6679+
6680+
fh = logging.handlers.TimedRotatingFileHandler(
6681+
self.fn, encoding="utf-8", when='W6', utc=False, interval=3,
6682+
atTime=datetime.time(12, 0, 0))
6683+
6684+
test(DT(2012, 2, 18, 11, 59, 59), DT(2012, 3, 4, 12, 0))
6685+
test(DT(2012, 2, 19, 12, 0), DT(2012, 3, 11, 12, 0))
6686+
test(DT(2012, 2, 19, 13, 0), DT(2012, 3, 11, 12, 0))
6687+
test(DT(2012, 3, 4, 11, 59, 59), DT(2012, 3, 18, 12, 0))
6688+
test(DT(2012, 3, 4, 12, 0), DT(2012, 3, 25, 12, 0))
6689+
test(DT(2012, 3, 4, 13, 0), DT(2012, 3, 25, 12, 0))
6690+
6691+
test(DT(2012, 10, 14, 11, 59, 59), DT(2012, 10, 28, 12, 0))
6692+
test(DT(2012, 10, 14, 12, 0), DT(2012, 11, 4, 12, 0))
6693+
test(DT(2012, 10, 14, 13, 0), DT(2012, 11, 4, 12, 0))
6694+
test(DT(2012, 10, 28, 11, 59, 59), DT(2012, 11, 11, 12, 0))
6695+
test(DT(2012, 10, 28, 12, 0), DT(2012, 11, 18, 12, 0))
6696+
test(DT(2012, 10, 28, 13, 0), DT(2012, 11, 18, 12, 0))
6697+
6698+
fh.close()
6699+
6700+
65786701
def secs(**kw):
65796702
return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
65806703

@@ -6586,40 +6709,49 @@ def secs(**kw):
65866709
# current time (epoch start) is a Thursday, W0 means Monday
65876710
('W0', secs(days=4, hours=24)),
65886711
):
6589-
def test_compute_rollover(self, when=when, exp=exp):
6590-
rh = logging.handlers.TimedRotatingFileHandler(
6591-
self.fn, encoding="utf-8", when=when, interval=1, backupCount=0, utc=True)
6592-
currentTime = 0.0
6593-
actual = rh.computeRollover(currentTime)
6594-
if exp != actual:
6595-
# Failures occur on some systems for MIDNIGHT and W0.
6596-
# Print detailed calculation for MIDNIGHT so we can try to see
6597-
# what's going on
6598-
if when == 'MIDNIGHT':
6599-
try:
6600-
if rh.utc:
6601-
t = time.gmtime(currentTime)
6602-
else:
6603-
t = time.localtime(currentTime)
6604-
currentHour = t[3]
6605-
currentMinute = t[4]
6606-
currentSecond = t[5]
6607-
# r is the number of seconds left between now and midnight
6608-
r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
6609-
currentMinute) * 60 +
6610-
currentSecond)
6611-
result = currentTime + r
6612-
print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
6613-
print('currentHour: %s' % currentHour, file=sys.stderr)
6614-
print('currentMinute: %s' % currentMinute, file=sys.stderr)
6615-
print('currentSecond: %s' % currentSecond, file=sys.stderr)
6616-
print('r: %s' % r, file=sys.stderr)
6617-
print('result: %s' % result, file=sys.stderr)
6618-
except Exception as e:
6619-
print('exception in diagnostic code: %s' % e, file=sys.stderr)
6620-
self.assertEqual(exp, actual)
6621-
rh.close()
6622-
setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
6712+
for interval in 1, 3:
6713+
def test_compute_rollover(self, when=when, interval=interval, exp=exp):
6714+
rh = logging.handlers.TimedRotatingFileHandler(
6715+
self.fn, encoding="utf-8", when=when, interval=interval, backupCount=0, utc=True)
6716+
currentTime = 0.0
6717+
actual = rh.computeRollover(currentTime)
6718+
if when.startswith('W'):
6719+
exp += secs(days=7*(interval-1))
6720+
else:
6721+
exp *= interval
6722+
if exp != actual:
6723+
# Failures occur on some systems for MIDNIGHT and W0.
6724+
# Print detailed calculation for MIDNIGHT so we can try to see
6725+
# what's going on
6726+
if when == 'MIDNIGHT':
6727+
try:
6728+
if rh.utc:
6729+
t = time.gmtime(currentTime)
6730+
else:
6731+
t = time.localtime(currentTime)
6732+
currentHour = t[3]
6733+
currentMinute = t[4]
6734+
currentSecond = t[5]
6735+
# r is the number of seconds left between now and midnight
6736+
r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
6737+
currentMinute) * 60 +
6738+
currentSecond)
6739+
result = currentTime + r
6740+
print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
6741+
print('currentHour: %s' % currentHour, file=sys.stderr)
6742+
print('currentMinute: %s' % currentMinute, file=sys.stderr)
6743+
print('currentSecond: %s' % currentSecond, file=sys.stderr)
6744+
print('r: %s' % r, file=sys.stderr)
6745+
print('result: %s' % result, file=sys.stderr)
6746+
except Exception as e:
6747+
print('exception in diagnostic code: %s' % e, file=sys.stderr)
6748+
self.assertEqual(exp, actual)
6749+
rh.close()
6750+
name = "test_compute_rollover_%s" % when
6751+
if interval > 1:
6752+
name += "_interval"
6753+
test_compute_rollover.__name__ = name
6754+
setattr(TimedRotatingFileHandlerTest, name, test_compute_rollover)
66236755

66246756

66256757
@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)