Tarkibga o'tish

Go’da sana va vaqt bilan ishlash

Go’da sana, vaqt va vaqt oralig‘i bilan ishlash uchun standart kutubxonadagi time paketi ishlatiladi. Backend API’da yozuv yaratilgan vaqtni saqlash, token muddatini tekshirish, vazifaga timeout qo‘yish va foydalanuvchi vaqt zonasida sanani ko‘rsatish shu paketning keng tarqalgan vazifalaridir.

Vaqt bilan ishlashda uch tushunchani ajratish muhim:

  • time.Time — vaqt chizig‘idagi muayyan nuqta;
  • time.Duration — ikki hodisa orasidagi davomiylik;
  • time.Location — vaqt nuqtasini qaysi vaqt zonasi qoidalari bilan ko‘rsatish kerakligi.

Masalan, bitta uchrashuv Toshkentda 14:00, Londonda boshqa mahalliy soatda ko‘rinishi mumkin. Vaqt nuqtasi bitta, uni ko‘rsatish zonasi esa turlicha.

Joriy vaqtni olish

time.Now() operatsion tizimdan joriy vaqtni olib, time.Time qiymatini qaytaradi:

package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()

    fmt.Println("Mahalliy vaqt:", now)
    fmt.Println("UTC vaqti:", now.UTC())
}

Natija dastur qachon va qaysi muhitda ishga tushirilganiga bog‘liq. Masalan:

Mahalliy vaqt: 2026-03-15 14:30:20.123456789 +0500 +05 m=+0.000031201
UTC vaqti: 2026-03-15 09:30:20.123456789 +0000 UTC

time.Now() natijasi serverning mahalliy vaqt zonasidan foydalanadi. Server UTCda sozlangan bo‘lsa, birinchi satr ham UTCda chiqadi. Kodni ma’lum zona deb taxmin qilish o‘rniga kerakli zonani ochiq ko‘rsating.

Standart matn ko‘rinishidagi qismlar:

Qism Ma’nosi
2026-03-15 yil, oy va kun
14:30:20.123456789 soat, daqiqa, soniya va soniyaning kasr qismi
+0500 UTCdan siljish (offset)
+05 vaqt zonasi nomi; tizimga qarab boshqa nom bo‘lishi mumkin
m=+... time.Time ichidagi ixtiyoriy monotonic clock o‘qishi

Monotonic vaqt nima?

Devor vaqti (wall clock) foydalanuvchi ko‘radigan sana va soatdir. U tizim vaqti tuzatilganda, NTP sinxronizatsiyasida yoki administrator o‘zgartirganda oldinga yoxud orqaga siljishi mumkin.

Monotonic clock esa o‘tgan davomiylikni o‘lchashga mo‘ljallangan va devor vaqtidagi tuzatishlardan ta’sirlanmaydi. time.Now() qaytargan qiymat monotonic o‘qishni ham saqlashi mumkin. Sub, Before, After va taqqoslash amallari ikkala operandda ham monotonic ma’lumot bo‘lsa, davomiylikni shu o‘qish asosida hisoblaydi.

m=+... qiymatini “dastur ishga tushganidan beri o‘tgan vaqt” deb talqin qilish kerak emas. U runtime chiqaradigan ichki monotonic ko‘rsatkich bo‘lib, saqlash yoki tizimlar orasida uzatish uchun mo‘ljallanmagan. Formatlash va serializatsiya odatda monotonic qismini olib tashlaydi.

Sana va vaqt qismlarini olish

time.Time metodlari alohida qismlarni qaytaradi:

package main

import (
    "fmt"
    "time"
)

func main() {
    moment := time.Date(2024, time.February, 29, 16, 45, 30, 0, time.UTC)

    fmt.Println("Yil:", moment.Year())
    fmt.Println("Oy:", moment.Month())
    fmt.Println("Kun:", moment.Day())
    fmt.Println("Hafta kuni:", moment.Weekday())
    fmt.Println("Soat:", moment.Hour())
    fmt.Println("Daqiqa:", moment.Minute())
    fmt.Println("Soniya:", moment.Second())
}

Natija:

Yil: 2024
Oy: February
Kun: 29
Hafta kuni: Thursday
Soat: 16
Daqiqa: 45
Soniya: 30

