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

A modern Python library for converting dates between Jalali (Persian/Solar), Hijri (Islamic/Lunar), and Gregorian calendars with full timezone support.


📦 Installation

pip install waxt

Requires Python 3.10+ and pytz.


✨ Features

Feature Description
🔄 Multi-Calendar Jalali (Persian/Solar), Hijri (Islamic/Lunar), Gregorian
🌍 Timezone Aware Full pytz timezone support — convert, localize, compare
📅 Date Arithmetic Add days/months with calendar-aware month lengths and leap years
🎨 Flexible Formatting Custom format strings (%Y, %m, %d, %H, %M, %S)
📝 Parsing Parse date strings back to datetime from any calendar
🌐 Localized Month Names Persian (fa), Arabic (ar), English (en)
🧪 Well Tested Comprehensive test suite with round-trip integrity checks
🎯 Type Hints Fully typed for great IDE support
🪶 Lightweight Only dependency: pytz

🚀 Quick Start

Create a Date

Date is the main entry point. Configure it with a timezone and calendar type.

from waxt.date_service import Date

# Jalali (Persian) calendar in Tehran
svc = Date(timezone="Asia/Tehran", calendar="jalali")

# Hijri (Islamic) calendar in Riyadh
svc = Date(timezone="Asia/Riyadh", calendar="hijri")

# Gregorian calendar in UTC (default-like)
svc = Date(timezone="UTC", calendar="gregorian")

Calendar options: "jalali", "hijri", "gregorian".


Current Date & Time

svc = Date(timezone="Asia/Tehran", calendar="jalali")

now = svc.now()                     # current datetime in Tehran
now_utc = svc.now_utc()            # current datetime in UTC

Get Date Components (Year, Month, Day)

Returns components in the configured calendar:

from datetime import datetime

svc = Date(timezone="Asia/Tehran", calendar="jalali")

dt = datetime(2025, 6, 15)
year, month, day = svc.get_date_components(dt)
# (1404, 3, 25) — Jalali equivalent

Format Dates

svc = Date(timezone="Asia/Tehran", calendar="jalali")
dt = datetime(2025, 6, 15, 14, 30, 0)

# Default format: YYYY/MM/DD
svc.format_date(dt)                        # "1404/03/25"

# With time
svc.format_date(dt, include_time=True)     # "1404/03/25 14:30:00"

# Custom format string
svc.format_date(dt, format_str="%Y-%m-%d") # "1404-03-25"
svc.format_date(dt, format_str="%d/%m/%Y %H:%M")  # "25/03/1404 14:30"

Format specifiers: %Y (year), %m (month), %d (day), %H (hour), %M (minute), %S (second).


Parse Date Strings

Parse a date string into a datetime object. Input is interpreted in the configured calendar.

svc = Date(timezone="Asia/Tehran", calendar="jalali")

dt = svc.parse_date("1404/03/25")
# datetime(2025, 6, 15, 0, 0, 0) — Gregorian equivalent

dt = svc.parse_date("1404-03-25 14:30:00")
# datetime(2025, 6, 15, 14, 30, 0)

Timezone Conversion

from datetime import datetime
import pytz

svc = Date(timezone="Asia/Tehran", calendar="gregorian")

utc_dt = datetime(2025, 6, 15, 10, 0, 0, tzinfo=pytz.UTC)

# UTC → Tehran
local_dt = svc.to_company_timezone(utc_dt)
# datetime(2025, 6, 15, 13, 30, 0, tzinfo=...)

# Tehran → UTC
back_to_utc = svc.to_utc(local_dt)
# datetime(2025, 6, 15, 10, 0, 0, tzinfo=UTC)

Date Arithmetic

from datetime import datetime

svc = Date(timezone="UTC", calendar="jalali")
dt = datetime(2025, 6, 15)

# Add days (works on Gregorian datetime)
dt2 = svc.add_days(dt, 10)        # datetime(2025, 6, 25)

# Add months (calendar-aware — respects month lengths and leap years)
dt3 = svc.add_months(dt, 3)       # +3 Jalali months from 1404/03/25

# Negative values work too
svc.add_days(dt, -5)
svc.add_months(dt, -2)

Month Names

svc = Date(timezone="UTC", calendar="jalali")
svc.get_month_name(1, locale="fa")  # "فروردین"
svc.get_month_name(1, locale="en")  # "Farvardin"

svc = Date(timezone="UTC", calendar="hijri")
svc.get_month_name(1, locale="ar")  # "محرم"
svc.get_month_name(1, locale="en")  # "Muharram"

svc = Date(timezone="UTC", calendar="gregorian")
svc.get_month_name(1, locale="en")  # "January"
svc.get_month_name(1, locale="fa")  # "ژانویه"

Day Boundaries

svc = Date(timezone="UTC", calendar="gregorian")
dt = datetime(2025, 6, 15, 14, 30, 0)

svc.start_of_day(dt)  # datetime(2025, 6, 15, 0, 0, 0)
svc.end_of_day(dt)    # datetime(2025, 6, 15, 23, 59, 59, 999999)

Factory Helper

from waxt.date_service import create_date_service_for_company

# Reads timezone and calendar attributes from a company object
svc = create_date_service_for_company(company)

📚 API Reference

Date(timezone, calendar)

Method Returns Description
now() datetime Current time in configured timezone
now_utc() datetime Current UTC time
to_company_timezone(utc_dt) datetime Convert UTC → local timezone
to_utc(local_dt) datetime Convert local → UTC
get_date_components(dt) (year, month, day) Extract date parts in configured calendar
format_date(dt, format_str, include_time) str Format date with calendar-aware year/month/day
parse_date(date_str, format_str, time_component) datetime Parse date string → Gregorian datetime
get_month_name(month, locale) str Month name in fa, ar, or en
add_days(dt, days) datetime Add/subtract days
add_months(dt, months) datetime Add/subtract months (calendar-aware)
get_year_month(dt) (year, month) Get year+month only
start_of_day(dt) datetime Set time to 00:00:00
end_of_day(dt) datetime Set time to 23:59:59.999999

Calendar Constants

Constant Value
Date.CALENDAR_JALALI "jalali"
Date.CALENDAR_HIJRI "hijri"
Date.CALENDAR_GREGORIAN "gregorian"

🧪 Development

Setup

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

Run tests

pytest           # with coverage
pytest -x -v     # stop on first failure, verbose

Lint & type check

black waxt tests
isort waxt tests
mypy waxt
flake8 waxt tests

📄 License

BSD-3-Clause. See 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.1.2.tar.gz (18.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.1.2-py3-none-any.whl (9.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: waxt-0.1.2.tar.gz
  • Upload date:
  • Size: 18.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.1.2.tar.gz
Algorithm Hash digest
SHA256 97420067ada630d4999520a4ac45b48997e8d4b64bc701dd1f6735aa5bac6be4
MD5 c040772bbae407abcf1597c28e2825e7
BLAKE2b-256 2ca83e872b5adf60da23d79ead36f983b577c6702134a2ce269d78a94fa1b7bb

See more details on using hashes here.

File details

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

File metadata

  • Download URL: waxt-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 9.6 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.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d8d66380e91b633319dd07667aea27cc15f7fafb1f5c553e3fe9ad8f1b972a58
MD5 bbb170d9735afdb448ab37da69992ead
BLAKE2b-256 f276f1e3ba6fc0326162106bd4ca0b2fb95d7837a0f94928d929b30e488ed009

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