Odia Panji
A pure-Python library for converting between Gregorian dates and the traditional Odia calendar: Utkalabda, Anka, tithi, solar month and Shakabda.
Sunia, tithis and sankrantis are computed from the positions of the Sun and Moon, not read from hard-coded tables, so the code works for any year in its supported range. There are no external dependencies.
Scope: this is a date-conversion library, not a full Panchanga/Panji. It does not cover Nakshatra, Yoga, Karana, Muhurta or festival rules.
Installation
pip install odia-panji
Until the first PyPI release, install straight from GitHub:
pip install git+https://github.com/srinibashsamal/odia-panji.git
Requires Python 3.9 or later.
Quick examples
Dates are written dd-mm-yyyy (/ and . separators also work), or passed as
datetime.date / datetime.datetime.
from odia_panji import english_to_odia, format_odia_date
print(format_odia_date("07-10-2026"))
# 07-10-2026 -> 1434 Utkalabda | 71 Anka (Divyasingha Deva IV) | Acce: 56 |
# Aswina Krushna Dwadasi | 21 Kanya | Sunia: 23-09-2026 | Shakabda: 1948
result = english_to_odia("07-10-2026") # same as convert(); returns a dict
result["utkalabda"] # 1434
result["anka"] # 71
result["tithi"] # 'Dwadasi'
Individual values:
from odia_panji import (
anka_year, lunar_date, odia_solar_date, shaka_year, sunia_date, utkalabda_year,
)
utkalabda_year("23-09-2026") # 1434
anka_year("23-09-2026") # 71
shaka_year("23-09-2026") # 1948
sunia_date(2026) # datetime.date(2026, 9, 23)
lunar_date("07-10-2026")
# LunarDate(month='Aswina', paksha='Krushna', tithi='Dwadasi', adhika=False)
odia_solar_date("07-10-2026")
# SolarDate(rashi='Kanya', month='Kanya', day=21, lunar_equivalent='Aswina')
Full output of convert()
{
"english_date": "23-09-2026",
"utkalabda": 1434,
"utkalabda_odia": "୧୪୩୪",
"gajapati": "Divyasingha Deva IV",
"gajapati_accession": "07-07-1970",
"is_accession_day": False,
"anka": 71,
"anka_odia": "୭୧",
"anka_index": 57,
"years_since_accession": 56,
"odia_year_start": "23-09-2026",
"odia_year_end": "11-09-2027",
"sunia_of_year": "23-09-2026",
"lunar_month": "Bhadraba",
"adhika": False,
"paksha": "Shukla",
"tithi": "Dwadasi",
"solar_rashi": "Kanya",
"solar_month": "Kanya",
"solar_day": 7,
"solar_month_lunar_equivalent": "Aswina",
"shakabda": 1948,
}
Reverse conversion
Utkalabda + Odia date → English
from odia_panji import english_date_from_lunar, english_date_from_solar
english_date_from_solar(1434, "Kanya", 21) # date(2026, 10, 7)
english_date_from_lunar(1434, "Aswina", "Krushna", "Dwadasi") # date(2026, 10, 7)
english_date_from_lunar(1433, "Jyestha", "Shukla", "Dasami", adhika=True)
Anka + Odia date → English
An Anka is a regnal year, so it is combined with a solar or lunar date:
from odia_panji import (
anka_year_span, english_date_from_anka_lunar, english_date_from_anka_solar,
)
english_date_from_anka_solar(71, "Kanya", 21) # date(2026, 10, 7)
english_date_from_anka_lunar(71, "Bhadraba", "Shukla", "Dwadasi") # date(2026, 9, 23)
# An earlier reign: pass the Gajapati's name
english_date_from_anka_solar(25, "Karkata", 31, "Ramachandra Deba IV") # date(1947, 8, 15)
# First and last English date of an Anka year
anka_year_span(71) # (date(2026, 9, 23), date(2027, 9, 11))
Names are case-insensitive. The Anka count restarts with every Gajapati;
gajapati defaults to the current reign.
Errors
Every invalid input raises OdiaCalendarError (a subclass of ValueError).
Reverse conversions raise it when no date matches, or when several do; they
list the matching dates instead of guessing.
Command line
odia-panji # sample conversions in every direction
odia-panji 23-09-2026 07-10-2026 # your own dates
python -m odia_panji --version
More in the examples/ folder.
Supported calendars
| Calendar | What it is | Year starts on |
|---|---|---|
| Utkalabda (Utkaliya San) | Odia era year: CE − 592 / − 593 | Sunia (Bhadraba Shukla Dwadasi) |
| Shakabda | Shaka era year: CE − 78 / − 79 | Chaitra Shukla Pratipada |
| Solar month | Sidereal sign the Sun is in (Lahiri ayanamsa); day 1 = Sankranti day | 1 Mesha (13/14 April) |
| Lunar month | Purnimanta (full moon to full moon), with adhika months detected | — |
| Tithi | Lunar day in force at sunrise in Puri; a kshaya tithi goes to the day it begins | — |
| Anka | Gajapati regnal year; skips 1, numbers ending in 6, and ending in 0 except 10 | Sunia |
Solar months pair with lunar month names as almanacs print them: Mesha–Baisakha, Vrusha–Jyestha, Mithuna–Asadha, Karkata–Srabana, Simha–Bhadraba, Kanya–Aswina, Tula–Kartika, Bruschika–Margasira, Dhanu–Pausa, Makara–Magha, Kumbha–Phalguna, Meena–Chaitra.
Accession days
At Puri the heir accedes on the day the reigning Gajapati dies, so a handover
date belongs to two reigns. By default it is credited to the incoming Gajapati;
pass inclusive_end=True to credit the outgoing one:
anka_year("07-07-1970") # 2 (Divyasingha Deva IV)
anka_year("07-07-1970", inclusive_end=True) # 15 (Birakisore Deva III)
Limitations
Astronomical assumptions
- Sun: Meeus, Astronomical Algorithms, ch. 25. Moon: Meeus ch. 47 (60 periodic terms).
- Ayanamsa: Lahiri / Chitrapaksha, linear approximation.
- Sunrise: NOAA algorithm (~1 minute accuracy), observer at Puri, times in IST.
- Civil calendar: proleptic Gregorian at every epoch.
Historical date support range
- Utkalabda, Shakabda, solar and lunar dates: 1 CE to 6782 CE.
- Anka: only from 14-02-1926 (earliest reign in
GAJAPATI_REIGNS). Add earlier Gajapatis at the start of that list to go further back.
Accuracy expectations
-
Trusted range: roughly 1800–2100. Error grows outside it because the ayanamsa is linear and the series are truncated.
-
Checked against published dates: Sunia 2011 and 2022–2026; Chaitra Shukla Pratipada 2019–2027. Not every year has been compared with a printed panji.
-
Printed panjis can differ by a day near a boundary (different constants, ayanamsa, sunrise definition or location). Pin such dates rather than editing the astronomy:
from datetime import date from odia_panji import SUNIA_OVERRIDES SUNIA_OVERRIDES[2031] = date(2031, 9, 4)
Known gaps
- A kshaya tithi (no sunrise inside it) cannot yet be found by the lunar reverse functions, e.g. Chaitra Shukla Pratipada 1433 = 19-03-2026.
- In a long (adhika) Odia year, a few Simha/Kanya solar dates near Sunia occur twice, e.g. 19 Simha in Utkalabda 1433 is both 04-09-2025 and 04-09-2026. The reverse functions report both instead of picking one.
- Kshaya months are not modelled.
Project structure
odia-panji/
├── odia_panji/
│ ├── __init__.py # public API and __version__
│ ├── odia_calendar.py # Gregorian -> Odia
│ ├── odia_to_english.py # Utkalabda + solar/lunar date -> Gregorian
│ ├── anka_to_english.py # Anka + solar/lunar date -> Gregorian
│ ├── anka.py # Anka numbering rules
│ ├── astronomy.py # Sun, Moon, ayanamsa, sunrise, sankranti
│ ├── lunation.py # tithis, new moons, lunar-month naming
│ ├── julian_day.py # Gregorian <-> Julian Day
│ ├── math_helpers.py # angle normalisation, bisection
│ ├── constants.py # constants, name tables, Gajapati reigns
│ ├── calendar_types.py # result types and OdiaCalendarError
│ ├── validation.py # parsing and input checks
│ ├── cli.py # `odia-panji` command
│ └── py.typed
├── tests/
├── examples/
├── .github/workflows/python.yml
├── CHANGELOG.md
├── LICENSE
├── pyproject.toml
└── README.md
Development
git clone https://github.com/srinibashsamal/odia-panji.git
cd odia-panji
pip install -e ".[dev]"
pytest # unit tests + every docstring example
ruff check . && ruff format --check .
mypy
Boundary dates matter most here, because dates come from astronomical events: Sunia, Sankranti, accession days and adhika months. Please add a test for any date you correct.
Versioning
The project follows Semantic Versioning. The version
lives in one place, odia_panji/__init__.py, and pyproject.toml reads it from
there. Changes are listed in CHANGELOG.md.
Design principles
- Calculation over lookup tables
- Conversion in both directions
- Calendar systems kept separate
- No external dependencies
- Explicit, documented assumptions
- Simple, reusable functions
References
- Odia calendar, Wikipedia
- Anka year, Wikipedia
- Jean Meeus, Astronomical Algorithms, 2nd ed.
- NOAA Solar Calculator
License
Released under the MIT License.
Author
Srinibash Samal — github.com/srinibashsamal
Metadata
Release files for odia-panji 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| odia_panji-0.2.0.tar.gz | 47.4 kB | With provenance | Details for odia_panji-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| odia_panji-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for odia_panji-0.2.0-py3-none-any.whl |
Total release size: 93.8 kB
Release files / odia_panji-0.2.0.tar.gz
| Download URL | odia_panji-0.2.0.tar.gz |
|---|---|
| Size | 47.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cec10740922e5d11e60781975fad0c1aed446c3ba43be9603ddc8cd750ed4cc6
|
|
BLAKE2b-256 checksum How to use checksums |
73762e08c49d4c3e6d355d743e5ba953239e5b8dc589412df389c47fa10e3ee3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logRelease files / odia_panji-0.2.0-py3-none-any.whl
| Download URL | odia_panji-0.2.0-py3-none-any.whl |
|---|---|
| Size | 46.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
26c44759ffebdda9a38a22828d8c8bf54ace0a9277de1813b0eb5be5aeedded3
|
|
BLAKE2b-256 checksum How to use checksums |
e1ec1675ac9758e9440f0c57b504ac370a07aa990cccede2055128d73955f491
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logSecurity for odia-panji 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for odia_panji-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.