hebrewcal 0.1.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.
- hebrewcal-0.1.0/LICENSE +21 -0
- hebrewcal-0.1.0/PKG-INFO +145 -0
- hebrewcal-0.1.0/README.md +103 -0
- hebrewcal-0.1.0/pyproject.toml +104 -0
- hebrewcal-0.1.0/setup.cfg +4 -0
- hebrewcal-0.1.0/src/hebrewcal/__init__.py +37 -0
- hebrewcal-0.1.0/src/hebrewcal/astro/__init__.py +7 -0
- hebrewcal-0.1.0/src/hebrewcal/calendars/__init__.py +5 -0
- hebrewcal-0.1.0/src/hebrewcal/calendars/gregorian.py +90 -0
- hebrewcal-0.1.0/src/hebrewcal/calendars/hebrew.py +65 -0
- hebrewcal-0.1.0/src/hebrewcal/calendars/julian.py +95 -0
- hebrewcal-0.1.0/src/hebrewcal/calendars_alt/__init__.py +4 -0
- hebrewcal-0.1.0/src/hebrewcal/conversion.py +32 -0
- hebrewcal-0.1.0/src/hebrewcal/core/__init__.py +6 -0
- hebrewcal-0.1.0/src/hebrewcal/core/calendar.py +55 -0
- hebrewcal-0.1.0/src/hebrewcal/core/rata_die.py +26 -0
- hebrewcal-0.1.0/src/hebrewcal/eras/__init__.py +6 -0
- hebrewcal-0.1.0/src/hebrewcal/eras/anno_mundi.py +34 -0
- hebrewcal-0.1.0/src/hebrewcal/formatting/__init__.py +1 -0
- hebrewcal-0.1.0/src/hebrewcal/formatting/dates.py +31 -0
- hebrewcal-0.1.0/src/hebrewcal/hebrew/__init__.py +7 -0
- hebrewcal-0.1.0/src/hebrewcal/hebrew/dechiyot.py +26 -0
- hebrewcal-0.1.0/src/hebrewcal/hebrew/keviah.py +48 -0
- hebrewcal-0.1.0/src/hebrewcal/hebrew/metonic.py +17 -0
- hebrewcal-0.1.0/src/hebrewcal/hebrew/molad.py +44 -0
- hebrewcal-0.1.0/src/hebrewcal/hebrew/yeartype.py +52 -0
- hebrewcal-0.1.0/src/hebrewcal/names.py +71 -0
- hebrewcal-0.1.0/src/hebrewcal/numerals.py +114 -0
- hebrewcal-0.1.0/src/hebrewcal/parsing/__init__.py +5 -0
- hebrewcal-0.1.0/src/hebrewcal/parsing/dates.py +35 -0
- hebrewcal-0.1.0/src/hebrewcal/py.typed +0 -0
- hebrewcal-0.1.0/src/hebrewcal/religious/__init__.py +6 -0
- hebrewcal-0.1.0/src/hebrewcal.egg-info/PKG-INFO +145 -0
- hebrewcal-0.1.0/src/hebrewcal.egg-info/SOURCES.txt +40 -0
- hebrewcal-0.1.0/src/hebrewcal.egg-info/dependency_links.txt +1 -0
- hebrewcal-0.1.0/src/hebrewcal.egg-info/requires.txt +13 -0
- hebrewcal-0.1.0/src/hebrewcal.egg-info/top_level.txt +1 -0
- hebrewcal-0.1.0/tests/test_conversion.py +48 -0
- hebrewcal-0.1.0/tests/test_metadata.py +28 -0
- hebrewcal-0.1.0/tests/test_names.py +38 -0
- hebrewcal-0.1.0/tests/test_numerals.py +36 -0
- hebrewcal-0.1.0/tests/test_reference_dates.py +60 -0
hebrewcal-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Benjamin Schnabel
|
|
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.
|
hebrewcal-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hebrewcal
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A pure-Python library for the Hebrew calendar: conversion, astronomy, holidays and religious times, computed entirely locally.
|
|
5
|
+
Author-email: Benjamin Schnabel <Benjamin-777@gmx.de>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/bsesic/hebrewcal
|
|
8
|
+
Project-URL: Repository, https://github.com/bsesic/hebrewcal
|
|
9
|
+
Project-URL: Documentation, https://hebrewcal.readthedocs.io
|
|
10
|
+
Project-URL: Issues, https://github.com/bsesic/hebrewcal/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/bsesic/hebrewcal/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: hebrew,calendar,jewish,zmanim,molad,gregorian,julian,calendrical
|
|
13
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Religion
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Religion
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.11
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
33
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
34
|
+
Requires-Dist: flake8>=7.0; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy>=1.11; extra == "dev"
|
|
36
|
+
Requires-Dist: pre-commit>=3.8; extra == "dev"
|
|
37
|
+
Provides-Extra: docs
|
|
38
|
+
Requires-Dist: sphinx>=7.0; extra == "docs"
|
|
39
|
+
Requires-Dist: furo>=2024.1; extra == "docs"
|
|
40
|
+
Requires-Dist: myst-parser>=3.0; extra == "docs"
|
|
41
|
+
Dynamic: license-file
|
|
42
|
+
|
|
43
|
+
# hebrewcal
|
|
44
|
+
|
|
45
|
+
[](https://github.com/bsesic/hebrewcal/actions/workflows/ci.yml)
|
|
46
|
+
[](https://hebrewcal.readthedocs.io/en/latest/?badge=latest)
|
|
47
|
+
[](https://pypi.org/project/hebrewcal/)
|
|
48
|
+
[](https://pypi.org/project/hebrewcal/)
|
|
49
|
+
[](LICENSE)
|
|
50
|
+
|
|
51
|
+
A pure-Python library for the Hebrew calendar.
|
|
52
|
+
|
|
53
|
+
`hebrewcal` makes the Hebrew calendar usable programmatically and converts it
|
|
54
|
+
bidirectionally against the Gregorian and Julian calendars. Every computation is
|
|
55
|
+
performed locally — the library never issues network calls to any external service.
|
|
56
|
+
|
|
57
|
+
It is built for two audiences:
|
|
58
|
+
|
|
59
|
+
- **Religious use** — holidays (Israel and the Diaspora, including minority feasts and
|
|
60
|
+
Shushan Purim), Shabbat candle lighting and Havdalah, zmanim, Torah readings, the Omer
|
|
61
|
+
count, yahrzeit, and the sabbatical and jubilee cycle.
|
|
62
|
+
- **Academic use** — historical, medieval and ancient dates, Babylonian and biblical
|
|
63
|
+
month names, proleptic calendars, the Julian/Gregorian reform, and the documented
|
|
64
|
+
"missing years" of the Anno Mundi count.
|
|
65
|
+
|
|
66
|
+
> **Project status.** Early development. The calendar core, conversion and date handling
|
|
67
|
+
> (Phase 1) are implemented. Astronomy, holidays and religious times follow on the
|
|
68
|
+
> [roadmap](docs/specs/2026-06-03-architecture-roadmap.md).
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install hebrewcal
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`hebrewcal` requires Python 3.11+ and has **no runtime dependencies** (standard library
|
|
77
|
+
only).
|
|
78
|
+
|
|
79
|
+
## Quick start
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from hebrewcal import GregorianDate, HebrewDate, to_hebrew, to_gregorian, weekday
|
|
83
|
+
|
|
84
|
+
# What Hebrew date and weekday corresponds to 31 October 1867?
|
|
85
|
+
g = GregorianDate(1867, 10, 31)
|
|
86
|
+
h = to_hebrew(g)
|
|
87
|
+
print(h) # HebrewDate(year=5628, month=8, day=2) -> 2 Marheshvan 5628
|
|
88
|
+
print(weekday(g).name) # THURSDAY
|
|
89
|
+
|
|
90
|
+
# Convert a Hebrew date back to Gregorian.
|
|
91
|
+
print(to_gregorian(HebrewDate(5785, 7, 1))) # GregorianDate(year=2024, month=10, day=3)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Every calendar reduces a date to an integer **Rata Die (RD)** day count and rebuilds a
|
|
95
|
+
date from it, so any two calendars are interconvertible through RD:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from hebrewcal import GregorianDate, to_julian
|
|
99
|
+
|
|
100
|
+
GregorianDate(2026, 6, 26).to_rd() # 739793
|
|
101
|
+
to_julian(GregorianDate(2026, 6, 26)) # JulianDate(year=2026, month=6, day=13)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Features
|
|
105
|
+
|
|
106
|
+
- Proleptic **Gregorian** and **Julian** calendars, with an explicit, configurable
|
|
107
|
+
Julian/Gregorian reform helper.
|
|
108
|
+
- A complete **Hebrew** calendar: molad and halakim, the dechiyot ("four gates"), year
|
|
109
|
+
typing (deficient / regular / complete), the keviah signature, and the Metonic cycle.
|
|
110
|
+
- Bidirectional **conversion** between any supported calendars through Rata Die.
|
|
111
|
+
- **Parsing** of Gregorian input (ISO 8601, DIN 5008 and slash form) and **formatting**
|
|
112
|
+
in numeric and named styles.
|
|
113
|
+
- A **gematria** converter between integers and Hebrew numerals.
|
|
114
|
+
- Month and weekday **name tables** (transliteration, Babylonian, biblical).
|
|
115
|
+
- The **Anno Mundi** era with a documented "missing years" notice.
|
|
116
|
+
|
|
117
|
+
## Documentation
|
|
118
|
+
|
|
119
|
+
Full documentation, including a user guide with many examples, lives at
|
|
120
|
+
**[hebrewcal.readthedocs.io](https://hebrewcal.readthedocs.io)**.
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
git clone https://github.com/bsesic/hebrewcal.git
|
|
126
|
+
cd hebrewcal
|
|
127
|
+
python -m venv .venv && source .venv/bin/activate
|
|
128
|
+
pip install -e ".[dev]"
|
|
129
|
+
pre-commit install
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Run the lint gate and the test suite:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
flake8
|
|
136
|
+
ruff check .
|
|
137
|
+
mypy
|
|
138
|
+
pytest
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the branching strategy and conventions.
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
[MIT](LICENSE) © Benjamin Schnabel
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# hebrewcal
|
|
2
|
+
|
|
3
|
+
[](https://github.com/bsesic/hebrewcal/actions/workflows/ci.yml)
|
|
4
|
+
[](https://hebrewcal.readthedocs.io/en/latest/?badge=latest)
|
|
5
|
+
[](https://pypi.org/project/hebrewcal/)
|
|
6
|
+
[](https://pypi.org/project/hebrewcal/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
A pure-Python library for the Hebrew calendar.
|
|
10
|
+
|
|
11
|
+
`hebrewcal` makes the Hebrew calendar usable programmatically and converts it
|
|
12
|
+
bidirectionally against the Gregorian and Julian calendars. Every computation is
|
|
13
|
+
performed locally — the library never issues network calls to any external service.
|
|
14
|
+
|
|
15
|
+
It is built for two audiences:
|
|
16
|
+
|
|
17
|
+
- **Religious use** — holidays (Israel and the Diaspora, including minority feasts and
|
|
18
|
+
Shushan Purim), Shabbat candle lighting and Havdalah, zmanim, Torah readings, the Omer
|
|
19
|
+
count, yahrzeit, and the sabbatical and jubilee cycle.
|
|
20
|
+
- **Academic use** — historical, medieval and ancient dates, Babylonian and biblical
|
|
21
|
+
month names, proleptic calendars, the Julian/Gregorian reform, and the documented
|
|
22
|
+
"missing years" of the Anno Mundi count.
|
|
23
|
+
|
|
24
|
+
> **Project status.** Early development. The calendar core, conversion and date handling
|
|
25
|
+
> (Phase 1) are implemented. Astronomy, holidays and religious times follow on the
|
|
26
|
+
> [roadmap](docs/specs/2026-06-03-architecture-roadmap.md).
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install hebrewcal
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`hebrewcal` requires Python 3.11+ and has **no runtime dependencies** (standard library
|
|
35
|
+
only).
|
|
36
|
+
|
|
37
|
+
## Quick start
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from hebrewcal import GregorianDate, HebrewDate, to_hebrew, to_gregorian, weekday
|
|
41
|
+
|
|
42
|
+
# What Hebrew date and weekday corresponds to 31 October 1867?
|
|
43
|
+
g = GregorianDate(1867, 10, 31)
|
|
44
|
+
h = to_hebrew(g)
|
|
45
|
+
print(h) # HebrewDate(year=5628, month=8, day=2) -> 2 Marheshvan 5628
|
|
46
|
+
print(weekday(g).name) # THURSDAY
|
|
47
|
+
|
|
48
|
+
# Convert a Hebrew date back to Gregorian.
|
|
49
|
+
print(to_gregorian(HebrewDate(5785, 7, 1))) # GregorianDate(year=2024, month=10, day=3)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Every calendar reduces a date to an integer **Rata Die (RD)** day count and rebuilds a
|
|
53
|
+
date from it, so any two calendars are interconvertible through RD:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from hebrewcal import GregorianDate, to_julian
|
|
57
|
+
|
|
58
|
+
GregorianDate(2026, 6, 26).to_rd() # 739793
|
|
59
|
+
to_julian(GregorianDate(2026, 6, 26)) # JulianDate(year=2026, month=6, day=13)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Features
|
|
63
|
+
|
|
64
|
+
- Proleptic **Gregorian** and **Julian** calendars, with an explicit, configurable
|
|
65
|
+
Julian/Gregorian reform helper.
|
|
66
|
+
- A complete **Hebrew** calendar: molad and halakim, the dechiyot ("four gates"), year
|
|
67
|
+
typing (deficient / regular / complete), the keviah signature, and the Metonic cycle.
|
|
68
|
+
- Bidirectional **conversion** between any supported calendars through Rata Die.
|
|
69
|
+
- **Parsing** of Gregorian input (ISO 8601, DIN 5008 and slash form) and **formatting**
|
|
70
|
+
in numeric and named styles.
|
|
71
|
+
- A **gematria** converter between integers and Hebrew numerals.
|
|
72
|
+
- Month and weekday **name tables** (transliteration, Babylonian, biblical).
|
|
73
|
+
- The **Anno Mundi** era with a documented "missing years" notice.
|
|
74
|
+
|
|
75
|
+
## Documentation
|
|
76
|
+
|
|
77
|
+
Full documentation, including a user guide with many examples, lives at
|
|
78
|
+
**[hebrewcal.readthedocs.io](https://hebrewcal.readthedocs.io)**.
|
|
79
|
+
|
|
80
|
+
## Development
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git clone https://github.com/bsesic/hebrewcal.git
|
|
84
|
+
cd hebrewcal
|
|
85
|
+
python -m venv .venv && source .venv/bin/activate
|
|
86
|
+
pip install -e ".[dev]"
|
|
87
|
+
pre-commit install
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Run the lint gate and the test suite:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
flake8
|
|
94
|
+
ruff check .
|
|
95
|
+
mypy
|
|
96
|
+
pytest
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the branching strategy and conventions.
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
[MIT](LICENSE) © Benjamin Schnabel
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hebrewcal"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A pure-Python library for the Hebrew calendar: conversion, astronomy, holidays and religious times, computed entirely locally."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Benjamin Schnabel", email = "Benjamin-777@gmx.de" }]
|
|
13
|
+
keywords = [
|
|
14
|
+
"hebrew",
|
|
15
|
+
"calendar",
|
|
16
|
+
"jewish",
|
|
17
|
+
"zmanim",
|
|
18
|
+
"molad",
|
|
19
|
+
"gregorian",
|
|
20
|
+
"julian",
|
|
21
|
+
"calendrical",
|
|
22
|
+
]
|
|
23
|
+
classifiers = [
|
|
24
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
"Intended Audience :: Religion",
|
|
27
|
+
"Intended Audience :: Science/Research",
|
|
28
|
+
"License :: OSI Approved :: MIT License",
|
|
29
|
+
"Operating System :: OS Independent",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Programming Language :: Python :: 3.13",
|
|
34
|
+
"Topic :: Religion",
|
|
35
|
+
"Topic :: Scientific/Engineering :: Astronomy",
|
|
36
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
37
|
+
"Typing :: Typed",
|
|
38
|
+
]
|
|
39
|
+
# No runtime dependencies: everything is computed locally using the standard library only.
|
|
40
|
+
dependencies = []
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://github.com/bsesic/hebrewcal"
|
|
44
|
+
Repository = "https://github.com/bsesic/hebrewcal"
|
|
45
|
+
Documentation = "https://hebrewcal.readthedocs.io"
|
|
46
|
+
Issues = "https://github.com/bsesic/hebrewcal/issues"
|
|
47
|
+
Changelog = "https://github.com/bsesic/hebrewcal/blob/main/CHANGELOG.md"
|
|
48
|
+
|
|
49
|
+
[project.optional-dependencies]
|
|
50
|
+
dev = [
|
|
51
|
+
"pytest>=8.0",
|
|
52
|
+
"pytest-cov>=5.0",
|
|
53
|
+
"ruff>=0.6",
|
|
54
|
+
"flake8>=7.0",
|
|
55
|
+
"mypy>=1.11",
|
|
56
|
+
"pre-commit>=3.8",
|
|
57
|
+
]
|
|
58
|
+
docs = [
|
|
59
|
+
"sphinx>=7.0",
|
|
60
|
+
"furo>=2024.1",
|
|
61
|
+
"myst-parser>=3.0",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[tool.setuptools]
|
|
65
|
+
package-dir = { "" = "src" }
|
|
66
|
+
|
|
67
|
+
[tool.setuptools.packages.find]
|
|
68
|
+
where = ["src"]
|
|
69
|
+
|
|
70
|
+
[tool.setuptools.package-data]
|
|
71
|
+
hebrewcal = ["py.typed"]
|
|
72
|
+
|
|
73
|
+
[tool.pytest.ini_options]
|
|
74
|
+
minversion = "8.0"
|
|
75
|
+
testpaths = ["tests"]
|
|
76
|
+
addopts = "-ra --strict-markers"
|
|
77
|
+
|
|
78
|
+
[tool.coverage.run]
|
|
79
|
+
branch = true
|
|
80
|
+
source = ["hebrewcal"]
|
|
81
|
+
|
|
82
|
+
[tool.coverage.report]
|
|
83
|
+
show_missing = true
|
|
84
|
+
|
|
85
|
+
[tool.ruff]
|
|
86
|
+
line-length = 100
|
|
87
|
+
target-version = "py311"
|
|
88
|
+
src = ["src", "tests"]
|
|
89
|
+
|
|
90
|
+
[tool.ruff.lint]
|
|
91
|
+
select = ["E", "F", "W", "I", "N", "UP", "B", "C4", "SIM", "RUF"]
|
|
92
|
+
# This library deliberately embeds Hebrew text in strings, comments and
|
|
93
|
+
# docstrings. The ambiguous-unicode checks (homoglyph detection for Latin code)
|
|
94
|
+
# are false positives in that context, so they are disabled project-wide.
|
|
95
|
+
ignore = ["RUF001", "RUF002", "RUF003"]
|
|
96
|
+
|
|
97
|
+
[tool.ruff.lint.isort]
|
|
98
|
+
known-first-party = ["hebrewcal"]
|
|
99
|
+
|
|
100
|
+
[tool.mypy]
|
|
101
|
+
python_version = "3.11"
|
|
102
|
+
strict = true
|
|
103
|
+
warn_unreachable = true
|
|
104
|
+
files = ["src", "tests"]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""hebrewcal — a pure-Python library for the Hebrew calendar.
|
|
2
|
+
|
|
3
|
+
The library makes the Hebrew calendar usable programmatically and converts it
|
|
4
|
+
bidirectionally against the Gregorian and Julian calendars. Every computation is
|
|
5
|
+
performed locally; the library never issues network calls to any external service.
|
|
6
|
+
|
|
7
|
+
The whole design pivots on the Rata Die (RD) day count from Dershowitz & Reingold,
|
|
8
|
+
*Calendrical Calculations*: every calendar implements only ``to_rd`` and ``from_rd``,
|
|
9
|
+
and conversion between any two calendars always goes through RD.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from importlib import metadata
|
|
15
|
+
|
|
16
|
+
from hebrewcal.calendars.gregorian import GregorianDate
|
|
17
|
+
from hebrewcal.calendars.hebrew import HebrewDate
|
|
18
|
+
from hebrewcal.calendars.julian import JulianDate
|
|
19
|
+
from hebrewcal.conversion import to_gregorian, to_hebrew, to_julian, weekday
|
|
20
|
+
from hebrewcal.core.calendar import Weekday
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
__version__ = metadata.version("hebrewcal")
|
|
24
|
+
except metadata.PackageNotFoundError: # pragma: no cover - source checkout without install
|
|
25
|
+
__version__ = "0.0.0.dev0"
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"GregorianDate",
|
|
29
|
+
"HebrewDate",
|
|
30
|
+
"JulianDate",
|
|
31
|
+
"Weekday",
|
|
32
|
+
"__version__",
|
|
33
|
+
"to_gregorian",
|
|
34
|
+
"to_hebrew",
|
|
35
|
+
"to_julian",
|
|
36
|
+
"weekday",
|
|
37
|
+
]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Astronomy and locations — pure Python, no dependencies.
|
|
2
|
+
|
|
3
|
+
Implements solar position, sunrise/sunset and twilight (Meeus / NOAA algorithms),
|
|
4
|
+
a geographic ``Location`` type, and time-zone handling via the standard-library
|
|
5
|
+
``zoneinfo`` module. This is the foundation for all time-of-day religious
|
|
6
|
+
calculations.
|
|
7
|
+
"""
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""The proleptic Gregorian calendar.
|
|
2
|
+
|
|
3
|
+
Valid for all years, including years <= 0 (proleptic). The RD value matches the
|
|
4
|
+
Python standard-library proleptic Gregorian ordinal, which makes cross-checking
|
|
5
|
+
straightforward.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
from hebrewcal.core.rata_die import RD_EPOCH
|
|
13
|
+
|
|
14
|
+
_MONTH_LENGTHS = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def is_leap_year(year: int) -> bool:
|
|
18
|
+
"""Return whether ``year`` is a Gregorian leap year."""
|
|
19
|
+
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def last_day_of_month(year: int, month: int) -> int:
|
|
23
|
+
"""Return the number of days in ``month`` of ``year``."""
|
|
24
|
+
if month == 2 and is_leap_year(year):
|
|
25
|
+
return 29
|
|
26
|
+
return _MONTH_LENGTHS[month - 1]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True, order=True)
|
|
30
|
+
class GregorianDate:
|
|
31
|
+
"""A date in the proleptic Gregorian calendar."""
|
|
32
|
+
|
|
33
|
+
year: int
|
|
34
|
+
month: int
|
|
35
|
+
day: int
|
|
36
|
+
|
|
37
|
+
def __post_init__(self) -> None:
|
|
38
|
+
if not 1 <= self.month <= 12:
|
|
39
|
+
raise ValueError(f"month out of range: {self.month}")
|
|
40
|
+
if not 1 <= self.day <= last_day_of_month(self.year, self.month):
|
|
41
|
+
raise ValueError(f"day out of range: {self.day}")
|
|
42
|
+
|
|
43
|
+
def to_rd(self) -> int:
|
|
44
|
+
"""Return the Rata Die day count for this date."""
|
|
45
|
+
y = self.year
|
|
46
|
+
if self.month <= 2:
|
|
47
|
+
correction = 0
|
|
48
|
+
elif is_leap_year(y):
|
|
49
|
+
correction = -1
|
|
50
|
+
else:
|
|
51
|
+
correction = -2
|
|
52
|
+
return (
|
|
53
|
+
RD_EPOCH
|
|
54
|
+
- 1
|
|
55
|
+
+ 365 * (y - 1)
|
|
56
|
+
+ (y - 1) // 4
|
|
57
|
+
- (y - 1) // 100
|
|
58
|
+
+ (y - 1) // 400
|
|
59
|
+
+ (367 * self.month - 362) // 12
|
|
60
|
+
+ correction
|
|
61
|
+
+ self.day
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def from_rd(cls, rd: int) -> GregorianDate:
|
|
66
|
+
"""Reconstruct a Gregorian date from an RD value."""
|
|
67
|
+
year = _year_from_rd(rd)
|
|
68
|
+
prior_days = rd - cls(year, 1, 1).to_rd()
|
|
69
|
+
if rd < cls(year, 3, 1).to_rd():
|
|
70
|
+
correction = 0
|
|
71
|
+
elif is_leap_year(year):
|
|
72
|
+
correction = 1
|
|
73
|
+
else:
|
|
74
|
+
correction = 2
|
|
75
|
+
month = (12 * (prior_days + correction) + 373) // 367
|
|
76
|
+
day = rd - cls(year, month, 1).to_rd() + 1
|
|
77
|
+
return cls(year, month, day)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _year_from_rd(rd: int) -> int:
|
|
81
|
+
"""Return the Gregorian year containing the given RD value."""
|
|
82
|
+
d0 = rd - RD_EPOCH
|
|
83
|
+
n400, d1 = divmod(d0, 146097)
|
|
84
|
+
n100, d2 = divmod(d1, 36524)
|
|
85
|
+
n4, d3 = divmod(d2, 1461)
|
|
86
|
+
n1 = d3 // 365
|
|
87
|
+
year = 400 * n400 + 100 * n100 + 4 * n4 + n1
|
|
88
|
+
if n100 == 4 or n1 == 4:
|
|
89
|
+
return year
|
|
90
|
+
return year + 1
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""The Hebrew calendar date type.
|
|
2
|
+
|
|
3
|
+
Month numbering is standard (Nisan = 1 ... Tishri = 7 ... Adar/Adar I = 12,
|
|
4
|
+
Adar II = 13). The civil year begins at Tishri. Conversion to and from RD uses
|
|
5
|
+
the year-typing machinery in :mod:`hebrewcal.hebrew`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
from hebrewcal.hebrew.yeartype import (
|
|
13
|
+
last_day_of_month,
|
|
14
|
+
last_month_of_year,
|
|
15
|
+
new_year_rd,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
_TISHRI = 7
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True, order=True)
|
|
22
|
+
class HebrewDate:
|
|
23
|
+
"""A date in the Hebrew calendar."""
|
|
24
|
+
|
|
25
|
+
year: int
|
|
26
|
+
month: int
|
|
27
|
+
day: int
|
|
28
|
+
|
|
29
|
+
def __post_init__(self) -> None:
|
|
30
|
+
if not 1 <= self.month <= last_month_of_year(self.year):
|
|
31
|
+
raise ValueError(f"month out of range: {self.month}")
|
|
32
|
+
if not 1 <= self.day <= last_day_of_month(self.year, self.month):
|
|
33
|
+
raise ValueError(f"day out of range: {self.day}")
|
|
34
|
+
|
|
35
|
+
def to_rd(self) -> int:
|
|
36
|
+
"""Return the Rata Die day count for this date."""
|
|
37
|
+
if self.month < _TISHRI:
|
|
38
|
+
# Months Nisan..Elul fall in the second half of the civil year.
|
|
39
|
+
months_after_tishri = range(_TISHRI, last_month_of_year(self.year) + 1)
|
|
40
|
+
months_before = range(1, self.month)
|
|
41
|
+
else:
|
|
42
|
+
months_after_tishri = range(_TISHRI, self.month)
|
|
43
|
+
months_before = range(0, 0) # empty
|
|
44
|
+
days_before = sum(
|
|
45
|
+
last_day_of_month(self.year, m) for m in months_after_tishri
|
|
46
|
+
) + sum(last_day_of_month(self.year, m) for m in months_before)
|
|
47
|
+
return new_year_rd(self.year) + days_before + self.day - 1
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def from_rd(cls, rd: int) -> HebrewDate:
|
|
51
|
+
"""Reconstruct a Hebrew date from an RD value."""
|
|
52
|
+
# Estimate the year, then correct by direct comparison.
|
|
53
|
+
approx = (rd - new_year_rd(1)) // 366 + 1
|
|
54
|
+
year = approx
|
|
55
|
+
while new_year_rd(year + 1) <= rd:
|
|
56
|
+
year += 1
|
|
57
|
+
while new_year_rd(year) > rd:
|
|
58
|
+
year -= 1
|
|
59
|
+
# Determine the starting month: Nisan (1) if on/after 1 Nisan, else Tishri (7).
|
|
60
|
+
start = 1 if rd >= cls(year, 1, 1).to_rd() else _TISHRI
|
|
61
|
+
month = start
|
|
62
|
+
while rd > cls(year, month, last_day_of_month(year, month)).to_rd():
|
|
63
|
+
month += 1
|
|
64
|
+
day = rd - cls(year, month, 1).to_rd() + 1
|
|
65
|
+
return cls(year, month, day)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""The proleptic Julian calendar with explicit reform handling.
|
|
2
|
+
|
|
3
|
+
The library never performs a silent Julian/Gregorian switch. Everything is
|
|
4
|
+
computed through RD; the historical 1582 (and later, regional) reform is exposed
|
|
5
|
+
as an explicit, configurable helper so callers decide when the cutover applies.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
from hebrewcal.calendars.gregorian import GregorianDate
|
|
13
|
+
|
|
14
|
+
# RD of Julian 1 January 1 == Gregorian 30 December 0 == -1.
|
|
15
|
+
JULIAN_EPOCH: int = -1
|
|
16
|
+
|
|
17
|
+
_MONTH_LENGTHS = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def is_leap_year(year: int) -> bool:
|
|
21
|
+
"""Return whether ``year`` is a Julian leap year (proleptic, no year 0)."""
|
|
22
|
+
if year > 0:
|
|
23
|
+
return year % 4 == 0
|
|
24
|
+
# There is no year 0; proleptically, leap years satisfy year % 4 == 3.
|
|
25
|
+
return year % 4 == 3
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def last_day_of_month(year: int, month: int) -> int:
|
|
29
|
+
"""Return the number of days in ``month`` of ``year``."""
|
|
30
|
+
if month == 2 and is_leap_year(year):
|
|
31
|
+
return 29
|
|
32
|
+
return _MONTH_LENGTHS[month - 1]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True, order=True)
|
|
36
|
+
class JulianDate:
|
|
37
|
+
"""A date in the proleptic Julian calendar."""
|
|
38
|
+
|
|
39
|
+
year: int
|
|
40
|
+
month: int
|
|
41
|
+
day: int
|
|
42
|
+
|
|
43
|
+
def __post_init__(self) -> None:
|
|
44
|
+
if self.year == 0:
|
|
45
|
+
raise ValueError("Julian calendar has no year 0")
|
|
46
|
+
if not 1 <= self.month <= 12:
|
|
47
|
+
raise ValueError(f"month out of range: {self.month}")
|
|
48
|
+
if not 1 <= self.day <= last_day_of_month(self.year, self.month):
|
|
49
|
+
raise ValueError(f"day out of range: {self.day}")
|
|
50
|
+
|
|
51
|
+
def to_rd(self) -> int:
|
|
52
|
+
"""Return the Rata Die day count for this date."""
|
|
53
|
+
y = self.year + 1 if self.year < 0 else self.year
|
|
54
|
+
if self.month <= 2:
|
|
55
|
+
correction = 0
|
|
56
|
+
elif is_leap_year(self.year):
|
|
57
|
+
correction = -1
|
|
58
|
+
else:
|
|
59
|
+
correction = -2
|
|
60
|
+
return (
|
|
61
|
+
JULIAN_EPOCH
|
|
62
|
+
- 1
|
|
63
|
+
+ 365 * (y - 1)
|
|
64
|
+
+ (y - 1) // 4
|
|
65
|
+
+ (367 * self.month - 362) // 12
|
|
66
|
+
+ correction
|
|
67
|
+
+ self.day
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def from_rd(cls, rd: int) -> JulianDate:
|
|
72
|
+
"""Reconstruct a Julian date from an RD value."""
|
|
73
|
+
approx = (4 * (rd - JULIAN_EPOCH) + 1464) // 1461
|
|
74
|
+
year = approx - 1 if approx <= 0 else approx
|
|
75
|
+
prior_days = rd - cls(year, 1, 1).to_rd()
|
|
76
|
+
if rd < cls(year, 3, 1).to_rd():
|
|
77
|
+
correction = 0
|
|
78
|
+
elif is_leap_year(year):
|
|
79
|
+
correction = 1
|
|
80
|
+
else:
|
|
81
|
+
correction = 2
|
|
82
|
+
month = (12 * (prior_days + correction) + 373) // 367
|
|
83
|
+
day = rd - cls(year, month, 1).to_rd() + 1
|
|
84
|
+
return cls(year, month, day)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def last_gregorian_before_reform() -> GregorianDate:
|
|
88
|
+
"""Return the last Gregorian-labelled date before the 1582 papal cutover.
|
|
89
|
+
|
|
90
|
+
The papal bull skipped from Julian Thursday 4 October 1582 to Gregorian
|
|
91
|
+
Friday 15 October 1582. Adoption was regional and much later in many places;
|
|
92
|
+
callers that need a different cutover should supply their own date. This
|
|
93
|
+
helper exists so the default reference point is explicit, not implicit.
|
|
94
|
+
"""
|
|
95
|
+
return GregorianDate(1582, 10, 4)
|