Kurdish solar calendar and conversions (Gregorian, Persian/Jalali, tabular Islamic) using the Python standard library only.
Project description
pyroj (Kurdish Calendar)
pyroj is the definitive Python library for the Kurdish solar calendar. It allows for highly accurate date conversions to and from Gregorian, Persian (Jalali), and Tabular Islamic dates.
Built exclusively under the Python standard library, it inherits natively from Python's own datetime ecosystem, serving as a dynamic, robust drop-in replacement for any application. Runtime dependencies: none.
- Python: 3.10+
- Dependency/tool workflow:
uv+pyproject.toml - Supported year range:
1..9999(aligned with Pythondatetime.date)
[Research Reference & Calculation Details]
For academics, developers, and researchers seeking the complete historical methodology, epoch derivations, and algorithmic calculations used to yield accurate Kurdish dates, please refer to the detailed Kurdish Date Calculation Research.
Quick Start
KurdishDate and KurdishDateTime inherit natively from python's built-in datetime.date and datetime.datetime. Any standard operation you perform on Python dates can be seamlessly performed on Kurdish dates!
uv pip install pyroj
# or
pip install pyroj
Documentation Index
Basic Creation and Conversions
from datetime import date
from pyroj.kurdish import KurdishDate
# 1. Start from a Gregorian Date
d = date(2026, 3, 23)
kd = KurdishDate.from_gregorian(d)
print(kd.year, kd.month, kd.day) # Output: 2726 1 3
# 2. Or initialize natively in Kurdish
kd_native = KurdishDate(2726, 1, 3)
# 3. Effortless Conversions to other systems
print(kd.to_gregorian()) # Output: 2026-03-23
print(kd.to_persian()) # Output: (1405, 1, 3)
print(kd.to_islamic()) # Output: (1447, 10, 4)
Advanced Date Mathematics
Because pyroj extends standard lib classes natively, you can rely on robust Python implementations for adding/subtracting ranges without worrying about leap years or skipped months!
from datetime import timedelta
from pyroj.kurdish import KurdishDateTime
# Adding 5 days over month boundaries calculates correctly
kd = KurdishDate(2726, 1, 30)
kd_new = kd + timedelta(days=5)
print(kd_new) # Output: 2726-02-04
# Full Time/Datetime wrappers exist
kdt = KurdishDateTime(2726, 1, 3, hour=15, minute=30, second=0)
kdt_shuffled = kdt - timedelta(hours=36)
print(kdt_shuffled) # Output: 2726-01-02 03:30:00
Beautiful Native Formatting (strftime)
Formatting strings out of the box matches Python's % standard exactly. Furthermore, pyroj natively maps out month and weekday translations depending on your selected locale.
Supported LocaleId dialects include KMR (Kurmanji / Kurdish-Latin script), CKB (Sorani / Kurdish-Arabic script), compatibility alias KU (maps to CKB), plus AR (Arabic), FA (Persian), TR (Turkish), and EN (English). Additional ISO aliases are resolved dynamically: sdh, lki, hac -> CKB, and zza (diq/kiu) -> KMR.
from pyroj.kurdish import KurdishDate
from pyroj.locales import LocaleId
kd = KurdishDate(2726, 1, 25)
# Standard English representation
print(kd.strftime("%A, %d %B %Y", locale=LocaleId.EN))
# Output: Tuesday, 25 Xakelêwe 2726
# Sorani execution (Arabic-script Kurdish)
print(kd.strftime("%A, %d %B %Y", locale=LocaleId.CKB))
# Output: سێشەممە, 25 خاکەلێوە 2726
# Kurmanji execution (Latin-script Kurdish)
print(kd.strftime("%A, %d %B %Y", locale=LocaleId.KMR))
# Output: Tuesday, 25 Xakelêwe 2726
# Persian representation
print(kd.strftime("%A, %d %B %Y", locale=LocaleId.FA))
# Output: سهشنبه, 25 خاکِلِیوَه 2726
# Available formats:
# %Y (4-digit Year), %y (2-digit Year)
# %B (Full Month), %b (Short Month), %m (2-digit Month), %-m (1-digit Month)
# %A (Full Weekday), %a (Short Weekday), %w (1-7 Index), %-w (1-7 Number)
# %d (2-digit Day), %-d (1-digit Day)
# %H:%M:%S etc. for KurdishDateTime.
String locale negotiation is also supported dynamically:
print(kd.strftime("%B %Y", locale="kmr")) # Kurmanji
print(kd.strftime("%B %Y", locale="ckb")) # Sorani
print(kd.strftime("%B %Y", locale="ku")) # Backward-compat alias -> CKB
You can also switch Kurdish month-name variants dynamically:
from pyroj import CalendarKind, format_calendar_date
print(format_calendar_date(kd, "%B", calendar=CalendarKind.KURDISH, locale="ckb", kurdish_variant="standard"))
print(format_calendar_date(kd, "%B", calendar=CalendarKind.KURDISH, locale="kmr", kurdish_variant="gelarêzan"))
For months that have multiple accepted names in a dialect table, pyroj preserves the canonical combined form from the source (for example, Nîsanê/Lîzan or نیسانە/لیزان) and returns it as-is when that variant is selected.
Complete Locale Tables and Detailed Mapping
For full per-dialect markdown tables (canonical names, aliases, month variants, and examples), see:
Detailed Formatting Examples
from datetime import date
from pyroj import CalendarKind, KurdishDate, LocaleId, format_calendar_date
kd = KurdishDate.from_gregorian(date(2026, 3, 24))
# Canonical locale IDs
print(format_calendar_date(kd, "%A, %d %B %Y", calendar=CalendarKind.KURDISH, locale=LocaleId.CKB))
print(format_calendar_date(kd, "%A, %d %B %Y", calendar=CalendarKind.KURDISH, locale=LocaleId.KMR))
# ISO-like dynamic aliases
print(format_calendar_date(kd, "%B %Y", calendar=CalendarKind.KURDISH, locale="sdh")) # -> CKB
print(format_calendar_date(kd, "%B %Y", calendar=CalendarKind.KURDISH, locale="lki")) # -> CKB
print(format_calendar_date(kd, "%B %Y", calendar=CalendarKind.KURDISH, locale="hac")) # -> CKB
print(format_calendar_date(kd, "%B %Y", calendar=CalendarKind.KURDISH, locale="zza")) # -> KMR
# Variant switching
print(
format_calendar_date(
kd,
"%B",
calendar=CalendarKind.KURDISH,
locale="ckb",
kurdish_variant="standard",
)
)
print(
format_calendar_date(
kd,
"%B",
calendar=CalendarKind.KURDISH,
locale="ckb",
kurdish_variant="gelarêzan",
)
)
All Dialect Variant Examples
from pyroj import CalendarKind, format_calendar_date
# Kurmanji (Wikipedia month-column set)
print(format_calendar_date(kd, "%B", calendar=CalendarKind.KURDISH, locale="kmr", kurdish_variant="kmr_wikipedia"))
# Kalhori / Southern Kurdish
print(format_calendar_date(kd, "%B", calendar=CalendarKind.KURDISH, locale="sdh", kurdish_variant="sdh_kelhuri"))
# Laki
print(format_calendar_date(kd, "%B", calendar=CalendarKind.KURDISH, locale="lki", kurdish_variant="lki_laki"))
# Hawrami / Gorani
print(format_calendar_date(kd, "%B", calendar=CalendarKind.KURDISH, locale="hac", kurdish_variant="hac_hawrami"))
# Zazaki
print(format_calendar_date(kd, "%B", calendar=CalendarKind.KURDISH, locale="zza", kurdish_variant="zza_zazaki"))
Multiple Names For One Month (Explicit Example)
from datetime import date
from pyroj import CalendarKind, KurdishDate, format_calendar_date
kd = KurdishDate.from_gregorian(date(2026, 3, 24)) # Kurdish month index 1
# Zazaki table in KMR family (Latin)
print(
format_calendar_date(
kd,
"%B",
calendar=CalendarKind.KURDISH,
locale="zza",
kurdish_variant="zza_zazaki",
)
)
# Output: Nîsanê/Lîzan
# Zazaki table in CKB family (Arabic script)
print(
format_calendar_date(
kd,
"%B",
calendar=CalendarKind.KURDISH,
locale="ckb",
kurdish_variant="zza_zazaki",
)
)
# Output: نیسانە/لیزان
Detailed DateTime Locale Example
from pyroj import KurdishDateTime
kdt = KurdishDateTime(2726, 1, 4, hour=15, minute=10, second=0)
# Locale-aware AM/PM
print(kdt.strftime("%Y-%m-%d %I:%M %p", locale="ckb"))
print(kdt.strftime("%Y-%m-%d %I:%M %p", locale="kmr"))
Historical Eras
Historically, different subsets of researchers align Year 1 of the Kurdish Calendar differently. pyroj accommodates this dynamically via the KurdishEra Enum:
- Median Empire Baseline
SOLAR_PERSIAN_OFFSET(Default): Evaluates the standard Kurdipedia offset whereKurdish Year = Jalali Year + 1321. (Anchored near 700 BC). - Fall of Nineveh Epoch
FALL_OF_NINEVEH: Tracks the exact 612 BC battle of Nineveh whereKurdish Year = Jalali Year + 1233.
from pyroj.kurdish import KurdishDate, KurdishEra
# Calculates the year offset depending on standard
kd_nineveh = KurdishDate(2638, 1, 3, era=KurdishEra.FALL_OF_NINEVEH)
print(kd_nineveh.to_gregorian()) # Extrapolates out correctly
Native Tooling (JDN Helpers & Timestamps)
You can convert any Gregorian or Julian representation efficiently down into int / float structs.
from pyroj._core.convert import gregorian_datetime_to_jdn, jdn_to_gregorian_datetime
from pyroj.kurdish import KurdishDate
# Extract absolute Julian Day Number directly
kd = KurdishDate(2726, 1, 1)
print(kd.to_jdn()) # Returns Absolute JDN float
# Restore from JDN
kd_restored = KurdishDate.from_jdn(2461122.5)
Development setup (uv + ruff)
uv sync --extra dev
uv run pytest -q
uv run ruff check pyroj tests
uv run mypy pyroj
Install the package locally for development:
uv pip install -e .
Continuous integration
GitHub Actions runs pytest, ruff, and mypy on Python 3.10–3.13 (see .github/workflows/ci.yml).
License
GPL.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyroj-1.2.4.tar.gz.
File metadata
- Download URL: pyroj-1.2.4.tar.gz
- Upload date:
- Size: 39.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e487e390f337ba8c627184f5e3e761ae2bb75b0285db7d907dac7fe59b969341
|
|
| MD5 |
632f58dd9944fe3b98d9865eb224fd32
|
|
| BLAKE2b-256 |
8cf5f5b8e13740cfcd13fcee152cc348b3c756c852edd9279add62947a51b17e
|
Provenance
The following attestation bundles were made for pyroj-1.2.4.tar.gz:
Publisher:
ci.yml on 0xdolan/pyroj
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyroj-1.2.4.tar.gz -
Subject digest:
e487e390f337ba8c627184f5e3e761ae2bb75b0285db7d907dac7fe59b969341 - Sigstore transparency entry: 1171693713
- Sigstore integration time:
-
Permalink:
0xdolan/pyroj@e0b3bd34d3a3d8d34820f6f8b2ab8a278fa8737e -
Branch / Tag:
refs/tags/v1.2.4 - Owner: https://github.com/0xdolan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@e0b3bd34d3a3d8d34820f6f8b2ab8a278fa8737e -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyroj-1.2.4-py3-none-any.whl.
File metadata
- Download URL: pyroj-1.2.4-py3-none-any.whl
- Upload date:
- Size: 36.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be327fbc14bfe37e26dcb820b307110718e6eabf5080a2deb1541f643d760191
|
|
| MD5 |
ab0d56d0c79cc5dd2625168a48dd6a6f
|
|
| BLAKE2b-256 |
f0da7a5e2c421475c8bd5cb80765a0173232791fc898cdc8af2cb65b51a8282f
|
Provenance
The following attestation bundles were made for pyroj-1.2.4-py3-none-any.whl:
Publisher:
ci.yml on 0xdolan/pyroj
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyroj-1.2.4-py3-none-any.whl -
Subject digest:
be327fbc14bfe37e26dcb820b307110718e6eabf5080a2deb1541f643d760191 - Sigstore transparency entry: 1171693719
- Sigstore integration time:
-
Permalink:
0xdolan/pyroj@e0b3bd34d3a3d8d34820f6f8b2ab8a278fa8737e -
Branch / Tag:
refs/tags/v1.2.4 - Owner: https://github.com/0xdolan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@e0b3bd34d3a3d8d34820f6f8b2ab8a278fa8737e -
Trigger Event:
push
-
Statement type: