zip2info 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,45 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ build:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Install uv
15
+ uses: astral-sh/setup-uv@v4
16
+
17
+ - name: Build package
18
+ run: uv build
19
+
20
+ - name: Upload artifacts
21
+ uses: actions/upload-artifact@v4
22
+ with:
23
+ name: dist
24
+ path: dist/
25
+
26
+ publish:
27
+ needs: build
28
+ runs-on: ubuntu-latest
29
+ environment:
30
+ name: pypi
31
+ url: https://pypi.org/p/zip2info
32
+ permissions:
33
+ id-token: write
34
+ steps:
35
+ - name: Download artifacts
36
+ uses: actions/download-artifact@v4
37
+ with:
38
+ name: dist
39
+ path: dist/
40
+
41
+ - name: Install uv
42
+ uses: astral-sh/setup-uv@v4
43
+
44
+ - name: Publish to PyPI
45
+ run: uv publish
@@ -0,0 +1,18 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Source data (not shipped in the package)
13
+ /data/*
14
+ !/data/coordinate_overrides.json
15
+
16
+ # Internal scripts for data processing
17
+ /scripts/*
18
+ !/scripts/compile_data.py
@@ -0,0 +1 @@
1
+ 3.13
zip2info-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,118 @@
1
+ Metadata-Version: 2.4
2
+ Name: zip2info
3
+ Version: 1.0.0
4
+ Summary: Fast US ZIP code lookups for timezone and geocoordinates
5
+ Author-email: kylehogate <kyle.holgate@gmail.com>
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+
10
+ # zip2info
11
+
12
+ Fast, zero-dependency US ZIP code lookups for Python.
13
+
14
+ - **38,000+ zip codes** mapped to IANA timezones
15
+ - **Geocoordinates** for ZIP centroids (latitude/longitude)
16
+ - **Zero dependencies** — pure Python, works everywhere
17
+ - **O(1) lookup** — instant hash table lookups, no database or file I/O
18
+ - **Type-annotated** — full type hints included
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pip install zip2info
24
+ ```
25
+
26
+ Or with [uv](https://docs.astral.sh/uv/):
27
+
28
+ ```bash
29
+ uv add zip2info
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ```python
35
+ import zip2info
36
+
37
+ # Timezone lookup
38
+ tz = zip2info.timezone("90210")
39
+ print(tz) # America/Los_Angeles
40
+
41
+ # Coordinate lookup (latitude, longitude)
42
+ coords = zip2info.coordinates("90210")
43
+ print(coords) # (34.0901, -118.4065)
44
+
45
+ # Rich metadata
46
+ record = zip2info.info("10001")
47
+ print(record.timezone) # America/New_York
48
+ print(record.latitude) # 40.7484
49
+ print(record.longitude) # -73.9967
50
+
51
+ # Works with integers too
52
+ tz = zip2info.timezone(60601)
53
+ print(tz) # America/Chicago
54
+
55
+ # Returns None if zip code not found
56
+ tz = zip2info.timezone("00000")
57
+ print(tz) # None
58
+ ```
59
+
60
+ ## API
61
+
62
+ ### `timezone(zipcode: str | int) -> str | None`
63
+
64
+ Returns the IANA timezone string for a US ZIP code.
65
+
66
+ ### `coordinates(zipcode: str | int) -> tuple[float, float] | None`
67
+
68
+ Returns `(latitude, longitude)` for a US ZIP code, or `None` if unavailable.
69
+
70
+ ### `info(zipcode: str | int) -> ZipInfo | None`
71
+
72
+ Returns a `ZipInfo` dataclass with `zipcode`, `timezone`, `latitude`, and `longitude`.
73
+ Returns `None` when timezone or coordinate data is unavailable.
74
+
75
+ ## Data sources
76
+
77
+ - **Timezones**: bundled US ZIP-to-timezone dataset (same coverage as the original `zip2tz` project)
78
+ - **Coordinates** (merged in priority order):
79
+ 1. [GeoNames](https://www.geonames.org/) US postal codes ([CC BY 4.0](https://creativecommons.org/licenses/by/4.0/))
80
+ 2. U.S. Census ZCTA gazetteer centroids (public domain)
81
+ 3. Manual overrides in `data/coordinate_overrides.json`
82
+ 4. 3-digit ZIP prefix centroid fallback for remaining USPS-only ZIPs (PO boxes, unique entity codes, etc.)
83
+
84
+ Regenerate packaged data with:
85
+
86
+ ```bash
87
+ python scripts/compile_data.py
88
+ ```
89
+
90
+ ## Migration from zip2tz
91
+
92
+ `zip2info` is a new PyPI package and import path. Existing `zip2tz` installs are unchanged.
93
+
94
+ ```python
95
+ # before
96
+ import zip2tz
97
+ zip2tz.timezone("90210")
98
+
99
+ # after
100
+ import zip2info
101
+ zip2info.timezone("90210")
102
+ ```
103
+
104
+ ## Coverage
105
+
106
+ Covers all 50 US states plus DC, including Alaska, Hawaii, Indiana county-level boundaries, and other edge cases.
107
+
108
+ ## Data accuracy
109
+
110
+ Timezone and coordinate mappings are provided on a **best-effort basis**. ZIP codes can span multiple timezones or areas, and coordinate points are centroids/estimates rather than exact address locations.
111
+
112
+ If you find incorrect data, please open an issue with the ZIP code, expected value, and a source if available.
113
+
114
+ ## License
115
+
116
+ MIT License — see [LICENSE](LICENSE) for details.
117
+
118
+ Coordinate data includes GeoNames material licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
@@ -0,0 +1,109 @@
1
+ # zip2info
2
+
3
+ Fast, zero-dependency US ZIP code lookups for Python.
4
+
5
+ - **38,000+ zip codes** mapped to IANA timezones
6
+ - **Geocoordinates** for ZIP centroids (latitude/longitude)
7
+ - **Zero dependencies** — pure Python, works everywhere
8
+ - **O(1) lookup** — instant hash table lookups, no database or file I/O
9
+ - **Type-annotated** — full type hints included
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ pip install zip2info
15
+ ```
16
+
17
+ Or with [uv](https://docs.astral.sh/uv/):
18
+
19
+ ```bash
20
+ uv add zip2info
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ```python
26
+ import zip2info
27
+
28
+ # Timezone lookup
29
+ tz = zip2info.timezone("90210")
30
+ print(tz) # America/Los_Angeles
31
+
32
+ # Coordinate lookup (latitude, longitude)
33
+ coords = zip2info.coordinates("90210")
34
+ print(coords) # (34.0901, -118.4065)
35
+
36
+ # Rich metadata
37
+ record = zip2info.info("10001")
38
+ print(record.timezone) # America/New_York
39
+ print(record.latitude) # 40.7484
40
+ print(record.longitude) # -73.9967
41
+
42
+ # Works with integers too
43
+ tz = zip2info.timezone(60601)
44
+ print(tz) # America/Chicago
45
+
46
+ # Returns None if zip code not found
47
+ tz = zip2info.timezone("00000")
48
+ print(tz) # None
49
+ ```
50
+
51
+ ## API
52
+
53
+ ### `timezone(zipcode: str | int) -> str | None`
54
+
55
+ Returns the IANA timezone string for a US ZIP code.
56
+
57
+ ### `coordinates(zipcode: str | int) -> tuple[float, float] | None`
58
+
59
+ Returns `(latitude, longitude)` for a US ZIP code, or `None` if unavailable.
60
+
61
+ ### `info(zipcode: str | int) -> ZipInfo | None`
62
+
63
+ Returns a `ZipInfo` dataclass with `zipcode`, `timezone`, `latitude`, and `longitude`.
64
+ Returns `None` when timezone or coordinate data is unavailable.
65
+
66
+ ## Data sources
67
+
68
+ - **Timezones**: bundled US ZIP-to-timezone dataset (same coverage as the original `zip2tz` project)
69
+ - **Coordinates** (merged in priority order):
70
+ 1. [GeoNames](https://www.geonames.org/) US postal codes ([CC BY 4.0](https://creativecommons.org/licenses/by/4.0/))
71
+ 2. U.S. Census ZCTA gazetteer centroids (public domain)
72
+ 3. Manual overrides in `data/coordinate_overrides.json`
73
+ 4. 3-digit ZIP prefix centroid fallback for remaining USPS-only ZIPs (PO boxes, unique entity codes, etc.)
74
+
75
+ Regenerate packaged data with:
76
+
77
+ ```bash
78
+ python scripts/compile_data.py
79
+ ```
80
+
81
+ ## Migration from zip2tz
82
+
83
+ `zip2info` is a new PyPI package and import path. Existing `zip2tz` installs are unchanged.
84
+
85
+ ```python
86
+ # before
87
+ import zip2tz
88
+ zip2tz.timezone("90210")
89
+
90
+ # after
91
+ import zip2info
92
+ zip2info.timezone("90210")
93
+ ```
94
+
95
+ ## Coverage
96
+
97
+ Covers all 50 US states plus DC, including Alaska, Hawaii, Indiana county-level boundaries, and other edge cases.
98
+
99
+ ## Data accuracy
100
+
101
+ Timezone and coordinate mappings are provided on a **best-effort basis**. ZIP codes can span multiple timezones or areas, and coordinate points are centroids/estimates rather than exact address locations.
102
+
103
+ If you find incorrect data, please open an issue with the ZIP code, expected value, and a source if available.
104
+
105
+ ## License
106
+
107
+ MIT License — see [LICENSE](LICENSE) for details.
108
+
109
+ Coordinate data includes GeoNames material licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
@@ -0,0 +1,25 @@
1
+ [project]
2
+ name = "zip2info"
3
+ version = "1.0.0"
4
+ description = "Fast US ZIP code lookups for timezone and geocoordinates"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "kylehogate", email = "kyle.holgate@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.10"
10
+ dependencies = []
11
+
12
+ [build-system]
13
+ requires = ["hatchling"]
14
+ build-backend = "hatchling.build"
15
+
16
+ [tool.hatch.build.targets.sdist]
17
+ exclude = [
18
+ "scripts/**",
19
+ "data/**",
20
+ ]
21
+
22
+ [dependency-groups]
23
+ dev = [
24
+ "pytest>=8.4.2",
25
+ ]
@@ -0,0 +1,95 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+
4
+ from zip2info._data import TIMEZONES, ZIP_INFO
5
+
6
+
7
+ @dataclass(frozen=True, slots=True)
8
+ class ZipInfo:
9
+ """Metadata for a US ZIP code."""
10
+
11
+ zipcode: int
12
+ timezone: str
13
+ latitude: float
14
+ longitude: float
15
+
16
+
17
+ def _normalize_zipcode(zipcode: str | int) -> Optional[int]:
18
+ try:
19
+ return int(zipcode)
20
+ except (ValueError, TypeError):
21
+ return None
22
+
23
+
24
+ def timezone(zipcode: str | int) -> Optional[str]:
25
+ """
26
+ Return the IANA timezone for a US ZIP code.
27
+
28
+ Args:
29
+ zipcode: ZIP code as a string or integer.
30
+
31
+ Returns:
32
+ Timezone string (for example, ``America/New_York``) or ``None``.
33
+ """
34
+ zip_int = _normalize_zipcode(zipcode)
35
+ if zip_int is None:
36
+ return None
37
+
38
+ record = ZIP_INFO.get(zip_int)
39
+ if record is None:
40
+ return None
41
+ tz_idx, _, _ = record
42
+ return TIMEZONES[tz_idx]
43
+
44
+
45
+ def coordinates(zipcode: str | int) -> Optional[tuple[float, float]]:
46
+ """
47
+ Return centroid latitude and longitude for a US ZIP code.
48
+
49
+ Coordinates come from the generated ZIP metadata dataset.
50
+
51
+ Args:
52
+ zipcode: ZIP code as a string or integer.
53
+
54
+ Returns:
55
+ ``(latitude, longitude)`` or ``None``.
56
+ """
57
+ zip_int = _normalize_zipcode(zipcode)
58
+ if zip_int is None:
59
+ return None
60
+
61
+ record = ZIP_INFO.get(zip_int)
62
+ if record is None:
63
+ return None
64
+ _, latitude, longitude = record
65
+ return latitude, longitude
66
+
67
+
68
+ def info(zipcode: str | int) -> Optional[ZipInfo]:
69
+ """
70
+ Return timezone and coordinate metadata for a US ZIP code.
71
+
72
+ Args:
73
+ zipcode: ZIP code as a string or integer.
74
+
75
+ Returns:
76
+ ``ZipInfo`` when timezone data exists, otherwise ``None``.
77
+ """
78
+ zip_int = _normalize_zipcode(zipcode)
79
+ if zip_int is None:
80
+ return None
81
+
82
+ record = ZIP_INFO.get(zip_int)
83
+ if record is None:
84
+ return None
85
+
86
+ tz_idx, latitude, longitude = record
87
+ return ZipInfo(
88
+ zipcode=zip_int,
89
+ timezone=TIMEZONES[tz_idx],
90
+ latitude=latitude,
91
+ longitude=longitude,
92
+ )
93
+
94
+
95
+ __all__ = ["ZipInfo", "coordinates", "info", "timezone"]