IlmHamroh
Python kursi/Standart kutubxona4/16-dars26 daqiqa
Mundarija (22)

15.4-dars: datetime

15-QISM — STANDART KUTUBXONA · 4-dars


1. Kirish va motivatsiya

Sana va vaqt — dasturlashdagi eng "oddiy ko'rinadigan", lekin eng ko'p xato beradigan mavzulardan biri. Sabab: kalendar muntazam emas. Oylar 28, 29, 30 yoki 31 kun; yillar 365 yoki 366 kun; "bir oydan keyin" degan ibora 31-yanvar uchun aniq javobga ega emas.

Real vaziyat. Obuna xizmati har oy to'lov oladi. Dasturchi keyingi to'lov sanasini shunday hisobladi:

python
keyingi = oxirgi_tolov + timedelta(days=30)

Bir yil ichida to'lov sanalari 5 kunga siljib ketdi. 31-yanvarda obuna bo'lganlardan esa fevralda umuman to'lov olinmadi, mayda — ikki marta. Tuzatish sifatida "oy qo'shish" funksiyasi yozildi — lekin u har safar oldingi sanadan hisoblagani uchun 31 → 28 → 28 → 28 bo'lib, mart va keyingi oylarda mijozlardan 2–3 kun erta pul yechildi.

Yana bir klassik: yoshni (bugun - tugilgan).days // 365 bilan hisoblash. 29-fevralda tug'ilgan foydalanuvchi 18 yoshga to'lmasdan "18 yosh" deb hisoblanib, cheklangan kontentga kirib ketdi.

Bu darsda datetime modulining turlarini, arifmetikasini, formatlash va tahlil qilishni hamda shunday tuzoqlarni o'rganamiz. Vaqt mintaqalari — keyingi darsda.

