Skip to main content

Odia Panji

CI PyPI version Python License: MIT GitHub Repo

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

Requires Python 3.9 or later. On Windows, use py -m pip install odia-panji if pip is not recognised.

Quick start

from odia_panji import convert, format_historical, to_english

# English -> Odia
convert("07-10-2026")["utkalabda"]  # 1434

# Odia -> English
to_english("Kanya", 21, utkalabda=1434)  # datetime.date(2026, 10, 7)

# Citation-style output
print(format_historical("07-10-2026"))
# 7 October 2026 = 1434 Utkalabda, Anka 71 of Divyasingha Deva IV
# Aswina Krushna Dwadasi | 21 Kanya | Shakabda 1948

English → Odia

Dates are written dd-mm-yyyy (/ and . separators also work), or passed as datetime.date / datetime.datetime.

from odia_panji import convert, 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 = convert("07-10-2026")  # returns a dict; english_to_odia() is an alias
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,
}

Citation-style output

For historical and archival work (land grants, palm-leaf records, Madala Panji style citations):

from odia_panji import format_historical

print(format_historical("07-10-2026"))
# 7 October 2026 = 1434 Utkalabda, Anka 71 of Divyasingha Deva IV
# Aswina Krushna Dwadasi | 21 Kanya | Shakabda 1948

print(format_historical("07-10-2026", style="compact"))
# 7 October 2026 / 1434 Utkalabda / Anka 71 of Divyasingha Deva IV / Aswina Krushna Dwadasi / 21 Kanya

print(format_historical("07-10-2026", style="lunar"))
# Aswina Krushna Dwadasi, 1434 Utkalabda (Anka 71 of Divyasingha Deva IV)
# = 7 October 2026

print(format_historical("15-08-1947", style="solar"))
# 31 Karkata, 1354 Utkalabda (Anka 25 of Ramachandra Deba IV)
# = 15 August 1947
style Layout
"full" (default) Two lines: English = Utkalabda, Anka; then lunar, solar and Shakabda
"compact" One line, separated by /
"lunar" Lunar date first, English date below
"solar" Solar date first, English date below

Pass include_english=False to leave out the Gregorian date. Dates in an intercalary month are prefixed with Adhika.

Odia → English

to_english() is the reverse of convert(): one function for every Odia date. Give the year as utkalabda= or anka=, and the date as a solar month + day or a lunar month + paksha= + tithi=.

from odia_panji import convert, to_english

to_english("Kanya", 21, utkalabda=1434)  # date(2026, 10, 7)
to_english(
    "Aswina", paksha="Krushna", tithi="Dwadasi", utkalabda=1434
)  # date(2026, 10, 7)
to_english("Kanya", 21, anka=71)  # date(2026, 10, 7)
to_english("Karkata", 31, anka=25, gajapati="Ramachandra Deba IV")  # date(1947, 8, 15)

# Intercalary (adhika) month
to_english("Jyestha", paksha="Shukla", tithi="Dasami", adhika=True, utkalabda=1433)

# Chain the two for the full Odia details of an Odia date
convert(to_english("Kanya", 21, anka=71))

Names are case-insensitive. The Anka count restarts with every Gajapati; gajapati defaults to the current reign.

Specialised functions (one per case)

to_english() calls these; they remain available for direct use.

from odia_panji import (
    anka_year_span,
    english_date_from_anka_lunar,
    english_date_from_anka_solar,
    english_date_from_lunar,
    english_date_from_solar,
)

# Utkalabda + Odia date
english_date_from_solar(1434, "Kanya", 21)  # date(2026, 10, 7)
english_date_from_lunar(1434, "Aswina", "Krushna", "Dwadasi")  # date(2026, 10, 7)

# Anka + Odia date
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)

# First and last English date of an Anka year
anka_year_span(71)  # (date(2026, 9, 23), date(2027, 9, 11))

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__
│   ├── __main__.py          # `python -m odia_panji`
│   ├── odia_calendar.py     # Gregorian -> Odia
│   ├── formatting.py        # format_odia_date, format_historical
│   ├── reverse.py           # to_english(): any Odia date -> Gregorian
│   ├── 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           # CI: tests, lint, build
│   └── publish.yml          # GitHub Release -> PyPI
├── 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 the CHANGELOG.

Design principles

  • Calculation over lookup tables
  • Conversion in both directions
  • Calendar systems kept separate
  • No external dependencies
  • Explicit, documented assumptions
  • Simple, reusable functions

References

License

Released under the MIT License.

Author

Srinibash Samal — github.com/srinibashsamal

Metadata

Release files for odia-panji 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for odia-panji 0.2.1
File Size Uploaded Provenance Actions
odia_panji-0.2.1.tar.gz 48.7 kB With provenance Details for odia_panji-0.2.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for odia-panji 0.2.1
File Interpreter ABI Platform Actions
odia_panji-0.2.1-py3-none-any.whl Python 3 none any Details for odia_panji-0.2.1-py3-none-any.whl

Total release size: 95.8 kB

Release files / odia_panji-0.2.1.tar.gz

Download URL odia_panji-0.2.1.tar.gz
Size 48.7 kB
Tags Source
SHA-256 checksum
How to use checksums
b9d224c5e11a2c1804979aa1edff648152bd343d28b91fefada28fb54969407f
BLAKE2b-256 checksum
How to use checksums
472719be228ed7146f2c059ef79c740da65513596665c03a30240accf4a80649
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 log

Release files / odia_panji-0.2.1-py3-none-any.whl

Download URL odia_panji-0.2.1-py3-none-any.whl
Size 47.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e8db84c5320ae16ae8f7d66095503fbbdb87dbc12b53e0f42ae8c908d4dd933a
BLAKE2b-256 checksum
How to use checksums
92b99031982e29cbd40bd5f53fcd1f3177207d87efdf90a58b5ad5f26ed27428
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 log

Security for odia-panji 0.2.1

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.

Select from 2 files

Attestation for odia_panji-0.2.1.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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page