Misolda qattiq berilgan vaqt ishlatilgani uchun natija barqaror. Month() time.Month turini qaytaradi va matnda inglizcha oy nomi sifatida ko‘rinadi. Mahalliylashtirilgan o‘zbekcha oy nomlari kerak bo‘lsa, ularni ilova darajasida alohida moslashtirish lozim.

Vaqtni formatlash

Format vaqtni satrga aylantiradi. Go boshqa ko‘p tillardagi YYYY-MM-DD belgilaridan foydalanmaydi. Layout — Go’ning maxsus namuna sanasi orqali yoziladi:

Mon Jan 2 15:04:05 MST 2006

Esda saqlash uchun raqamlar ketma-ketligi: 01/02 03:04:05PM '06 -0700. Layoutdagi qismlar aynan shu namuna vaqtidagi qiymatlar bilan ifodalanadi.

package main

import (
    "fmt"
    "time"
)

func main() {
    moment := time.Date(2024, time.August, 9, 16, 7, 5, 123_000_000, time.UTC)

    fmt.Println("Sana:", moment.Format("02-01-2006"))
    fmt.Println("Vaqt:", moment.Format("15:04:05"))
    fmt.Println("API:", moment.Format(time.RFC3339Nano))
}

Natija:

Sana: 09-08-2024
Vaqt: 16:07:05
API: 2024-08-09T16:07:05.123Z

02-01-2006 kun-oy-yil, 15:04:05 esa 24 soatlik vaqt ko‘rinishini beradi. API va tizimlar orasidagi almashinuv uchun time.RFC3339 yoki aniqlikni saqlash kerak bo‘lsa time.RFC3339Nano kabi tayyor layoutlardan foydalanish ma’qul.

Asosiy layout qismlari

Layout qismi Natija ma’nosi
2006 to‘rt xonali yil
06 ikki xonali yil
01 ikki xonali oy
1 bosh nol qo‘yilmagan oy
Jan qisqa oy nomi
January to‘liq oy nomi
02 ikki xonali kun
2 bosh nol qo‘yilmagan kun
Mon qisqa hafta kuni
Monday to‘liq hafta kuni
15 24 soatlik soat
03 12 soatlik soat
PM yoki pm 12 soatlik davr belgisi
04 daqiqa
05 soniya
.000 millisekund, doim uch xona
.000000 mikrosekund, doim olti xona
.000000000 nanosekund, doim to‘qqiz xona
MST zona nomi
-0700 +0500 ko‘rinishidagi offset
-07:00 +05:00 ko‘rinishidagi offset
Z07:00 UTC uchun Z, boshqa zonada sonli offset

Layoutda 2006 o‘rniga odatiy YYYY yozilsa, Go uni yil belgisi deb tanimaydi va oddiy matn sifatida chiqaradi:

// Noto‘g‘ri: "YYYY" Go layout belgisi emas.
fmt.Println(moment.Format("YYYY-MM-DD"))

Bu kod kompilyatsiyadan o‘tadi, ammo kutilgan sana o‘rniga layoutdagi oddiy harflarni chiqaradi. Shu sabab format xatosi runtime’da darhol xato qaytarmasligi mumkin.

Satrni vaqtga aylantirish

time.Parse satrni layout bo‘yicha tahlil qilib, time.Time qaytaradi:

package main

import (
    "fmt"
    "time"
)

func main() {
    input := "2024-08-09"
    moment, err := time.Parse("2006-01-02", input)
    if err != nil {
        fmt.Println("Sana noto‘g‘ri:", err)
        return
    }

    fmt.Println(moment.Format(time.RFC3339))
}

Natija:

2024-08-09T00:00:00Z

Layoutda zona bo‘lmasa, time.Parse vaqtni UTCda yaratadi. Vaqt ko‘rsatilmagan qismlar nol qiymat oladi, shu sabab soat 00:00:00.

Satr layoutga to‘liq mos kelishi kerak. Masalan, 2024-02-30 mavjud bo‘lmagan sana bo‘lgani uchun xato qaytaradi. Tashqaridan kelgan sanada errni tekshirmaslik nol qiymat yoki noto‘g‘ri biznes qaroriga olib kelishi mumkin.

Ma’lum zonadagi mahalliy vaqtni parse qilish

Foydalanuvchi 2024-08-09 14:30 deb yuborsa, satrning o‘zida zona yo‘q. Ilova bu vaqt qaysi Locationga tegishli ekanini bilishi kerak:

package main

import (
    "fmt"
    "time"
)

func main() {
    location, err := time.LoadLocation("Asia/Tashkent")
    if err != nil {
        fmt.Println("Vaqt zonasi yuklanmadi:", err)
        return
    }

    input := "2024-08-09 14:30"
    moment, err := time.ParseInLocation("2006-01-02 15:04", input, location)
    if err != nil {
        fmt.Println("Vaqt noto‘g‘ri:", err)
        return
    }

    fmt.Println("Toshkent:", moment.Format(time.RFC3339))
    fmt.Println("UTC:", moment.UTC().Format(time.RFC3339))
}

Natija:

Toshkent: 2024-08-09T14:30:00+05:00
UTC: 2024-08-09T09:30:00Z

ParseInLocation zonasi ko‘rsatilmagan qismlarni berilgan Location qoidalari bilan izohlaydi. UTC() vaqt nuqtasini o‘zgartirmaydi; faqat uni UTC ko‘rinishida qaytaradi.

time.LoadLocation operatsion tizimdagi IANA vaqt zonasi ma’lumotlariga tayanishi mumkin. Minimal container image’da zoneinfo bazasi bo‘lmasa, yuklash xato qaytaradi. Bunday muhitda zoneinfo paketini image’ga o‘rnatish yoki zarur bo‘lsa dasturga _ "time/tzdata" importi orqali ma’lumotni qo‘shish mumkin. Bu executable hajmini oshiradi, shuning uchun deployment talabi asosida tanlanadi.

Warning

time.FixedZone("UZT", 5*60*60) doimiy +05:00 zona yaratadi, ammo tarixiy yoki yozgi vaqt qoidalarini bilmaydi. Qoidalari o‘zgaradigan hududlar uchun time.LoadLocation bilan IANA nomidan foydalaning.

time.Date bilan vaqt yaratish

time.Date yil, oy, kun, vaqt va locationdan time.Time yaratadi:

package main

import (
    "fmt"
    "time"
)

func main() {
    moment := time.Date(
        2024,
        time.August,
        9,
        14,
        30,
        0,
        0,
        time.UTC,
    )

    fmt.Println(moment.Format(time.RFC3339))
}

Natija:

2024-08-09T14:30:00Z

Oy uchun 8 yozish kompilyatsiyadan o‘tadi, chunki time.Month butun son asosidagi tur. time.August yozuvi esa ma’noni aniqroq ko‘rsatadi.

time.Date diapazondan tashqaridagi qismlarni normalizatsiya qiladi. Masalan, yanvarning 32-kuni fevraldagi sanaga o‘tadi. Shuning uchun undan foydalanuvchi kiritgan sana to‘g‘riligini tekshiruvchi validator sifatida foydalanmang. Tashqi matn uchun qat’iy layout bilan time.Parse ishlating va xatoni tekshiring.

Duration: vaqt oralig‘i

time.Duration nanosekundlar sonini saqlaydigan int64 asosidagi tur. Tayyor konstantalar kodni tushunarli qiladi:

package main

import (
    "fmt"
    "time"
)

func main() {
    start := time.Date(2024, time.January, 1, 12, 0, 0, 0, time.UTC)
    duration := 90 * time.Minute
    end := start.Add(duration)

    fmt.Println("Boshlanish:", start.Format(time.RFC3339))
    fmt.Println("Tugash:", end.Format(time.RFC3339))
    fmt.Println("Davomiylik:", end.Sub(start))
}

Natija:

Boshlanish: 2024-01-01T12:00:00Z
Tugash: 2024-01-01T13:30:00Z
Davomiylik: 1h30m0s

Asosiy birliklar: time.Nanosecond, time.Microsecond, time.Millisecond, time.Second, time.Minute va time.Hour. Kun uchun time.Day konstantasi yo‘q, chunki calendar kuni vaqt zonasi va yozgi vaqt almashinuvi sabab har doim 24 soat bo‘lavermaydi.

