GaianCalendar 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.
- gaiancalendar-0.1.0/GaianCalendar.egg-info/PKG-INFO +136 -0
- gaiancalendar-0.1.0/GaianCalendar.egg-info/SOURCES.txt +18 -0
- gaiancalendar-0.1.0/GaianCalendar.egg-info/dependency_links.txt +1 -0
- gaiancalendar-0.1.0/GaianCalendar.egg-info/top_level.txt +1 -0
- gaiancalendar-0.1.0/LICENSE +21 -0
- gaiancalendar-0.1.0/PKG-INFO +136 -0
- gaiancalendar-0.1.0/README.md +111 -0
- gaiancalendar-0.1.0/gaian_calendar/__init__.py +21 -0
- gaiancalendar-0.1.0/gaian_calendar/_convert.py +83 -0
- gaiancalendar-0.1.0/gaian_calendar/_data.py +80 -0
- gaiancalendar-0.1.0/gaian_calendar/_format.py +88 -0
- gaiancalendar-0.1.0/gaian_calendar/date.py +223 -0
- gaiancalendar-0.1.0/gaian_calendar/month.py +114 -0
- gaiancalendar-0.1.0/gaian_calendar/weekday.py +90 -0
- gaiancalendar-0.1.0/pyproject.toml +35 -0
- gaiancalendar-0.1.0/setup.cfg +4 -0
- gaiancalendar-0.1.0/tests/test_convert.py +172 -0
- gaiancalendar-0.1.0/tests/test_date.py +155 -0
- gaiancalendar-0.1.0/tests/test_format.py +99 -0
- gaiancalendar-0.1.0/tests/test_month_weekday.py +108 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: GaianCalendar
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python library for the Gaian Calendar — a perpetual 13-month solar calendar based on ISO week-year arithmetic.
|
|
5
|
+
Author: Immanuelle
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Emma-Leonhart/GaianCalendar
|
|
8
|
+
Project-URL: Issues, https://github.com/Emma-Leonhart/GaianCalendar/issues
|
|
9
|
+
Keywords: calendar,gaian,date,alternative calendar,perpetual calendar,ISO week,zodiac
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Classifier: Topic :: Utilities
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# GaianCalendar
|
|
27
|
+
|
|
28
|
+
A pure-Python library for the **Gaian Calendar** — a perpetual 13-month solar calendar based on ISO week-year arithmetic.
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from gaian_calendar import GaianDate
|
|
32
|
+
|
|
33
|
+
today = GaianDate.today()
|
|
34
|
+
print(today) # e.g. "Aquarius 22, 12026 GE"
|
|
35
|
+
print(today.day_of_year) # e.g. 78
|
|
36
|
+
print(today.is_leap_year) # True
|
|
37
|
+
print(today.format("WWWW, MMMM d, yyyy GE")) # "Sunday, Aquarius 22, 12026 GE"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Status
|
|
43
|
+
|
|
44
|
+
**Pre-release — planning phase.** See [`planning/`](planning/) for design documents.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## What is the Gaian Calendar?
|
|
49
|
+
|
|
50
|
+
The Gaian Calendar is a perpetual reform calendar with these properties:
|
|
51
|
+
|
|
52
|
+
- **13 regular months** of exactly 28 days (4 weeks × 7 days), named after zodiac constellations
|
|
53
|
+
- **1 intercalary month** (Horus, 7 days) in leap years only — years with ISO week 53
|
|
54
|
+
- **Perpetual**: every calendar date always falls on the same weekday, every year
|
|
55
|
+
- **Year numbering**: Gaian year = ISO week-year + 10,000 (so 2026 CE = 12026 GE)
|
|
56
|
+
- **Zero weekday drift**: recurring events stay on the same day of week indefinitely
|
|
57
|
+
|
|
58
|
+
The 13 months in order: Sagittarius · Capricorn · Aquarius · Pisces · Aries · Taurus · Gemini · Cancer · Leo · Virgo · Libra · Scorpius · Ophiuchus · (Horus in leap years)
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install gaian-calendar
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
*(Not yet published — coming soon)*
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Usage
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from datetime import date, timedelta
|
|
76
|
+
from gaian_calendar import GaianDate, GaianMonth, is_leap_year
|
|
77
|
+
|
|
78
|
+
# Today
|
|
79
|
+
d = GaianDate.today()
|
|
80
|
+
|
|
81
|
+
# From Gregorian
|
|
82
|
+
d = GaianDate.from_gregorian(date(2026, 2, 22))
|
|
83
|
+
print(d) # "Aquarius 22, 12026 GE"
|
|
84
|
+
print(d.to_gregorian()) # 2026-02-22
|
|
85
|
+
|
|
86
|
+
# Properties
|
|
87
|
+
print(d.year) # 12026
|
|
88
|
+
print(d.month) # 3
|
|
89
|
+
print(d.day) # 22
|
|
90
|
+
print(d.month_name) # "Aquarius"
|
|
91
|
+
print(d.month_symbol) # "♒"
|
|
92
|
+
print(d.weekday_name) # "Sunday"
|
|
93
|
+
print(d.weekday_symbol) # "☉"
|
|
94
|
+
print(d.day_of_year) # 78
|
|
95
|
+
print(d.is_leap_year) # True
|
|
96
|
+
|
|
97
|
+
# Arithmetic
|
|
98
|
+
next_week = d + timedelta(weeks=1)
|
|
99
|
+
yesterday = d - timedelta(days=1)
|
|
100
|
+
delta = d - GaianDate(12026, 1, 1) # timedelta
|
|
101
|
+
|
|
102
|
+
# Parsing
|
|
103
|
+
d = GaianDate.parse("Aquarius 22, 12026")
|
|
104
|
+
d = GaianDate.parse("12026-03-22")
|
|
105
|
+
d = GaianDate.parse("3/22/12026")
|
|
106
|
+
|
|
107
|
+
# Formatting
|
|
108
|
+
d.format("MMMM d, yyyy GE") # "Aquarius 22, 12026 GE"
|
|
109
|
+
d.format("MMM* DDD") # "♒ 078"
|
|
110
|
+
d.format("yyyy-MM-dd") # "12026-03-22"
|
|
111
|
+
d.format("ddd") # "22nd"
|
|
112
|
+
|
|
113
|
+
# Leap year check
|
|
114
|
+
is_leap_year(12026) # True
|
|
115
|
+
is_leap_year(12025) # False
|
|
116
|
+
|
|
117
|
+
# Month info
|
|
118
|
+
from gaian_calendar import GaianMonth
|
|
119
|
+
m = GaianMonth.AQUARIUS
|
|
120
|
+
print(m.symbol) # "♒"
|
|
121
|
+
print(m.element) # "Air"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Similar Libraries
|
|
127
|
+
|
|
128
|
+
This library is modeled after:
|
|
129
|
+
- [`hijridate`](https://pypi.org/project/hijridate/) — Islamic Hijri calendar
|
|
130
|
+
- [`pyluach`](https://pypi.org/project/pyluach/) — Hebrew calendar
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
GaianCalendar.egg-info/PKG-INFO
|
|
5
|
+
GaianCalendar.egg-info/SOURCES.txt
|
|
6
|
+
GaianCalendar.egg-info/dependency_links.txt
|
|
7
|
+
GaianCalendar.egg-info/top_level.txt
|
|
8
|
+
gaian_calendar/__init__.py
|
|
9
|
+
gaian_calendar/_convert.py
|
|
10
|
+
gaian_calendar/_data.py
|
|
11
|
+
gaian_calendar/_format.py
|
|
12
|
+
gaian_calendar/date.py
|
|
13
|
+
gaian_calendar/month.py
|
|
14
|
+
gaian_calendar/weekday.py
|
|
15
|
+
tests/test_convert.py
|
|
16
|
+
tests/test_date.py
|
|
17
|
+
tests/test_format.py
|
|
18
|
+
tests/test_month_weekday.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gaian_calendar
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Immanuelle
|
|
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,136 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: GaianCalendar
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python library for the Gaian Calendar — a perpetual 13-month solar calendar based on ISO week-year arithmetic.
|
|
5
|
+
Author: Immanuelle
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Emma-Leonhart/GaianCalendar
|
|
8
|
+
Project-URL: Issues, https://github.com/Emma-Leonhart/GaianCalendar/issues
|
|
9
|
+
Keywords: calendar,gaian,date,alternative calendar,perpetual calendar,ISO week,zodiac
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Classifier: Topic :: Utilities
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# GaianCalendar
|
|
27
|
+
|
|
28
|
+
A pure-Python library for the **Gaian Calendar** — a perpetual 13-month solar calendar based on ISO week-year arithmetic.
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from gaian_calendar import GaianDate
|
|
32
|
+
|
|
33
|
+
today = GaianDate.today()
|
|
34
|
+
print(today) # e.g. "Aquarius 22, 12026 GE"
|
|
35
|
+
print(today.day_of_year) # e.g. 78
|
|
36
|
+
print(today.is_leap_year) # True
|
|
37
|
+
print(today.format("WWWW, MMMM d, yyyy GE")) # "Sunday, Aquarius 22, 12026 GE"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Status
|
|
43
|
+
|
|
44
|
+
**Pre-release — planning phase.** See [`planning/`](planning/) for design documents.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## What is the Gaian Calendar?
|
|
49
|
+
|
|
50
|
+
The Gaian Calendar is a perpetual reform calendar with these properties:
|
|
51
|
+
|
|
52
|
+
- **13 regular months** of exactly 28 days (4 weeks × 7 days), named after zodiac constellations
|
|
53
|
+
- **1 intercalary month** (Horus, 7 days) in leap years only — years with ISO week 53
|
|
54
|
+
- **Perpetual**: every calendar date always falls on the same weekday, every year
|
|
55
|
+
- **Year numbering**: Gaian year = ISO week-year + 10,000 (so 2026 CE = 12026 GE)
|
|
56
|
+
- **Zero weekday drift**: recurring events stay on the same day of week indefinitely
|
|
57
|
+
|
|
58
|
+
The 13 months in order: Sagittarius · Capricorn · Aquarius · Pisces · Aries · Taurus · Gemini · Cancer · Leo · Virgo · Libra · Scorpius · Ophiuchus · (Horus in leap years)
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install gaian-calendar
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
*(Not yet published — coming soon)*
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Usage
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from datetime import date, timedelta
|
|
76
|
+
from gaian_calendar import GaianDate, GaianMonth, is_leap_year
|
|
77
|
+
|
|
78
|
+
# Today
|
|
79
|
+
d = GaianDate.today()
|
|
80
|
+
|
|
81
|
+
# From Gregorian
|
|
82
|
+
d = GaianDate.from_gregorian(date(2026, 2, 22))
|
|
83
|
+
print(d) # "Aquarius 22, 12026 GE"
|
|
84
|
+
print(d.to_gregorian()) # 2026-02-22
|
|
85
|
+
|
|
86
|
+
# Properties
|
|
87
|
+
print(d.year) # 12026
|
|
88
|
+
print(d.month) # 3
|
|
89
|
+
print(d.day) # 22
|
|
90
|
+
print(d.month_name) # "Aquarius"
|
|
91
|
+
print(d.month_symbol) # "♒"
|
|
92
|
+
print(d.weekday_name) # "Sunday"
|
|
93
|
+
print(d.weekday_symbol) # "☉"
|
|
94
|
+
print(d.day_of_year) # 78
|
|
95
|
+
print(d.is_leap_year) # True
|
|
96
|
+
|
|
97
|
+
# Arithmetic
|
|
98
|
+
next_week = d + timedelta(weeks=1)
|
|
99
|
+
yesterday = d - timedelta(days=1)
|
|
100
|
+
delta = d - GaianDate(12026, 1, 1) # timedelta
|
|
101
|
+
|
|
102
|
+
# Parsing
|
|
103
|
+
d = GaianDate.parse("Aquarius 22, 12026")
|
|
104
|
+
d = GaianDate.parse("12026-03-22")
|
|
105
|
+
d = GaianDate.parse("3/22/12026")
|
|
106
|
+
|
|
107
|
+
# Formatting
|
|
108
|
+
d.format("MMMM d, yyyy GE") # "Aquarius 22, 12026 GE"
|
|
109
|
+
d.format("MMM* DDD") # "♒ 078"
|
|
110
|
+
d.format("yyyy-MM-dd") # "12026-03-22"
|
|
111
|
+
d.format("ddd") # "22nd"
|
|
112
|
+
|
|
113
|
+
# Leap year check
|
|
114
|
+
is_leap_year(12026) # True
|
|
115
|
+
is_leap_year(12025) # False
|
|
116
|
+
|
|
117
|
+
# Month info
|
|
118
|
+
from gaian_calendar import GaianMonth
|
|
119
|
+
m = GaianMonth.AQUARIUS
|
|
120
|
+
print(m.symbol) # "♒"
|
|
121
|
+
print(m.element) # "Air"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Similar Libraries
|
|
127
|
+
|
|
128
|
+
This library is modeled after:
|
|
129
|
+
- [`hijridate`](https://pypi.org/project/hijridate/) — Islamic Hijri calendar
|
|
130
|
+
- [`pyluach`](https://pypi.org/project/pyluach/) — Hebrew calendar
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# GaianCalendar
|
|
2
|
+
|
|
3
|
+
A pure-Python library for the **Gaian Calendar** — a perpetual 13-month solar calendar based on ISO week-year arithmetic.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from gaian_calendar import GaianDate
|
|
7
|
+
|
|
8
|
+
today = GaianDate.today()
|
|
9
|
+
print(today) # e.g. "Aquarius 22, 12026 GE"
|
|
10
|
+
print(today.day_of_year) # e.g. 78
|
|
11
|
+
print(today.is_leap_year) # True
|
|
12
|
+
print(today.format("WWWW, MMMM d, yyyy GE")) # "Sunday, Aquarius 22, 12026 GE"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Status
|
|
18
|
+
|
|
19
|
+
**Pre-release — planning phase.** See [`planning/`](planning/) for design documents.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## What is the Gaian Calendar?
|
|
24
|
+
|
|
25
|
+
The Gaian Calendar is a perpetual reform calendar with these properties:
|
|
26
|
+
|
|
27
|
+
- **13 regular months** of exactly 28 days (4 weeks × 7 days), named after zodiac constellations
|
|
28
|
+
- **1 intercalary month** (Horus, 7 days) in leap years only — years with ISO week 53
|
|
29
|
+
- **Perpetual**: every calendar date always falls on the same weekday, every year
|
|
30
|
+
- **Year numbering**: Gaian year = ISO week-year + 10,000 (so 2026 CE = 12026 GE)
|
|
31
|
+
- **Zero weekday drift**: recurring events stay on the same day of week indefinitely
|
|
32
|
+
|
|
33
|
+
The 13 months in order: Sagittarius · Capricorn · Aquarius · Pisces · Aries · Taurus · Gemini · Cancer · Leo · Virgo · Libra · Scorpius · Ophiuchus · (Horus in leap years)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install gaian-calendar
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
*(Not yet published — coming soon)*
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from datetime import date, timedelta
|
|
51
|
+
from gaian_calendar import GaianDate, GaianMonth, is_leap_year
|
|
52
|
+
|
|
53
|
+
# Today
|
|
54
|
+
d = GaianDate.today()
|
|
55
|
+
|
|
56
|
+
# From Gregorian
|
|
57
|
+
d = GaianDate.from_gregorian(date(2026, 2, 22))
|
|
58
|
+
print(d) # "Aquarius 22, 12026 GE"
|
|
59
|
+
print(d.to_gregorian()) # 2026-02-22
|
|
60
|
+
|
|
61
|
+
# Properties
|
|
62
|
+
print(d.year) # 12026
|
|
63
|
+
print(d.month) # 3
|
|
64
|
+
print(d.day) # 22
|
|
65
|
+
print(d.month_name) # "Aquarius"
|
|
66
|
+
print(d.month_symbol) # "♒"
|
|
67
|
+
print(d.weekday_name) # "Sunday"
|
|
68
|
+
print(d.weekday_symbol) # "☉"
|
|
69
|
+
print(d.day_of_year) # 78
|
|
70
|
+
print(d.is_leap_year) # True
|
|
71
|
+
|
|
72
|
+
# Arithmetic
|
|
73
|
+
next_week = d + timedelta(weeks=1)
|
|
74
|
+
yesterday = d - timedelta(days=1)
|
|
75
|
+
delta = d - GaianDate(12026, 1, 1) # timedelta
|
|
76
|
+
|
|
77
|
+
# Parsing
|
|
78
|
+
d = GaianDate.parse("Aquarius 22, 12026")
|
|
79
|
+
d = GaianDate.parse("12026-03-22")
|
|
80
|
+
d = GaianDate.parse("3/22/12026")
|
|
81
|
+
|
|
82
|
+
# Formatting
|
|
83
|
+
d.format("MMMM d, yyyy GE") # "Aquarius 22, 12026 GE"
|
|
84
|
+
d.format("MMM* DDD") # "♒ 078"
|
|
85
|
+
d.format("yyyy-MM-dd") # "12026-03-22"
|
|
86
|
+
d.format("ddd") # "22nd"
|
|
87
|
+
|
|
88
|
+
# Leap year check
|
|
89
|
+
is_leap_year(12026) # True
|
|
90
|
+
is_leap_year(12025) # False
|
|
91
|
+
|
|
92
|
+
# Month info
|
|
93
|
+
from gaian_calendar import GaianMonth
|
|
94
|
+
m = GaianMonth.AQUARIUS
|
|
95
|
+
print(m.symbol) # "♒"
|
|
96
|
+
print(m.element) # "Air"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Similar Libraries
|
|
102
|
+
|
|
103
|
+
This library is modeled after:
|
|
104
|
+
- [`hijridate`](https://pypi.org/project/hijridate/) — Islamic Hijri calendar
|
|
105
|
+
- [`pyluach`](https://pypi.org/project/pyluach/) — Hebrew calendar
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
MIT
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
gaian_calendar — Python library for the Gaian Calendar.
|
|
3
|
+
|
|
4
|
+
A perpetual 13-month solar calendar based on ISO week-year arithmetic.
|
|
5
|
+
Gaian year = ISO week-year + 10,000.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
|
|
10
|
+
from .date import GaianDate
|
|
11
|
+
from .month import GaianMonth
|
|
12
|
+
from .weekday import GaianWeekday
|
|
13
|
+
from ._convert import is_leap_year
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"GaianDate",
|
|
17
|
+
"GaianMonth",
|
|
18
|
+
"GaianWeekday",
|
|
19
|
+
"is_leap_year",
|
|
20
|
+
"__version__",
|
|
21
|
+
]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core Gaian Calendar arithmetic: conversions, validation, derived properties.
|
|
3
|
+
All functions work with plain ints and datetime.date — no class dependencies.
|
|
4
|
+
"""
|
|
5
|
+
from datetime import date
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# ---------------------------------------------------------------------------
|
|
9
|
+
# Leap year
|
|
10
|
+
# ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
def _iso_weeks_in_year(iso_year: int) -> int:
|
|
13
|
+
"""Return 52 or 53: the number of ISO weeks in the given ISO week-year."""
|
|
14
|
+
# Dec 28 is always in the last real ISO week of the year (never week 1 of next)
|
|
15
|
+
return date(iso_year, 12, 28).isocalendar()[1]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def is_leap_year(gaian_year: int) -> bool:
|
|
19
|
+
"""Return True if the Gaian year has a Horus month (53 ISO weeks)."""
|
|
20
|
+
return _iso_weeks_in_year(gaian_year - 10_000) == 53
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
# Validation
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
def validate_date(year: int, month: int, day: int) -> None:
|
|
28
|
+
"""Raise ValueError if (year, month, day) is not a valid Gaian date."""
|
|
29
|
+
if year < 10_001 or year > 19_999:
|
|
30
|
+
raise ValueError(f"Year {year} out of supported range (10001–19999)")
|
|
31
|
+
leap = is_leap_year(year)
|
|
32
|
+
max_month = 14 if leap else 13
|
|
33
|
+
if month < 1 or month > max_month:
|
|
34
|
+
if month == 14 and not leap:
|
|
35
|
+
raise ValueError(
|
|
36
|
+
f"Month 14 (Horus) only exists in leap years; {year} is not a leap year"
|
|
37
|
+
)
|
|
38
|
+
raise ValueError(f"Month {month} out of range (1–{max_month}) for year {year}")
|
|
39
|
+
max_day = 7 if month == 14 else 28
|
|
40
|
+
if day < 1 or day > max_day:
|
|
41
|
+
raise ValueError(
|
|
42
|
+
f"Day {day} out of range for month {month} "
|
|
43
|
+
f"({'Horus, max 7' if month == 14 else 'max 28'})"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
# Conversion: Gregorian ↔ Gaian
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
def gregorian_to_gaian(d: date) -> tuple[int, int, int]:
|
|
52
|
+
"""Convert a Gregorian date to a (gaian_year, month, day) tuple."""
|
|
53
|
+
iso_year, iso_week, iso_weekday = d.isocalendar()
|
|
54
|
+
month = (iso_week - 1) // 4 + 1 # 1–14
|
|
55
|
+
week_in_month = (iso_week - 1) % 4 # 0–3
|
|
56
|
+
day = week_in_month * 7 + iso_weekday # 1–28 (or 1–7 for Horus)
|
|
57
|
+
gaian_year = iso_year + 10_000
|
|
58
|
+
return gaian_year, month, day
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def gaian_to_gregorian(year: int, month: int, day: int) -> date:
|
|
62
|
+
"""Convert a Gaian date to a Gregorian datetime.date."""
|
|
63
|
+
iso_year = year - 10_000
|
|
64
|
+
iso_week = (month - 1) * 4 + (day - 1) // 7 + 1 # 1–53
|
|
65
|
+
iso_weekday = (day - 1) % 7 + 1 # 1–7
|
|
66
|
+
return date.fromisocalendar(iso_year, iso_week, iso_weekday)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# ---------------------------------------------------------------------------
|
|
70
|
+
# Derived properties
|
|
71
|
+
# ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
def day_of_year(month: int, day: int) -> int:
|
|
74
|
+
"""Return the day-of-year (1–364, or up to 371 for Horus days)."""
|
|
75
|
+
if month <= 13:
|
|
76
|
+
return (month - 1) * 28 + day
|
|
77
|
+
else: # Horus (month 14)
|
|
78
|
+
return 364 + day
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def day_of_week(day: int) -> int:
|
|
82
|
+
"""Return ISO weekday (1=Monday … 7=Sunday). Same for all years — perpetual."""
|
|
83
|
+
return (day - 1) % 7 + 1
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Static data: month and weekday metadata for the Gaian Calendar.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
MONTHS: list[dict] = [
|
|
6
|
+
{"number": 1, "name": "Sagittarius", "abbrev": "Sag", "symbol": "♐", "element": "Fire", "iso_weeks": (1, 4)},
|
|
7
|
+
{"number": 2, "name": "Capricorn", "abbrev": "Cap", "symbol": "♑", "element": "Earth", "iso_weeks": (5, 8)},
|
|
8
|
+
{"number": 3, "name": "Aquarius", "abbrev": "Aqu", "symbol": "♒", "element": "Air", "iso_weeks": (9, 12)},
|
|
9
|
+
{"number": 4, "name": "Pisces", "abbrev": "Pis", "symbol": "♓", "element": "Water", "iso_weeks": (13, 16)},
|
|
10
|
+
{"number": 5, "name": "Aries", "abbrev": "Ari", "symbol": "♈", "element": "Fire", "iso_weeks": (17, 20)},
|
|
11
|
+
{"number": 6, "name": "Taurus", "abbrev": "Tau", "symbol": "♉", "element": "Earth", "iso_weeks": (21, 24)},
|
|
12
|
+
{"number": 7, "name": "Gemini", "abbrev": "Gem", "symbol": "♊", "element": "Air", "iso_weeks": (25, 28)},
|
|
13
|
+
{"number": 8, "name": "Cancer", "abbrev": "Can", "symbol": "♋", "element": "Water", "iso_weeks": (29, 32)},
|
|
14
|
+
{"number": 9, "name": "Leo", "abbrev": "Leo", "symbol": "♌", "element": "Fire", "iso_weeks": (33, 36)},
|
|
15
|
+
{"number": 10, "name": "Virgo", "abbrev": "Vir", "symbol": "♍", "element": "Earth", "iso_weeks": (37, 40)},
|
|
16
|
+
{"number": 11, "name": "Libra", "abbrev": "Lib", "symbol": "♎", "element": "Air", "iso_weeks": (41, 44)},
|
|
17
|
+
{"number": 12, "name": "Scorpius", "abbrev": "Sco", "symbol": "♏", "element": "Water", "iso_weeks": (45, 48)},
|
|
18
|
+
{"number": 13, "name": "Ophiuchus", "abbrev": "Oph", "symbol": "⛎", "element": "Healing", "iso_weeks": (49, 52)},
|
|
19
|
+
{"number": 14, "name": "Horus", "abbrev": "Hor", "symbol": "𓅃", "element": None, "iso_weeks": (53, 53)},
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
WEEKDAYS: list[dict] = [
|
|
23
|
+
{"number": 1, "name": "Monday", "abbrev": "Mon", "symbol": "☽", "planet": "Moon"},
|
|
24
|
+
{"number": 2, "name": "Tuesday", "abbrev": "Tue", "symbol": "♂", "planet": "Mars"},
|
|
25
|
+
{"number": 3, "name": "Wednesday", "abbrev": "Wed", "symbol": "☿", "planet": "Mercury"},
|
|
26
|
+
{"number": 4, "name": "Thursday", "abbrev": "Thu", "symbol": "♃", "planet": "Jupiter"},
|
|
27
|
+
{"number": 5, "name": "Friday", "abbrev": "Fri", "symbol": "♀", "planet": "Venus"},
|
|
28
|
+
{"number": 6, "name": "Saturday", "abbrev": "Sat", "symbol": "♄", "planet": "Saturn"},
|
|
29
|
+
{"number": 7, "name": "Sunday", "abbrev": "Sun", "symbol": "☉", "planet": "Sun"},
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
# Ordinal suffixes for days 1–28
|
|
33
|
+
_ORDINAL_SUFFIXES = {1: "st", 2: "nd", 3: "rd"}
|
|
34
|
+
|
|
35
|
+
def ordinal(n: int) -> str:
|
|
36
|
+
"""Return ordinal string: 1 → '1st', 2 → '2nd', 15 → '15th'."""
|
|
37
|
+
suffix = _ORDINAL_SUFFIXES.get(n % 10, "th")
|
|
38
|
+
# Special case: 11th, 12th, 13th (not 11st, 12nd, 13rd)
|
|
39
|
+
if 11 <= n % 100 <= 13:
|
|
40
|
+
suffix = "th"
|
|
41
|
+
return f"{n}{suffix}"
|
|
42
|
+
|
|
43
|
+
_NUMBER_WORDS = [
|
|
44
|
+
"", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh",
|
|
45
|
+
"Eighth", "Ninth", "Tenth", "Eleventh", "Twelfth", "Thirteenth",
|
|
46
|
+
"Fourteenth", "Fifteenth", "Sixteenth", "Seventeenth", "Eighteenth",
|
|
47
|
+
"Nineteenth", "Twentieth", "Twenty-first", "Twenty-second", "Twenty-third",
|
|
48
|
+
"Twenty-fourth", "Twenty-fifth", "Twenty-sixth", "Twenty-seventh", "Twenty-eighth",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
def number_word(n: int) -> str:
|
|
52
|
+
"""Return word form: 1 → 'First', 15 → 'Fifteenth'."""
|
|
53
|
+
if 1 <= n <= 28:
|
|
54
|
+
return _NUMBER_WORDS[n]
|
|
55
|
+
raise ValueError(f"number_word only supports 1–28, got {n}")
|
|
56
|
+
|
|
57
|
+
# Fast lookup maps
|
|
58
|
+
_MONTH_BY_NUMBER: dict[int, dict] = {m["number"]: m for m in MONTHS}
|
|
59
|
+
_MONTH_BY_NAME: dict[str, dict] = {}
|
|
60
|
+
for _m in MONTHS:
|
|
61
|
+
_MONTH_BY_NAME[_m["name"].lower()] = _m
|
|
62
|
+
_MONTH_BY_NAME[_m["abbrev"].lower()] = _m
|
|
63
|
+
|
|
64
|
+
_WEEKDAY_BY_NUMBER: dict[int, dict] = {w["number"]: w for w in WEEKDAYS}
|
|
65
|
+
|
|
66
|
+
def get_month(number: int) -> dict:
|
|
67
|
+
if number not in _MONTH_BY_NUMBER:
|
|
68
|
+
raise ValueError(f"Invalid month number: {number}")
|
|
69
|
+
return _MONTH_BY_NUMBER[number]
|
|
70
|
+
|
|
71
|
+
def get_month_by_name(name: str) -> dict:
|
|
72
|
+
key = name.lower().rstrip(".")
|
|
73
|
+
if key not in _MONTH_BY_NAME:
|
|
74
|
+
raise ValueError(f"Unknown month name: {name!r}")
|
|
75
|
+
return _MONTH_BY_NAME[key]
|
|
76
|
+
|
|
77
|
+
def get_weekday(number: int) -> dict:
|
|
78
|
+
if number not in _WEEKDAY_BY_NUMBER:
|
|
79
|
+
raise ValueError(f"Invalid weekday number: {number}")
|
|
80
|
+
return _WEEKDAY_BY_NUMBER[number]
|