ZonedDateTime — bu nədir, saat qurşaqları və zamanla iş

Müəllif: IT Sectr Dərc olunub: 2026-07-13 Oxuma vaxtı: 10 dəq

ZonedDateTime — java.time paketindən immutable sinif, tarix və vaxtı saat qurşağı (ZoneId) haqqında məlumatla birlikdə saxlayır. LocalDateTime-dən fərqli olaraq, ZonedDateTime zaman şkalasında anı birmənalı olaraq müyyənləşdirir. Oracle Java 17 (2024) spesifikasiyasına görə, sinif IANA Time Zone Database bazasından qaydalar vasitəsilə yay vaxtına (DST) keçidi düzgün emal edir.

Başlıca

  • ZonedDateTime — immutable sinif, tarix, vaxt və saat qurşağını (ZoneId) bir obyektdə birləşdirir.
  • LocalDateTime-dən fərqli olaraq, ZonedDateTime zaman şkalasında anı birmənalı müyyənləşdirir və qlobal sistemlər üçün uyğundur.
  • Sinif IANA Time Zone Database qaydalarına uyğun olaraq yay vaxtına (DST) keçidi avtomatik emal edir.
  • Saat qurşaqları arasında konvertasiya üçün withZoneSameInstant(ZoneId) metodu istifadə olunur.
  • ZonedDateTime məlumat bazalarında saxlamaq — OffsetDateTime və ya TIMESTAMP WITH TIME ZONE vasitəsilə tövsiyə olunur.

ZonedDateTime nədir?

ZonedDateTime — java.time paketinin əsas siniflərindən biri, tarix və vaxtı saat qurşağı haqqında tam məlumatla təqdim edir. O, üç komponenti birləşdirir: LocalDateTime (tarix və vaxt), ZoneId (qurşaq identifikatoru) və ZoneOffset (UTC-yə nisbətən yerdəyişmə).

LocalDateTime-dən fərqli olaraq, o, yalnız „divar saatı” (wall-clock time) saxlayır, ZonedDateTime anı birmənalı müyyənləşdirir. Müxtəlif saat qurşaqlarında iki eyni LocalDateTime fərqli zaman anlarını təmsil edir. İki eyni ZonedDateTime — eyni anı.

Sinif tamamilə immutable və thread-safe-dir. Bütün arifmetik əməliyyatlar yeni obyekt qaytarır. ZonedDateTime ChronoZonedDateTime interfeysini həyata keçirir və Java-da zonal vaxtla iş tələb olunan hər yerdə istifadə oluna bilər.

Oracle Java 17 spesifikasiyasına görə, ZonedDateTime 600-dən çox saat qurşağını əhatə edən IANA Time Zone Database-dən istənilən qurşaqla işləyə bilər.

ZonedDateTime vs LocalDateTime: fərq nədir?

Əsas fərq — ZonedDateTime saat qurşağını ehtiva edir, LocalDateTime isə yox. Bu əsas fərq hər bir sinfin tətbiq sahəsini müyyənləşdirir.

LocalDateTime lokal hadisələr üçün istifadə olunur: konsert vaxtı, dərs cədvəli, doğum tarixi. Hadisə Moskvada saat 15:00-da baş verirsə, LocalDateTime 15:00-ı qeydiyyata alır. Serveri Nyu-Yorka köçürsəniz, vaxt 15:00 olaraq qalır — lakin bu, artıq fərqli fiziki andır.

ZonedDateTime qlobal məlumatlar üçün tətbiq olunur: server logları, API-də zaman damğaları, beynəlxalq görüşlər. Görüş saat 15:00 MSK təyin edilərsə, ZonedDateTime həm vaxtı, həm də qurşağı saxlayır. Nyu-Yorkda bu, düzgün şəkildə 8:00 EST kimi göstərilir. Baeldung (2024)-yə görə, LocalDateTime və ZonedDateTime arasında seçim tarixlərlə işləyərkən ən çox yayılmış memarlıq qərarıdır.

Praktik qayda: məlumatlar bir region üçün saxlanılırsa — LocalDateTime istifadə edin. Məlumatlar saat qurşaqlarının sərhədlərini keçirsə — ZonedDateTime istifadə edin. Mütləq anı ötürmək lazımdırsa — Instant istifadə edin.

java.time-da saat qurşağı necə işləyir?

