Sana va vaqt — date(), DateTime, timezone

~15 daq · PHP · Fayllar, Sana va Xatolar

Muammodan boshlaylik

Saytingiz serveri Germaniyada. Toshkentdagi foydalanuvchi soat 14:00 da buyurtma berdi, lekin bazada 09:00 yozildi. Hisobotda "bugungi buyurtmalar" ro'yxatiga kechqurun 19:30 dagi buyurtmalar tushmayapti. Obuna "30 kundan keyin tugaydi" deb hisoblangan, lekin fevralda noto'g'ri sana chiqdi.

Sana va vaqt — dasturlashdagi eng ko'p xato chiqadigan mavzulardan biri. Sababi: vaqt mintaqalari, oy uzunliklari, kabisa yillar. PHP bu murakkablikni o'z zimmasiga oladi — agar to'g'ri vositalardan foydalansangiz.


Timestamp — vaqtning asosi

Kompyuter uchun vaqt — bitta son: 1970-yil 1-yanvar 00:00 UTC dan beri o'tgan soniyalar. Bu Unix timestamp deyiladi.

<?php

echo time();   // 1793631313 — hozirgi lahza

Timestamp vaqt mintaqasiga bog'liq emas — Toshkentda ham, Nyu-Yorkda ham bir lahzada bir xil son.


date() — formatlash

<?php

echo date('Y-m-d');          // 2026-11-02
echo date('d.m.Y H:i');      // 02.11.2026 14:30
echo date('Y-m-d H:i:s');    // 2026-11-02 14:30:05 — bazaga yozish formati
echo date('d.m.Y', 0);       // 01.01.1970 — ikkinchi parametr: timestamp
Belgi Ma'nosi Misol
Y Yil, 4 raqam 2026
m Oy, 2 raqam 03
n Oy, nolsiz 3
d Kun, 2 raqam 07
j Kun, nolsiz 7
H Soat (24) 14
i Daqiqa 05
s Soniya 09
N Hafta kuni (1=dushanba, 7=yakshanba) 1
t Oydagi kunlar soni 30

Vaqt mintaqasi — eng muhim sozlama

<?php

echo date_default_timezone_get();   // UTC — ko'p serverlarda standart
echo date('H:i');                   // 09:30 — Toshkentda esa 14:30!

Skript boshida mintaqani belgilang:

<?php

date_default_timezone_set('Asia/Tashkent');
echo date('H:i');   // 14:30

Yoki php.ini da: date.timezone = Asia/Tashkent.

[!TIP] Katta loyihalardagi qoida: bazada va serverda hammasi UTC, foydalanuvchiga ko'rsatishda — uning mintaqasiga o'giriladi. Shunda turli mamlakatlardagi foydalanuvchilar va serverlar chalkashmaydi. Faqat O'zbekiston uchun kichik sayt bo'lsa, Asia/Tashkent ni hamma joyda ishlatish ham normal.


DateTimeImmutable — zamonaviy usul

date() va time() oddiy holatlar uchun yaxshi. Sana bilan hisob-kitob uchun esa ob'ektlar ishlatiladi:

<?php

$hozir = new DateTimeImmutable();
echo $hozir->format('d.m.Y H:i');

$sana = new DateTimeImmutable('2026-03-15 10:00');
$tugilgan = new DateTimeImmutable('1998-07-21');
$ertaga = new DateTimeImmutable('tomorrow');
$keyingiDushanba = new DateTimeImmutable('next monday');

Qo'shish va ayirish

<?php

$bugun = new DateTimeImmutable('2026-01-31');

echo $bugun->modify('+1 day')->format('Y-m-d');     // 2026-02-01
echo $bugun->modify('+30 days')->format('Y-m-d');   // 2026-03-02
echo $bugun->modify('-1 week')->format('Y-m-d');    // 2026-01-24

echo $bugun->add(new DateInterval('P1M'))->format('Y-m-d');   // 2026-03-03 ?!

Oxirgisi g'alati: 31-yanvar + 1 oy = 31-fevral, bunday kun yo'q, PHP uni 3-martga "toshirib" yuboradi. Oyning oxirgi kuni kerak bo'lsa:

<?php

echo $bugun->modify('last day of next month')->format('Y-m-d');   // 2026-02-28

Nima uchun Immutable?

<?php

// DateTime (o'zgaruvchan) — tuzoq
$boshlanish = new DateTime('2026-01-01');
$tugash = $boshlanish->modify('+30 days');
echo $boshlanish->format('Y-m-d');   // 2026-01-31 ← boshlanish ham o'zgarib ketdi!

// DateTimeImmutable — xavfsiz
$boshlanish = new DateTimeImmutable('2026-01-01');
$tugash = $boshlanish->modify('+30 days');
echo $boshlanish->format('Y-m-d');   // 2026-01-01 ← o'zgarmadi

DateTime ning metodlari ob'ektning o'zini o'zgartiradi. DateTimeImmutable esa yangi ob'ekt qaytaradi. Doim DateTimeImmutable ni ishlating.


Ikki sana orasidagi farq

<?php

$tugilgan = new DateTimeImmutable('1998-07-21');
$bugun = new DateTimeImmutable('today');

$farq = $tugilgan->diff($bugun);

echo $farq->y;      // 28 — to'liq yillar (yosh)
echo $farq->m;      // 3 — qolgan oylar
echo $farq->d;      // 12 — qolgan kunlar
echo $farq->days;   // 10331 — jami kunlar

Obuna qolgan kunlari:

<?php

$tugash = new DateTimeImmutable('2026-12-01');
$qoldi = (new DateTimeImmutable('today'))->diff($tugash);

if ($qoldi->invert) {
    echo "Obuna tugagan";
} else {
    echo "Obuna tugashiga {$qoldi->days} kun qoldi";
}

invert — 1 bo'lsa, ikkinchi sana birinchisidan oldin (ya'ni muddat o'tib ketgan).


Foydalanuvchi kiritgan sanani tekshirish

<?php

$kiritilgan = $_POST['sana'] ?? '';   // "2026-02-30"

$sana = DateTimeImmutable::createFromFormat('!Y-m-d', $kiritilgan);
$xatolar = DateTimeImmutable::getLastErrors();

if ($sana === false || ($xatolar && ($xatolar['warning_count'] || $xatolar['error_count']))) {
    echo "Sana noto'g'ri";
} else {
    echo $sana->format('d.m.Y');
}

createFromFormat aniq formatni kutadi. ! — vaqtni 00:00:00 ga tushiradi. Ogohlantirishlar ham tekshiriladi, chunki aks holda 2026-02-30 jimgina 2026-03-02 ga aylanadi.


Mintaqalar orasida o'girish

<?php

$utc = new DateTimeImmutable('2026-11-02 09:30:00', new DateTimeZone('UTC'));

$toshkent = $utc->setTimezone(new DateTimeZone('Asia/Tashkent'));
echo $toshkent->format('H:i');   // 14:30

$nyuYork = $utc->setTimezone(new DateTimeZone('America/New_York'));
echo $nyuYork->format('H:i');    // 04:30

O'zbekcha oy nomlari

date('F') inglizcha nom beradi (November). O'zbekcha uchun oddiy massiv yetarli:

<?php

function sanaUz(DateTimeInterface $sana): string
{
    $oylar = [1 => 'yanvar', 'fevral', 'mart', 'aprel', 'may', 'iyun',
              'iyul', 'avgust', 'sentabr', 'oktabr', 'noyabr', 'dekabr'];
    return $sana->format('j') . '-' . $oylar[(int) $sana->format('n')] . ', ' . $sana->format('Y');
}

echo sanaUz(new DateTimeImmutable('2026-11-02'));   // 2-noyabr, 2026

Kengaytirilgan variant — intl kengaytmasidagi IntlDateFormatter (uz_Latn lokali).


Nima uchun shunday?

Nima uchun vaqt 1970-yildan hisoblanadi?

Unix operatsion tizimi 1970-yillar boshida yaratilgan. Ishlab chiquvchilar "yaqin, yumaloq sana" sifatida 1970-yil 1-yanvarni tanlashgan. 32-bitli tizimlarda bu son 2038-yil 19-yanvarda to'lib qoladi ("2038-yil muammosi") — xuddi 2000-yil muammosi kabi. 64-bitli PHP da bu muammo yo'q: chegara milliardlab yil keyin.


Xulosa

Eslab qoling:

  • date_default_timezone_set('Asia/Tashkent') — yoki php.ini da; bu birinchi ish
  • date('Y-m-d H:i:s') — formatlash; time() — joriy timestamp
  • Hisob-kitob uchun DateTimeImmutable — DateTime emas
  • ->modify('+30 days'), ->diff(), ->setTimezone()
  • Foydalanuvchi sanasini createFromFormat + getLastErrors() bilan tekshiring
  • "+1 month" oy oxirida kutilmagan natija beradi — last day of next month

Keyingi dars

27-dars: Xatolar — try/catch/finally, Exception, error_reporting