Bu darsda:

  • To'rt asosiy tur: date, time, datetime, timedelta
  • Naive va aware — qisqacha (to'liq — 15.5-dars)
  • Arifmetika: timedelta qanday me'yorlanadi, manfiy oraliqlar
  • "Oy qo'shish" yo'q — va uni to'g'ri yozish
  • Formatlash: strftime, f-string, isoformat
  • Tahlil qilish: strptime, fromisoformat, date.strptime (3.14)
  • Tuzoqlar: datetime — date ning vorisi, naive/aware solishtirish, utcnow, %y, yosh hisoblash
  • time moduli soatlari: time(), monotonic(), perf_counter()
  • Amaliy: obuna to'lovlari jadvali va ish kunlari

2. Nazariya — chuqur tushuntirish

2.1. Asosiy turlar

Tur Nimani bildiradi Misol
date Kalendar sanasi date(2026, 9, 17)
time Kun ichidagi vaqt time(14, 3, 22)
datetime Sana + vaqt datetime(2026, 9, 17, 14, 3, 22)
timedelta Davomiylik (oraliq) timedelta(days=1, hours=2)
timezone Qat'iy UTC siljishi timezone(timedelta(hours=5))

Hammasi o'zgarmas (immutable) va xeshlanadi — dict kaliti bo'la oladi.

python
from datetime import date, datetime, time, timedelta, timezone

date(2026, 2, 29)          # ❌ ValueError: day 29 must be in range 1..28 for month 2
date(2026, 9, 17).weekday()      # 3  (dushanba = 0)
date(2026, 9, 17).isoweekday()   # 4  (dushanba = 1)

2.2. Naive va aware

Naive Aware
tzinfo None Bor
Ma'nosi "Qaysidir mintaqadagi" vaqt Aniq vaqt nuqtasi
Misol datetime(2026, 9, 17, 14, 0) datetime(2026, 9, 17, 9, 0, tzinfo=timezone.utc)

Naive va aware ni < bilan solishtirish — TypeError, == bilan esa jim False. Batafsil — 15.5-darsda.

Qoida: tizim ichida aware UTC saqlang, foydalanuvchiga ko'rsatishda mahalliy vaqtga o'giring.

2.3. timedelta va arifmetika

Amal Natija
date - date timedelta
datetime - datetime timedelta
date + timedelta date (faqat days hisobga olinadi)
datetime + timedelta datetime
timedelta * son, / son timedelta
timedelta / timedelta float — nechta sig'adi
timedelta // timedelta, % int, timedelta
time + timedelta TypeError

Ichki me'yorlash. timedelta faqat uchta maydon saqlaydi: days, seconds (0..86399), microseconds:

python
timedelta(days=1, hours=25, minutes=-30, seconds=3661.5)
# → timedelta(days=2, seconds=5461, microseconds=500000)   "2 days, 1:31:01.500000"

timedelta(hours=-1)
# → timedelta(days=-1, seconds=82800)   "-1 day, 23:00:00"

td.seconds — jami soniyalar emas, faqat kun qoldig'i. Jami uchun — td.total_seconds().

timedelta da months va years yo'q — ular qat'iy uzunlikka ega emas.

2.4. Oy va yil qo'shish

"Bir oydan keyin" uchun qaror qabul qilish kerak: 31-yanvar + 1 oy = ? Keng tarqalgan qoida — oyning oxiriga qisish (clamp): 28/29-fevral.

python
import calendar

def oy_qosh(d: date, n: int, asl_kun: int | None = None) -> date:
    oy0 = d.month - 1 + n
    yil, oy = d.year + oy0 // 12, oy0 % 12 + 1
    kun = min(asl_kun or d.day, calendar.monthrange(yil, oy)[1])
    return d.replace(year=yil, month=oy, day=kun)

Zanjir bilan hisoblash siljiydi: 31.01 → 28.02 → 28.03 → 28.04. To'g'risi — har safar asl sanadan hisoblash (yoki asl kunni saqlash): 31.01 → 28.02 → 31.03 → 30.04.

replace ham tekshiradi: datetime(2026, 1, 31).replace(month=2) → ValueError.

Murakkab kalendar qoidalari uchun tashqi kutubxona — python-dateutil (relativedelta).

2.5. Formatlash

python
d = datetime(2026, 9, 17, 14, 3, 22)
d.isoformat()                        # '2026-09-17T14:03:22'  ⭐ mashina uchun
d.isoformat(timespec="minutes")      # '2026-09-17T14:03'
d.strftime("%d.%m.%Y %H:%M")         # '17.09.2026 14:03'
f"{d:%d.%m.%Y}"                      # '17.09.2026'
Kod Ma'nosi Kod Ma'nosi
%Y Yil, 4 xona %H Soat 00–23
%y Yil, 2 xona %M Daqiqa
%m Oy 01–12 %S Soniya
%d Kun 01–31 %f Mikrosoniya
%j Yil kuni 001–366 %z UTC siljishi +0500
%A, %B Hafta kuni, oy nomi %% % belgisi

%A va %B lokalga bog'liq — serverda odatda inglizcha. O'zbekcha nomlar uchun o'z lug'atingizni ishlating.

2.6. Tahlil qilish

Usul Qachon
datetime.fromisoformat(s) ISO 8601 (API, baza, JSON) — tez va qat'iy
datetime.strptime(s, format) Boshqa formatlar
date.strptime(s, format) 3.14+: to'g'ridan-to'g'ri date
datetime.fromtimestamp(t, tz=...) Unix vaqtidan

3.11 dan beri fromisoformat ISO 8601 ning deyarli barcha shakllarini tushunadi: "2026-09-17 14:03", "20260917T140322Z", "...+05:00".

strptime xulqi:

Holat Natija
"1/2/2026", "%d/%m/%Y" bitta xonali ham o'tadi
"31.02.2026" ValueError: day 31 must be in range...
"17.09.2026 " (oxirida bo'shliq) unconverted data remains
%y bilan "68" / "69" 2068 / 1969

15.1-darsdagi regex 2026-02-30 ni o'tkazib yuborgan edi — strptime esa kalendarni biladi.

2.7. Nozik joylar

Tuzoq Tafsilot
datetime — date ning vorisi isinstance(datetime(...), date) → True; lekin datetime(2026,9,17) == date(2026,9,17) → False, < → TypeError
datetime.utcnow() 3.12+ DeprecationWarning: naive qaytaradi. O'rniga datetime.now(UTC)
datetime.now() Naive mahalliy vaqt — server mintaqasiga bog'liq
Yosh = days // 365 Kabisa yillari tufayli xato
sum(timedelta_lar) TypeError — sum(..., timedelta())
bool(time(0, 0)) True (3.5 dan beri)

Yoshni to'g'ri hisoblash:

python
def yosh(tugilgan: date, bugun: date) -> int:
    return bugun.year - tugilgan.year - ((bugun.month, bugun.day) < (tugilgan.month, tugilgan.day))

2.8. time moduli soatlari

Funksiya Nima uchun Xususiyati
time.time() Hozirgi vaqt (Unix) Tizim soati o'zgarsa sakraydi
time.monotonic() Muddatlar, kutish Faqat oldinga yuradi
time.perf_counter() Tezlikni o'lchash Eng aniq
time.process_time() CPU vaqti Kutish hisobga olinmaydi (14.8-dars)
*_ns() variantlari Butun nanosoniyalar Suzuvchi nuqta xatosiz

Davomiylikni datetime.now() yoki time.time() ayirmasi bilan o'lchamang — NTP sinxronlash yoki qo'lda soat o'zgarsa, natija manfiy ham bo'lishi mumkin.


3. Tez ma'lumotnoma

python
from datetime import UTC, date, datetime, timedelta

hozir = datetime.now(UTC)                        # ⭐ aware UTC
bugun = date.today()
d = datetime.fromisoformat("2026-09-17T14:03:22+05:00")
d = datetime.strptime("17.09.2026 14:03", "%d.%m.%Y %H:%M")
f"{d:%d.%m.%Y %H:%M}", d.isoformat()
oraliq = d2 - d1; oraliq.total_seconds()
kun_soni = (date(2027, 1, 1) - bugun).days
jami = sum(oraliqlar, timedelta())

Qoidalar

ichkarida aware UTC, ko'rsatishda mahalliy
td.seconds emas — td.total_seconds()
oy qo'shish — asl kundan, oy oxiriga qisib
yosh — yil farqi va (oy, kun) solishtiruvi
ISO 8601 — fromisoformat / isoformat
davomiylik — monotonic / perf_counter

4. Batafsil misollar

Misol 1 — Turlar va arifmetika

python
"""date, time, datetime, timedelta; hafta kuni va ISO hafta; timedelta me'yorlanishi; manfiy oraliq; bo'lish va qoldiq."""

from datetime import date, datetime, time, timedelta


def main() -> None:
    print("=== 1. Sana ===")
    d = date(2026, 9, 17)
    print(f"  {d!r} → {d}")
    print(f"  weekday()={d.weekday()} (dushanba=0), isoweekday()={d.isoweekday()} (dushanba=1)")
    print(f"  isocalendar(): yil={d.isocalendar().year}, hafta={d.isocalendar().week}")
    print(f"  yil boshidan {d.timetuple().tm_yday}-kun")
    try:
        date(2026, 2, 29)
    except ValueError as xato:
        print(f"  date(2026, 2, 29) → ValueError: {xato}")
    print(f"  date(2024, 2, 29) → {date(2024, 2, 29)} (kabisa yili)")

    print("\n=== 2. Sana ayirmasi ===")
    print(f"  yil oxirigacha: {(date(2027, 1, 1) - d).days} kun")
    print(f"  fevral 2026: {(date(2026, 3, 1) - date(2026, 2, 1)).days} kun, fevral 2028: {(date(2028, 3, 1) - date(2028, 2, 1)).days} kun")
    davr = datetime(2026, 9, 17, 14, 3) - datetime(2026, 9, 16, 15, 30)
    print(f"  kecha 15:30 dan bugun 14:03 gacha: {davr} ({davr.total_seconds() / 3600:.2f} soat)")

    print("\n=== 3. ⭐ timedelta me'yorlanishi ===")
    td = timedelta(days=1, hours=25, minutes=-30, seconds=3661.5)
    print(f"  kiritilgan: days=1, hours=25, minutes=-30, seconds=3661.5")
    print(f"  saqlangan:  {td!r}")
    print(f"  str:        {td}")
    print(f"  ⚠️ td.seconds={td.seconds}  (faqat kun qoldig'i)")
    print(f"  ✅ td.total_seconds()={td.total_seconds()}")

    print("\n=== 4. Manfiy oraliq ===")
    manfiy = timedelta(hours=-1, minutes=-30)
    print(f"  {manfiy!r}")
    print(f"  str: {manfiy}   days={manfiy.days}, seconds={manfiy.seconds}")
    print(f"  abs: {abs(manfiy)}, total_seconds: {manfiy.total_seconds()}")

    print("\n=== 5. Oraliqlar bilan hisob ===")
    dars = timedelta(minutes=45)
    kun = timedelta(hours=6)
    print(f"  6 soatga nechta 45 daqiqalik dars: {kun / dars} → butun: {kun // dars}, qoldiq: {kun % dars}")
    print(f"  5 ta dars: {dars * 5}")
    print(f"  darslar yig'indisi: {sum([dars] * 5, timedelta())}")
    try:
        sum([dars] * 5)
    except TypeError as xato:
        print(f"  ⚠️ sum() boshlang'ich qiymatsiz: TypeError: {xato}")

    print("\n=== 6. time va combine ===")
    boshlanish = time(9, 30)
    print(f"  {boshlanish}, {boshlanish < time(10)}")
    try:
        boshlanish + timedelta(minutes=45)
    except TypeError:
        print("  time + timedelta → TypeError")
    toliq = datetime.combine(date(2026, 9, 17), boshlanish) + timedelta(minutes=45)
    print(f"  combine + 45 daqiqa → {toliq.time()}")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Sana ===
  datetime.date(2026, 9, 17) → 2026-09-17
  weekday()=3 (dushanba=0), isoweekday()=4 (dushanba=1)
  isocalendar(): yil=2026, hafta=38
  yil boshidan 260-kun
  date(2026, 2, 29) → ValueError: day 29 must be in range 1..28 for month 2 in year 2026
  date(2024, 2, 29) → 2024-02-29 (kabisa yili)

=== 2. Sana ayirmasi ===
  yil oxirigacha: 106 kun
  fevral 2026: 28 kun, fevral 2028: 29 kun
  kecha 15:30 dan bugun 14:03 gacha: 22:33:00 (22.55 soat)

=== 3. ⭐ timedelta me'yorlanishi ===
  kiritilgan: days=1, hours=25, minutes=-30, seconds=3661.5
  saqlangan:  datetime.timedelta(days=2, seconds=5461, microseconds=500000)
  str:        2 days, 1:31:01.500000
  ⚠️ td.seconds=5461  (faqat kun qoldig'i)
  ✅ td.total_seconds()=178261.5

=== 4. Manfiy oraliq ===
  datetime.timedelta(days=-1, seconds=81000)
  str: -1 day, 22:30:00   days=-1, seconds=81000
  abs: 1:30:00, total_seconds: -5400.0

=== 5. Oraliqlar bilan hisob ===
  6 soatga nechta 45 daqiqalik dars: 8.0 → butun: 8, qoldiq: 0:00:00
  5 ta dars: 3:45:00
  darslar yig'indisi: 3:45:00
  ⚠️ sum() boshlang'ich qiymatsiz: TypeError: unsupported operand type(s) for +: 'int' and 'datetime.timedelta'

=== 6. time va combine ===
  09:30:00, True
  time + timedelta → TypeError
  combine + 45 daqiqa → 10:15:00

Nima ko'rsatdi: 2.1, 2.3-bo'limlar.

Misol 2 — Formatlash va tahlil

python
"""isoformat va fromisoformat; strftime va f-string; strptime formatlari va xatolari; date.strptime 3.14-bob; %y tuzog'i; o'zbekcha nomlar."""

from datetime import date, datetime

OYLAR = ["yanvar", "fevral", "mart", "aprel", "may", "iyun",
         "iyul", "avgust", "sentabr", "oktabr", "noyabr", "dekabr"]
KUNLAR = ["dushanba", "seshanba", "chorshanba", "payshanba", "juma", "shanba", "yakshanba"]


def ozbekcha(d: date) -> str:
    return f"{d.day}-{OYLAR[d.month - 1]} {d.year}-yil, {KUNLAR[d.weekday()]}"


def main() -> None:
    d = datetime(2026, 9, 17, 14, 3, 22, 500)

    print("=== 1. Formatlash ===")
    print(f"  isoformat():                  {d.isoformat()}")
    print(f"  isoformat(timespec='minutes'): {d.isoformat(timespec='minutes')}")
    print(f"  strftime('%d.%m.%Y %H:%M'):   {d.strftime('%d.%m.%Y %H:%M')}")
    print(f"  f-string:                     {d:%Y/%m/%d, %j-kun}")
    print(f"  %A %B (lokalga bog'liq):     {d:%A, %B}")
    print(f"  o'zbekcha:                    {ozbekcha(d)}")

    print("\n=== 2. fromisoformat ===")
    for s in ("2026-09-17", "2026-09-17T14:03:22", "2026-09-17 14:03",
              "2026-09-17T14:03:22+05:00", "20260917T140322Z"):
        natija = datetime.fromisoformat(s)
        print(f"  {s!r:29} → {natija}  tzinfo={natija.tzinfo}")
    try:
        datetime.fromisoformat("17.09.2026")
    except ValueError as xato:
        print(f"  '17.09.2026' → ValueError: {xato}")

    print("\n=== 3. strptime ===")
    for s, fmt in (("17.09.2026 14:03", "%d.%m.%Y %H:%M"),
                   ("1/2/2026", "%d/%m/%Y"),
                   ("2026-09-17T14:03:22+0500", "%Y-%m-%dT%H:%M:%S%z")):
        print(f"  {s!r:28} {fmt!r:24} → {datetime.strptime(s, fmt)}")
    print(f"  date.strptime 3.14-bob: {date.strptime('17.09.2026', '%d.%m.%Y')!r}")

    print("\n=== 4. ⚠️ strptime xatolari ===")
    for s in ("31.02.2026", "17.09.2026 ", "17-09-2026", "2026.09.17"):
        try:
            datetime.strptime(s, "%d.%m.%Y")
        except ValueError as xato:
            print(f"  {s!r:14} → {xato}")
    print("  ⭐ regex 2026-02-30 ni o'tkazgan edi 15.1-bob — strptime kalendarni biladi")

    print("\n=== 5. ⚠️ %y — ikki xonali yil ===")
    for yy in ("26", "68", "69", "99"):
        print(f"  '{yy}' → {datetime.strptime(yy, '%y').year}")
    print("  ⭐ 1969-yilda tug'ilgan '69' — to'g'ri; 2069-yilgi shartnoma muddati — noto'g'ri")

    print("\n=== 6. Foydalanuvchi kiritgan sana: bir nechta format ===")
    FORMATLAR = ("%d.%m.%Y", "%d/%m/%Y", "%Y-%m-%d")

    def sana_oqi(s: str) -> date | None:
        s = s.strip()
        for fmt in FORMATLAR:
            try:
                return date.strptime(s, fmt)
            except ValueError:
                continue
        return None

    for s in (" 17.09.2026", "17/09/2026", "2026-09-17", "09/17/2026", "30.02.2026"):
        print(f"  {s!r:14} → {sana_oqi(s)}")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Formatlash ===
  isoformat():                  2026-09-17T14:03:22.000500
  isoformat(timespec='minutes'): 2026-09-17T14:03
  strftime('%d.%m.%Y %H:%M'):   17.09.2026 14:03
  f-string:                     2026/09/17, 260-kun
  %A %B (lokalga bog'liq):     Thursday, September
  o'zbekcha:                    17-sentabr 2026-yil, payshanba

=== 2. fromisoformat ===
  '2026-09-17'                  → 2026-09-17 00:00:00  tzinfo=None
  '2026-09-17T14:03:22'         → 2026-09-17 14:03:22  tzinfo=None
  '2026-09-17 14:03'            → 2026-09-17 14:03:00  tzinfo=None
  '2026-09-17T14:03:22+05:00'   → 2026-09-17 14:03:22+05:00  tzinfo=UTC+05:00
  '20260917T140322Z'            → 2026-09-17 14:03:22+00:00  tzinfo=UTC
  '17.09.2026' → ValueError: Invalid isoformat string: '17.09.2026'

=== 3. strptime ===
  '17.09.2026 14:03'           '%d.%m.%Y %H:%M'         → 2026-09-17 14:03:00
  '1/2/2026'                   '%d/%m/%Y'               → 2026-02-01 00:00:00
  '2026-09-17T14:03:22+0500'   '%Y-%m-%dT%H:%M:%S%z'    → 2026-09-17 14:03:22+05:00
  date.strptime 3.14-bob: datetime.date(2026, 9, 17)

=== 4. ⚠️ strptime xatolari ===
  '31.02.2026'   → day 31 must be in range 1..28 for month 2 in year 2026
  '17.09.2026 '  → unconverted data remains:
  '17-09-2026'   → time data '17-09-2026' does not match format '%d.%m.%Y'
  '2026.09.17'   → time data '2026.09.17' does not match format '%d.%m.%Y'
  ⭐ regex 2026-02-30 ni o'tkazgan edi 15.1-bob — strptime kalendarni biladi

=== 5. ⚠️ %y — ikki xonali yil ===
  '26' → 2026
  '68' → 2068
  '69' → 1969
  '99' → 1999
  ⭐ 1969-yilda tug'ilgan '69' — to'g'ri; 2069-yilgi shartnoma muddati — noto'g'ri

=== 6. Foydalanuvchi kiritgan sana: bir nechta format ===
  ' 17.09.2026'  → 2026-09-17
  '17/09/2026'   → 2026-09-17
  '2026-09-17'   → 2026-09-17
  '09/17/2026'   → None
  '30.02.2026'   → None

Nima ko'rsatdi: 2.5, 2.6-bo'limlar.

Misol 3 — Tuzoqlar

python
"""Oy qo'shish: zanjir va asl kun; yosh hisoblash; datetime va date merosi; naive va aware solishtirish; utcnow; monotonic soat."""

import calendar
import time
import warnings
from datetime import UTC, date, datetime, timedelta, timezone


def oy_qosh(d: date, n: int, asl_kun: int | None = None) -> date:
    oy0 = d.month - 1 + n
    yil, oy = d.year + oy0 // 12, oy0 % 12 + 1
    kun = min(asl_kun or d.day, calendar.monthrange(yil, oy)[1])
    return d.replace(year=yil, month=oy, day=kun)


def yosh(tugilgan: date, bugun: date) -> int:
    return bugun.year - tugilgan.year - ((bugun.month, bugun.day) < (tugilgan.month, tugilgan.day))


def main() -> None:
    boshi = date(2026, 1, 31)

    print("=== 1. ⚠️ '30 kun' — 'bir oy' emas ===")
    sanalar = [boshi + timedelta(days=30 * i) for i in range(13)]
    print(f"  12 marta +30 kun: {sanalar[-1]}  (bir yildan keyin kutilgan: 2027-01-31)")
    print(f"  siljish: {(date(2027, 1, 31) - sanalar[-1]).days} kun")

    print("\n=== 2. ⚠️ Oy qo'shish: zanjir va asl sana ===")
    zanjir = [boshi]
    for _ in range(4):
        zanjir.append(oy_qosh(zanjir[-1], 1))
    asldan = [oy_qosh(boshi, i) for i in range(5)]
    print(f"  zanjir:    {[f'{d:%d.%m}' for d in zanjir]}")
    print(f"  asl sanadan: {[f'{d:%d.%m}' for d in asldan]}")
    print(f"  kabisa yili: {oy_qosh(date(2028, 1, 31), 1)}, orqaga: {oy_qosh(boshi, -2)}")
    try:
        datetime(2026, 1, 31).replace(month=2)
    except ValueError as xato:
        print(f"  replace(month=2) → ValueError: {xato}")

    print("\n=== 3. ⚠️ Yosh hisoblash (29-fevralda tug'ilgan) ===")
    tugilgan = date(2008, 2, 29)
    for bugun in (date(2026, 2, 28), date(2026, 3, 1)):
        xato_usul = (bugun - tugilgan).days // 365
        print(f"  {bugun}: days//365 = {xato_usul}, to'g'ri = {yosh(tugilgan, bugun)}")

    print("\n=== 4. ⚠️ datetime — date ning vorisi ===")
    dt, d = datetime(2026, 9, 17), date(2026, 9, 17)
    print(f"  isinstance(datetime, date): {isinstance(dt, date)}")
    print(f"  datetime(2026,9,17) == date(2026,9,17): {dt == d}")
    try:
        d < datetime(2026, 9, 18)
    except TypeError as xato:
        print(f"  date < datetime → TypeError: {xato}")
    print(f"  lug'at kaliti: {{date: ...}}.get(datetime) → {({d: 'topildi'}).get(dt)}")
    print(f"  ✅ .date() bilan: {dt.date() == d}")

    print("\n=== 5. ⚠️ Naive va aware ===")
    naive = datetime(2026, 9, 17, 12, 0)
    aware = datetime(2026, 9, 17, 12, 0, tzinfo=UTC)
    print(f"  naive == aware: {naive == aware}  ← jim False")
    try:
        naive < aware
    except TypeError as xato:
        print(f"  naive < aware → TypeError: {xato}")
    toshkent = aware.astimezone(timezone(timedelta(hours=5)))
    print(f"  bir vaqt nuqtasi: {aware} == {toshkent} → {aware == toshkent}")

    print("\n=== 6. ⚠️ utcnow() eskirgan ===")
    with warnings.catch_warnings(record=True) as ogoh:
        warnings.simplefilter("always")
        eski = datetime.utcnow()
    print(f"  {ogoh[0].category.__name__}: tzinfo={eski.tzinfo}")
    print(f"  ✅ datetime.now(UTC).tzinfo = {datetime.now(UTC).tzinfo}")

    print("\n=== 7. Davomiylikni o'lchash ===")
    bosh_m, bosh_p = time.monotonic(), time.perf_counter()
    time.sleep(0.05)
    print(f"  monotonic ayirmasi 0.05 s dan katta: {time.monotonic() - bosh_m >= 0.05}")
    print(f"  perf_counter ayirmasi 0.05 s dan katta: {time.perf_counter() - bosh_p >= 0.05}")
    print(f"  time.monotonic hech qachon orqaga yurmaydi: {time.get_clock_info('monotonic').monotonic}")
    print(f"  time.time esa sozlanadi: {time.get_clock_info('time').adjustable}")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. ⚠️ '30 kun' — 'bir oy' emas ===
  12 marta +30 kun: 2027-01-26  (bir yildan keyin kutilgan: 2027-01-31)
  siljish: 5 kun

=== 2. ⚠️ Oy qo'shish: zanjir va asl sana ===
  zanjir:    ['31.01', '28.02', '28.03', '28.04', '28.05']
  asl sanadan: ['31.01', '28.02', '31.03', '30.04', '31.05']
  kabisa yili: 2028-02-29, orqaga: 2025-11-30
  replace(month=2) → ValueError: day 31 must be in range 1..28 for month 2 in year 2026

=== 3. ⚠️ Yosh hisoblash (29-fevralda tug'ilgan) ===
  2026-02-28: days//365 = 18, to'g'ri = 17
  2026-03-01: days//365 = 18, to'g'ri = 18

=== 4. ⚠️ datetime — date ning vorisi ===
  isinstance(datetime, date): True
  datetime(2026,9,17) == date(2026,9,17): False
  date < datetime → TypeError: '<' not supported between instances of 'datetime.date' and 'datetime.datetime'
  lug'at kaliti: {date: ...}.get(datetime) → None
  ✅ .date() bilan: True

=== 5. ⚠️ Naive va aware ===
  naive == aware: False  ← jim False
  naive < aware → TypeError: can't compare offset-naive and offset-aware datetimes
  bir vaqt nuqtasi: 2026-09-17 12:00:00+00:00 == 2026-09-17 17:00:00+05:00 → True

=== 6. ⚠️ utcnow() eskirgan ===
  DeprecationWarning: tzinfo=None
  ✅ datetime.now(UTC).tzinfo = UTC

=== 7. Davomiylikni o'lchash ===
  monotonic ayirmasi 0.05 s dan katta: True
  perf_counter ayirmasi 0.05 s dan katta: True
  time.monotonic hech qachon orqaga yurmaydi: True
  time.time esa sozlanadi: True

Nima ko'rsatdi: 2.2, 2.4, 2.7, 2.8-bo'limlar.

Misol 4 — Amaliy: obuna to'lovlari va ish kunlari

Kirishdagi vaziyatni to'g'ri hal qilamiz. Obuna asl sana bo'yicha oyma-oy yangilanadi (oy oxiriga qisib), to'lov sanasi dam olish kuniga tushsa — keyingi ish kuniga suriladi, to'lov o'tmasa — 3 ish kuni imtiyoz beriladi. Qat'iy bayram sanalari kodda, harakatlanuvchi bayramlar (hayitlar) esa har yili rasmiy e'lon qilingani uchun ma'lumot sifatida tashqaridan beriladi.

python
"""Asl sanadan oy qo'shish; dam olish va bayramlarni hisobga olgan ish kunlari; imtiyoz davri; yil davomidagi to'lovlar jadvali va tekshiruvlar."""

import calendar
from dataclasses import dataclass
from datetime import date, timedelta

QATIY_BAYRAMLAR = {(1, 1), (3, 8), (3, 21), (5, 9), (9, 1), (10, 1), (12, 8)}


def oy_qosh(d: date, n: int, asl_kun: int) -> date:
    oy0 = d.month - 1 + n
    yil, oy = d.year + oy0 // 12, oy0 % 12 + 1
    return date(yil, oy, min(asl_kun, calendar.monthrange(yil, oy)[1]))


@dataclass(frozen=True, slots=True)
class Kalendar:
    qoshimcha_bayramlar: frozenset[date] = frozenset()

    def ish_kunimi(self, d: date) -> bool:
        return (d.weekday() < 5
                and (d.month, d.day) not in QATIY_BAYRAMLAR
                and d not in self.qoshimcha_bayramlar)

    def keyingi_ish_kuni(self, d: date) -> date:
        while not self.ish_kunimi(d):
            d += timedelta(days=1)
        return d

    def ish_kuni_qosh(self, d: date, n: int) -> date:
        while n > 0:
            d += timedelta(days=1)
            if self.ish_kunimi(d):
                n -= 1
        return d


@dataclass(frozen=True, slots=True)
class Tolov:
    davr: int
    reja: date
    haqiqiy: date
    imtiyoz_oxiri: date


def jadval(obuna_sanasi: date, oylar: int, kal: Kalendar) -> list[Tolov]:
    natija = []
    for i in range(1, oylar + 1):
        reja = oy_qosh(obuna_sanasi, i, obuna_sanasi.day)
        haqiqiy = kal.keyingi_ish_kuni(reja)
        natija.append(Tolov(i, reja, haqiqiy, kal.ish_kuni_qosh(haqiqiy, 3)))
    return natija


def main() -> None:
    # Harakatlanuvchi bayramlar har yili rasmiy e'lon qilinadi — bu yerda namuna sifatida berilgan
    kal = Kalendar(qoshimcha_bayramlar=frozenset({date(2026, 3, 20), date(2026, 5, 27)}))
    obuna = date(2026, 1, 31)
    tolovlar = jadval(obuna, 12, kal)

    print(f"=== 1. Obuna: {obuna:%d.%m.%Y}, 12 oylik jadval ===")
    sarlavha = ("davr", "reja", "kun", "to'lov", "imtiyoz oxiri")
    print(f"  {sarlavha[0]:>4}  {sarlavha[1]:10}  {sarlavha[2]:9}  {sarlavha[3]:10}  {sarlavha[4]}")
    for t in tolovlar:
        kun = ["du", "se", "chor", "pay", "ju", "shan", "yak"][t.reja.weekday()]
        surildi = " ←" if t.haqiqiy != t.reja else ""
        print(f"  {t.davr:>4}  {t.reja:%d.%m.%Y}  {kun:9}  {t.haqiqiy:%d.%m.%Y}  {t.imtiyoz_oxiri:%d.%m.%Y}{surildi}")

    print("\n=== 2. Tekshiruvlar ===")
    print(f"  har oy aynan bitta to'lov: {len({(t.reja.year, t.reja.month) for t in tolovlar}) == 12}")
    print(f"  reja sanalari asl kundan siljimadi (31 yoki oy oxiri): "
          f"{all(t.reja.day == min(31, calendar.monthrange(t.reja.year, t.reja.month)[1]) for t in tolovlar)}")
    print(f"  hamma to'lovlar ish kunida: {all(kal.ish_kunimi(t.haqiqiy) for t in tolovlar)}")
    print(f"  imtiyoz doim 3 ish kuni: "
          f"{all(sum(kal.ish_kunimi(t.haqiqiy + timedelta(days=k)) for k in range(1, (t.imtiyoz_oxiri - t.haqiqiy).days + 1)) == 3 for t in tolovlar)}")
    surilganlar = [t for t in tolovlar if t.haqiqiy != t.reja]
    print(f"  surilgan to'lovlar: {len(surilganlar)} ta")

    print("\n=== 3. Noto'g'ri usul bilan solishtirish ===")
    xato = [obuna + timedelta(days=30 * i) for i in range(1, 13)]
    ikki_marta = [oy for oy in range(1, 13) if sum(d.month == oy and d.year == 2026 for d in xato) == 2]
    otkazilgan = [oy for oy in range(2, 13) if not any(d.month == oy and d.year == 2026 for d in xato)]
    print(f"  +30 kun: oxirgi to'lov {xato[-1]:%d.%m.%Y}, to'g'risi {tolovlar[-1].reja:%d.%m.%Y}")
    print(f"  ikki marta to'lov olingan oylar: {ikki_marta}")
    print(f"  to'lovsiz qolgan oylar: {otkazilgan}")

    print("\n=== 4. Bayram atrofida ish kunlari ===")
    for d in (date(2026, 3, 19), date(2026, 12, 31)):
        print(f"  {d:%d.%m.%Y} + 3 ish kuni → {kal.ish_kuni_qosh(d, 3):%d.%m.%Y}")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Obuna: 31.01.2026, 12 oylik jadval ===
  davr  reja        kun        to'lov      imtiyoz oxiri
     1  28.02.2026  shan       02.03.2026  05.03.2026 ←
     2  31.03.2026  se         31.03.2026  03.04.2026
     3  30.04.2026  pay        30.04.2026  05.05.2026
     4  31.05.2026  yak        01.06.2026  04.06.2026 ←
     5  30.06.2026  se         30.06.2026  03.07.2026
     6  31.07.2026  ju         31.07.2026  05.08.2026
     7  31.08.2026  du         31.08.2026  04.09.2026
     8  30.09.2026  chor       30.09.2026  06.10.2026
     9  31.10.2026  shan       02.11.2026  05.11.2026 ←
    10  30.11.2026  du         30.11.2026  03.12.2026
    11  31.12.2026  pay        31.12.2026  06.01.2027
    12  31.01.2027  yak        01.02.2027  04.02.2027 ←

=== 2. Tekshiruvlar ===
  har oy aynan bitta to'lov: True
  reja sanalari asl kundan siljimadi (31 yoki oy oxiri): True
  hamma to'lovlar ish kunida: True
  imtiyoz doim 3 ish kuni: True
  surilgan to'lovlar: 4 ta

=== 3. Noto'g'ri usul bilan solishtirish ===
  +30 kun: oxirgi to'lov 26.01.2027, to'g'risi 31.01.2027
  ikki marta to'lov olingan oylar: [5]
  to'lovsiz qolgan oylar: [2]

=== 4. Bayram atrofida ish kunlari ===
  19.03.2026 + 3 ish kuni → 25.03.2026
  31.12.2026 + 3 ish kuni → 06.01.2027

Nima ko'rsatdi: 2.3, 2.4-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Bir oy = 30 kun" Oylar 28–31 kun; "oy qo'shish" alohida qoida talab qiladi
"td.seconds — jami soniyalar" Faqat kun qoldig'i; jami — total_seconds()
"timedelta(months=1) bor" Yo'q — TypeError
"datetime.now() — universal vaqt" Naive, server mintaqasidagi vaqt
"utcnow() aware UTC qaytaradi" Naive qaytaradi, 3.12+ eskirgan
"datetime == date sana bir xil bo'lsa True" Doim False
"Yosh = kunlar // 365" Kabisa yillari tufayli xato
"Davomiylik = time.time() ayirmasi" monotonic yoki perf_counter

6. Keng tarqalgan xatolar va yechimlari

1. Oyni kunlar bilan qo'shish

python
keyingi = sana + timedelta(days=30)             # ❌
keyingi = oy_qosh(asl_sana, n, asl_sana.day)    # ✅

2. td.seconds dan foydalanish

python
soat = (tugash - boshlanish).seconds / 3600     # ❌ 1 kundan uzunida xato
soat = (tugash - boshlanish).total_seconds() / 3600   # ✅

3. Naive vaqt saqlash

python
yaratilgan = datetime.now()                     # ❌
yaratilgan = datetime.now(UTC)                  # ✅

4. utcnow

python
datetime.utcnow()                               # ❌ naive, eskirgan
datetime.now(UTC)                               # ✅

5. Sanani regex bilan tekshirish

python
re.fullmatch(r"\d{2}\.\d{2}\.\d{4}", s)         # ⚠️ 31.02.2026 o'tadi
datetime.strptime(s, "%d.%m.%Y")                # ✅

6. Sana va vaqtni aralashtirish

python
if yozuv.yaratilgan == date.today(): ...        # ❌ datetime == date → False
if yozuv.yaratilgan.date() == date.today(): ... # ✅

7. Ikki xonali yil

python
datetime.strptime("01.01.69", "%d.%m.%y")       # ⚠️ 1969
# ✅ foydalanuvchidan 4 xonali yil so'rang

8. Davomiylikni devor soati bilan o'lchash

python
bosh = time.time(); ish(); davom = time.time() - bosh     # ❌
bosh = time.monotonic(); ish(); davom = time.monotonic() - bosh   # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 15.1–15.3-darslar (o'tilgan): sanalarni matndan ajratish
  • 15.5-dars: vaqt mintaqalari, zoneinfo, yozgi vaqt
  • 15.16-dars: logging — vaqt belgilari
  • 16.2-dars: json da sanalarni saqlash (ISO 8601)
  • 16.7-dars: sqlite3 — sana ustunlari
  • 17-qism: vaqtga bog'liq kodni testlash (vaqtni "muzlatish")
  • 23-qism: PostgreSQL timestamptz
  • 24-qism: pandas vaqt qatorlari

8. Eng yaxshi amaliyotlar

  1. Tizim ichida aware UTC, foydalanuvchiga mahalliy vaqt.

  2. Almashinuv formati — ISO 8601 (isoformat / fromisoformat).

  3. Oy va yil arifmetikasi uchun aniq qoida yozing va hujjatlashtiring — qisish, asl kun.

  4. Yosh va yilliklarni kunlar bilan emas, kalendar maydonlari bilan hisoblang.

  5. total_seconds() ishlating, seconds emas.

  6. Davomiylik — monotonic/perf_counter.

  7. Foydalanuvchi sanasini strptime bilan tahlil qiling — bir nechta ruxsat etilgan format bilan.

  8. Joriy vaqtni funksiyaga parametr sifatida bering (bugun: date) — kod testlanadigan bo'ladi.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
from datetime import date, datetime, timedelta
1.  print(date(2026, 9, 17).weekday())
2.  print((date(2026, 3, 1) - date(2026, 2, 1)).days)
3.  print(timedelta(hours=25))
4.  print(timedelta(hours=25).seconds)
5.  print(timedelta(hours=-1))
6.  print(date(2026, 12, 31) + timedelta(days=1))
7.  print(timedelta(hours=3) // timedelta(minutes=40))
8.  print(datetime(2026, 9, 17) == date(2026, 9, 17))
9.  print(datetime.strptime("5/3/2026", "%d/%m/%Y").month)
10. print(datetime.fromisoformat("2026-09-17 14:03").minute)
11. print(f"{date(2026, 1, 5):%d.%m.%y}")
12. print(datetime.strptime("69", "%y").year)
Javoblar
  1. 3 — payshanba (dushanba = 0)
  2. 28
  3. 1 day, 1:00:00
  4. 3600
  5. -1 day, 23:00:00
  6. 2027-01-01
  7. 4
  8. False
  9. 3
  10. 3
  11. 05.01.26
  12. 1969

Vazifa 2: Xatolarni tuzating

python
1.  def soatlar(boshlanish, tugash):
        return (tugash - boshlanish).seconds / 3600

2.  def keyingi_oy(sana):
        return sana.replace(month=sana.month + 1)

3.  def yosh(tugilgan):
        return (date.today() - tugilgan).days // 365

4.  def bugungimi(yozuv_vaqti):
        return yozuv_vaqti == date.today()          # yozuv_vaqti — datetime

5.  def yaratish_vaqti():
        return datetime.utcnow().isoformat()
Javoblar
python
1.  def soatlar(boshlanish, tugash):
        return (tugash - boshlanish).total_seconds() / 3600

2.  def keyingi_oy(sana, asl_kun=None):
        oy0 = sana.month                              # 12-oy → keyingi yil
        yil, oy = sana.year + oy0 // 12, oy0 % 12 + 1
        kun = min(asl_kun or sana.day, calendar.monthrange(yil, oy)[1])
        return sana.replace(year=yil, month=oy, day=kun)

3.  def yosh(tugilgan, bugun):                        # bugun — parametr
        return bugun.year - tugilgan.year - ((bugun.month, bugun.day) < (tugilgan.month, tugilgan.day))

4.  def bugungimi(yozuv_vaqti, bugun):
        return yozuv_vaqti.date() == bugun

5.  def yaratish_vaqti():
        return datetime.now(UTC).isoformat()

Vazifa 3: Kalendar chop etuvchi

oy_kalendari(yil, oy) funksiyasini yozing:

  1. Dushanbadan boshlanadigan jadval (o'zbekcha kun nomlari: Du Se Ch Pa Ju Sh Ya)
  2. Bugungi kun [17] ko'rinishida belgilansin (bugun parametr orqali)
  3. Qat'iy bayramlar * bilan
  4. calendar modulisiz, faqat date va timedelta bilan
  5. Natijani calendar.TextCalendar bilan solishtiring

Vazifa 4: Nisbiy vaqt

nisbiy(vaqt, hozir) → "hozirgina", "5 daqiqa oldin", "kecha 14:03", "3 kun oldin", "17-sentabr", "2025-yil 3-mart":

  1. Kelajak uchun ham: "2 soatdan keyin", "ertaga 09:00"
  2. "Kecha" va "ertaga" — kalendar kuniga qarab (24 soatga emas)
  3. O'zbekcha oy nomlari
  4. 20 ta chegara holati bilan sinang (yarim tun, yil almashishi)

Vazifa 5: Ish vaqti hisoblagichi

Xodimning kirish-chiqish yozuvlari ("2026-09-17 08:55 kirdi", "... 18:10 chiqdi"):

  1. Kunlik ishlagan vaqt — timedelta
  2. Tushlik (13:00–14:00) ichiga tushgan vaqt ayirilsin
  3. 9:00 dan kechikishlar va 18:00 dan keyingi qo'shimcha vaqt
  4. Tungi smena (22:00 → 06:00) to'g'ri hisoblansin
  5. Oylik hisobot: jami soat, kechikishlar soni, qo'shimcha soat

Vazifa 6: Muddatlar kalkulyatori

  1. ish_kuni_qosh(sana, n) — manfiy n bilan ham
  2. ish_kunlari_orasida(a, b) — ikki sana orasidagi ish kunlari soni, tsiklsiz formula bilan (haftalar soni × 5 + qoldiq)
  3. Tsiklli va formulali variant natijasi 1000 ta tasodifiy juftlikda bir xil ekanini tekshiring
  4. Bayramlarni fayldan o'qing

Vazifa 7: O'ylash

Python datetime modulida timedelta faqat kun, soniya va mikrosoniyani biladi, "oy" va "yil" esa yo'q. PostgreSQL'da esa interval '1 month' bor, Java'da Period (kalendar) va Duration (vaqt) alohida turlar. Nega "davomiylik" ikki xil tushuncha va Python dizayni qanday kelishuvga borgan?

Javob

Qisqa javob: "Davomiylik" ikki xil narsa: fizik vaqt (aniq soniyalar soni — Duration) va kalendar oralig'i (oy, yil — Period), uzunligi qaysi sanaga qo'shilganiga bog'liq. Python faqat birinchisini standart kutubxonaga kiritgan: u aniq, solishtiriladigan va arifmetik jihatdan izchil. Kalendar oralig'i esa qoidalar talab qiladi (31-yanvar + 1 oy?) — ular dateutil.relativedelta kabi tashqi kutubxonalarga qoldirilgan.

1. Ikki xil tushuncha

Fizik davomiylik Kalendar oralig'i
Misol 90 daqiqa, 3 kun (72 soat) 1 oy, 1 yil
Uzunligi Qat'iy Sanaga bog'liq (28–31 kun)
Solishtirish Doim mumkin 1 oy va 30 kun — qaysi katta?
Qo'shish Kommutativ, assotsiativ (31.01 + 1 oy) + 1 oy ≠ 31.01 + 2 oy
Python timedelta Yo'q (dateutil.relativedelta)
Java Duration Period

2. Kalendar arifmetikasining muammolari

  1. Qisish: 31-yanvar + 1 oy = 28-fevral? 3-mart?
  2. Assotsiativ emas: zanjir va to'g'ridan-to'g'ri hisob har xil (Misol 3)
  3. Qaytuvchan emas: 31.01 + 1 oy - 1 oy = 28.01
  4. Solishtirib bo'lmaydi: interval '1 month' > interval '30 days' — PostgreSQL buni 30 kunlik oy deb qabul qilib hal qiladi, bu ham kelishuv

3. Python dizayni

  • timedelta — algebraik jihatdan "yaxshi" tur: qo'shish, ayirish, ko'paytirish, bo'lish, solishtirish, xesh
  • Kalendar qoidalari biznesga bog'liq (bank, sug'urta, obuna har xil qisish qoidasini ishlatadi) — standart kutubxona bitta qoidani majburlamaydi
  • Tashqi kutubxona: relativedelta(months=+1) — oy oxiriga qisish bilan

4. Boshqa tillar

Til/tizim Yondashuv
Java (java.time) Duration va Period alohida — eng aniq dizayn
PostgreSQL Bitta interval: oy, kun va mikrosoniya alohida maydonlar
JavaScript (Temporal) Duration ichida yil/oy/kun/soat, hisoblashda relativeTo sana talab qilinadi
.NET TimeSpan (fizik), oy uchun AddMonths metodi
Python timedelta + dateutil.relativedelta

5. Amaliy xulosa

  1. "Davomiylik" deganda avval qaysi turdagi ekanini aniqlang
  2. Obuna, shartnoma, kredit — kalendar oralig'i; aniq qoida yozing va hujjatlashtiring
  3. Taymer, muddat, SLA — fizik davomiylik; timedelta va aware UTC
  4. Kalendar oralig'ini doim asl sanadan hisoblang

Nimani mustahkamlaydi: 2.1–2.8-bo'limlar.


Xulosa

Bu darsda sana va vaqt bilan ishlashning asoslarini va tuzoqlarini o'rgandik.

Eng muhim uch fikr:

  1. timedelta — fizik davomiylik, oy emas. U faqat kun, soniya va mikrosoniyani saqlaydi va avtomatik me'yorlanadi: timedelta(hours=-1) — -1 day, 23:00:00, td.seconds esa jami emas, faqat kun qoldig'i (total_seconds() kerak). "Bir oy" uchun alohida qoida yoziladi: oy oxiriga qisish va har safar asl sanadan hisoblash — aks holda 31 → 28 → 28 bo'lib siljiydi. Yosh ham kunlar bilan emas, kalendar maydonlari bilan hisoblanadi.

  2. Formatlash va tahlil: ISO 8601 va strptime. Mashinalar orasida — isoformat()/fromisoformat(), odamlar uchun — strftime yoki f-string va o'zbekcha nomlar lug'ati (%A, %B lokalga bog'liq). strptime kalendarni biladi — 31.02.2026 ni rad etadi, regex esa yo'q; 3.14 da date.strptime to'g'ridan-to'g'ri sana qaytaradi. %y esa 69 ni 1969 deb o'qiydi.

  3. Jim tuzoqlar. datetime — date ning vorisi, lekin datetime == date doim False; naive va aware == bilan False, < bilan TypeError; utcnow() naive qaytaradi va eskirgan — o'rniga datetime.now(UTC). Davomiylik esa devor soati bilan emas, monotonic() yoki perf_counter() bilan o'lchanadi.

Keyingi darsda vaqt mintaqalariga o'tamiz: zoneinfo, yozgi vaqtga o'tish kunlaridagi "mavjud bo'lmagan" va "ikki marta takrorlanadigan" soatlar, UTC da saqlash va mahalliy vaqtda ko'rsatish.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
15.4-dars: datetime — IlmHamroh