java.time-da saat qurşağı ZoneId sinfi ilə təmsil olunur. ZoneId — „continent/region” formatında qurşaq identifikatorudur, məsələn „Europe/Moscow”, „America/New_York”, „Asia/Tokyo”. ZoneId of(String zoneId) statik metodu və ya standart sistem saat qurşağı vasitəsilə əldə edilir.

ZoneId iki növə bölünür: fixed offset (sabit yerdəyişmə, məsələn „+03:00”) və region-based (regional qurşaqlar, məsələn „Europe/London”). Regional qurşaqlar yay vaxtına keçid qaydaları və tarixi dəyişiklikləri ehtiva edir. Fixed offset — sadəcə sabit yerdəyişmədir.

Müəyyən bir anda ZoneId-nin cari yerdəyişməsini əldə etmək üçün getRules() metodu istifadə olunur, ZoneRules qaytarır. ZoneRules verilmiş qurşaq üçün bütün keçidləri və yerdəyişmələri ehtiva edir. Bu, DST-nin düzgün emalı üçün əsas mexanizmdir.

Bütün saat qurşaqları JDK ilə tzdata (IANA Time Zone Database) faylları vasitəsilə təmin edilir və müntəzəm yenilənir. Android-də tzdata versiyası Google Play Services vasitəsilə sistem yeniləmələrindən asılıdır.

ZonedDateTime yaradılması

ZonedDateTime yaratmağın bir neçə yolu var. Ən sadəsi — now(), sistem saat qurşağında cari vaxtı qaytarır. now(ZoneId) variantı müəyyən edilmiş qurşaqda cari vaxtı əldə etməyə imkan verir.

of(LocalDateTime, ZoneId) metodu lokal vaxt və qurşaqdan ZonedDateTime yaradır. of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond, ZoneId zone) variantı — komponentlərdən.

LocalDateTime atZone(ZoneId) metodu vasitəsilə ZonedDateTime-ə çevrilə bilər. Instant — Instant.atZone(ZoneId) vasitəsilə. Date — Date.toInstant().atZone(ZoneId) vasitəsilə.

kotlin
val moscowZone = ZoneId.of("Europe/Moscow")

val nowInMoscow = ZonedDateTime.now(moscowZone)

val fromComponents = ZonedDateTime.of(
    2026, 7, 21, 15, 30, 0, 0, moscowZone
)

val fromLocal = LocalDateTime.now().atZone(moscowZone)

val fromInstant = Instant.now().atZone(moscowZone)

Saat qurşaqları arasında konvertasiya

Əsas konvertasiya metodu — withZoneSameInstant(ZoneId). ZonedDateTime-əçərək eyni anı qoruyaraq başqa saat qurşağına çevirir. Məsələn, 15:00 MSK → 8:00 EST. withZoneSameLocal(ZoneId) metodu qurşağı dəyişir, lokal vaxtı qoruyur — bu, fərqli an verir.

UTC-yə nisbətən yerdəyişməni əldə etmək üçün getOffset() metodu istifadə olunur, ZoneOffset qaytarır. ZoneOffset — „+HH:mm” və ya „-HH:mm” formatında sabit yerdəyişməni təmsil edən ZoneId-nin varisidir.

Instant-ə çevirmə toInstant() metodu vasitəsilə həyata keçirilir. Instant — saat qurşağından asılı olmayan mütləq zaman anıdır. Tərs çevirmə — Instant.atZone(ZoneId).

kotlin
val moscow = ZonedDateTime.of(
    2026, 7, 21, 15, 0, 0, 0,
    ZoneId.of("Europe/Moscow")
)

val newYork = moscow.withZoneSameInstant(
    ZoneId.of("America/New_York")
)

val utcInstant = moscow.toInstant()
val backToMoscow = utcInstant.atZone(ZoneId.of("Europe/Moscow"))

Yay vaxtı ilə iş (DST)

Yay vaxtına keçid iki problem yaradır: boşluqlar (gap)üst-üstə düşmələr (overlap). Boşluq yazda saatlar irəli çəkiləndə yaranır — müəyyən vaxt mövcud deyil. Üst-üstə düşmə — payızda vaxt geri çəkiləndə, eyni vaxt iki dəfə mövcud olur.

ZonedDateTime bu halları resolve strategiyası vasitəsilə emal edir. Boşluq zamanı obyekt yaradılarkən java.time avtomatik olaraq vaxtı yerdəyişmə miqdarı qədər irəli aparır. Üst-üstə düşmə zamanı yaradılarkən birinci variant (dəyişiklikdən əvvəl) seçilir. Davranışı withZoneSameInstant vasitəsilə dəyişmək olar.

