Programming

Fix ZIP Timestamps Error in Homebrew Python Hatchling

Resolve 'ZIP does not support timestamps before 1980' error in Homebrew Python formulas like kosmorro with termcolor 3.3.0 and hatchling. Set SOURCE_DATE_EPOCH to clamp timestamps and fix wheel builds during pip install.

1 answer 1 view

How to fix ‘ZIP does not support timestamps before 1980’ error in Homebrew formula installation for Python package (termcolor with hatchling)?

I maintain a custom Homebrew tap with a formula for Kosmorro (ephemerides calculation program). After updating the termcolor resource from version 2.5.0 to 3.3.0, adding pytz resource and python-argcomplete dependency, and updating url/sha256 values, brew install --verbose kosmorro fails during pip installation of termcolor.

Formula (kosmorro.rb)

ruby
class Kosmorro < Formula
 include Language::Python::Virtualenv

 desc "Ephemerides calculation program"
 homepage "https://kosmorro.space"
 url "https://files.pythonhosted.org/packages/8e/62/9c1f4377a50615be7046e3c6112ea7238bc5296d622946a3b360b6807765/kosmorro-1.0.1.tar.gz"
 sha256 "7eabe34410ace99d850786665276bf417ed33df998376adbc0af2d1c4431b873"
 revision 1

 depends_on "python@3.14"
 depends_on "certifi"
 depends_on "numpy"
 depends_on "python-argcomplete"

 resource "babel" do
 url "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz"
 sha256 "0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"
 end

 resource "jplephem" do
 url "https://files.pythonhosted.org/packages/3c/42/0545c37e070e5f940eb52987fb25a351ab3c9327b40bab6ad3dd6125b2e1/jplephem-2.23.tar.gz"
 sha256 "d3fb9477e4bf4c39d10497d4ff15e5271b7ac03fa101e1821aac527d646eccf9"
 end

 resource "kosmorrolib" do
 url "https://files.pythonhosted.org/packages/b0/04/699202c54fbcd60d8935a61faccb1ca4bcb2b5e53c13102ed476402e5f6f/kosmorrolib-1.0.13.tar.gz"
 sha256 "d327a3f20f485e46f520c2c55d573fcbe804bdb40131946f815feabc8a964d39"
 end

 resource "python-dateutil" do
 url "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz"
 sha256 "37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"
 end

 resource "pytz" do
 url "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz"
 sha256 "360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"
 end

 resource "sgp4" do
 url "https://files.pythonhosted.org/packages/6e/d0/fc467010d17742321f73b16a71acac88439a88f2b166641942a6566c9b2a/sgp4-2.25.tar.gz"
 sha256 "e19edc6dcc25d69fb8fde0a267b8f0c44d7e915c7bcbeacf5d3a8b595baf0674"
 end

 resource "six" do
 url "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz"
 sha256 "ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"
 end

 resource "skyfield" do
 url "https://files.pythonhosted.org/packages/2d/cb/0c9f9685f6ff3b9c268b1d995c584f0d09de160f46fbd6c1df0631565bfd/skyfield-1.53.tar.gz"
 sha256 "24099855f3ba3906663ac1c10e650041e747680b986e807400eddedc0be4a8b4"
 end

 resource "skyfield-data" do
 url "https://files.pythonhosted.org/packages/d3/6a/2f8d001dfcacb737cbdbc96db566eb9de9ebf0493bf9780785c42215c397/skyfield_data-7.0.0.tar.gz"
 sha256 "df3b4f0f5b5b57e5adf5a0a5d30d73ca23dd4066ee666da3222d7bd63d580bbd"
 end

 resource "tabulate" do
 url "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz"
 sha256 "0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"
 end

 resource "termcolor" do
 url "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz"
 sha256 "348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5"
 end

 def install
 virtualenv_install_with_resources
 end

 test do
 assert_includes shell_output("#{bin}/kosmorro --version"), "Kosmorro #{version}\n"
 end
end

Error Output (key parts)

==> python3.14 -m pip ... install ... termcolor-3.3.0
...
ValueError: ZIP does not support timestamps before 1980
error: subprocess-exited-with-error

