Skip to main content

PersianTools

PyPI test workflow codecov PyPI - Python Version PyPI - License

PersianTools provides Jalali (Shamsi) dates and datetimes that work just like Python's datetime — plus handy tools for Persian text: digit conversion between Persian, Arabic, and English, character normalization, and numbers to Persian words.

If you know datetime.date and datetime.datetime, you already know JalaliDate and JalaliDateTime. They support the same operations: comparison, arithmetic with timedelta, timezones, strftime/strptime and formatting, hashing, and pickling.

Installation

pip install persiantools

Requires Python 3.9 or newer. No dependencies, except tzdata on Windows for timezone data.

Quick start

>>> from persiantools.jdatetime import JalaliDate, JalaliDateTime
>>> import datetime

>>> JalaliDate.today()
JalaliDate(1405, 4, 12, Jomeh)

>>> JalaliDate(datetime.date(1988, 5, 4))       # Gregorian → Jalali
JalaliDate(1367, 2, 14, Chaharshanbeh)

>>> JalaliDate(1367, 2, 14).to_gregorian()      # Jalali → Gregorian
datetime.date(1988, 5, 4)

>>> JalaliDateTime.now().strftime("%A %d %B %Y, %H:%M")
'Jomeh 12 Tir 1405, 14:30'

Dates

Create a JalaliDate from Jalali values, a Gregorian date, an ISO string, or a timestamp:

>>> from persiantools.jdatetime import JalaliDate
>>> import datetime

>>> JalaliDate(1367, 2, 14)
JalaliDate(1367, 2, 14, Chaharshanbeh)

>>> JalaliDate.to_jalali(2013, 9, 16)
JalaliDate(1392, 6, 25, Doshanbeh)

>>> JalaliDate.fromisoformat("1404-01-01")
JalaliDate(1404, 1, 1, Jomeh)

>>> JalaliDate.fromtimestamp(578707200)
JalaliDate(1367, 2, 14, Chaharshanbeh)

>>> JalaliDate(1400, 1, 1).replace(month=2, day=10)
JalaliDate(1400, 2, 10, Jomeh)

The week starts on Shanbeh (Saturday). weekday() counts from 0 (Shanbeh) to 6 (Jomeh), and isoweekday() from 1 to 7:

>>> d = JalaliDate(1367, 2, 14)  # a Chaharshanbeh (Wednesday)
>>> d.weekday()
4
>>> d.isoweekday()
5
>>> d.isocalendar()
IsoCalendarDate(year=1367, week=7, weekday=5)
>>> d.isoformat()
'1367-02-14'

Datetimes

JalaliDateTime adds time and timezone support on top of JalaliDate:

>>> from persiantools.jdatetime import JalaliDateTime
>>> from zoneinfo import ZoneInfo
>>> import datetime

>>> JalaliDateTime.now(ZoneInfo("Asia/Tehran"))
JalaliDateTime(1405, 4, 12, 14, 30, 7, 907909, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tehran'))

>>> JalaliDateTime(datetime.datetime(1988, 5, 4, 14, 30, 15))
JalaliDateTime(1367, 2, 14, 14, 30, 15)

>>> JalaliDateTime(1367, 2, 14, 14, 30, 15).to_gregorian()
datetime.datetime(1988, 5, 4, 14, 30, 15)

>>> JalaliDateTime(1367, 2, 14, 14, 30, tzinfo=datetime.timezone.utc).isoformat(timespec="minutes")
'1367-02-14T14:30+00:00'

Formatting and parsing

Both classes support strftime and strptime with the familiar directives, in English or Persian:

>>> from persiantools.jdatetime import JalaliDate, JalaliDateTime

>>> JalaliDate(1367, 2, 14).strftime("%A %d %B %Y")
'Chaharshanbeh 14 Ordibehesht 1367'

>>> JalaliDateTime(1367, 2, 14, 14, 30).strftime("%c", locale="fa")
'چهارشنبه ۱۴ اردیبهشت ۱۳۶۷ ۱۴:۳۰:۰۰'

>>> JalaliDate.strptime("1367-02-14", "%Y-%m-%d")
JalaliDate(1367, 2, 14, Chaharshanbeh)

>>> JalaliDateTime.strptime("1367/02/14 14:30", "%Y/%m/%d %H:%M")
JalaliDateTime(1367, 2, 14, 14, 30)

A date created with locale="fa" renders itself with Persian digits and names everywhere:

>>> JalaliDate(1367, 2, 14, locale="fa").isoformat()
'۱۳۶۷-۰۲-۱۴'

Comparison and arithmetic

Jalali objects compare and do arithmetic with each other, with timedelta, and directly with their Gregorian counterparts:

>>> from persiantools.jdatetime import JalaliDate, JalaliDateTime
>>> import datetime

>>> JalaliDate(1367, 2, 14) == datetime.date(1988, 5, 4)
True

>>> JalaliDate(1395, 2, 14) + datetime.timedelta(days=38)
JalaliDate(1395, 3, 21, Jomeh)

>>> JalaliDateTime(1395, 12, 30) - JalaliDateTime(1395, 1, 1)
datetime.timedelta(days=365)

>>> JalaliDate(1399, 12, 30) > JalaliDate(1399, 12, 29)   # leap-year Esfand 30
True

They are also hashable (usable as dict keys) and picklable, like the standard library types.

Digits

Convert digits between English, Persian, and Arabic:

>>> from persiantools import digits

>>> digits.en_to_fa("0987654321")
'۰۹۸۷۶۵۴۳۲۱'

>>> digits.ar_to_fa("٠٩٨٧٦٥٤٣٢١")
'۰۹۸۷۶۵۴۳۲۱'

>>> digits.fa_to_en("۰۹۸۷۶۵۴۳۲۱")
'0987654321'

>>> digits.fa_to_ar("۰۹۸۷۶۵۴۳۲۱")
'٠٩٨٧٦٥٤٣٢١'

And spell numbers out in Persian — integers, floats, and negatives:

>>> digits.to_word(9512026)
'نه میلیون و پانصد و دوازده هزار و بیست و شش'

>>> digits.to_word(15.007)
'پانزده و هفت هزارم'

>>> digits.to_word(-123.45)
'منفی یکصد و بیست و سه و چهل و پنج صدم'

Characters

Arabic and Persian share letters that look alike but have different Unicode code points (ك vs ک, ي vs ی) — a common source of failed string matching and broken search. Normalize them in either direction:

>>> from persiantools import characters

>>> characters.ar_to_fa("كيك")
'کیک'

>>> characters.fa_to_ar("کیک")
'كيك'

AI Agent Skill

Need your AI assistant to convert Persian (Shamsi/Jalali) dates accurately? Install the convert-persian-dates — it teaches the agent to convert between Shamsi and Gregorian (Miladi) without guessing calendar math.

npx skills add majiidd/persiantools --skill convert-persian-dates

Add --global to enable it across all projects. OpenAI Codex and Cursor also pick it up automatically when this repo is open. After install, ask naturally:

امروز چندمه؟
What is today's date in Shamsi?
Convert 1405-01-01 Shamsi to Gregorian.

The skill needs pip install persiantools in the environment where the agent runs Python.

Support this project

If persiantools saves you time, you can support its development with a donation:

Coin Address
Bitcoin (BTC) bc1qg5rp7ymznc98wmhltzvpwl2dvfuvjr33m4hy77
Ethereum (ETH) 0xC7D6bf306E456632764D0aD111C8dBBb43a3B9ad
Tron (TRX) TDd63bVWZDBHmwVNFgJ6T2WdWmk9z7PBLg
Stellar (XLM) GDSFPPLY34QSAOTOP4DQDXAI2YDRNRIADZHTN3HCGMQXRLIGPYOEH7L5
USDT (BSC) 0xC7D6bf306E456632764D0aD111C8dBBb43a3B9ad

Release files for persiantools 6.2.0

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

Source distribution (sdist)

Source distribution for persiantools 6.2.0
File Size Uploaded
persiantools-6.2.0.tar.gz 56.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for persiantools 6.2.0
File Interpreter ABI Platform
persiantools-6.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 84.7 kB

Release files / persiantools-6.2.0.tar.gz

Download URL persiantools-6.2.0.tar.gz
Size 56.0 kB
Tags Source
SHA-256 checksum
How to use checksums
21ae1ed27292d8a153946f6b16a660cb84a2d829e0a3a0afefee3e9f83ad41b8
BLAKE2b-256 checksum
How to use checksums
dea2c8f3930c3f0c50b20098e28160a29d0962eedb93cced89bb366b0262be0b
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 Aug 5, 2026.

Transparency log

Release files / persiantools-6.2.0-py3-none-any.whl

Download URL persiantools-6.2.0-py3-none-any.whl
Size 28.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
02d0f1c9f8784b20700f23ef96f01706e8cc1b9bf9fe105b3a6c44492f4d61c6
BLAKE2b-256 checksum
How to use checksums
81146e5862cc921df714840fda14f7d65fdd52b3630fd83c66d905a3240da775
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 Aug 5, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

6.2.0 This release

2 release files

6.1.0

2 release files

6.0.2

2 release files

6.0.1

2 release files

6.0.0

2 release files

5.5.1

2 release files

5.5.0

2 release files

5.4.0

2 release files

5.3.0

2 release files

5.2.1

2 release files

5.2.0

2 release files

5.1.1

2 release files

5.1.0

2 release files

4.2.0

2 release files

4.1.2

2 release files

4.1.1

2 release files

4.1.0

2 release files

4.0.3

2 release files

4.0.2

2 release files

4.0.0

2 release files

3.0.1

2 release files

3.0.0

2 release files

2.4.1

2 release files

2.3.2

2 release files

2.2.0

2 release files

2.1.2

2 release files

2.1.1

2 release files

2.0.0

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.0

3 release files

1.2.2

2 release files

1.2.1

2 release files

1.2.0

1 release file

1.1.1

1 release file

1.1.0

2 release files

0.0.7

1 release file

0.0.6

1 release file

0.0.5

1 release file

0.0.4

1 release file

0.0.3

1 release file

0.0.2

0.0.1

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