Mundarija (29)
- Bu darsda
- 1. Nega bu kerak?
- 2. Qoida: UTC'da saqlash, chegarada aylantirish
- 2.1 Uch joy
- 2.2 Kanon: ombordagi sanalar
- 3. Serverda Date tuzoqlari
- 3.1 Kanon o'lchovi
- 3.2 Haqiqiy sanani tekshirish
- 4. Mahalliy kun → UTC oraliq
- 4.1 Muammo
- 4.2 Kanon: src/umumiy/sana.ts
- 4.3 Kanon testi — Malikaning holati
- 5. O'zbekcha formatlash va ICU
- 5.1 Kanon o'lchovi
- 5.2 tz ma'lumotlar bazasi
- 6. Temporal va kutubxonalar
- 6.1 Temporal Node 24 da
- 6.2 Kutubxonalar
- 7. Cron va mintaqa
- 8. API qadami: sana va holat 40 commit'i (holat 40, 6-qism)
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): UTC'da nima?
- 2-mashq (o'rta): kun oralig'i
- 3-mashq (qiyin): "har kuni 09:00 Toshkent" — keyingi ishga tushish
- 4-mashq: API qadami
- 11. Real ishda
- Xulosa
- Manbalar
Backend'da sana va vaqt: UTC'da saqlash, Asia/Tashkent kuni va Temporal
Qisqacha: Serverda vaqt doim UTC'da saqlanadi va uzatiladi — ISO 8601, oxirida
Z:"2026-10-23T19:30:00.000Z". Mintaqa (Toshkent, UTC+5) faqat ikki joyda kerak: foydalanuvchi "24-oktabr" deganda uni UTC oralig'iga aylantirishda va matnni ko'rsatishda.vazifalar-apida?dan=2026-10-24— Toshkent kuni:[2026-10-23T19:00Z, 2026-10-24T19:00Z). Server jarayonining o'z mintaqasiga (TZ) hech narsa bog'lanmaydi —getDate()Toshkentda 24, UTC'da 23 qaytaradi. Temporal Node 24 da hali bayroq ortida (--harmony-temporal).
Bu darsda
- Nega vaqt UTC'da saqlanishini va mintaqa qayerda kerakligini tushuntira olasiz.
Datening serverdagi tuzoqlarini (sana-only satr, jim to'g'rilash, jarayonTZsi) bilasiz.- Mahalliy kun chegarasini
Intlbilan UTC lahzaga aylantirasiz (DST'li mintaqada ham). - O'zbekcha sana formatlash va uning ICU'ga bog'liq tuzoqlarini bilasiz.
- Temporal, date-fns, Luxon va cron mintaqasi haqida qaror qabul qila olasiz; holat 40 ni commit qilasiz.
Oldin bilishingiz kerak: Date va vaqt zonalari, Temporal API va sana kutubxonalari, Intl.DateTimeFormat, Env validatsiyasi va tipli config.
1. Nega bu kerak?
Malika yarim tundan keyin, 00:30 da vazifa qo'shdi. Ertalab ilovada "Bugungi vazifalar" ni ochdi — vazifasi yo'q. "Kechagi" ro'yxatda turibdi.
Sabab: server vazifani 2026-10-23T19:30:00.000Z deb saqlagan (to'g'ri), "bugun" filtri esa UTC sanasi bo'yicha ishlagan: UTC'da hali 23-oktabr. Malika uchun esa bu 24-oktabrning yarim tundan keyingi daqiqalari.
Boshqa ko'rinishi: server Germaniyadagi VPS'da, TZ=Europe/Berlin. Dasturchi new Date().getDate() yozdi — kod lokalda (Toshkent) bir xil, serverda boshqa natija beradi. Yoki aksincha — testlar dasturchi kompyuterida o'tadi, CI'da (UTC) yiqiladi.
Date va vaqt zonalari darsida bularni brauzerda ko'rgansiz. Serverda qo'shimcha muammo bor: server ko'p foydalanuvchiga xizmat qiladi va ularning mintaqasi har xil bo'lishi mumkin. Shuning uchun qoida qat'iy.
O'xshatish: aeroport tablosi. Hamma reyslar bitta umumiy soatda (UTC) rejalashtiriladi — uchuvchi, dispetcher, kompyuter bir-birini adashtirmasin. Yo'lovchiga esa tabloda mahalliy vaqt ko'rsatiladi. Rejalashtirish va ko'rsatish — ikki xil ish.
2. Qoida: UTC'da saqlash, chegarada aylantirish
2.1 Uch joy
| Joy | Shakl | vazifalar-api da |
|---|---|---|
| Saqlash (ombor, baza) | UTC lahza | yaratildi: "2026-10-23T19:30:00.000Z" |
| Uzatish (JSON javob) | ISO 8601, Z bilan |
o'sha satr |
| Foydalanuvchi kiritishi / ko'rsatish | Mahalliy kun yoki matn | ?dan=2026-10-24 (APP_TZ), frontend Intl |
Ikki atama:
- Lahza (instant) — vaqt o'qidagi bitta nuqta. Butun dunyoda bir xil:
19:30ZToshkentda 00:30, Londonda 20:30 — lekin lahza bitta. - Kalendar kuni — "24-oktabr". U lahza emas: Toshkentda 24-oktabr UTC bo'yicha 23-oktabr 19:00 da boshlanadi, Nyu-Yorkda — 04:00 da. Kun mintaqasiz ma'nosiz.
Frontend'ga lahzani (…Z) yuboring, matn emas. Brauzer foydalanuvchining o'z mintaqasida formatlaydi (Intl darsi).
2.2 Kanon: ombordagi sanalar
Kanon ombori (xotira-ombor.ts) vazifa yaratishda this.#soat().toISOString() ishlatadi — toISOString doim UTC va doim Z bilan (Date va vaqt zonalari darsidagi qoida). Holat 40 dagi shartnoma testlari (pastda, «API qadami») har omborda buni tekshiradi:
assert.match(v.yaratildi,
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/);26-qismda Postgres'da ustun turi timestamptz bo'ladi — u ham ichida UTC saqlaydi. Shartnoma testi Postgres ombori ham shu shaklni qaytarishini talab qiladi.
3. Serverda Date tuzoqlari
3.1 Kanon o'lchovi
t40-sana.txt (Node 24.21.0, tizim mintaqasi — Toshkent). Har qatorda chapda — kod, o'ngda — natija. Avval birinchi ikki qatorni solishtiring: kirish satrlari deyarli bir xil, natijalar esa 5 soat farq qiladi:
new Date("2026-10-24") → 2026-10-24T00:00:00.000Z (sana-only satr — UTC deb o'qiladi)
new Date("2026-10-24T00:00") → 2026-10-23T19:00:00.000Z (vaqtli, Z siz — MAHALLIY: TZ=tizim, Asia/Tashkent)
new Date(2026, 9, 24) → 2026-10-23T19:00:00.000Z (oy 0 dan; mahalliy)
new Date("2026-02-30") → Mon Mar 02 2026 05:00:00 GMT+0500 (Uzbekistan Standard Time); Date.UTC(2026,1,30) → 2026-03-02T00:00:00.000Z (jim to'g'rilaydi)
UTC lahza 2026-10-23T19:30:00.000Z → getDate() 24 (jarayon mintaqasi), getUTCDate() 23
TZ=UTC getDate/getHours: 23 19 Fri Oct 23 2026 19:30:00 GMT+0000 (Coordinated Universal Time)
TZ=Asia/Tashkent getDate/getHours: 24 0 Sat Oct 24 2026 00:30:00 GMT+0500 (Uzbekistan Standard Time)
TZ=America/New_York getDate/getHours: 23 15 Fri Oct 23 2026 15:30:00 GMT-0400 (Eastern Daylight Time)To'rt saboq:
- Bir xil ko'rinadigan ikki satr —
"2026-10-24"(UTC) va"2026-10-24T00:00"(mahalliy) — 5 soat farq. Serverda mahalliy = serverningTZsi, ya'ni tasodif. 2026-02-30xato emas —Dateuni jim 2-martga aylantiradi. Foydalanuvchi kiritgan sanani shunchakinew Date()ga berish — tekshiruv emas.getDate(),getHours()— jarayon mintaqasida. Bitta kod, uch xilTZ— uch xil javob. Serverda faqatgetUTC…yoki aniqtimeZonebilanIntl.TZmuhit o'zgaruvchisi — Node jarayonining mintaqasini o'zgartiradi. Ko'p server va konteynerlarda u odatda UTC; lekin bunga tayanib kod yozmang.
3.2 Haqiqiy sanani tekshirish
Kanon ?dan= uchun shunday tekshiradi (sxema.ts):
function haqiqiySana(x) {
if (!/^\d{4}-\d{2}-\d{2}$/.test(x)) return false;
const t = Date.parse(`${x}T00:00:00Z`);
return !Number.isNaN(t) && new Date(t).toISOString().startsWith(x);
}
for (const x of ["2026-10-24", "2026-02-30", "2028-02-29",
"2026-2-5", "2026-13-01"]) {
console.log(x, haqiqiySana(x));
}Konsolda:
2026-10-24 true
2026-02-30 false
2028-02-29 true
2026-2-5 false
2026-13-01 falseHiyla: sanani UTC yarim tuni sifatida o'qib, qayta matnga aylantiramiz. 2026-02-30 → 2026-03-02… — boshlanishi mos kelmadi, demak "jim to'g'rilangan". 2028-02-29 — kabisa yili, haqiqiy. T00:00:00Z — Z bilan: jarayon mintaqasi aralashmasin. Kanon jonli chiqishi:
$ curl $A/v1/vazifalar?dan=2026-02-30
{"type":"https://vazifalar-api.example/xatolar/parametr","title":"Parametr noto'g'ri","status":400,"detail":"dan — YYYY-MM-DD (haqiqiy sana)",…}4. Mahalliy kun → UTC oraliq
4.1 Muammo
?dan=2026-10-24&gacha=2026-10-24 — "Toshkentda 24-oktabr kuni yaratilgan vazifalar". Bizga UTC oraliq kerak: [24-okt 00:00 Toshkent, 25-okt 00:00 Toshkent) = [23-okt 19:00Z, 24-okt 19:00Z).
Toshkent uchun oddiy: har doim UTC+5 (IANA bazasiga ko'ra, O'zbekiston yozgi vaqtni oxirgi marta 1991 yilda ishlatgan, 1992 dan beri — yo'q). Lekin APP_TZ istalgan IANA mintaqa bo'lishi mumkin — Nyu-Yorkda yozda −4, qishda −5. Siljishni qo'lda yozish ("+5") — keyinchalik xato.
4.2 Kanon: src/umumiy/sana.ts
Temporal yo'q, shuning uchun Intl.DateTimeFormat ning formatToParts imkoniyati bilan. Kanon kodi (JS ko'rinishida, brauzerda ham ishlaydi):
function siljish(tz, lahza) {
const q = new Intl.DateTimeFormat("en-US", {
timeZone: tz, hourCycle: "h23",
year: "numeric", month: "2-digit", day: "2-digit",
hour: "2-digit", minute: "2-digit", second: "2-digit",
}).formatToParts(lahza);
const ol = (tur) => Number(q.find((p) => p.type === tur)?.value);
const mahalliy = Date.UTC(ol("year"), ol("month") - 1, ol("day"),
ol("hour"), ol("minute"), ol("second"));
const butun = Math.floor(lahza.getTime() / 1000) * 1000;
return Math.round((mahalliy - butun) / 60_000);
}
function kunBoshiUtc(sana, tz) {
const [y, m, d] = sana.split("-").map(Number);
const taxmin = Date.UTC(y, m - 1, d);
const birinchi = taxmin - siljish(tz, new Date(taxmin)) * 60_000;
const s = siljish(tz, new Date(birinchi));
return new Date(taxmin - s * 60_000).toISOString();
}
console.log(siljish("Asia/Tashkent", new Date("2026-10-24T00:00Z")));
console.log(kunBoshiUtc("2026-10-24", "Asia/Tashkent"));
console.log(kunBoshiUtc("2026-07-01", "America/New_York"));
console.log(kunBoshiUtc("2026-12-01", "America/New_York"));Konsolda:
300
2026-10-23T19:00:00.000Z
2026-07-01T04:00:00.000Z
2026-12-01T05:00:00.000ZQanday ishlaydi:
siljish— "shu lahzada mintaqadagi soat necha?" ni qismlarga bo'lib so'raydi, ularni go'yo UTC debDate.UTCga beradi va haqiqiy lahzadan ayiradi. Farq — siljish daqiqalarda (Toshkent: +300).kunBoshiUtc— ikki marta hisoblaydi. Birinchi taxmin "UTC yarim tun"dagi siljish bilan. DST o'tadigan kunda siljish yarim tunga yaqin o'zgarishi mumkin — shuning uchun topilgan nuqtadagi siljish bilan qayta hisoblanadi.- Natija: Nyu-Yorkda yozda kun 04:00Z da, qishda 05:00Z da boshlanadi — qo'lda yozilgan "+5" yoki "−4" bu farqni ko'rmaydi.
?gacha= uchun keyingi kun boshi olinadi va oraliq ochiq: yaratildi < gacha. Shunda 23:59:59.999 ham kiradi, keyingi kunning 00:00 i — yo'q. Kanon sorov.ts:
if (q.dan !== undefined) filtr.yaratildiDan = kunBoshiUtc(q.dan, tz);
if (q.gacha !== undefined)
filtr.yaratildiGacha = kunBoshiUtc(keyingiKun(q.gacha), tz);ISO satrlarni solishtirish (v.yaratildi >= filtr.yaratildiDan) to'g'ri ishlaydi. Satrlar lug'atdagi kabi belgima-belgi solishtiriladi (leksik tartib): avval yil, keyin oy, kun, soat. Ikkalasi bir xil formatda va UTC'da bo'lgani uchun leksik tartib vaqt tartibiga teng.
flowchart LR
A["?dan=2026-10-24<br/>(Toshkent kuni)"] --> B["kunBoshiUtc<br/>APP_TZ bilan"]
B --> C["23-okt 19:00Z"]
D["?gacha=2026-10-24"] --> E["keyingiKun → 25"]
E --> F["24-okt 19:00Z"]
C --> G["yaratildi ≥ dan<br/>va < gacha"]
F --> GDiagrammada foydalanuvchi bilan gaplashadigan qism (chapda) — mahalliy kun, ombor bilan gaplashadigan qism (o'ngda) — faqat UTC.
4.3 Kanon testi — Malikaning holati
test/sana.test.ts soxta soat bilan ikki vazifa yaratadi: 18:30Z (Toshkentda 23-okt 23:30) va 19:30Z (Toshkentda 24-okt 00:30):
assert.deepEqual(await ol("dan=2026-10-24&gacha=2026-10-24"),
["yarim tundan keyin"]);
assert.deepEqual(await ol("gacha=2026-10-23"), ["kech"]);
assert.equal((await fetch(`${asos}/v1/vazifalar?dan=2026-02-30`))
.status, 400);UTC sanasi bo'yicha ikkalasi ham 23-oktabr. Toshkent kuni bo'yicha — har xil. Test «Nega bu kerak?» bo'limidagi xatoni qaytarilmas qiladi.
Tekshirib ko'ring: Nega
?dan=niAPP_TZbo'yicha emas, har foydalanuvchining o'z mintaqasi bo'yicha hisoblash yaxshiroq bo'lardi? Kanon nega bunday qilmadi?
Javob
Haqiqiy ko'p mintaqali ilovada har foydalanuvchining "bugun" i o'z mintaqasida. Buning uchun foydalanuvchi mintaqasini bilish kerak (profil sozlamasi yoki so'rov parametri ?tz=). vazifalar-api da foydalanuvchilar hali yo'q (auth — 28-qism) va auditoriya O'zbekistonda — bitta APP_TZ yetarli. Kanon buni config'da qoldirdi: kerak bo'lsa keyin so'rov darajasiga tushiriladi.
5. O'zbekcha formatlash va ICU
5.1 Kanon o'lchovi
Bitta UTC lahza, timeZone: "Asia/Tashkent", dateStyle: "full", timeStyle: "short" (t40-sana.txt):
uz shanba, 24-oktabr, 2026, 00:30
uz-Latn-UZ shanba, 24-oktabr, 2026, 00:30
uz-Cyrl-UZ шанба, 24 октябр, 2026 00:30
ru-RU суббота, 24 октября 2026 г. в 00:30
en-GB Saturday, 24 October 2026 at 00:30
uz, month long: 24-oktabr; resolvedOptions: "uz"
RelativeTimeFormat uz: kecha, 3 soatdan keyin
ICU: 78.3, CLDR 48.0, tz 2026cOxirgi qator — eng muhimi. Formatlash natijasini Node emas, ICU kutubxonasi va CLDR (til ma'lumotlari bazasi) belgilaydi. Ular har Node (va har brauzer) versiyasida yangilanadi. Kecha 24-oktabr, ertaga — boshqa tinish belgisi. Bu matn brauzerda tekshirilmagan — Chrome o'z ICU'siga ega.
Uch qoida:
- Formatlangan matnni saqlamang va tahlil qilmang — faqat ko'rsating.
- Testda formatlangan matnni aynan solishtirish — mo'rt; Node yangilansa yiqiladi.
- Serverda formatlash kerak bo'lsa (email, PDF, Telegram xabari) —
timeZoneni doim aniq bering, aks holda jarayonTZsi ishlatiladi.
5.2 tz ma'lumotlar bazasi
tz 2026c — IANA mintaqalar bazasining versiyasi. Davlatlar soat qoidalarini o'zgartirib turadi; Node (ICU orqali) bu bazani o'zi bilan olib yuradi. Eski Node — eski qoidalar. Kanon APP_TZ ni Intl.supportedValuesOf("timeZone") bilan tekshiradi — mavjud bo'lmagan mintaqa (Toshkent) config xatosi beradi (tipli config darsi).
6. Temporal va kutubxonalar
6.1 Temporal Node 24 da
Temporal darsida uni brauzerda ko'rgansiz. Node 24 da (t40):
typeof Temporal: undefined
node --harmony-temporal: exit 0 object 2026-10-23T19:30:00Z
node --js-temporal: exit 9 …node.exe: bad option: --js-temporalSukut bo'yicha yo'q, V8 ning --harmony-temporal bayrog'i bilan — bor. Kanon tajribasidagi qatorni o'zimiz ham takrorlaymiz (blok Node'da bayroq bilan ishlaydi):
// node --harmony-temporal
const z = Temporal.ZonedDateTime.from(
"2026-10-24T00:30[Asia/Tashkent]");
console.log(z.toInstant().toString());
const kun = Temporal.PlainDate.from("2026-10-24");
const bosh = kun.toZonedDateTime("Asia/Tashkent").toInstant();
console.log(bosh.toString());Konsolda:
2026-10-23T19:30:00Z
2026-10-23T19:00:00ZIkkinchi natija — kunBoshiUtc ning bitta qatorli ekvivalenti, DST hisobi bilan. harmony bayroqlari — V8 ning tajriba imkoniyatlari; production'da ishlatilmaydi. Qaror (kanon): Temporal ishlatilmadi, Intl + Date.UTC — 20 qator. Temporal barqaror bo'lgach, sana.ts shunga almashadi.
6.2 Kutubxonalar
| Kutubxona | Mintaqa bilan ishlash | Qachon |
|---|---|---|
date-fns + @date-fns/tz (v4) |
TZDate |
Funksional uslub, tree-shaking |
Day.js + utc, timezone plaginlari |
plagin | Kichik hajm, Moment.js'ga o'xshash API |
| Luxon | ichida (DateTime.setZone) |
Mintaqa ko'p ishlatiladigan ilovalar |
| Moment.js | moment-timezone |
Eski — 2020 dan "texnik xizmat rejimi", yangi loyihaga yo'q |
Kanon hech birini o'rnatmadi: vazifalar-api da ikki funksiya kerak edi. Ular haqida batafsil — Temporal va sana kutubxonalari darsida. Ko'p sana amallari (oy qo'shish, "2 hafta oldin", ish kunlari) kerak bo'lsa — kutubxona arziydi.
7. Cron va mintaqa
"Har kuni ertalab 09:00 da kunlik hisobot yubor". Kimning 09:00 i? Server UTC'da bo'lsa — 0 9 * * * Toshkentda 14:00 da ishlaydi.
Ikki to'g'ri yo'l:
- Cron ifodani UTC'da yozish va izohlash:
0 4 * * * # 09:00 Toshkent (UTC+5). Toshkentda DST yo'q — bu barqaror. - Rejalashtiruvchiga mintaqani berish: ko'p kutubxonalarda
timezone: "Asia/Tashkent"opsiyasi bor. DST'li mintaqada bu yagona to'g'ri yo'l — aks holda yilda ikki marta vazifa bir soat siljiydi yoki ikki marta ishlaydi.
Rejali vazifalar to'liq — 29-qismda va Linux cron'i — 33-qismda.
8. API qadami: sana va holat 40 commit'i (holat 40, 6-qism)
Holat 40 olti darsning yig'indisi. Bugungi qism — sana:
| Fayl | O'zgarish |
|---|---|
src/umumiy/sana.ts |
Yangi: siljish, kunBoshiUtc, keyingiKun |
src/vazifalar/sxema.ts |
dan, gacha — haqiqiySana bilan |
src/vazifalar/sorov.ts |
?dan/?gacha → Filtr.yaratildiDan/Gacha (UTC ISO) |
src/config.ts |
APP_TZ (IANA, sukut Asia/Tashkent) |
test/sana.test.ts |
3 test: Toshkent, Nyu-York DST, API filtri |
Holat 40 ning boshqa qismlari oldingi darslarda qo'shildi: .env.example va skriptlar, SAQLASH=fayl (FaylOmbori), shartnoma testlari va tartib — env darsida; config.ts — tipli config darsida. Eslatma sifatida:
SAQLASH=fayl—src/vazifalar/fayl-ombor.ts:FaylOmborivazifalarni JSON faylda saqlaydi. Har yozuvdan keyin butun holat atomik yoziladi —.<pid>.tmp+rename,mode 0o600(Xavfsiz fayl operatsiyalari darsidagi usul). Javob diskka yozilgandan keyin qaytadi; buzilgan fayl — xato (jim bo'sh ro'yxat emas). Tanlov —src/ishga-tushir.tsdagiomborYarat(config): 26-qism shuswitchgapostgresni qo'shadi.- Shartnoma testlari —
test/ombor-shartnoma.ts:omborShartnomasi(nom, yarat)— 5 test, har ombor (xotira, fayl, keyin Postgres) bir xil testdan o'tadi. tartibmaydoni vaMAKS_MATN = 100— frontend «Tartiblash» (20-qism) tartibniPUT /v1/vazifalarmassiv tartibi bilan yuboradi; servertartib = indeks + 1yozadi. Matn chegarasi 200 dan 100 ga — frontend va 26-qism sxemasi bilan bir xil.
Kanon fayllari to'liq (holat 40; qatorlar telefon uchun Prettier bilan bo'lindi, kod aynan). src/umumiy/sana.ts:
// sana.ts — saqlash UTC'da, foydalanuvchi esa "24-oktabr" deganda
// Toshkent kunini nazarda tutadi.
// Mahalliy kun chegarasi → UTC lahza (Temporal Node 24 da yo'q — Intl
// bilan)
/**
* tz mintaqasining berilgan UTC lahzadagi siljishi (daqiqa):
* Asia/Tashkent → +300
*/
export function siljish(tz: string, lahza: Date): number {
const qismlar = new Intl.DateTimeFormat("en-US", {
timeZone: tz,
hourCycle: "h23",
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
}).formatToParts(lahza);
const q = (tur: Intl.DateTimeFormatPartTypes): number =>
Number(qismlar.find((p) => p.type === tur)?.value);
const mahalliy = Date.UTC(
q("year"),
q("month") - 1,
q("day"),
q("hour"),
q("minute"),
q("second"),
);
return Math.round(
(mahalliy - Math.floor(lahza.getTime() / 1000) * 1000) / 60_000,
);
}
/**
* "2026-10-24" (tz bo'yicha kun) → shu kun boshlanishining UTC ISO
* lahzasi: Toshkent → "2026-10-23T19:00:00.000Z"
*/
export function kunBoshiUtc(sana: string, tz: string): string {
const [y, m, d] = sana.split("-").map(Number);
const taxmin = Date.UTC(y ?? 0, (m ?? 1) - 1, d ?? 1);
// DST bo'lgan mintaqalarda siljish kunga qarab o'zgaradi — ikki
// marta hisoblaymiz
const birinchi = taxmin - siljish(tz, new Date(taxmin)) * 60_000;
return new Date(
taxmin - siljish(tz, new Date(birinchi)) * 60_000,
).toISOString();
}
/** Keyingi kun (mahalliy kalendar bo'yicha) */
export function keyingiKun(sana: string): string {
const [y, m, d] = sana.split("-").map(Number);
return new Date(Date.UTC(y ?? 0, (m ?? 1) - 1, (d ?? 1) + 1))
.toISOString()
.slice(0, 10);
}src/vazifalar/sorov.ts — query → ombor so'rovi (dan/gacha shu yerda UTC'ga aylanadi):
// sorov.ts — tekshirilgan ro'yxat query'sini (sxema.ts → RoyxatQuery)
// ombor so'roviga aylantirish: kursorni ochish, imzo, proyeksiya
// Konvensiya:
// ?bajarildi=true&teg=uy&teg=ish&q=non
// &sort=-yaratildi,matn&fields=id,matn&limit=20&offset=40
// | &cursor=...&jami=true
import { ParametrXatosi } from "../umumiy/xatolar.ts";
import { kursorOqi } from "../umumiy/kursor.ts";
import { keyingiKun, kunBoshiUtc } from "../umumiy/sana.ts";
import type { Filtr, RoyxatSorovi } from "./ombor.ts";
import type { Maydon, RoyxatQuery } from "./sxema.ts";
import type { Vazifa } from "./vazifa.ts";
export type TekshirilganSorov = {
sorov: RoyxatSorovi;
maydonlar?: Maydon[];
imzo: string;
};
/**
* @param tz foydalanuvchi kuni qaysi mintaqada (config APP_TZ) —
* saqlangan vaqt esa UTC
*/
export function royxatSorovi(
q: RoyxatQuery,
tz = "Asia/Tashkent",
): TekshirilganSorov {
const filtr: Filtr = {};
if (q.dan !== undefined)
filtr.yaratildiDan = kunBoshiUtc(q.dan, tz);
if (q.gacha !== undefined)
filtr.yaratildiGacha = kunBoshiUtc(keyingiKun(q.gacha), tz);
if (q.bajarildi !== undefined) filtr.bajarildi = q.bajarildi;
if (q.teg !== undefined) filtr.teglar = q.teg;
if (q.q !== undefined) filtr.qidiruv = q.q;
const saralash = q.sort ?? [];
// Imzo — kursor boshqa filtr/saralash bilan qayta ishlatilmasin
const imzo = JSON.stringify([saralash, filtr]);
let sahifa: RoyxatSorovi["sahifa"];
if (q.cursor !== undefined) {
const keyin = kursorOqi(q.cursor, imzo);
if (keyin === undefined)
throw new ParametrXatosi(
"cursor yaroqsiz yoki boshqa saralash/filtr uchun",
[{ joy: "query.cursor", xabar: "yaroqsiz" }],
);
sahifa = { tur: "kursor", keyin };
} else {
// Parametrsiz — kursor rejimi (birinchi sahifa); offset faqat
// aniq so'ralganda
sahifa =
q.offset === undefined
? { tur: "kursor" }
: { tur: "offset", offset: q.offset };
}
const sorov: RoyxatSorovi = {
filtr,
saralash,
limit: q.limit,
sahifa,
};
if (q.jami !== undefined) sorov.jami = q.jami;
return q.fields === undefined
? { sorov, imzo }
: { sorov, maydonlar: q.fields, imzo };
}
/** Sparse fieldsets: faqat so'ralgan maydonlar */
export function proyeksiya(
v: Vazifa,
maydonlar: readonly Maydon[] | undefined,
): Partial<Vazifa> {
if (maydonlar === undefined) return v;
return Object.fromEntries(maydonlar.map((m) => [m, v[m]]));
}Qolgan o'zgarishlar — farq (diff) ko'rinishida. Farq Prettier bilan bo'lingan holat 34 va holat 40 fayllari orasida olingan (kanon fayllarida qatorlar uzun — faqat format farqi). sxema.ts — haqiqiySana, dan/gacha, tartib maydoni va MAKS_MATN = 100:
--- a/sxema.ts
+++ b/sxema.ts
@@ -6,5 +6,7 @@
import type { Saralash, SaralashMaydoni } from "./ombor.ts";
-export const MAKS_MATN = 200;
+// 22-qism frontend (Vazifa.MAKS_UZUNLIK) va 26-qism sxemasi (CHECK
+// 1–100) bilan bir xil
+export const MAKS_MATN = 100;
export const MAKS_TOPLAM = 1000;
export const SUKUT_LIMIT = 20;
@@ -96,4 +98,19 @@
});
const TEG = /^[\p{L}\p{N}_'‘’]{1,50}$/u;
+/**
+ * YYYY-MM-DD va haqiqiy kalendar sanasi (2026-02-30 — yo'q: Date uni
+ * 03-02 ga "to'g'rilab" yuboradi)
+ */
+function haqiqiySana(x: string): boolean {
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(x)) return false;
+ const t = Date.parse(`${x}T00:00:00Z`);
+ return (
+ !Number.isNaN(t) && new Date(t).toISOString().startsWith(x)
+ );
+}
+const Sana = (nom: string) =>
+ Bitta(nom).refine(haqiqiySana, {
+ error: `${nom} — YYYY-MM-DD (haqiqiy sana)`,
+ });
const MAYDONLAR = [
"id",
@@ -101,4 +118,5 @@
"bajarildi",
"teglar",
+ "tartib",
"yaratildi",
"yangilandi",
@@ -189,4 +207,8 @@
cursor: Bitta("cursor").max(1000).optional(),
jami: Mantiqiy("jami").optional(),
+ // Mahalliy kalendar kuni (APP_TZ, sukut Asia/Tashkent):
+ // ?dan=2026-10-24&gacha=2026-10-24 — ikkalasi ham kiradi
+ dan: Sana("dan").optional(),
+ gacha: Sana("gacha").optional(),
},
{marshrut.ts — mintaqa routerga parametr bo'lib keladi:
--- a/marshrut.ts
+++ b/marshrut.ts
@@ -51,7 +51,10 @@
return `${req.baseUrl}?${p.toString()}`;
}
-export function vazifalarRouteri(xizmat: VazifaXizmati): Router {
+export function vazifalarRouteri(
+ xizmat: VazifaXizmati,
+ { tz = "Asia/Tashkent" } = {},
+): Router {
const r = Router();
r.route("/")
@@ -61,7 +64,10 @@
tekshirib(
{ query: RoyxatQuerySxemasi },
async ({ query }, req, res) => {
- const { sorov, maydonlar, imzo } = royxatSorovi(query);
+ const { sorov, maydonlar, imzo } = royxatSorovi(
+ query,
+ tz,
+ );
const natija = await xizmat.royxat(sorov);
const sahifa: Record<string, unknown> = {
limit: sorov.limit,test/xato.test.ts — matn chegarasi 100:
--- a/xato.test.ts
+++ b/xato.test.ts
@@ -49,13 +49,13 @@
type: "https://vazifalar-api.example/xatolar/validatsiya",
title: "So'rov ma'lumoti noto'g'ri",
status: 422,
- detail: "matn 1–200 belgi bo'lishi kerak",
+ detail: "matn 1–100 belgi bo'lishi kerak",
instance: "/v1/vazifalar",
kod: "validatsiya",
xatolar: [
{
joy: "body.matn",
- xabar: "matn 1–200 belgi bo'lishi kerak",
+ xabar: "matn 1–100 belgi bo'lishi kerak",
},
],
sorovId: r.headers.get("x-request-id"),test/sana.test.ts:
import { after, before, test } from "node:test";
import assert from "node:assert/strict";
import {
keyingiKun,
kunBoshiUtc,
siljish,
} from "../src/umumiy/sana.ts";
import { ishgaTushir } from "../src/ishga-tushir.ts";
import { yop } from "../src/yopish.ts";
import { XotiraOmbori } from "../src/vazifalar/xotira-ombor.ts";
test("Toshkent kuni → UTC: 24-oktabr 00:00 (UTC+5) = 23-oktabr 19:00Z", () => {
assert.equal(
siljish("Asia/Tashkent", new Date("2026-10-24T00:00:00Z")),
300,
);
assert.equal(
kunBoshiUtc("2026-10-24", "Asia/Tashkent"),
"2026-10-23T19:00:00.000Z",
);
assert.equal(
kunBoshiUtc("2026-10-24", "UTC"),
"2026-10-24T00:00:00.000Z",
);
assert.equal(keyingiKun("2026-12-31"), "2027-01-01");
});
test("DST bor mintaqa: Nyu-York yoz (UTC-4) va qish (UTC-5)", () => {
assert.equal(
kunBoshiUtc("2026-07-01", "America/New_York"),
"2026-07-01T04:00:00.000Z",
);
assert.equal(
kunBoshiUtc("2026-12-01", "America/New_York"),
"2026-12-01T05:00:00.000Z",
);
});
let asos = "";
let toxta: () => Promise<boolean>;
before(async () => {
// Soxta soat: UTC 23-okt 18:30 (Toshkentda 23:30) va 19:30
// (Toshkentda 24-okt 00:30)
const vaqtlar = [
"2026-10-23T18:30:00.000Z",
"2026-10-23T19:30:00.000Z",
];
const ombor = new XotiraOmbori(
() => new Date(vaqtlar.shift() ?? "2026-10-25T00:00:00.000Z"),
);
await ombor.yarat({ matn: "kech" });
await ombor.yarat({ matn: "yarim tundan keyin" });
const { server, manzil } = await ishgaTushir({
port: 0,
bogliqliklar: { ombor, tz: "Asia/Tashkent" },
});
asos = manzil;
toxta = () => yop(server, 1000);
});
after(() => toxta());
test("?dan=&gacha= — Toshkent kalendar kuni bo'yicha (UTC sanasi bo'yicha emas)", async () => {
const ol = async (q: string) =>
(
(await (
await fetch(`${asos}/v1/vazifalar?${q}&fields=matn`)
).json()) as { vazifalar: { matn: string }[] }
).vazifalar.map((v) => v.matn);
assert.deepEqual(await ol("dan=2026-10-24&gacha=2026-10-24"), [
"yarim tundan keyin",
]);
assert.deepEqual(await ol("gacha=2026-10-23"), ["kech"]);
assert.equal(
(await fetch(`${asos}/v1/vazifalar?dan=2026-02-30`)).status,
400,
);
});src/ishga-tushir.ts (omborYarat bilan):
// ishga-tushir.ts — ombor tanlash (config bo'yicha), http server
// (Express ilovasi ishlovchi sifatida), timeout'lar va tinglash
import { createServer } from "node:http";
import type { Server } from "node:http";
import type { AddressInfo } from "node:net";
import { ilovaYarat } from "./ilova.ts";
import type { Bogliqliklar } from "./ilova.ts";
import type { Config } from "./config.ts";
import { XotiraOmbori } from "./vazifalar/xotira-ombor.ts";
import { FaylOmbori } from "./vazifalar/fayl-ombor.ts";
import type { VazifaOmbori } from "./vazifalar/ombor.ts";
export type Ishlayotgan = { server: Server; manzil: string };
/**
* SAQLASH=xotira|fayl — 26-qismda shu yerga "postgres" (pg Pool)
* qo'shiladi; qolgan kod VazifaOmbori interfeysini biladi, xolos
*/
export async function omborYarat(
config: Pick<Config, "SAQLASH" | "FAYL_YOLI">,
): Promise<VazifaOmbori> {
switch (config.SAQLASH) {
case "xotira":
return new XotiraOmbori();
case "fayl":
return FaylOmbori.och(config.FAYL_YOLI);
}
}
type Sozlama = {
port?: number;
host?: string;
bogliqliklar?: Bogliqliklar;
};
export async function ishgaTushir({
port = 0,
host = "127.0.0.1",
bogliqliklar = { ombor: new XotiraOmbori() },
}: Sozlama = {}): Promise<Ishlayotgan> {
// app.listen() ham shuni qiladi; createServer — timeout'larni
// berish va server obyektini olish uchun
const server = createServer(
{
// Load balancer (nginx, ALB) bo'sh ulanishni ~60 s ushlaydi —
// Node undan KEYIN yopsin
keepAliveTimeout: 65_000,
// Slowloris: sarlavhalar 10 s ichida to'liq kelmasa — 408
headersTimeout: 10_000,
requestTimeout: 30_000,
},
ilovaYarat(bogliqliklar),
);
await new Promise<void>((resolve, reject) => {
server.once("error", reject); // EADDRINUSE va h.k.
server.listen(port, host, () => {
server.off("error", reject);
resolve();
});
});
const { port: haqiqiy } = server.address() as AddressInfo;
return { server, manzil: `http://${host}:${String(haqiqiy)}` };
}Kanon jonli chiqishlari (tekshiruv-40.txt). Uch so'rovda vazifalar massiviga qarang — nechta vazifa qaytdi:
$ curl $A/v1/vazifalar?dan=2026-10-24&fields=matn,yaratildi
{"versiya":1,"vazifalar":[{"id":1,"matn":"Bugungi vazifa #uy","yaratildi":"2026-10-24T05:55:46.307Z"}],"sahifa":{"limit":20,"keyingi":null}}
$ curl $A/v1/vazifalar?gacha=2026-01-01&fields=matn
{"versiya":1,"vazifalar":[],"sahifa":{"limit":20,"keyingi":null}}
--- SAQLASH=fayl: ikki marta ishga tushirish
$ curl 'http://127.0.0.1:4199/v1/vazifalar?fields=matn' (qayta ishga tushgandan keyin)
{"versiya":1,"vazifalar":[{"id":1,"matn":"diskda saqlanadi"}],"sahifa":{"limit":20,"keyingi":null}}
fayl: 208 baytNima bo'ldi? Bugun (24-oktabr, Toshkent) yaratilgan vazifa ?dan=2026-10-24 bilan topildi; ?gacha=2026-01-01 bilan — bo'sh ro'yxat, chunki 1-yanvar va undan oldin hech narsa yaratilmagan. Uchinchi so'rov SAQLASH=fayl ni tekshiradi: server o'chirilib qayta yoqilgandan keyin ham vazifa joyida — u diskdagi 208 baytli faylda saqlangan.
Tekshiring:
npm run checkKanon natijasi: tip 0 (3.1 s), lint 0 (6.1 s), testlar 54/54. Yangi 18 test: config 2, sana 3, shartnoma 5 × 2, FaylOmbori 3. Prod bog'liqlik 4 — o'zgarmadi; sovuq start 540 ms (mediana, mashina yuklangan). Yuk (autocannon, bitta mashina, bitta o'lchov — faqat holatlarni solishtirish uchun): ?limit=100 1 660, filtrli 3 196, /v1/vazifalar/5 6 448, 404 5 116 req/s.
Commit (kanon):
git add -A
git commit -m "Config: Zod bilan tipli va muzlatilgan, fail-fast (exit 78); .env.example; SAQLASH=fayl (atomik JSON); ?dan/?gacha Toshkent kuni; tartib; matn 100"9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Yarim tundan keyingi vazifa "kecha" da | Filtr UTC sanasi bo'yicha | kunBoshiUtc(sana, APP_TZ) |
| Lokalda o'tadi, CI'da yiqiladi | getDate() — jarayon TZ si |
getUTC… yoki Intl + timeZone |
2026-02-30 qabul qilindi |
Date jim to'g'rilaydi |
toISOString().startsWith(x) tekshiruvi |
"2026-10-24T00:00" 5 soat siljidi |
Z siz satr — mahalliy |
Doim Z yoki aniq siljish |
| Hisobot 14:00 da keladi | Cron UTC'da | UTC'da yozing yoki timezone opsiyasi |
| Node yangilangach test yiqildi | ICU/CLDR o'zgardi | Formatlangan matnni aynan solishtirmang |
| Nyu-York filtri yilda ikki marta xato | Siljish qo'lda (−5) | IANA mintaqa + Intl / Temporal |
10. Mashqlar
Yechimlar kurs/mashqlar/25/40-sana-vaqt/ da.
1-mashq (oson): UTC'da nima?
Toshkent (UTC+5, DST yo'q):
- Toshkentda 24-okt 00:30 — UTC:
2026-10-23T:00Z - Toshkentda 24-okt kuni boshi — UTC:
2026-10-T19:00:00Z new Date("2026-10-24").toISOString()—2026-10-24T:00.000Z?gacha=2026-10-24filtriyaratildi <qaysi lahza?2026-10-T19:00:00.000Z
Yechim
- 19:30 — 00:30 − 5 soat, oldingi kun.
- 23 — Toshkent kuni UTC'da 5 soat oldin boshlanadi.
- 00:00 — sana-only satr UTC deb o'qiladi.
- 24 — keyingi kun (25-okt) boshi Toshkentda = 24-okt 19:00Z; oraliq ochiq (
<).
2-mashq (o'rta): kun oralig'i
kunOraligi(dan, gacha, tz) yozing: darsdagi kunBoshiUtc bilan { dan, gacha } (UTC ISO) qaytarsin; gacha — keyingi kun boshi. dan > gacha bo'lsa — RangeError("dan gacha'dan keyin"). Sinang: Toshkent 24–24, Nyu-York 2026-03-08 (DST boshlangan kun — kun 23 soat).
Yechim
function siljish(tz, lahza) {
const q = new Intl.DateTimeFormat("en-US", {
timeZone: tz, hourCycle: "h23",
year: "numeric", month: "2-digit", day: "2-digit",
hour: "2-digit", minute: "2-digit", second: "2-digit",
}).formatToParts(lahza);
const ol = (tur) => Number(q.find((p) => p.type === tur)?.value);
const mahalliy = Date.UTC(ol("year"), ol("month") - 1, ol("day"),
ol("hour"), ol("minute"), ol("second"));
const butun = Math.floor(lahza.getTime() / 1000) * 1000;
return Math.round((mahalliy - butun) / 60_000);
}
function kunBoshiUtc(sana, tz) {
const [y, m, d] = sana.split("-").map(Number);
const taxmin = Date.UTC(y, m - 1, d);
const birinchi = taxmin - siljish(tz, new Date(taxmin)) * 60_000;
const s = siljish(tz, new Date(birinchi));
return new Date(taxmin - s * 60_000).toISOString();
}
function keyingiKun(sana) {
const [y, m, d] = sana.split("-").map(Number);
const t = Date.UTC(y, m - 1, d + 1);
return new Date(t).toISOString().slice(0, 10);
}
function kunOraligi(dan, gacha, tz) {
if (dan > gacha) throw new RangeError("dan gacha'dan keyin");
return { dan: kunBoshiUtc(dan, tz),
gacha: kunBoshiUtc(keyingiKun(gacha), tz) };
}
const tosh = "Asia/Tashkent";
console.log(kunOraligi("2026-10-24", "2026-10-24", tosh));
const ny = kunOraligi("2026-03-08", "2026-03-08", "America/New_York");
console.log(ny, (Date.parse(ny.gacha) - Date.parse(ny.dan)) / 3.6e6);
try {
kunOraligi("2026-10-25", "2026-10-24", "UTC");
} catch (x) {
console.log(x.name, x.message);
}Konsolda:
{ dan: '2026-10-23T19:00:00.000Z', gacha: '2026-10-24T19:00:00.000Z' }
{ dan: '2026-03-08T05:00:00.000Z', gacha: '2026-03-09T04:00:00.000Z' } 23
RangeError dan gacha'dan keyinNyu-Yorkda 8-mart kuni 23 soat: ertalab soat 02:00 da 03:00 ga sakraydi. Oraliq 05:00Z (qish, −5) dan 04:00Z (yoz, −4) gacha — "+24 soat" qo'shganda bir soat xato bo'lardi. dan > gacha — YYYY-MM-DD satrlari leksik solishtirishda ham to'g'ri tartiblanadi.
3-mashq (qiyin): "har kuni 09:00 Toshkent" — keyingi ishga tushish
keyingiIsh(hozir, soat, daqiqa, tz) yozing: berilgan UTC lahzadan keyingi birinchi "tz mintaqasida soat:daqiqa" lahzasini UTC ISO'da qaytarsin. siljish va kunBoshiUtc dan foydalaning (DST'ni hisobga olsin).
Yechim
function siljish(tz, lahza) {
const q = new Intl.DateTimeFormat("en-US", {
timeZone: tz, hourCycle: "h23",
year: "numeric", month: "2-digit", day: "2-digit",
hour: "2-digit", minute: "2-digit", second: "2-digit",
}).formatToParts(lahza);
const ol = (tur) => Number(q.find((p) => p.type === tur)?.value);
const mahalliy = Date.UTC(ol("year"), ol("month") - 1, ol("day"),
ol("hour"), ol("minute"), ol("second"));
const butun = Math.floor(lahza.getTime() / 1000) * 1000;
return Math.round((mahalliy - butun) / 60_000);
}
function mahalliySana(tz, lahza) {
const s = siljish(tz, lahza);
const t = lahza.getTime() + s * 60_000;
return new Date(t).toISOString().slice(0, 10);
}
function lahzaga(sana, soat, daqiqa, tz) {
const [y, m, d] = sana.split("-").map(Number);
const taxmin = Date.UTC(y, m - 1, d, soat, daqiqa);
const birinchi = taxmin - siljish(tz, new Date(taxmin)) * 60_000;
return taxmin - siljish(tz, new Date(birinchi)) * 60_000;
}
function keyingiIsh(hozir, soat, daqiqa, tz) {
const bugun = mahalliySana(tz, hozir);
let t = lahzaga(bugun, soat, daqiqa, tz);
if (t <= hozir.getTime()) {
const [y, m, d] = bugun.split("-").map(Number);
const ertaga = new Date(Date.UTC(y, m - 1, d + 1))
.toISOString().slice(0, 10);
t = lahzaga(ertaga, soat, daqiqa, tz);
}
return new Date(t).toISOString();
}
const tz = "Asia/Tashkent";
console.log(keyingiIsh(new Date("2026-10-24T03:00:00Z"), 9, 0, tz));
console.log(keyingiIsh(new Date("2026-10-24T04:00:00Z"), 9, 0, tz));
console.log(keyingiIsh(new Date("2026-10-23T20:00:00Z"), 9, 0, tz));Konsolda:
2026-10-24T04:00:00.000Z
2026-10-25T04:00:00.000Z
2026-10-24T04:00:00.000ZUchinchi qator qiziq: UTC'da hali 23-oktabr, lekin Toshkentda allaqachon 24-oktabr 01:00 — demak "bugungi" 09:00 hali oldinda. mahalliySana aynan shuni hal qiladi. Ikkinchi qatorda vaqt aynan 09:00 — <= tufayli keyingi kunga o'tamiz (bir lahzada ikki marta ishga tushmasin).
4-mashq: API qadami
src/umumiy/sana.ts ni yozing, sxema.ts ga dan/gacha (haqiqiySana), sorov.ts ga filtrni qo'shing. test/sana.test.ts — Malikaning holati (18:30Z va 19:30Z). «API qadami» dagi qolgan fayllarni qo'shing (marshrut.ts, ishga-tushir.ts — omborYarat, matn 100) va tipli config darsidagi vaqtinchalik kesimni (new XotiraOmbori(), tz siz) holat 40 ko'rinishiga qaytaring. npm run check — 54/54. Commit — «API qadami» dagi kanon xabari bilan.
Yechim
Kod — «API qadami» bo'limidagi to'liq fayllar va farqlar. Soxta soat: new XotiraOmbori(() => new Date(vaqtlar.shift() ?? …)) — ombor soat parametrini oladi (24-qismdagi taymer va vaqt testlari g'oyasi). SAQLASH=fayl ni qo'lda tekshiring: vazifa qo'shing, serverni to'xtating, qayta ishga tushiring — vazifa joyida bo'lishi kerak (kanonda 208 baytli fayl).
11. Real ishda
- "Vaqt zonasi xatosi" — production'dagi eng ko'p uchraydigan va eng kech topiladigan xatolardan. Hisobotlar, to'lov muddatlari, "bugun tug'ilgan kun" xabarlari — hammasi shu darsdagi qoidalarga tayanadi.
- Postgres'da
timestamptzvaAT TIME ZONE 'Asia/Tashkent'— 26-qismda shu g'oyaning SQL ko'rinishi. - Intervyu savollari: "Sanani bazada qanday saqlaysiz?", "
new Date('2026-10-24')vanew Date('2026-10-24T00:00')farqi?", "DST bo'lgan mintaqada 'har kuni 09:00' ni qanday rejalashtirasiz?"
Xulosa
- Saqlash va uzatish — UTC lahza, ISO 8601
Zbilan; mintaqa — faqat kiritish va ko'rsatishda. - Kalendar kuni lahza emas: Toshkent kuni =
[oldingi kun 19:00Z, shu kun 19:00Z). getDate()/getHours()vaZsiz satrlar — jarayonTZsiga bog'liq; serverda ishlatmang.Datenoto'g'ri sanani jim to'g'rilaydi —toISOString().startsWithbilan tekshiring.Intlformatlash ICU/CLDR versiyasiga bog'liq — saqlamang, tahlil qilmang, aynan solishtirmang.- Temporal Node 24 da faqat
--harmony-temporalbilan; kanonIntlbilan yechdi. Cron — mintaqani aniq bering.
Keyingi dars: Logging asoslari — console.log o'rniga darajali JSON loglar: nimani yozish va nimani hech qachon yozmaslik.
Manbalar
- RFC 3339 — Date and Time on the Internet: Timestamps (ISO 8601 profili): https://www.rfc-editor.org/rfc/rfc3339
- MDN —
Intl.DateTimeFormat.prototype.formatToParts,Intl.supportedValuesOf: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/formatToParts - TC39 Temporal: https://tc39.es/proposal-temporal/docs/ ; IANA Time Zone Database: https://www.iana.org/time-zones
- date-fns v4 va
@date-fns/tz: https://date-fns.org/ ; Luxon: https://moment.github.io/luxon/ ; Moment.js "Project Status" (2020): https://momentjs.com/docs/#/-project-status/ - Kanon:
vazifalar-apiholat 40 (src/umumiy/sana.ts,sxema.ts,sorov.ts,fayl-ombor.ts,test/sana.test.ts,test/ombor-shartnoma.ts),natijalar/tajriba/t40-sana.txt(Date tuzoqlari,TZ, Intl lokallar, ICU 78.3 / CLDR 48.0 / tz 2026c, Temporal bayroqlari),natijalar/tekshiruv-40.txt(jonli so'rovlar, testlar 54/54, yuk).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!