فصل ۷: ماژول‌ها، کتابخانه‌ی استاندارد و شیءگرایی

datetime، منطقه‌ی زمانی تهران و تاریخ شمسی با jdatetime

زمان، سخت‌تر از آن است که به نظر می‌رسد

ماژول استاندارد datetime چند نوع اصلی دارد: date (فقط تاریخ)، datetime (تاریخ و ساعت)، timedelta (فاصله‌ی زمانی) و timezone. از Python 3.9 هم ماژول zoneinfo منطقه‌های زمانی واقعی مثل Asia/Tehran را می‌شناسد.

from datetime import date, datetime, timedelta, UTC
from zoneinfo import ZoneInfo

TEHRAN = ZoneInfo("Asia/Tehran")

now = datetime.now(TEHRAN)                  # زمان «آگاه» (aware) با منطقه‌ی زمانی
print(now.isoformat())                      # مثلاً 2026-09-28T14:05:12+03:30
print(now.astimezone(UTC))                  # همان لحظه به وقت جهانی

due = date.today() + timedelta(days=45)     # تحویل ۴۵ روز بعد
days_left = (due - date.today()).days

stamp = datetime.strptime("2025-03-20 14:30", "%Y-%m-%d %H:%M")   # رشته به تاریخ
print(stamp.strftime("%d/%m/%Y"))                                 # تاریخ به رشته

naive در برابر aware

datetime.now() بدون آرگومان یک زمان naive می‌دهد: عدد ساعت دارد ولی نمی‌داند مال کدام منطقه است. روی لپ‌تاپ شما وقت تهران است و روی سرور خارجی وقت UTC؛ و اختلاف سه‌ونیم ساعته‌ای که گزارش‌ها را به روز اشتباه می‌برد. قاعده: در ذخیره‌سازی UTC، در نمایش تهران. مقایسه‌ی یک زمان naive با aware هم TypeError می‌دهد.

تاریخ شمسی با jdatetime

کتابخانه‌ی استاندارد تقویم شمسی ندارد. کتابخانه‌ی jdatetime رابطی تقریباً یکسان با datetime برای تقویم جلالی فراهم می‌کند:

python -m pip install jdatetime tzdata
import jdatetime
from datetime import date, datetime

today = jdatetime.date.today()
print(today.strftime("%Y/%m/%d"))            # مثلاً 1405/07/06

j = jdatetime.date.fromgregorian(date=date(2025, 3, 21))
print(j)                                     # 1404-01-01
print(jdatetime.date(1404, 1, 1).togregorian())   # 2025-03-21

jnow = jdatetime.datetime.fromgregorian(datetime=datetime.now(TEHRAN))
print(jnow.strftime("%Y/%m/%d %H:%M"))

parsed = jdatetime.datetime.strptime("1403/05/20", "%Y/%m/%d")
print(parsed.togregorian().date())           # 2024-08-10

jdatetime.set_locale(jdatetime.FA_LOCALE)    # نام روز و ماه فارسی
print(jdatetime.date(1404, 1, 1).strftime("%A %d %B %Y"))

راهبرد درست در یک برنامه‌ی واقعی: همه‌جا با datetime میلادی (ترجیحاً UTC) کار و ذخیره کنید و فقط در لحظه‌ی نمایش یا دریافت از کاربر، به شمسی تبدیل کنید. ذخیره‌ی رشته‌ی «۱۴۰۳/۰۵/۲۰» در دیتابیس، مرتب‌سازی، فیلتر بازه و محاسبه‌ی فاصله را سخت و پرخطا می‌کند.

کد قالبمعنی
%Y / %m / %dسال چهاررقمی / ماه / روز
%H:%M:%Sساعت ۲۴ ساعته
%A / %Bنام روز هفته / نام ماه
%jشماره‌ی روز در سال

نکته‌هایی که کمتر کسی می‌داند

  • ویندوز پایگاه داده‌ی منطقه‌های زمانی IANA را ندارد؛ بدون نصب بسته‌ی tzdata، دستور ZoneInfo("Asia/Tehran") در ویندوز خطای ZoneInfoNotFoundError می‌دهد.
  • ایران از سال ۱۴۰۲ دیگر ساعت تابستانی ندارد؛ tzdata به‌روز این را می‌داند. اگر اختلاف تهران در سرور شما در تابستان ‎+04:30 است، tzdata سیستم قدیمی است.
  • datetime.utcnow() در Python 3.12 منسوخ (deprecated) شده، چون زمان naive برمی‌گرداند؛ به‌جایش datetime.now(UTC) بنویسید.
  • jdatetime.date(1403, 12, 30) معتبر است (۱۴۰۳ کبیسه بود) ولی jdatetime.date(1404, 12, 30) ValueError می‌دهد؛ اعتبارسنجی ورودی تاریخ شمسی را به خود کتابخانه بسپارید و isleap() را برای بررسی سال به کار ببرید.
  • datetime.fromisoformat از 3.11 تقریباً هر رشته‌ی ISO 8601 (از جمله +03:30 و Z) را می‌خواند و برای داده‌ی API ها بهترین انتخاب است.

برای ذخیره‌ی پیشرفت و شرکت در آزمون، وارد شوید — رایگان است.