Skip to content

Commit 7466ff4

Browse files
committed
gh-154667: Distinguish platform timestamp limits from ZIP bounds
1 parent 885bfbd commit 7466ff4

2 files changed

Lines changed: 39 additions & 2 deletions

File tree

‎Lib/test/test_zipfile/test_core.py‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5561,7 +5561,13 @@ def test_from_file_clamps_timestamp_after_2107_round_trip(self):
55615561
def test_from_file_clamps_localtime_overflow(self):
55625562
cases = (
55635563
(2**63, (2107, 12, 31, 23, 59, 58)),
5564+
(2**48, (2107, 12, 31, 23, 59, 58)),
5565+
(4386441600, (2107, 12, 31, 23, 59, 58)), # 2109-01-01 UTC
55645566
(-2**63, (1980, 1, 1, 0, 0, 0)),
5567+
(-2**48, (1980, 1, 1, 0, 0, 0)),
5568+
(0, (1980, 1, 1, 0, 0, 0)), # 1970-01-01 UTC
5569+
(157766400, (1980, 1, 1, 0, 0, 0)), # 1975-01-01 UTC
5570+
(283996799, (1980, 1, 1, 0, 0, 0)), # 1978-12-31 23:59:59 UTC
55655571
)
55665572
for exception in (OverflowError, OSError):
55675573
for timestamp, expected in cases:
@@ -5572,10 +5578,39 @@ def test_from_file_clamps_localtime_overflow(self):
55725578
zipfile.os, 'stat', return_value=st), \
55735579
mock.patch.object(zipfile.time, 'localtime',
55745580
side_effect=exception):
5581+
with self.assertRaises(exception):
5582+
zipfile.ZipInfo.from_file('future')
55755583
zinfo = zipfile.ZipInfo.from_file(
55765584
'future', strict_timestamps=False)
55775585
self.assertEqual(zinfo.date_time, expected)
55785586

5587+
def test_from_file_preserves_in_range_localtime_errors(self):
5588+
timestamps = (
5589+
283996800, # 1979-01-01 00:00:00 UTC (conservative lower bound)
5590+
315532799, # 1979-12-31 23:59:59 UTC
5591+
315532800, # 1980-01-01 00:00:00 UTC
5592+
2146003200, # 2038-01-02 00:00:00 UTC (AIX localtime overflow)
5593+
2**31, # Beyond the signed 32-bit time_t range
5594+
4354819199, # 2107-12-31 23:59:59 UTC
5595+
4354819200, # 2108-01-01 00:00:00 UTC
5596+
4386441599, # 2108-12-31 23:59:59 UTC
5597+
)
5598+
for exception in (OverflowError, OSError):
5599+
for timestamp in timestamps:
5600+
for strict_timestamps in (True, False):
5601+
with self.subTest(exception=exception, timestamp=timestamp,
5602+
strict_timestamps=strict_timestamps):
5603+
st = mock.Mock(st_mode=stat.S_IFREG | 0o644,
5604+
st_mtime=timestamp, st_size=5)
5605+
with mock.patch.object(
5606+
zipfile.os, 'stat', return_value=st), \
5607+
mock.patch.object(zipfile.time, 'localtime',
5608+
side_effect=exception):
5609+
with self.assertRaises(exception):
5610+
zipfile.ZipInfo.from_file(
5611+
'in_range',
5612+
strict_timestamps=strict_timestamps)
5613+
55795614
def test_from_file_pathlike(self):
55805615
zi = zipfile.ZipInfo.from_file(FakePath(__file__))
55815616
self.assertEqual(posixpath.basename(zi.filename), 'test_core.py')

‎Lib/zipfile/__init__.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -648,9 +648,11 @@ def from_file(cls, filename, arcname=None, *, strict_timestamps=True):
648648
try:
649649
mtime = time.localtime(st.st_mtime)
650650
except (OverflowError, OSError):
651-
if strict_timestamps:
651+
# A platform limit (such as 2038) need not be a ZIP limit.
652+
# UTC years 1979 through 2108 leave room for local time offsets.
653+
if strict_timestamps or 283996800 <= st.st_mtime < 4386441600:
652654
raise
653-
if st.st_mtime < 0:
655+
if st.st_mtime < 283996800:
654656
date_time = (1980, 1, 1, 0, 0, 0)
655657
else:
656658
date_time = (2107, 12, 31, 23, 59, 58)

0 commit comments

Comments
 (0)