× Preparing metadata (pyproject.toml) did not run successfully.
│ exit code: 1
...
ERROR: Failed to build 'file:///private/tmp/kosmorro--termcolor-20260108-17232-gwhic8/termcolor-3.3.0' when installing build dependencies

The traceback points to hatchling during wheel building for hatch-vcs build dependencies, specifically in zipfile.ZipInfo.

Python 3.14 is used (was already before). Installation worked fine prior to these changes.

Is this due to termcolor 3.3.0 using newer build tools (hatchling/hatch-vcs) incompatible with Homebrew’s environment? How to resolve this in the formula (e.g., patch timestamps, use bottles, alternative build method)?

Set SOURCE_DATE_EPOCH to a value >= 315532800 (1980-01-01 UTC) in your formula before the virtualenv install — for example add ENV['SOURCE_DATE_EPOCH'] = [Time.now.to_i, 315532800].max.to_s to kosmorro.rb. That prevents the python zipfile (zipfile.ZipInfo) error during hatchling python wheel builds; upgrading to a hatchling release that clamps timestamps is a longer-term fix.


Contents


Homebrew formula fix: set SOURCE_DATE_EPOCH (homebrew python)

Short version: add a build-time environment variable in the formula so wheel builders stamp files with a legal DOS timestamp.

Why this is the least-invasive fix

  • It’s tiny and local to the kosmorro build.
  • It avoids changing packaging backends or vendoring patched packages.
  • It fixes the immediate ValueError from Python’s zip handling.

Recommended code (insert before virtualenv install):

ruby
def install
 # Ensure any build-time timestamp is at least 1980-01-01 UTC
 ENV["SOURCE_DATE_EPOCH"] = [Time.now.to_i, 315532800].max.to_s

 # Optional: make conversions use UTC to avoid negative local tz offsets
 # ENV["TZ"] = "UTC"

 virtualenv_install_with_resources
end

Notes

  • 315532800 is the Unix timestamp for 1980-01-01 00:00:00 UTC. Zip stores DOS timestamps and zipfile rejects dates earlier than that.
  • Using the clamp [Time.now.to_i, 315532800].max.to_s keeps builds non-statically-dated on normal machines while guaranteeing the DOS minimum.
  • You can instead hard-code "315532800" if you want fully reproducible build timestamps for this formula.
  • If you prefer, set ENV["TZ"]="UTC" as an extra safeguard against build-time conversions to a negative local offset (see the CPython issue linked below). That’s optional.

After the change:

  • Rebuild from source: brew reinstall --build-from-source --verbose kosmorro (or brew install --build-from-source kosmorro).
  • The pip/hatchling wheel build step should no longer raise ValueError: ZIP does not support timestamps before 1980.

Why the error happens (python zipfile)

Short explanation: Zip archives store file modification times using a DOS date/time format that has a minimum of 1980‑01‑01. If a build backend stamps files with an epoch timestamp before that, Python’s zipfile.ZipInfo raises a ValueError.

Key mechanics

  • Many PEP 517 build backends (hatchling, setuptools, etc.) set file mtimes in generated wheels using SOURCE_DATE_EPOCH when that environment variable exists. The reproducible-build ecosystem relies on that variable to make builds deterministic.
  • If SOURCE_DATE_EPOCH is 0 or otherwise earlier than 1980, wheel builders may use it directly (or convert it to local time incorrectly), producing a pre‑1980 timestamp that zipfile.ZipInfo rejects.
  • There have been concrete reports and fixes: see the CPython issue about negative timezone offsets and zip timestamp underflow and the hatchling PR that clamps timestamps to 1980 to avoid the problem.

Relevant reads

In practice this means: the termcolor 3.3.0 packaging (or one of its build-time dependencies such as hatch-vcs/hatchling) ends up stamping files with a timestamp that zip rejects. Homebrew’s build environment can expose this when SOURCE_DATE_EPOCH is set to a value earlier than 1980 (or when a backend mishandles timezone conversion).


Alternative resolutions: upgrade hatchling, bottles, pinning (hatchling python)

If you prefer not to change the formula environment, other options exist — each has tradeoffs.

  1. Wait for / require a patched hatchling
  • The upstream fix clamps timestamps to 1980 in the builder (see the hatch PR). Once that version is on PyPI and picked up during the isolated build, the problem disappears.
  • Pro: upstream, permanent solution.
  • Con: you have to wait for new releases and for pip to install them during PEP 517 builds.
  1. Vendor or patch the build backend during the build
  • You could vendor a patched hatchling or apply a small patch to the wheel-builder in the build environment.
  • Pro: immediate and surgical.
  • Con: more brittle, complicated in Homebrew because PEP 517 build isolation installs build dependencies into a temporary environment that Homebrew formulae don’t control easily.
  1. Use a bottle (prebuild)
  • Build and publish a bottled kosmorro so end users install the binary instead of building wheels on their machines.
  • Pro: avoids building termcolor (and others) on the user’s machine.
  • Con: extra CI work and you must update bottles for new releases/architectures.
  1. Pin termcolor to an older release (temporary)
  • Roll back to termcolor 2.5.0 in the resource if that version uses a different build backend.
  • Pro: quick workaround.
  • Con: avoids the newer release and is temporary.
  1. Patch formula to clamp SOURCE_DATE_EPOCH (recommended)

How to reproduce, test, and debug (zip timestamps)

Commands to reproduce locally (simulate the problematic environment)

  • Reproduce the failing build step outside Homebrew:
  • env SOURCE_DATE_EPOCH=0 python3.14 -m pip install --no-binary :all: --no-build-isolation --verbose termcolor==3.3.0
  • You should see the hatchling wheel build step and a traceback referencing zipfile.ZipInfo and the ValueError if the environment triggers a pre‑1980 timestamp.

Check your Homebrew build environment

  • Run Homebrew with verbose/debug to see environment variables:
  • brew install --build-from-source --verbose --debug kosmorro
  • Inspect the build temporary path shown in the failure logs (e.g., /private/tmp/.../termcolor-3.3.0) to confirm SOURCE_DATE_EPOCH in that environment.

Quick Python check for the zip behavior

  • This demonstrates the acceptance/rejection of DOS timestamps:
python
# save as test_zip.py and run with python3.14
import zipfile

# this should succeed (1980)
z = zipfile.ZipFile("ok.zip", "w")
zi = zipfile.ZipInfo("ok.txt", (1980, 1, 1, 0, 0, 0))
z.writestr(zi, "x")
z.close()
print("ok.zip created")

# this should raise ValueError (1979)
z = zipfile.ZipFile("bad.zip", "w")
zi = zipfile.ZipInfo("bad.txt", (1979, 12, 31, 23, 59, 59))
try:
 z.writestr(zi, "x")
except Exception as e:
 print("expected failure:", type(e).__name__, e)

If your modified formula fixes the build, the pip/hatchling step will proceed and the wheel will be created without the ZIP timestamp error.


Patch example for kosmorro.rb

Minimal patch to the formula (context only):

diff
 def install
- virtualenv_install_with_resources
+ # Ensure ZIP file timestamps are valid for DOS format (>= 1980-01-01 UTC)
+ ENV["SOURCE_DATE_EPOCH"] = [Time.now.to_i, 315532800].max.to_s
+
+ # Optional: avoid timezone conversion surprises (uncomment if needed)
+ # ENV["TZ"] = "UTC"
+
+ virtualenv_install_with_resources
 end

Notes on committing:

  • Update the formula in your tap and run brew install --build-from-source kosmorro locally.
  • If this is intended as a permanent reproducibility measure, consider leaving the clamp as-is; if you want the build timestamp to reflect build time (but still be safe), use the clamp expression shown above.

Sources


Conclusion

The quickest, least invasive way to fix the “ZIP does not support timestamps before 1980” failure when installing termcolor with homebrew python is to ensure the build uses a legal timestamp: add ENV["SOURCE_DATE_EPOCH"] = [Time.now.to_i, 315532800].max.to_s (or "315532800") to kosmorro.rb before virtualenv_install_with_resources. That removes the python zipfile error during hatchling python wheel creation; upgrading to the hatchling release that clamps timestamps is the long-term upstream fix.

Authors
Verified by moderation
Moderation
Fix ZIP Timestamps Error in Homebrew Python Hatchling