Konfiguratsiyadan kelgan "1h30m" kabi qiymatni time.ParseDuration bilan tekshirish mumkin:

timeout, err := time.ParseDuration("1.5s")
if err != nil {
    return err
}

Bu parcha 1.5sni 1500ms davomiylikka aylantiradi. Noma’lum birlik yoki noto‘g‘ri matn xato qaytaradi. Duration cheklangan int64 diapazoniga ega; juda katta qiymatlarni hisoblashda overflow ehtimolini hisobga olish kerak.

Add va AddDate farqi

Add aniq davomiylik qo‘shadi:

later := moment.Add(2 * time.Hour)
earlier := moment.Add(-30 * time.Minute)

AddDate calendar bo‘yicha yil, oy va kun qo‘shadi:

nextMonth := moment.AddDate(0, 1, 0)
twoMonthsAgo := moment.AddDate(0, -2, 0)
nextWeek := moment.AddDate(0, 0, 7)

Add(24*time.Hour) va AddDate(0, 0, 1) bir xil tushuncha emas. Yozgi vaqt qo‘llanadigan zonada keyingi calendar kuni 23 yoki 25 real soatdan keyin kelishi mumkin. “Ertaga shu mahalliy soatda” uchun AddDate, “aniq 24 soatdan keyin” uchun Add(24*time.Hour) ishlatiladi.

Oy qo‘shishda ham normalizatsiyani hisobga oling. Masalan, ayrim oylarning 31-kuni yo‘q. AddDate natijani time.Date qoidalari bo‘yicha normalizatsiya qiladi. “Keyingi oyning oxirgi kuni” kabi biznes talabi uchun alohida calendar qoidasi yozish kerak.

Vaqt zonalari bilan ishlash

time.Timedagi location vaqt nuqtasini qanday ko‘rsatishni belgilaydi. In boshqa locationdagi ko‘rinishni qaytaradi:

package main

import (
    "fmt"
    "time"
)

func main() {
    tashkent, err := time.LoadLocation("Asia/Tashkent")
    if err != nil {
        fmt.Println("Zona yuklanmadi:", err)
        return
    }

    moment := time.Date(2024, time.January, 15, 9, 0, 0, 0, time.UTC)

    fmt.Println("UTC:", moment.Format(time.RFC3339))
    fmt.Println("Toshkent:", moment.In(tashkent).Format(time.RFC3339))
}

Natija:

UTC: 2024-01-15T09:00:00Z
Toshkent: 2024-01-15T14:00:00+05:00

In soat ko‘rinishini o‘zgartiradi, lekin vaqt chizig‘idagi nuqta o‘sha-o‘sha qoladi. Backend tizimlarda vaqtni UTCda saqlash va uzatish, foydalanuvchiga chiqarishda uning locationiga o‘tkazish odatda xatolarni kamaytiradi.

Faqat +05:00 offset hududning barcha tarixiy va kelajak qoidalarini ifodalamaydi. Asia/Tashkent kabi location nomi esa zoneinfo bazasidagi qoidalarni ishlatadi.

Vaqtlarni taqqoslash

Vaqt nuqtalarini Before, After va Equal bilan taqqoslang:

package main

import (
    "fmt"
    "time"
)

func main() {
    utc := time.Date(2024, time.January, 1, 9, 0, 0, 0, time.UTC)
    fixed := time.FixedZone("UTC+5", 5*60*60)
    local := time.Date(2024, time.January, 1, 14, 0, 0, 0, fixed)

    fmt.Println("Bir vaqt nuqtasimi:", utc.Equal(local))
    fmt.Println("UTC oldinmi:", utc.Before(local))
}

Natija:

Bir vaqt nuqtasimi: true
UTC oldinmi: false

Soatlar va zonalar boshqa ko‘rinsa ham, ikkala qiymat bir xil vaqt nuqtasini ifodalaydi.

time.Timeni == bilan solishtirish uning location va monotonic ma’lumoti kabi ichki ko‘rinishini ham hisobga oladi. Shu sabab vaqt nuqtasining tengligi uchun Equal ishlating. time.Time map kaliti bo‘lishi kerak bo‘lsa, qiymatlarni oldindan bir xil ko‘rinishga keltirish talabini aniq belgilang.

Unix timestamp

Unix timestamp vaqtni Unix epoch — 1970-01-01T00:00:00Zdan o‘tgan soniyalar yoki kichikroq birliklarda ifodalaydi:

package main

import (
    "fmt"
    "time"
)

func main() {
    moment := time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC)
    seconds := moment.Unix()
    restored := time.Unix(seconds, 0).UTC()

    fmt.Println("Timestamp:", seconds)
    fmt.Println("Qayta tiklandi:", restored.Format(time.RFC3339))
}

Natija:

Timestamp: 1704067200
Qayta tiklandi: 2024-01-01T00:00:00Z

API shartnomasida birlikni aniq yozing. Soniyadagi timestampni millisekund deb talqin qilish juda uzoq yoki noto‘g‘ri sana hosil qiladi. Go’da Unix, UnixMilli, UnixMicro va UnixNano metodlari birlikni ochiq ko‘rsatadi.

Sonli timestamp vaqt zonasini saqlamaydi. U vaqt nuqtasini ifodalaydi; ko‘rsatish zonasi keyin tanlanadi.

JSON va API formatlari

time.Time standart encoding/json bilan odatda RFC 3339 matni sifatida yoziladi. Tizimlar orasida almashishda zona offseti bor aniq format ishlating:

{
  "created_at": "2024-08-09T09:30:00Z"
}

Faqat sana kerak bo‘lsa, API shartnomasida YYYY-MM-DD ma’nosidagi Go layoutni 2006-01-02 deb belgilang. Tug‘ilgan kun kabi qiymatni yarim tun UTCga aylantirish ba’zi zonalarda oldingi yoki keyingi calendar kun sifatida ko‘rinishi mumkin. “Vaqt nuqtasi” va “calendar sanasi”ni ma’lumot modelida aralashtirmang.

Database turi ham talabga mos bo‘lishi kerak. Vaqt nuqtasi, mahalliy uchrashuv va faqat sanadan iborat qiymat bir xil semantikaga ega emas. Database driver va ustun turi timezone bilan qanday ishlashini tekshiring.

Sleep, timer va ticker

time.Sleep joriy goroutineni kamida berilgan davomiylikka to‘xtatadi:

time.Sleep(500 * time.Millisecond)

Nol yoki manfiy duration berilsa, Sleep darhol qaytadi. Operatsion tizim scheduler’i sabab goroutine aynan belgilangan nanosekundda davom etishi kafolatlanmaydi.

Bir martalik kelajak signal uchun time.NewTimer, davriy signal uchun time.NewTicker ishlatiladi:

package main

import (
    "fmt"
    "time"
)

func main() {
    timer := time.NewTimer(20 * time.Millisecond)
    defer timer.Stop()

    <-timer.C
    fmt.Println("Timer tugadi")
}

Natija:

Timer tugadi

timer.C signal keladigan kanaldir. Stop kerak bo‘lmay qolgan faol timer yoki tickerni to‘xtatishda muhim. Ticker iste’molchi sekin bo‘lsa, barcha oraliq ticklarni cheksiz navbatga yig‘maydi; dastur har bir tick albatta qayta ishlanadi degan taxminga tayanmasligi kerak.

time.Sleepni goroutine tugashini kutish uchun ishlatmang. Buning uchun WaitGroup yoki channel kerak. Timeout va bekor qilish uchun backend kodida ko‘pincha context.WithTimeout mos keladi.

O‘tgan vaqtni o‘lchash

Operatsiya qancha davom etganini time.Now va time.Since bilan o‘lchash mumkin:

start := time.Now()
// O‘lchanadigan ish.
elapsed := time.Since(start)
fmt.Println(elapsed)

start monotonic ma’lumotga ega bo‘lsa, time.Since devor vaqti tuzatilishidan ta’sirlanmaydi. Performance tahlilida bitta o‘lchovdan yakuniy xulosa chiqarmang; takroriy o‘lchov uchun Go benchmarklaridan foydalaning.

Vaqtga bog‘liq kodni testlash

Funksiya ichida time.Now()ni bevosita chaqirish test natijasini joriy vaqtga bog‘laydi. Vaqtni parametr yoki funksiya sifatida uzatish testni takrorlanuvchi qiladi:

