Skip to main content

A modern Python library for multi-calendar date conversions (Jalali, Hijri, Gregorian)

Project description

Waxt

PyPI version Python versions License Code style: Black Typed

Waxt is a dependency-free Python library for working with Gregorian, Jalali (Persian/Solar), and civil Hijri (Islamic/tabular) dates through one immutable, datetime-like API.

from waxt import DateTime

jalali = DateTime(1404, 3, 25, 14, 30, calendar="jalali")

jalali.to_datetime()                 # datetime.datetime(2025, 6, 15, 14, 30)
jalali.to_calendar("hijri")          # same value, displayed as a Hijri date
jalali.strftime("%d %B %Y, %H:%M")  # "25 Khordad 1404, 14:30"
  • Familiar API modeled on datetime.datetime
  • Calendar conversion without changing the represented value
  • Calendar-aware parsing, formatting, and month arithmetic
  • Standard tzinfo and zoneinfo interoperability
  • Full type hints and no third-party runtime dependencies

Installation

python -m pip install waxt

Waxt requires Python 3.10 or newer.

Quick start

Create and convert dates

The calendar argument controls how the constructor interprets year, month, and day. It accepts "gregorian" (the default), "jalali", or "hijri".

from waxt import DateTime

jalali = DateTime(1404, 3, 25, 14, 30, calendar="jalali")
gregorian = jalali.to_calendar("gregorian")
hijri = jalali.to_calendar("hijri")

(jalali.year, jalali.month, jalali.day)        # (1404, 3, 25)
(gregorian.year, gregorian.month, gregorian.day)  # (2025, 6, 15)
jalali == gregorian == hijri                   # True

to_calendar() changes the calendar representation only. It does not change the underlying wall time, timezone, or represented instant.

Interoperate with the standard library

from datetime import datetime
from waxt import DateTime

standard = datetime(2025, 6, 15, 14, 30)
jalali = DateTime.from_datetime(standard, calendar="jalali")

jalali.isoformat()   # "1404-03-25T14:30:00"
jalali.to_datetime() # datetime.datetime(2025, 6, 15, 14, 30)

to_datetime() always returns the equivalent Gregorian datetime.datetime. Waxt values can also be compared with standard-library datetime values when Python's usual naive/aware comparison rules permit it.

Get the current date and time

from datetime import timezone
from zoneinfo import ZoneInfo
from waxt import DateTime

tehran_now = DateTime.now(ZoneInfo("Asia/Tehran"), calendar="jalali")
utc_now = DateTime.now(timezone.utc, calendar="gregorian")
today = DateTime.today(calendar="hijri")

Parse and format

from waxt import DateTime

value = DateTime.strptime(
    "1404-03-25 14:30 +0330",
    "%Y-%m-%d %H:%M %z",
    calendar="jalali",
)

value.strftime("%Y/%m/%d %H:%M") # "1404/03/25 14:30"
value.strftime("%d %B %Y")       # "25 Khordad 1404"
value.isoformat(timespec="minutes") # "1404-03-25T14:30+03:30"

For Jalali and Hijri values, calendar-aware formatting includes %Y, %y, %m, %d, %j, %b, and %B. Other directives, including time and timezone directives, follow the standard library's strftime() behavior.

Perform date arithmetic

from datetime import timedelta
from waxt import DateTime

value = DateTime(1404, 6, 31, calendar="jalali")

value + timedelta(days=1) # 1404-07-01
value.add_days(1)         # 1404-07-01
value.add_months(1)       # 1404-07-30 (clamped to the target month)

timedelta arithmetic operates on the underlying Gregorian datetime and keeps the selected calendar for the result. add_months() advances calendar months and clamps the day when the target month is shorter.

Work with timezones

Waxt accepts any standard tzinfo implementation, including zoneinfo.ZoneInfo. Its country-based tzinfo() helper also uses IANA rules, including daylight-saving and historical changes.

from datetime import timezone
from zoneinfo import ZoneInfo
from waxt import DateTime

utc_value = DateTime(2025, 6, 15, 10, tzinfo=timezone.utc)
tehran_value = utc_value.astimezone(ZoneInfo("Asia/Tehran"))

tehran_value.to_datetime().isoformat() # "2025-06-15T13:30:00+03:30"

Waxt also bundles basic metadata for ISO 3166-1 alpha-2 country codes:

from waxt import country, tzinfo

iran = country("IR")
iran.name       # "Iran, Islamic Republic of"
iran.timezone   # "Asia/Tehran"
iran.calendar   # "persian"
iran.rtl        # True

tehran = tzinfo("IR")
tehran.utcoffset(None) # datetime.timedelta(seconds=12600)

The object returned by Waxt's tzinfo() helper applies daylight-saving transitions and historical offset changes from the representative IANA timezone listed for the country. For countries spanning multiple timezones, use ZoneInfo directly to select a more specific zone.

Important calendar semantics

Waxt stores every value internally as a Gregorian datetime, while the calendar property controls the exposed date fields and calendar-aware output. As a result:

  • year, month, day, isoformat(), and calendar-aware strftime() output use the selected calendar.
  • to_datetime() and date() return standard-library Gregorian values.
  • POSIX timestamps, ordinals, ISO week dates, timezone behavior, and comparisons retain standard Python semantics.
  • Hijri conversion uses the arithmetic civil/tabular calendar. It may differ from observational, regional, or Umm al-Qura calendars by one or more days.

API overview

DateTime is available from waxt and waxt.datetime. A lowercase datetime alias is also available from waxt.datetime.

API Purpose
DateTime(...) Construct a value using Gregorian, Jalali, or Hijri fields
now(), today() Create the current local value in a selected calendar
from_datetime(), to_datetime() Convert to and from datetime.datetime
fromisoformat(), strptime() Parse calendar date fields
to_calendar() Return the same value in another calendar representation
isoformat(), strftime() Format using the selected calendar
astimezone() Convert an aware value to another timezone
add_days(), add_months() Apply calendar-preserving arithmetic
replace() Return a value with selected fields changed
country() Look up bundled country metadata
tzinfo() Return a country timezone with IANA transition rules

Calendar constants are available as DateTime.CALENDAR_GREGORIAN, DateTime.CALENDAR_JALALI, and DateTime.CALENDAR_HIJRI.

Development

Clone the repository and install the development and test dependencies:

git clone https://github.com/kak-smko/waxt.git
cd waxt
python -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev,test]"

Run the test suite:

pytest

Run formatting, linting, and type checks:

black --check waxt tests
isort --check-only waxt tests
flake8 waxt tests
mypy waxt

Contributing

Bug reports and pull requests are welcome on GitHub. When changing behavior, include or update tests that cover the affected calendar and boundary cases.

License

Waxt is distributed under the BSD 3-Clause License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

waxt-0.2.0.tar.gz (49.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

waxt-0.2.0-py3-none-any.whl (19.5 kB view details)

Uploaded Python 3

File details

Details for the file waxt-0.2.0.tar.gz.

File metadata

  • Download URL: waxt-0.2.0.tar.gz
  • Upload date:
  • Size: 49.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for waxt-0.2.0.tar.gz
Algorithm Hash digest
SHA256 e82294f4c40e1d98bce1aa515d307f2bdb3dfdefc08c17974cacfff9dd4a566d
MD5 e67ca5ed6b97920c0bdb9db53c0574d1
BLAKE2b-256 f8c93a523eda96c910aa671a41569236ab7566a180b5db7719306172dd149856

See more details on using hashes here.

File details

Details for the file waxt-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: waxt-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 19.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for waxt-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 233185bdf2c559e8c0175e871c931030a141f52bb1fba7fec3e3bc1e69795721
MD5 22b4c8134c190263824187dc2a2047aa
BLAKE2b-256 fd976b0d274c6c654e29711a66ed4466370e804ab9bb7f825cfb8b329fa7596b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page