diff --git a/Lib/test/test_zipfile/test_core.py b/Lib/test/test_zipfile/test_core.py index 4f20209927e7b3d..ed73d8f0946b577 100644 --- a/Lib/test/test_zipfile/test_core.py +++ b/Lib/test/test_zipfile/test_core.py @@ -4020,6 +4020,37 @@ def test_write_without_source_date_epoch(self): zip_info = zf.getinfo("test_no_source_date_epoch.txt") self.assertTimestampAlmostEqual(time.localtime(), zip_info.date_time, tolerance=2) + def test_writestr_strict_timestamps_false_with_pre1980_source_date_epoch(self): + # gh-152445: writestr() with strict_timestamps=False should clamp + # SOURCE_DATE_EPOCH before 1980 to 1980-01-01, not raise. + with os_helper.EnvironmentVarGuard() as env: + env['SOURCE_DATE_EPOCH'] = '0' # 1970-01-01 + with zipfile.ZipFile(TESTFN, 'w', strict_timestamps=False) as zf: + zf.writestr('test.txt', 'Hello World') + with zipfile.ZipFile(TESTFN, 'r') as zf: + info = zf.getinfo('test.txt') + self.assertEqual(info.date_time[:3], (1980, 1, 1)) + + def test_writestr_strict_timestamps_false_with_post2107_source_date_epoch(self): + # gh-152445: writestr() with strict_timestamps=False should clamp + # SOURCE_DATE_EPOCH after 2107 to 2107-12-31, not raise. + with os_helper.EnvironmentVarGuard() as env: + env['SOURCE_DATE_EPOCH'] = '4354819200' # 2108-01-01 + with zipfile.ZipFile(TESTFN, 'w', strict_timestamps=False) as zf: + zf.writestr('test.txt', 'Hello World') + with zipfile.ZipFile(TESTFN, 'r') as zf: + info = zf.getinfo('test.txt') + self.assertEqual(info.date_time[:3], (2107, 12, 31)) + + def test_writestr_strict_timestamps_true_raises_for_pre1980_source_date_epoch(self): + # gh-152445: writestr() with strict_timestamps=True (default) should + # still raise when SOURCE_DATE_EPOCH is before 1980. + with os_helper.EnvironmentVarGuard() as env: + env['SOURCE_DATE_EPOCH'] = '0' # 1970-01-01 + with zipfile.ZipFile(TESTFN, 'w') as zf: + with self.assertRaises((struct.error, ValueError)): + zf.writestr('test.txt', 'Hello World') + def assertTimestampAlmostEqual(self, time1, time2, tolerance): import datetime dt1 = datetime.datetime(*time1[:6]) diff --git a/Lib/zipfile/__init__.py b/Lib/zipfile/__init__.py index 418933a2e8d9e87..6b2cd84887ef444 100644 --- a/Lib/zipfile/__init__.py +++ b/Lib/zipfile/__init__.py @@ -683,6 +683,14 @@ def _for_archive(self, archive): else: self.date_time = time.localtime(time.time())[:6] + # gh-152445: Clamp date_time when strict_timestamps=False, + # mirroring the behaviour of ZipInfo.from_file(). + if not archive._strict_timestamps: + if self.date_time[0] < 1980: + self.date_time = (1980, 1, 1, 0, 0, 0) + elif self.date_time[0] > 2107: + self.date_time = (2107, 12, 31, 23, 59, 59) + self.compress_type = archive.compression self.compress_level = archive.compresslevel if self.filename.endswith('/'): # pragma: no cover diff --git a/Misc/NEWS.d/next/Library/2026-06-27-18-52-36.gh-issue-152445.jn6xhX.rst b/Misc/NEWS.d/next/Library/2026-06-27-18-52-36.gh-issue-152445.jn6xhX.rst new file mode 100644 index 000000000000000..8c6d3b36737ca4d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-27-18-52-36.gh-issue-152445.jn6xhX.rst @@ -0,0 +1 @@ +Fix :func:`zipfile.ZipFile.writestr` ignoring ``strict_timestamps=False`` when ``SOURCE_DATE_EPOCH`` is set to a date before 1980 or after 2107.