Waxt
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
tzinfoandzoneinfointeroperability - 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-awarestrftime()output use the selected calendar.to_datetime()anddate()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.
Metadata
Release files for waxt 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 | |
|---|---|---|---|
| waxt-0.2.0.tar.gz | 49.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| waxt-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 69.1 kB
Release files / waxt-0.2.0.tar.gz
| Download URL | waxt-0.2.0.tar.gz |
|---|---|
| Size | 49.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e82294f4c40e1d98bce1aa515d307f2bdb3dfdefc08c17974cacfff9dd4a566d
|
|
BLAKE2b-256 checksum How to use checksums |
f8c93a523eda96c910aa671a41569236ab7566a180b5db7719306172dd149856
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / waxt-0.2.0-py3-none-any.whl
| Download URL | waxt-0.2.0-py3-none-any.whl |
|---|---|
| Size | 19.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
233185bdf2c559e8c0175e871c931030a141f52bb1fba7fec3e3bc1e69795721
|
|
BLAKE2b-256 checksum How to use checksums |
fd976b0d274c6c654e29711a66ed4466370e804ab9bb7f825cfb8b329fa7596b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|