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 DateService

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

from waxt.date_service import DateService

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

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

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

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


Current Date & Time

svc = DateService(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 = DateService(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 = DateService(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 = DateService(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 = DateService(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 = DateService(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 = DateService(timezone="UTC", calendar="jalali")
svc.get_month_name(1, locale="fa")  # "فروردین"
svc.get_month_name(1, locale="en")  # "Farvardin"

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

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

Day Boundaries

svc = DateService(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

DateService(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
DateService.CALENDAR_JALALI "jalali"
DateService.CALENDAR_HIJRI "hijri"
DateService.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.0.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.0-py3-none-any.whl (9.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: waxt-0.1.0.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.0.tar.gz
Algorithm Hash digest
SHA256 06024b853bece859897c27394f19daf5d6cf16f61850953ab88503c570ab0132
MD5 abf39b1fdbb90d7110d24eae3d4dcf3b
BLAKE2b-256 705b2497f046675a0cba17d338df5faf226a9a5725f734d648dd41b9605891e4

See more details on using hashes here.

File details

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

File metadata

  • Download URL: waxt-0.1.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c943f3d7879868e742a59ff986a9c417f723a6d1dbb83a12c01c6ce97b944c11
MD5 92d8e3e23c09d44f1c7ee24a205612ea
BLAKE2b-256 8cdd02c1bb74209f66c15bd21fe51e0a06c7e0bc6f47cd52223b61744c088bf1

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