Vaxtın DST qurşağında olub-olmadığını zone.getRules().isDaylightSavings(instant) vasitəsilə yoxlamaq olar. getOffset() metodu müəyyən an üçün cari yerdəyişməni göstərir, getRules().getDaylightSavings(instant) isə — DST düzəlişinin millisaniyələrlə ölçüsünü.

kotlin
fun checkDST(zdt: ZonedDateTime) {
    val rules = zdt.getZone().getRules()
    val instant = zdt.toInstant()

    if (rules.isDaylightSavings(instant)) {
        val dstAmount = rules.getDaylightSavings(instant)
        Log.d("DST", "DST yerdəyişməsi: $dstAmount")
    }
}

ZonedDateTime formatlaşdırma

ZonedDateTime formatlaşdırmaq üçün DateTimeFormatter istifadə olunur. Standart ISO formatı tarix, vaxt və yerdəyişməni əhatə edir: „2026-07-21T15:30:00+03:00[Europe/Moscow]”. Öncədən müəyyən edilmiş formatlar: ISO_ZONED_DATE_TIME, ISO_OFFSET_DATE_TIME, ISO_INSTANT.

Yerləşdirilmiş formatlaşdırma üçün DateTimeFormatter.ofLocalizedDateTime(FormatStyle) istifadə edin. FormatStyle SHORT, MEDIUM, LONG, FULL ola bilər. LONG qurşaq adını („MSK”), FULL — tam adı („Moscow Standard Time”) ehtiva edir.

Vacib: ZonedDateTime ilə sətri təhlil edərkən format qurşaq və ya yerdəyişmə haqqında məlumat ehtiva etməlidir. Qurşaq göstərilməyibsə, LocalDateTime.parse() və sonra atZone() istifadə edin.

kotlin
val zdt = ZonedDateTime.now(ZoneId.of("Europe/Moscow"))

val iso = zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)

val custom = DateTimeFormatter
    .ofPattern("dd.MM.yyyy HH:mm z")
val formatted = zdt.format(custom)

val parsed = ZonedDateTime.parse(
    "2026-07-21T15:30:00+03:00",
    DateTimeFormatter.ISO_OFFSET_DATE_TIME
)

Android-də ZonedDateTime: praktik nümunələr

Birinci nümunə — istifadəçiyə öz saat qurşağında görüş vaxtını göstərmək. Server ZonedDateTime-ı UTC-də qaytarır, müştəri cihazın lokal saat qurşağına çevirir.

kotlin
fun displayMeetingTime(
    serverUtc: ZonedDateTime
): String {
    val deviceZone = ZoneId.systemDefault()
    val localTime = serverUtc.withZoneSameInstant(deviceZone)
    val formatter = DateTimeFormatter
        .ofPattern("dd.MM.yyyy HH:mm z")
    return localTime.format(formatter)
}

İkinci nümunə — saat qurşağını nəzərə alaraq növbəti hadisəyə qədər vaxtın hesablanması. Server vaxtı üçün ZonedDateTime və fərqi hesablamaq üçün Duration.between() istifadə edirik.

kotlin
fun timeUntilEvent(eventTime: ZonedDateTime): String {
    val now = ZonedDateTime.now()
    val duration = Duration.between(now, eventTime)

    val hours = duration.toHours()
    val minutes = duration.toMinutes() % 60
    return "Remaining $hours h $minutes min"
}

Üçüncü nümunə — Retrofit API ilə iş. Server ISO-8601 formatında qurşaqla birlikdə sətr qaytarır. ZonedDateTime-yə çevirmək üçün xüsusi deserializatordan istifadə edirik.

kotlin
data class EventResponse(
    @JsonAdapter(ZonedDateTimeAdapter::class)
    val eventTime: ZonedDateTime
)

class ZonedDateTimeAdapter : JsonAdapter<ZonedDateTime>() {
    override fun fromJson(reader: JsonReader): ZonedDateTime? {
        return ZonedDateTime.parse(
            reader.nextString()
        )
    }
}

Saat qurşaqları ilə işdə səhvlər

Birinci səhv — server kodunda ZoneId.systemDefault() istifadəsi. Serverin saat qurşağı müştəridən fərqlənə bilər və serverdə sistem qurşağının istifadəsi səhv hesablamalara səbəb olur. Həmişə açıq şəkildə qurşağı göstərin və ya UTC-ni etalon kimi istifadə edin.