package main

import (
    "fmt"
    "time"
)

func expired(now, deadline time.Time) bool {
    return !now.Before(deadline)
}

func main() {
    now := time.Date(2024, time.January, 1, 12, 0, 0, 0, time.UTC)
    deadline := now.Add(30 * time.Minute)

    fmt.Println(expired(now, deadline))
    fmt.Println(expired(deadline, deadline))
}

Natija:

false
true

Chegara qoidasi bu yerda ochiq: now deadlinega teng bo‘lsa ham muddat tugagan hisoblanadi. Real loyihada aynan shu kabi > yoki >= biznes qarorini test bilan mustahkamlash kerak.

Murakkab servisda quyidagi kabi clock funksiya berish mumkin:

type Clock func() time.Time

Ishlab chiqarish kodida time.Now, testda esa oldindan belgilangan vaqt qaytaruvchi funksiya uzatiladi. Timerlarga tayangan testlarda haqiqiy Sleep ishlatish testni sekin va beqaror qiladi; imkon qadar signal yoki boshqariladigan clockdan foydalaning.

Keng tarqalgan xatolar

  • time.Now() doim UTC qaytaradi deb o‘ylash. U mahalliy locationdan foydalanadi; UTC kerak bo‘lsa time.Now().UTC() yozing.
  • Layoutda YYYY-MM-DD ishlatish. Go layouti namuna vaqt qismlaridan tuziladi: 2006-01-02.
  • time.Parse xatosini tekshirmaslik. Noto‘g‘ri yoki mavjud bo‘lmagan sana rad etilishi kerak.
  • Zonasiz satrni noto‘g‘ri zonada parse qilish. UTC uchun Parse, ma’lum mahalliy zona uchun ParseInLocation ishlating.
  • Offset va locationni bir xil deb hisoblash. +05:00 hududning tarixiy va DST qoidalarini saqlamaydi.
  • Calendar kuni doim 24 soat deb hisoblash. Calendar amal uchun AddDate, aniq davomiylik uchun Add ishlatiladi.
  • Vaqt nuqtalarini formatlangan satr yoki == bilan solishtirish. Before, After va Equal metodlarini ishlating.
  • Unix timestamp birligini ko‘rsatmaslik. Soniya, millisekund va nanosekundni API shartnomasida aniq belgilang.
  • time.Sleep bilan goroutine yoki testni sinxronlashtirish. Aniq signal va sinxronizatsiya vositasidan foydalaning.
  • time.Time ichidagi monotonic qiymat serializatsiyada saqlanadi deb o‘ylash. Tizimlar orasida faqat devor vaqti hamda zona/offset uzatiladi.

Interviewda nimalarga e’tibor beriladi?

  • Go layouti Mon Jan 2 15:04:05 MST 2006 namuna vaqtiga asoslanadi.
  • time.Time vaqt nuqtasini, time.Duration esa davomiylikni ifodalaydi.
  • Parse zonasiz qiymatni UTC deb oladi; ParseInLocation berilgan locationdan foydalanadi.
  • Add aniq duration, AddDate calendar yil, oy va kun bilan ishlaydi.
  • Vaqt nuqtasi tengligini Equal bilan tekshirish kerak.
  • time.Now() monotonic o‘qishni saqlashi mumkin, ammo u formatlash va serializatsiyada uzatilmaydi.
  • Saqlash va servislararo almashuvda odatda UTC, foydalanuvchiga ko‘rsatishda uning locationi ishlatiladi.

Xulosa

Go’da sana va vaqt bilan ishlash faqat Format chaqirishdan iborat emas. Vaqt nuqtasi, davomiylik va locationni alohida tushunish kerak. Satrlarni qat’iy layout bilan parse qiling, xatolarni tekshiring va tizimlar orasida zona offseti aniq bo‘lgan formatdan foydalaning.

Calendar hisoblarida AddDate, aniq vaqt oralig‘ida Add va Sub ishlatiladi. Vaqtlarni Equal, Before va After bilan solishtiring. Testlarda joriy vaqtni tashqaridan uzatish kodni barqaror va tekshiriladigan qiladi.