IlmHamroh
JavaScript Full-stack/26-qism. SQL va PostgreSQL32/64-dars17 daqiqa
Mundarija (28)

SQL'da sana va vaqt zonalari: timestamptz, AT TIME ZONE va kun chegarasi

Qisqacha: timestamptz ustuni vaqtni ichida doim UTC'da saqlaydi — zona faqat ko'rsatishda qo'shiladi. Qaysi zonada ko'rsatishni sessiya zonasi (SET TIME ZONE) yoki AT 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

  • timestamptz qanday saqlanishini va nega ekranda +00 yoki +05 chiqishini tushuntira olasiz.
  • AT TIME ZONE ning ikki yo'nalishini farqlaysiz va to'g'ri ishlatasiz.
  • "Bugun", "shu hafta" kabi oraliqlarni Toshkent kuni bo'yicha yozasiz.
  • date_trunc ni zona bilan ishlatib, kunlik va haftalik hisobot tuzasiz.
  • Yozgi vaqt (DST), oy oxiri va zonasiz timestamp tuzoqlarini 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:

sql
SHOW timezone;
text
 TimeZone
----------
 UTC
(1 row)
sql
SELECT matn, muddat FROM vazifalar WHERE id = 1;
text
                       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:

sql
SET TIME ZONE 'Asia/Tashkent';
SELECT matn, muddat FROM vazifalar WHERE id = 1;
text
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):

sql
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;
text
      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:

sql
SET TIME ZONE 'Asia/Tashkent';
SELECT '2026-10-24 09:00'::timestamp AT TIME ZONE 'Asia/Tashkent'
  AS toshkentdagi_9;
text
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' natijasini timestamptz ustuniga 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:

sql
INSERT INTO vazifalar (egasi_id, matn, yaratilgan_vaqt)
VALUES (1, 'Tungi reja #ish', '2026-10-05 02:00+05')
RETURNING id, yaratilgan_vaqt;
text
 id |    yaratilgan_vaqt
----+------------------------
 48 | 2026-10-04 21:00:00+00
(1 row)

INSERT 0 1

Toshkentda 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:

sql
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;
text
 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', …):

sql
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;
text
          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:

sql
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;
text
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:

sql
DELETE FROM vazifalar WHERE matn = 'Tungi reja #ish';
text
DELETE 1

Ikkinchi 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_trunc ning uch argumentli shakli PostgreSQL 12 da qo'shilgan (PostgreSQL 12 reliz eslatmalari). Undan eski serverda xuddi shu natijani date_trunc('day', yaratilgan_vaqt AT TIME ZONE 'Asia/Tashkent') beradi — natija zonasiz timestamp bo'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):

sql
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;
text
                   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.5 dagi vazifa tushib qoladi — timestamptz mikrosekundgacha aniq.
  • WHERE da ustunga funksiya qo'llamadik (muddat o'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):

sql
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;
text
       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):

sql
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;
text
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:

sql
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;
text
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:

sql
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;
text
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):

sql
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');
text
  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:

js
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:

text
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.000Z

Natija 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):

sql
SELECT '2026-10-24 09:00+05'::timestamp AS offset_yoqoldi;
text
   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
sql
SELECT muddat AT TIME ZONE 'Asia/Tokyo' AS tokio,
       muddat AT TIME ZONE 'Europe/London' AS london
FROM vazifalar WHERE id = 1;
text
        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
sql
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;
text
    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
sql
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;
text
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: timestamptz UTC'da saqlaydi, zona — IANA nomi, ko'rsatish — chekkada.

Xulosa

  • timestamptz ichida UTC lahza saqlanadi; +05 yoki +00 — faqat ko'rinish.
  • AT TIME ZONE turni 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 timestamp offsetni jim tashlaydi — vaqt ustunlari uchun timestamptz.

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_trunc uchinchi argumenti — vaqt zonasi): https://www.postgresql.org/docs/release/12.0/
  • IANA Time Zone Database — Asia/Tashkent tarixi (Postgres o'z nusxasini ishlatadi).
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
SQL'da sana va vaqt zonalari: timestamptz, AT TIME ZONE va kun chegarasi — IlmHamroh