İkinci səhv — müddəti hesablayarkən DST-yə məhəl qoymamaq. Duration.between() keçidləri düzgün emal edir, lakin timestamp-ları əl ilə çıxarsanız, yay vaxtına keçid 1 saatlıq səhv verə bilər. Əl ilə riyaziyyat əvəzinə ChronoUnit.HOURS.between() metodlarından istifadə edin.

Üçüncü səhv — withZoneSameInstant və withZoneSameLocal arasında qarışıqlıq. Birincisi qurşağı dəyişir, anı qoruyur — vaxt dəyişir. İkincisi qurşağı dəyişir, lokal vaxtı qoruyur — an dəyişir. Yanlış metod seçimi SonarSource (2024)-yə görə ən çox yayılmış səhvlərdən biridir.

Dördüncü səhv — cihazın saat qurşağının həmişə istifadəçinin saat qurşağı ilə eyni olduğunu güman etmək. İstifadəçi səyahət edə bilər və proqramın vaxtı cari deyil, öz „ev” qurşağında göstərməsini gözləyə bilər. Bu halda interfeys vasitəsilə qurşaq seçimini təmin etmək lazımdır.

Tez-tez verilən suallar

ZonedDateTime və OffsetDateTime arasında fərq nədir?

ZonedDateTime regional qurşaq identifikatorunu (məsələn „Europe/Moscow”) ehtiva edir və DST-ni emal edir. OffsetDateTime yalnız sabit yerdəyişməni (+03:00) regional qaydalar olmadan saxlayır. Məlumat bazasında saxlamaq üçün OffsetDateTime tövsiyə olunur.

ZonedDateTime vasitəsilə UTC-də cari vaxtı necə əldə etmək olar?

ZonedDateTime.now(ZoneOffset.UTC) və ya Instant.now().atZone(ZoneOffset.UTC) istifadə edin. Hər iki variant sıfır yerdəyişmə ilə cari anı qaytarır. Sadə zaman damğası üçün qurşağa bağlı olmadan Instant.now() istifadə edin.

ZonedDateTime-ı Gson və ya Moshi vasitəsilə serializasiya etmək olarmı?

Bəli, lakin xüsusi adapter tələb olunur. Gson ZonedDateTime-ı standart olaraq dəstəkləmir. Moshi — Rfc3339DateJsonAdapter adapteri vasitəsilə dəstəkləyir. Jackson üçün Kotlinx Serialization və ya JavaTimeModule kitabxanasından istifadə etmək tövsiyə olunur.

Vaxt DST boşluğuna düşdükdə vəziyyəti necə emal etmək olar?

java.time avtomatik olaraq vaxtı yerdəyişmə miqdarı qədər irəli aparır. Məsələn, saat 02:30 03:00-a keçid zamanı mövcud deyilsə, ZonedDateTime 03:30 obyektini yaradır. Boşluğun varlığını ZoneRules.getTransition(instant) vasitəsilə yoxlamaq olar.

Niyə ZonedDateTime SQL məlumat bazaları üçün tövsiyə edilmir?

JDBC 4.2 OffsetDateTime-ı dəstəkləyir, lakin ZonedDateTime-ı birbaşa dəstəkləmir. ZonedDateTime SQL-də analoqu olmayan regional qurşaq ehtiva edir. OffsetDateTime və ya Instant saxlamaq, qurşağı isə ayrı sütunda saxlamaq tövsiyə olunur.

Nəticə

  • ZonedDateTime — saat qurşağı ilə tarix və vaxt üçün immutable sinif, IANA Time Zone Database vasitəsilə DST-ni düzgün emal edir.
  • LocalDateTime-dən əsas fərq — qurşağın olması, ZonedDateTime-ı zaman anının birmənalı identifikatoru edir.
  • Qurşaqlar arasında konvertasiya üçün withZoneSameInstant() istifadə edin, anı qoruyur, withZoneSameLocal yox.
  • Yay vaxtına keçid zamanı java.time daxili qurşaq qaydaları vasitəsilə boşluqları və üst-üstə düşmələri avtomatik həll edir.
  • Məlumat bazasında saxlamaq üçün OffsetDateTime istifadə edin və ya Instant və ZoneId ayrıca saxlayın.
  • Android-də ZonedDateTime-ı cihazın lokal vaxtına çevirmək üçün ZoneId.systemDefault() ilə birlikdə withZoneSameInstant istifadə edin.
  • JSON serializasiyası üçün xüsusi adapter tələb olunur — Kotlinx Serialization və ya Jackson JavaTimeModule istifadə edin.

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun