Mundarija (28)
- Bu darsda
- 1. Nega bu kerak?
- 2. timestamptz ichida nima saqlanadi
- 2.1 Bitta lahza, ikki ko'rinish
- 2.2 Sessiya zonasi qayerdan keladi
- 3. AT TIME ZONE: ikki yo'nalish
- 3.1 timestamptz → "devor soati"
- 3.2 timestamp → timestamptz
- 3.3 Zona nomi kirish satrida
- 4. Kun chegarasi: hisobotdagi jim xato
- 4.1 Xato qanday tug'iladi
- 4.2 Kunlik hisobot: noto'g'ri va to'g'ri
- 4.3 "Bugun muddati tugaydiganlar"
- 5. Yozgi vaqt (DST) va tarix
- 5.1 Zona nomi va offset — boshqa narsalar
- 5.2 "1 kun" ≠ "24 soat"
- 5.3 Tarix ham zonada
- 6. Interval arifmetikasi: oy oxiri
- 7. Node tomoni: Date — bitta lahza
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson)
- 2-mashq (o'rta)
- 3-mashq (qiyin)
- 10. SQL qadami
- 11. Real ishda
- Xulosa
- Manbalar
SQL'da sana va vaqt zonalari: timestamptz, AT TIME ZONE va kun chegarasi
Qisqacha:
timestamptzustuni vaqtni ichida doim UTC'da saqlaydi — zona faqat ko'rsatishda qo'shiladi. Qaysi zonada ko'rsatishni sessiya zonasi (SET TIME ZONE) yokiAT TIME ZONE 'Asia/Tashkent'hal qiladi. Kunlik hisobotda "kun" qaysi zonada boshlanishini aniq ayting: aks holda Toshkentda tungi 02:00 da yaratilgan vazifa oldingi kunga tushadi.
Bu darsda
timestamptzqanday saqlanishini va nega ekranda+00yoki+05chiqishini tushuntira olasiz.AT TIME ZONEning ikki yo'nalishini farqlaysiz va to'g'ri ishlatasiz.- "Bugun", "shu hafta" kabi oraliqlarni Toshkent kuni bo'yicha yozasiz.
date_truncni zona bilan ishlatib, kunlik va haftalik hisobot tuzasiz.- Yozgi vaqt (DST), oy oxiri va zonasiz
timestamptuzoqlarini oldindan bilasiz.
Oldin bilishingiz kerak: PostgreSQL ma'lumot turlari, Ifodalar va o'rnatilgan funksiyalar, Tahliliy SQL naqshlari, Date va vaqt zonalari.
1. Nega bu kerak?
O'tgan darsda kunlik hisobot yozdik: har kuni nechta vazifa yaratilgan. U yerda bir qator ko'zga tashlanmay o'tdi — SET TIME ZONE 'Asia/Tashkent'. Shu qatorsiz hisobot boshqacha chiqardi.
Tasavvur qiling: Aziz soat 02:00 da uxlay olmay, ertangi ishlarni vazifalar ga yozdi. Uning uchun bu — 5-oktyabr. Server esa UTC'da ishlaydi va u yerda soat hali 21:00, ya'ni 4-oktyabr. Ertalab Aziz "kecha nechta vazifa qo'shdim?" deb hisobotni ochadi va raqam bittaga kam chiqadi.
Bu xato hech qanday ogohlantirish bermaydi. So'rov ishlaydi, raqam chiqadi — faqat noto'g'ri. Shuning uchun vaqt zonasi bilan xato ishlab chiqarishda eng uzoq yashaydigan xatolardan biri.
O'xshatish: xalqaro reys chiptasi. Chiptada "jo'nash 23:40" deb yozilgan, lekin qaysi shahar soati bo'yicha? Aeroport doim mahalliy soatni yozadi, aviakompaniya ichida esa hamma narsa bitta umumiy soatda (UTC) yuradi. Postgres ham shunday: ichida bitta soat, ko'rsatishda — siz so'ragan shahar soati.
2. timestamptz ichida nima saqlanadi
2.1 Bitta lahza, ikki ko'rinish
Ma'lumot turlari darsida ikki turni ko'rdik: timestamp (zonasiz) va timestamptz (zonali). vazifalar dagi hamma vaqt ustunlari — timestamptz.
Nomi aldaydi: timestamptz zonani saqlamaydi. U kirgan vaqtni UTC'ga o'girib, 8 baytli son sifatida yozadi. Zona faqat kirishda (o'girish uchun) va chiqishda (ko'rsatish uchun) ishlatiladi.
Kanon bazasida sessiya zonasi — UTC (rasmiy postgres:18 Docker image'ining sukuti). 1-vazifaning muddatini so'raymiz:
SHOW timezone; TimeZone
----------
UTC
(1 row)SELECT matn, muddat FROM vazifalar WHERE id = 1; matn | muddat
--------------------------------------------------+------------------------
JavaScript kursining 26-qismini boshlash #o'qish | 2026-10-31 18:59:00+00
(1 row)+00 — bu UTC soati. Endi sessiya zonasini Toshkentga o'zgartiramiz va xuddi o'sha qatorni o'qiymiz:
SET TIME ZONE 'Asia/Tashkent';
SELECT matn, muddat FROM vazifalar WHERE id = 1;SET
matn | muddat
--------------------------------------------------+------------------------
JavaScript kursining 26-qismini boshlash #o'qish | 2026-10-31 23:59:00+05
(1 row)Bazada hech narsa o'zgarmadi — biz UPDATE qilmadik. O'zgargani faqat ko'rinish: 18:59 UTC va 23:59 Toshkent — bitta lahza. Bu yerdagi chiqishlar kanon bazasidan (PostgreSQL 18.6); biz o'z PostgreSQL 17.6 klasterimizda ham ishga tushirdik — natija harfma-harf bir xil.
2.2 Sessiya zonasi qayerdan keladi
Sessiya zonasi (session time zone) — shu ulanish uchun "ko'rsatish soati". Uni bir necha joy belgilaydi:
| Qayerda | Misol | Ta'siri |
|---|---|---|
| Server sozlamasi | timezone = 'UTC' (postgresql.conf) |
Hamma yangi ulanishlar |
| Baza yoki rol | ALTER DATABASE … SET timezone … |
Shu bazaga ulanganlar |
| Ulanish | PGTZ=Asia/Tashkent (psql muhiti) |
Shu ulanish |
| Sessiya | SET TIME ZONE 'Asia/Tashkent' |
Shu ulanish, hozirdan |
Docker image'da sukut — UTC. Windows'dagi EDB o'rnatuvchisi esa odatda kompyuter zonasini yozadi. Shuning uchun bir xil so'rov ikki kompyuterda boshqacha ko'rinishi mumkin, lekin saqlangan qiymat bir xil.
Maslahat: Server va baza zonasini UTC'da qoldiring. Toshkent soati kerak bo'lgan joyda — so'rovda aniq
AT TIME ZONE 'Asia/Tashkent'yozing. Sessiya sozlamasiga tayangan so'rov boshqa ulanishda boshqacha natija beradi.
Quyidagi diagrammada butun yo'l: kirish satri → UTC'da saqlash → har xil zonada ko'rsatish. Nimaga qarang: o'rtadagi "saqlangan qiymat" bitta, chiqishlar uchta.
flowchart LR
K["Kirish: '2026-10-31 23:59+05'"] --> S["Saqlanadi: 18:59 UTC"]
S --> U["UTC sessiya: 18:59+00"]
S --> T["Toshkent sessiya: 23:59+05"]
S --> N["AT TIME ZONE New York: 14:59"]Tekshirib ko'ring: Ali
SET TIME ZONE 'Asia/Tashkent'qilib, 1-vazifani o'qidi. Malika boshqa ulanishdan, UTC sessiyada o'qidi. Kim to'g'ri vaqtni ko'rdi?
Javob
Ikkalasi ham. Ular bitta lahzani ko'rdi: 23:59+05 va 18:59+00 — bir xil vaqt, faqat boshqa soat bo'yicha yozilgan. +05 va +00 qo'shimchasi qaysi soat ekanini aytib turibdi.
3. AT TIME ZONE: ikki yo'nalish
3.1 timestamptz → "devor soati"
Ba'zan sizga zonasiz, "devordagi soat"dek qiymat kerak: Toshkentda o'sha paytda soat necha edi? Buni AT TIME ZONE beradi. Kirish timestamptz bo'lsa, natija timestamp (zonasiz):
SELECT muddat AT TIME ZONE 'UTC' AS utc_soat,
muddat AT TIME ZONE 'Asia/Tashkent' AS toshkent_soat,
muddat AT TIME ZONE 'America/New_York' AS nyu_york
FROM vazifalar WHERE id = 1; utc_soat | toshkent_soat | nyu_york
---------------------+---------------------+---------------------
2026-10-31 18:59:00 | 2026-10-31 23:59:00 | 2026-10-31 14:59:00
(1 row)E'tibor bering: natijada +05 yo'q. Bu endi "lahza" emas, "Toshkent devoridagi soat". Uni ekranga chiqarish va kunni ajratib olish uchun ishlatamiz.
3.2 timestamp → timestamptz
Teskari yo'nalish ham bor. Kirish zonasiz timestamp bo'lsa, AT TIME ZONE uni "shu shahar soati" deb tushunadi va haqiqiy lahzaga aylantiradi:
SET TIME ZONE 'Asia/Tashkent';
SELECT '2026-10-24 09:00'::timestamp AT TIME ZONE 'Asia/Tashkent'
AS toshkentdagi_9;SET
toshkentdagi_9
------------------------
2026-10-24 09:00:00+05
(1 row)Sessiya Toshkentda, shuning uchun natija +05 bilan ko'rindi. UTC sessiyada xuddi shu lahza 04:00:00+00 bo'lib ko'rinardi.
Qoida bitta jadvalda:
| Kirish turi | x AT TIME ZONE 'Asia/Tashkent' |
Natija turi |
|---|---|---|
timestamptz (lahza) |
Toshkentda soat necha edi? | timestamp |
timestamp (devor soati) |
Bu Toshkent soati — qaysi lahza? | timestamptz |
Boshida chalkash tuyulishi tabiiy. Eslab qolish uchun: AT TIME ZONE har safar turni almashtiradi — zonali kirsa zonasiz chiqadi, zonasiz kirsa zonali chiqadi.
3.3 Zona nomi kirish satrida
Uchinchi usul ham bor: zonani satrning o'ziga yozish. '2026-10-25 00:00 Asia/Tashkent' — Postgres buni "Toshkentdagi yarim tun" deb o'qiydi. Keyingi bo'limda shu usuldan foydalanamiz.
Tekshirib ko'ring:
muddat AT TIME ZONE 'Asia/Tashkent'natijasinitimestamptzustuniga qayta yozsangiz nima bo'ladi?
Javob
Natija zonasiz timestamp. Uni timestamptz ustuniga yozganda Postgres uni sessiya zonasi soati deb o'qiydi. UTC sessiyada 23:59 "23:59 UTC" bo'lib qoladi — vaqt 5 soatga siljiydi. Shuning uchun AT TIME ZONE natijasini faqat ko'rsatish va guruhlash uchun ishlating, saqlash uchun emas.
4. Kun chegarasi: hisobotdagi jim xato
4.1 Xato qanday tug'iladi
"Qaysi kuni yaratilgan?" degan savolni ko'pchilik shunday yozadi: yaratilgan_vaqt::date. Lekin ::date kunni sessiya zonasida kesadi. Kanon namuna ma'lumotida hamma vazifalar Toshkentda 06:00 dan 23:59 gacha yaratilgan — shuning uchun UTC kuni va Toshkent kuni hech qayerda farq qilmaydi (kanon tekshiruvi: 0 qator). Bu xatoni ko'rish uchun Azizning tungi vazifasini qo'shamiz (bo'lim oxirida o'chirib tashlaymiz). Sessiya zonasi — kanondagi sukut, UTC:
INSERT INTO vazifalar (egasi_id, matn, yaratilgan_vaqt)
VALUES (1, 'Tungi reja #ish', '2026-10-05 02:00+05')
RETURNING id, yaratilgan_vaqt; id | yaratilgan_vaqt
----+------------------------
48 | 2026-10-04 21:00:00+00
(1 row)
INSERT 0 1Toshkentda 5-oktyabr, 02:00. UTC'da hali 4-oktyabr, 21:00. Endi ikki xil "kun"ni yonma-yon qo'yamiz va faqat farq qiladiganlarini olamiz:
SELECT id,
yaratilgan_vaqt::date AS utc_kuni,
(yaratilgan_vaqt AT TIME ZONE 'Asia/Tashkent')::date
AS toshkent_kuni
FROM vazifalar
WHERE (yaratilgan_vaqt AT TIME ZONE 'Asia/Tashkent')::date
<> yaratilgan_vaqt::date; id | utc_kuni | toshkent_kuni
----+------------+---------------
48 | 2026-10-04 | 2026-10-05
(1 row)Mana u — bitta vazifa, ikki xil kun. Bu va keyingi bo'limdagi tungi vazifa misolini biz o'z PostgreSQL 17.6 klasterimizda, kanon holat 23 sxemasi va namunasi ustida ishga tushirdik.
4.2 Kunlik hisobot: noto'g'ri va to'g'ri
Endi 4–5 oktyabr bo'yicha kunlik sonni ikki usulda hisoblaymiz. Avval "sodda" usul — UTC sessiyada date_trunc('day', …):
SELECT date_trunc('day', yaratilgan_vaqt) AS kun, count(*)
FROM vazifalar
WHERE yaratilgan_vaqt >= '2026-10-04 00:00 Asia/Tashkent'
AND yaratilgan_vaqt < '2026-10-06 00:00 Asia/Tashkent'
GROUP BY kun
ORDER BY kun; kun | count
------------------------+-------
2026-10-04 00:00:00+00 | 3
2026-10-05 00:00:00+00 | 6
(2 rows)Endi to'g'ri usul. PostgreSQL 12 dan boshlab date_trunc uchinchi argument — zonani oladi. U kunni o'sha zonaning yarim tunidan kesadi:
SET TIME ZONE 'Asia/Tashkent';
SELECT date_trunc('day', yaratilgan_vaqt, 'Asia/Tashkent') AS kun,
count(*)
FROM vazifalar
WHERE yaratilgan_vaqt >= '2026-10-04 00:00 Asia/Tashkent'
AND yaratilgan_vaqt < '2026-10-06 00:00 Asia/Tashkent'
GROUP BY kun
ORDER BY kun;SET
kun | count
------------------------+-------
2026-10-04 00:00:00+05 | 2
2026-10-05 00:00:00+05 | 7
(2 rows)Jami ikkalasida ham 9. Lekin taqsimot boshqa: UTC hisobotida 4-oktyabr "3", Toshkent hisobotida "2". Tungi vazifa noto'g'ri kunga tushgan. Bir vazifa kam ko'rinadi — lekin 10 000 foydalanuvchili ilovada bu har kuni yuzlab yozuvni noto'g'ri kunga suradi.
Tajriba vazifasini o'chiramiz — namuna ma'lumot asl holiga qaytsin:
DELETE FROM vazifalar WHERE matn = 'Tungi reja #ish';DELETE 1Ikkinchi so'rovdan oldin SET TIME ZONE 'Asia/Tashkent' qildik — faqat chiqish chiroyli bo'lishi uchun (00:00+05). UTC sessiyada ham guruhlash to'g'ri bo'lardi, faqat kun boshi 2026-10-03 19:00:00+00 bo'lib ko'rinardi. Guruhlashni uchinchi argument hal qiladi, sessiya emas.
Diqqat:
date_truncning uch argumentli shakli PostgreSQL 12 da qo'shilgan (PostgreSQL 12 reliz eslatmalari). Undan eski serverda xuddi shu natijanidate_trunc('day', yaratilgan_vaqt AT TIME ZONE 'Asia/Tashkent')beradi — natija zonasiztimestampbo'ladi.
4.3 "Bugun muddati tugaydiganlar"
Ilova kuni bilan ko'p ishlaydi: "bugun", "ertaga", "shu hafta". Eng ishonchli shakl — yarim ochiq oraliq: >= kun boshi va < keyingi kun boshi. "Bugun" — 2026-10-25 deb olamiz (kanon so'rovi):
SELECT matn, muddat AT TIME ZONE 'Asia/Tashkent' AS toshkentda
FROM vazifalar
WHERE muddat >= '2026-10-25 00:00 Asia/Tashkent'
AND muddat < '2026-10-26 00:00 Asia/Tashkent'
ORDER BY muddat; matn | toshkentda
------------------------------------------+---------------------
Soliq hisobotini topshirish #ish #to'lov | 2026-10-25 18:00:00
SQL JOIN mashqlarini yechish #o'qish | 2026-10-25 21:00:00
(2 rows)Nega BETWEEN '…00:00' AND '…23:59:59' emas? Ikki sabab:
23:59:59.5dagi vazifa tushib qoladi —timestamptzmikrosekundgacha aniq.WHEREda ustunga funksiya qo'llamadik (muddato'zi solishtirildi). Shunday so'rov keyinchalik indeksdan foydalana oladi;(muddat AT TIME ZONE …)::date = '2026-10-25'esa har qatorni hisoblashga majbur qiladi. Indekslarni Indeks nima darsida o'rganamiz.
Tekshirib ko'ring: "Ertaga muddati tugaydiganlar" uchun qaysi ikki chegara kerak (bugun 2026-10-25)?
Javob
muddat >= '2026-10-26 00:00 Asia/Tashkent' AND muddat < '2026-10-27 00:00 Asia/Tashkent'. Chap chegara kiradi (>=), o'ng chegara kirmaydi (<) — shunda ketma-ket kunlar bir-birini kesmaydi va bo'shliq qolmaydi.
5. Yozgi vaqt (DST) va tarix
5.1 Zona nomi va offset — boshqa narsalar
Offset — UTC'dan farq: +05. Zona nomi — Asia/Tashkent, ya'ni shu shahar soatining butun tarixi. Ba'zi mamlakatlarda yilda ikki marta soat suriladi — yozgi vaqt (daylight saving time, DST). Shuning uchun offset yil davomida o'zgaradi, zona nomi esa o'zgarmaydi.
Postgres o'zi biladigan zonalarni pg_timezone_names ko'rinishida beradi. Natija bugungi sanaga bog'liq — biz 2026-10-24 da o'z PostgreSQL 17.6 klasterimizda ishga tushirdik (kanon 18.6 ham shu kuni aynan shunday chiqargan):
SELECT name, utc_offset, is_dst
FROM pg_timezone_names
WHERE name IN ('Asia/Tashkent', 'Asia/Samarkand',
'Europe/Berlin', 'America/New_York')
ORDER BY name; name | utc_offset | is_dst
------------------+------------+--------
America/New_York | -04:00:00 | t
Asia/Samarkand | 05:00:00 | f
Asia/Tashkent | 05:00:00 | f
Europe/Berlin | 02:00:00 | t
(4 rows)O'zbekistonda hozir yozgi vaqt yo'q (is_dst = f). Berlin va Nyu-York esa ayni paytda yozgi vaqtda.
5.2 "1 kun" ≠ "24 soat"
Berlinda 2026-yil 25-oktyabr kechasi soat bir soat orqaga suriladi. Shu kunni kesib o'tadigan ikki qo'shishni solishtiramiz (kanon chiqishi):
SET TIME ZONE 'Europe/Berlin';
SELECT '2026-10-24 12:00'::timestamptz + interval '1 day'
AS bir_kun,
'2026-10-24 12:00'::timestamptz + interval '24 hours'
AS yigirma_tort_soat;SET
bir_kun | yigirma_tort_soat
------------------------+------------------------
2026-10-25 12:00:00+01 | 2026-10-25 11:00:00+01
(1 row)'1 day' — "ertaga xuddi shu soatda" (taqvim kuni, bu safar 25 soat). '24 hours' — aniq 24 soat. DST bo'lmagan Toshkentda ikkalasi bir xil:
SET TIME ZONE 'Asia/Tashkent';
SELECT '2026-10-24 12:00'::timestamptz + interval '1 day'
AS bir_kun,
'2026-10-24 12:00'::timestamptz + interval '24 hours'
AS yigirma_tort_soat;SET
bir_kun | yigirma_tort_soat
------------------------+------------------------
2026-10-25 12:00:00+05 | 2026-10-25 12:00:00+05
(1 row)Qaysi biri kerakligi vazifaga bog'liq. "Har kuni 09:00 da eslatma" — '1 day'. "Token 24 soatdan keyin eskiradi" — '24 hours'. Ilovangiz faqat Toshkentda ishlasa ham farqni biling: foydalanuvchi Berlindan kirishi mumkin.
5.3 Tarix ham zonada
Zona nomi o'tmishni ham eslaydi. Postgres vaqt zonalari bazasi (IANA tz database) bo'yicha 1982-yil yozida Toshkentda soat boshqacha edi:
SET TIME ZONE 'Asia/Tashkent';
SELECT '1982-07-01 12:00'::timestamp AT TIME ZONE 'Asia/Tashkent'
AS yoz_1982,
'2026-07-01 12:00'::timestamp AT TIME ZONE 'Asia/Tashkent'
AS yoz_2026;SET
yoz_1982 | yoz_2026
------------------------+------------------------
1982-07-01 12:00:00+07 | 2026-07-01 12:00:00+05
(1 row)1982-yilning yozida +07, bugun +05. Shuning uchun bazaga offset (+05) emas, zona nomi (Asia/Tashkent) bilan ishlash to'g'ri: tarixiy va kelajakdagi o'zgarishlarni zona bazasi o'zi hisobga oladi.
Tekshirib ko'ring: Foydalanuvchi profilida zonani qanday saqlaysiz:
'+05'yoki'Asia/Tashkent'?
Javob
'Asia/Tashkent'. Offset faqat bitta lahzaga to'g'ri. Zona nomi yozgi vaqtni, tarixni va kelajakdagi qonun o'zgarishlarini o'zi hisoblaydi. Berlinlik foydalanuvchi uchun +02 yozsangiz, noyabrda hamma eslatmasi bir soat siljiydi.
6. Interval arifmetikasi: oy oxiri
Interval — vaqt oralig'i ('1 month', '3 days'). Oy qo'shish "shuncha kun qo'shish" emas — oylar uzunligi har xil. Postgres oy oxirida kunni qisqartiradi (kanon chiqishi):
SELECT '2026-01-31'::date + interval '1 month' AS yanvar31_plus_oy,
'2026-03-31'::date - interval '1 month' AS mart31_minus_oy,
age('2026-10-24', '2026-01-31'); yanvar31_plus_oy | mart31_minus_oy | age
---------------------+---------------------+----------------
2026-02-28 00:00:00 | 2026-02-28 00:00:00 | 8 mons 24 days
(1 row)Ikki narsaga qarang. Birinchidan, 31-yanvar + 1 oy = 28-fevral (31-fevral yo'q). Ikkinchidan, 28-fevral + 1 oy = 28-mart, 31 emas — ya'ni + 1 month - 1 month asl sanaga qaytarmasligi mumkin. Oylik obuna kabi narsalarda "har oyning oxirgi kuni" kerak bo'lsa, buni date_trunc('month', …) + interval '1 month' - interval '1 day' bilan aniq yozing.
age natijasi — "8 oy 24 kun". Funksiyalar darsida ko'rganimizdek, u yosh yoki staj kabi "inson tilidagi" farq uchun qulay.
7. Node tomoni: Date — bitta lahza
Postgres'dan kelgan timestamptz JavaScript'da Date bo'ladi — u ham bitta lahza (1970-yildan beri millisekundlar). Uni qaysi zonada ko'rsatish — frontend yoki server kodining ishi (Intl.DateTimeFormat). sv-SE lokali sanani YYYY-MM-DD HH:MM:SS ko'rinishida beradi — psql chiqishi bilan solishtirish oson:
const muddat = new Date("2026-10-31T18:59:00Z");
for (const zona of ["UTC", "Asia/Tashkent", "America/New_York"]) {
const soat = muddat.toLocaleString("sv-SE", { timeZone: zona });
console.log(zona.padEnd(17), soat);
}
console.log(muddat.toISOString());Konsolda:
UTC 2026-10-31 18:59:00
Asia/Tashkent 2026-10-31 23:59:00
America/New_York 2026-10-31 14:59:00
2026-10-31T18:59:00.000ZNatija AT TIME ZONE bilan aynan bir xil. Qoida ikki tomonda ham bir: saqlash va uzatish — UTC lahza (toISOString() — Z bilan), ko'rsatish — foydalanuvchi zonasida.
Bitta tuzoqni oldindan aytib qo'yamiz: zonasiz timestamp va date turlari Node drayverida boshqacha o'qiladi — bu haqida node-postgres asoslari darsida o'lchov bilan gaplashamiz. Hozircha bilish kifoya: vaqt ustunlarida timestamptz tanlash shu muammoning yarmini oldindan yo'q qiladi.
8. Ko'p uchraydigan xatolar
1. Zonasiz timestamp ga offset yozish. Kirish satrida +05 bor, lekin ustun timestamp bo'lsa — offset jim tashlab yuboriladi (kanon chiqishi):
SELECT '2026-10-24 09:00+05'::timestamp AS offset_yoqoldi; offset_yoqoldi
---------------------
2026-10-24 09:00:00
(1 row)Hech qanday xato yo'q. "09:00" qaysi zonada ekani endi hech kimga ma'lum emas. Tuzatish: vaqt ustunlari uchun doim timestamptz.
2. ::date bilan kunni ajratish. Kun sessiya zonasida kesiladi — UTC serverda Toshkentning 00:00–05:00 oralig'i oldingi kunga tushadi. Tuzatish: (ustun AT TIME ZONE 'Asia/Tashkent')::date yoki date_trunc('day', ustun, 'Asia/Tashkent').
3. AT TIME ZONE natijasini saqlash. Natija zonasiz — timestamptz ustuniga yozilganda sessiya zonasida qayta o'qiladi va siljiydi. Tuzatish: saqlashda asl timestamptz qiymatni yozing.
4. BETWEEN … AND '23:59:59'. Oxirgi soniyaning ichidagi vaqtlar tushib qoladi. Tuzatish: yarim ochiq oraliq >= boshi AND < keyingi_boshi.
5. Offset bilan zona saqlash. '+05' yozgi vaqtni bilmaydi. Tuzatish: IANA nomi ('Asia/Tashkent').
6. Noto'g'ri zona nomi. SET TIME ZONE 'Toshkent' yoki 'Asia/Toshkent' — xato: ERROR: invalid value for parameter "TimeZone": "Asia/Toshkent". Rasmiy nom ingliz yozuvida: Asia/Tashkent. To'g'ri nomlar ro'yxati — pg_timezone_names.
9. Mashqlar
Hamma mashqlar holat 23 sxemasida (namuna ma'lumot bilan), sessiya zonasi UTC.
1-mashq (oson)
1-vazifaning muddat ini Tokio ('Asia/Tokyo') va London ('Europe/London') devor soatida chiqaring. Ustun nomlari: tokio, london. Ishora: «AT TIME ZONE: ikki yo'nalish» bo'limidagi birinchi so'rov.
Yechim
SELECT muddat AT TIME ZONE 'Asia/Tokyo' AS tokio,
muddat AT TIME ZONE 'Europe/London' AS london
FROM vazifalar WHERE id = 1; tokio | london
---------------------+---------------------
2026-11-01 03:59:00 | 2026-10-31 18:59:00
(1 row)Tokioda bu paytda allaqachon 1-noyabr. Londonda soat UTC bilan bir xil: 2026-yilda Britaniya 25-oktyabrda yozgi vaqtdan chiqadi, 31-oktyabr — qishki vaqt (+00). Yozda bu ustun 19:59 bo'lardi.
2-mashq (o'rta)
1–3 oktyabr kunlari (Toshkent kuni bo'yicha) har kuni nechta vazifa yaratilganini chiqaring. Ustunlar: kun (date), soni. WHERE da ustunga funksiya qo'llamang — yarim ochiq oraliq ishlating. Ishora: guruhlash — GROUP BY, kun — (… AT TIME ZONE 'Asia/Tashkent')::date.
Yechim
SELECT (yaratilgan_vaqt AT TIME ZONE 'Asia/Tashkent')::date
AS kun,
count(*) AS soni
FROM vazifalar
WHERE yaratilgan_vaqt >= '2026-10-01 00:00 Asia/Tashkent'
AND yaratilgan_vaqt < '2026-10-04 00:00 Asia/Tashkent'
GROUP BY kun
ORDER BY kun; kun | soni
------------+------
2026-10-01 | 3
2026-10-02 | 7
2026-10-03 | 6
(3 rows)WHERE chegaralari zona nomi bilan yozilgan satrlar — Postgres ularni bir marta lahzaga o'giradi va ustunni to'g'ridan-to'g'ri solishtiradi. Guruhlash esa Toshkent kunida.
3-mashq (qiyin)
Bajarilmagan, muddati bor vazifalarni muddatining Toshkent haftasi bo'yicha guruhlang (hafta dushanbadan boshlanadi). Ustunlar: hafta, soni. Natija +05 bilan ko'rinsin. Ishora: date_trunc('week', …) Funksiyalar darsida bor; bu yerda uchinchi argument kerak.
Yechim
SET TIME ZONE 'Asia/Tashkent';
SELECT date_trunc('week', muddat, 'Asia/Tashkent') AS hafta,
count(*) AS soni
FROM vazifalar
WHERE muddat IS NOT NULL AND NOT bajarildi
GROUP BY hafta
ORDER BY hafta;SET
hafta | soni
------------------------+------
2026-09-28 00:00:00+05 | 1
2026-10-05 00:00:00+05 | 2
2026-10-12 00:00:00+05 | 1
2026-10-19 00:00:00+05 | 5
2026-10-26 00:00:00+05 | 5
2026-11-09 00:00:00+05 | 1
(6 rows)SET TIME ZONE faqat ko'rinish uchun — guruhlashni uchinchi argument bajaradi. 2-noyabr haftasi yo'q: o'sha haftada bajarilmagan muddatli vazifa yo'q, shuning uchun qator ham yo'q. Bo'sh haftalarni 0 bilan ko'rsatish kerak bo'lsa — Tahliliy SQL naqshlari dagi generate_series usuli.
10. SQL qadami
Bu darsda vazifalar sxemasi o'zgarmaydi — holat 23 da qolamiz. Sabab: sxema allaqachon to'g'ri. Hamma vaqt ustunlari (yaratilgan_vaqt, muddat, bajarilgan_vaqt) birinchi kundan timestamptz, namuna ma'lumot vaqtlari esa aniq offset bilan yozilgan ('2026-10-01 08:30+05'), now() bilan emas — shuning uchun darsdagi chiqishlar har qurishda bir xil.
Zona qarori ilova so'rovlarida: server va baza UTC'da qoladi, "kun", "hafta", "bugun" so'rovlari esa 'Asia/Tashkent' ni aniq yozadi. Foydalanuvchi zonasini profilda saqlash (foydalanuvchilar.zona) — hozircha kerak emas: ilova faqat Toshkent uchun. Kerak bo'lsa, bu ham IANA nomi bilan text ustun bo'ladi.
Commit yo'q — sxema fayllari o'zgarmadi.
11. Real ishda
- Hisobotlar va dashboard'lar. "Kunlik faol foydalanuvchilar", "bugungi buyurtmalar" — hammasi kun chegarasiga bog'liq. Analitika jadvallarida xato zona hisoboti oylab sezilmasdan qolishi mumkin.
- Eslatmalar va cron. "Har kuni 09:00 da" — foydalanuvchi zonasidagi 09:00. Server UTC'da, shuning uchun hisob
'1 day'va zona nomi bilan. - To'lov tizimlari. Click, Payme kabi xizmatlar hisobotida "kun" Toshkent vaqtida. UTC'da kesilgan hisobot bank ko'chirmasiga mos kelmaydi.
- Intervyuda: "timestamp va timestamptz farqi?", "foydalanuvchi zonasini qanday saqlaysiz?" — tez-tez so'raladi. Javob:
timestamptzUTC'da saqlaydi, zona — IANA nomi, ko'rsatish — chekkada.
Xulosa
timestamptzichida UTC lahza saqlanadi;+05yoki+00— faqat ko'rinish.AT TIME ZONEturni almashtiradi: lahza → devor soati va aksincha.- Kun chegarasini aniq zona bilan kesing:
date_trunc('day', x, 'Asia/Tashkent')(PG 12+). - Oraliqlar — yarim ochiq:
>= boshi AND < keyingi_boshi, ustunga funksiya qo'llamasdan. '1 day'va'24 hours'DST'da farq qiladi; zonani offset emas, IANA nomi bilan saqlang.- Zonasiz
timestampoffsetni jim tashlaydi — vaqt ustunlari uchuntimestamptz.
Keyingi dars: ER modellash va diagrammalar — SQL so'rovlaridan sxema loyihalashga o'tamiz: jadval yozishdan oldin entity va bog'lanishlarni chizib olishni o'rganamiz.
Manbalar
- PostgreSQL 18 hujjati: "Date/Time Types" va "Date/Time Functions and Operators" (
AT TIME ZONE,date_trunc) — postgresql.org/docs/18, 2026-10-24 da ko'rildi. - PostgreSQL 12 reliz eslatmalari (
date_truncuchinchi argumenti — vaqt zonasi): https://www.postgresql.org/docs/release/12.0/ - IANA Time Zone Database —
Asia/Tashkenttarixi (Postgres o'z nusxasini ishlatadi).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!