ZonedDateTime — ano ito, pagtatrabaho sa mga time zone at oras

May-akda: IT Sectr Nai-publish: 2026-07-13 Oras ng pagbabasa: 10 min

ZonedDateTime — isang immutable na klase mula sa package na java.time na nag-iimbak ng petsa at oras kasama ng impormasyon tungkol sa time zone (ZoneId). Hindi tulad ng LocalDateTime, ang ZonedDateTime ay natatanging tumutukoy sa sandali sa timeline. Ayon sa detalye ng Oracle Java 17 (2024), wastong pinoproseso ng klase ang paglipat sa daylight saving time (DST) sa pamamagitan ng mga panuntunan ng zone mula sa IANA Time Zone Database.

Mga Pangunahing

  • ZonedDateTime — immutable na klase na pinagsasama ang petsa, oras at time zone (ZoneId) sa isang bagay.
  • Hindi tulad ng LocalDateTime, ang ZonedDateTime ay natatanging tumutukoy sa sandali sa timeline at angkop para sa mga pandaigdigang sistema.
  • Awtomatikong pinoproseso ng klase ang paglipat sa daylight saving time (DST) ayon sa mga panuntunan ng IANA Time Zone Database.
  • Para sa conversion sa pagitan ng mga time zone, ginagamit ang pamamaraang withZoneSameInstant(ZoneId).
  • Ang pag-iimbak ng ZonedDateTime sa mga database ay inirerekomenda sa pamamagitan ng OffsetDateTime o TIMESTAMP WITH TIME ZONE.

Ano ang ZonedDateTime?

ZonedDateTime — isa sa mga pangunahing klase ng package na java.time, na kumakatawan sa petsa at oras na may kumpletong impormasyon tungkol sa time zone. Pinagsasama nito ang tatlong bahagi: LocalDateTime (petsa at oras), ZoneId (identifier ng zone) at ZoneOffset (offset na may kaugnayan sa UTC).

Hindi tulad ng LocalDateTime, na nag-iimbak lamang ng wall-clock time na walang koneksyon sa zone, ang ZonedDateTime ay natatanging tumutukoy sa sandali. Dalawang magkaparehong LocalDateTime sa iba't ibang time zone ay kumakatawan sa magkaibang sandali ng oras. Dalawang magkaparehong ZonedDateTime — parehong sandali.

Ang klase ay ganap na immutable at thread-safe. Lahat ng aritmetikong operasyon ay nagbabalik ng bagong bagay. Ang ZonedDateTime ay nagpapatupad ng interface na ChronoZonedDateTime at maaaring gamitin saanman kung saan kinakailangan ang pagtatrabaho sa zonal time sa Java.

Ayon sa detalye ng Oracle Java 17, sinusuportahan ng ZonedDateTime ang pagtatrabaho sa anumang zone mula sa IANA Time Zone Database, na sumasaklaw sa mahigit 600 time zone.

ZonedDateTime vs LocalDateTime: ano ang pagkakaiba?

Ang pangunahing pagkakaiba — ang ZonedDateTime ay naglalaman ng time zone, habang ang LocalDateTime ay hindi. Ang pangunahing pagkakaibang ito ay tumutukoy sa saklaw ng paggamit ng bawat klase.

Ang LocalDateTime ay ginagamit para sa mga lokal na kaganapan: oras ng konsiyerto, iskedyul ng klase, petsa ng kapanganakan. Kung ang kaganapan ay magaganap sa Moscow sa 15:00, itatala ng LocalDateTime ang 15:00 na walang koneksyon. Kung ililipat mo ang server sa New York, mananatiling 15:00 ang oras — ngunit ito ay ibang pisikal na sandali na.

Ang ZonedDateTime ay ginagamit para sa pandaigdigang data: mga server log, mga timestamp sa API, mga internasyonal na pagpupulong. Kung ang pulong ay nakatakda sa 15:00 MSK, iimbak ng ZonedDateTime ang parehong oras at zone. Sa New York ito ay maipapakita nang tama bilang 8:00 EST. Ayon sa Baeldung (2024), ang pagpili sa pagitan ng LocalDateTime at ZonedDateTime ay ang pinakakaraniwang desisyon sa arkitektura kapag nagtatrabaho sa mga petsa.

Praktikal na panuntunan: kung ang data ay nakaimbak para sa isang rehiyon — gamitin ang LocalDateTime. Kung ang data ay tumatawid sa mga hangganan ng time zone — gamitin ang ZonedDateTime. Kung kailangan mong magpasa ng ganap na sandali — gamitin ang Instant.

Paano gumagana ang time zone sa java.time?

Ang time zone sa java.time ay kinakatawan ng klase na ZoneId. Ang ZoneId ay isang identifier ng zone sa format na “continent/region”, halimbawa “Europe/Moscow”, “America/New_York”, “Asia/Tokyo”. Ang ZoneId ay nakukuha sa pamamagitan ng static na pamamaraan na of(String zoneId) o sa pamamagitan ng default na system time zone.

Ang ZoneId ay nahahati sa dalawang uri: fixed offset (fixed offset, halimbawa “+03:00”) at region-based (mga regional zone, halimbawa “Europe/London”). Ang mga regional zone ay naglalaman ng mga panuntunan sa paglipat sa daylight saving time at mga makasaysayang pagbabago. Ang fixed offset — fixed offset lang.

Upang makuha ang kasalukuyang offset ng ZoneId sa isang partikular na sandali, ginagamit ang pamamaraang getRules(), na nagbabalik ng ZoneRules. Ang ZoneRules ay naglalaman ng lahat ng transition at offset para sa zone na iyon. Ito ang pangunahing mekanismo para sa tamang pagproseso ng DST.

Lahat ng time zone ay ibinibigay kasama ng JDK sa pamamagitan ng mga file na tzdata (IANA Time Zone Database) at regular na ina-update. Sa Android, ang bersyon ng tzdata ay nakadepende sa mga update ng system sa pamamagitan ng Google Play Services.

Paglikha ng ZonedDateTime

Mayroong ilang mga paraan upang lumikha ng ZonedDateTime. Ang pinakasimple — now(), na nagbabalik ng kasalukuyang oras sa system time zone. Ang variant na now(ZoneId) ay nagbibigay-daan upang makuha ang kasalukuyang oras sa isang tinukoy na zone.

Ang pamamaraang of(LocalDateTime, ZoneId) ay lumilikha ng ZonedDateTime mula sa lokal na oras at zone. Ang variant na of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond, ZoneId zone) — mula sa mga bahagi.

Ang LocalDateTime ay maaaring i-convert sa ZonedDateTime sa pamamagitan ng pamamaraang atZone(ZoneId). Instant — sa pamamagitan ng Instant.atZone(ZoneId). Date — sa pamamagitan ng Date.toInstant().atZone(ZoneId).

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)

Conversion sa pagitan ng mga time zone

Ang pangunahing pamamaraan ng conversion — withZoneSameInstant(ZoneId). Kino-convert ang ZonedDateTime sa ibang time zone, pinapanatili ang parehong sandali ng oras. Halimbawa, 15:00 MSK → 8:00 EST. Ang pamamaraang withZoneSameLocal(ZoneId) ay nagbabago ng zone, pinapanatili ang lokal na oras — ito ay nagbibigay ng ibang sandali.

Upang makuha ang offset na may kaugnayan sa UTC, ginagamit ang pamamaraang getOffset(), na nagbabalik ng ZoneOffset. Ang ZoneOffset ay isang subclass ng ZoneId na kumakatawan sa isang fixed offset sa format na “+HH:mm” o “-HH:mm”.

Ang conversion sa Instant ay ginagawa sa pamamagitan ng pamamaraang toInstant(). Ang Instant ay isang ganap na sandali ng oras, hindi nakadepende sa time zone. Ang baligtad na conversion — 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"))

Pagtatrabaho sa daylight saving time (DST)

Ang paglipat sa daylight saving time ay lumilikha ng dalawang problema: gap at overlap. Ang gap ay nangyayari sa tagsibol kapag ang orasan ay isinulong — ang isang tiyak na oras ay hindi umiiral. Ang overlap — sa taglagas kapag ang oras ay ibinalik — ang parehong oras ay umiiral nang dalawang beses.

Pinoproseso ng ZonedDateTime ang mga sitwasyong ito sa pamamagitan ng estratehiyang resolve. Kapag lumilikha ng bagay sa panahon ng gap, awtomatikong isinusulong ng java.time ang oras ng halaga ng offset. Kapag lumilikha sa panahon ng overlap, ang unang variant (bago ang pagbabago) ang pipiliin. Ang pag-uugali ay maaaring baguhin sa pamamagitan ng withZoneSameInstant.

Maaaring suriin kung ang oras ay nasa DST zone sa pamamagitan ng zone.getRules().isDaylightSavings(instant). Ang pamamaraang getOffset() ay nagpapakita ng kasalukuyang offset para sa sandaling iyon, at getRules().getDaylightSavings(instant) — ang halaga ng DST correction sa millisecond.

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 offset: $dstAmount")
    }
}

Pag-format ng ZonedDateTime

Para sa pag-format ng ZonedDateTime, ginagamit ang DateTimeFormatter. Ang karaniwang ISO format ay may kasamang petsa, oras at offset: “2026-07-21T15:30:00+03:00[Europe/Moscow]”. Mga paunang natukoy na format: ISO_ZONED_DATE_TIME, ISO_OFFSET_DATE_TIME, ISO_INSTANT.

Gumamit ng DateTimeFormatter.ofLocalizedDateTime(FormatStyle) para sa localized na pag-format. Ang FormatStyle ay maaaring SHORT, MEDIUM, LONG, FULL. Ang LONG ay may kasamang pangalan ng zone (“MSK”), FULL — buong pangalan (“Moscow Standard Time”).

Mahalaga: kapag nagpa-parse ng string na may ZonedDateTime, ang format ay dapat maglaman ng impormasyon tungkol sa zone o offset. Kung hindi tinukoy ang zone, gamitin ang LocalDateTime.parse() at pagkatapos ay atZone().

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
)

ZonedDateTime sa Android: mga praktikal na halimbawa

Unang halimbawa — pagpapakita ng oras ng pulong para sa user sa kanyang sariling time zone. Ibinabalik ng server ang ZonedDateTime sa UTC, kino-convert ng client sa lokal na time zone ng device.

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)
}

Pangalawang halimbawa — pagkalkula ng oras hanggang sa susunod na kaganapan na isinasaalang-alang ang time zone. Ginagamit namin ang ZonedDateTime para sa oras ng server at Duration.between() para sa pagkalkula ng pagkakaiba.

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"
}

Pangatlong halimbawa — pagtatrabaho sa Retrofit API. Ibinabalik ng server ang isang string sa ISO-8601 na may zone. Gumagamit kami ng custom na deserializer para sa conversion sa ZonedDateTime.

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()
        )
    }
}

Mga error sa pagtatrabaho sa mga time zone

Unang error — paggamit ng ZoneId.systemDefault() sa server code. Ang time zone ng server ay maaaring naiiba sa client, at ang paggamit ng system zone sa server ay humahantong sa maling kalkulasyon. Palaging tukuyin nang tahasan ang zone o gamitin ang UTC bilang sanggunian.

Pangalawang error — pagbalewala sa DST kapag kinakalkula ang tagal. Ang Duration.between() ay wastong pinoproseso ang mga transition, ngunit kung manu-mano mong ibawas ang mga timestamp, ang paglipat sa daylight saving time ay maaaring magbigay ng 1 oras na error. Gamitin ang mga pamamaraan ng ChronoUnit.HOURS.between() sa halip na manu-manong matematika.

Pangatlong error — pagkalito sa pagitan ng withZoneSameInstant at withZoneSameLocal. Ang una ay nagbabago ng zone, pinapanatili ang sandali — ang oras ay nagbabago. Ang pangalawa ay nagbabago ng zone, pinapanatili ang lokal na oras — ang sandali ay nagbabago. Ang pagpili ng maling pamamaraan ay isa sa mga pinakakaraniwang error ayon sa SonarSource (2024).

Pang-apat na error — pag-aakalang ang time zone ng device ay laging pareho sa time zone ng user. Ang user ay maaaring maglakbay at asahan na ang app ay magpapakita ng oras sa kanyang “tahanan” na zone, hindi sa kasalukuyang zone. Sa kasong ito, kailangan mong magbigay ng pagpili ng zone sa pamamagitan ng interface.

Mga Madalas Itanong

Ano ang pagkakaiba sa pagitan ng ZonedDateTime at OffsetDateTime?

Ang ZonedDateTime ay naglalaman ng regional zone identifier (halimbawa “Europe/Moscow”) at nagpoproseso ng DST. Ang OffsetDateTime ay nag-iimbak lamang ng fixed offset (+03:00) nang walang regional rules. Para sa pag-iimbak sa database, inirerekomenda ang OffsetDateTime.

Paano makukuha ang kasalukuyang oras sa UTC sa pamamagitan ng ZonedDateTime?

Gamitin ang ZonedDateTime.now(ZoneOffset.UTC) o Instant.now().atZone(ZoneOffset.UTC). Ang parehong variant ay nagbabalik ng kasalukuyang sandali na may zero offset. Para sa simpleng timestamp, gamitin ang Instant.now() nang walang koneksyon sa zone.

Maaari bang i-serialize ang ZonedDateTime sa pamamagitan ng Gson o Moshi?

Oo, ngunit kinakailangan ang custom na adapter. Hindi sinusuportahan ng Gson ang ZonedDateTime bilang default. Sinusuportahan ito ng Moshi — sa pamamagitan ng Rfc3339DateJsonAdapter adapter. Para sa Jackson, inirerekomenda ang paggamit ng Kotlinx Serialization o library na JavaTimeModule.

Paano hahawakan ang sitwasyon kung saan ang oras ay nahuhulog sa isang DST gap?

Awtomatikong isinusulong ng java.time ang oras ng halaga ng offset. Halimbawa, kung ang oras na 02:30 ay hindi umiiral sa transition sa 03:00, ang ZonedDateTime ay lilikha ng bagay sa 03:30. Ang pagkakaroon ng gap ay maaaring suriin sa pamamagitan ng ZoneRules.getTransition(instant).

Bakit hindi inirerekomenda ang ZonedDateTime para sa mga SQL database?

Sinusuportahan ng JDBC 4.2 ang OffsetDateTime, ngunit hindi direktang ZonedDateTime. Ang ZonedDateTime ay naglalaman ng regional zone na walang katumbas sa SQL. Inirerekomenda na mag-imbak ng OffsetDateTime o Instant, at ang zone ay iimbak sa isang hiwalay na column.

Buod

  • ZonedDateTime — immutable na klase para sa petsa at oras na may time zone, na wastong nagpoproseso ng DST sa pamamagitan ng IANA Time Zone Database.
  • Ang pangunahing pagkakaiba mula sa LocalDateTime — ang pagkakaroon ng zone, na ginagawang natatanging identifier ng sandali ng oras ang ZonedDateTime.
  • Para sa conversion sa pagitan ng mga zone, gamitin ang withZoneSameInstant(), na nagpapanatili ng sandali, hindi withZoneSameLocal.
  • Sa paglipat sa daylight saving time, awtomatikong nilulutas ng java.time ang mga gap at overlap sa pamamagitan ng built-in na mga panuntunan ng zone.
  • Para sa pag-iimbak sa database, gamitin ang OffsetDateTime o iimbak ang Instant at ZoneId nang hiwalay.
  • Sa Android, para i-convert ang ZonedDateTime sa lokal na oras ng device, gamitin ang ZoneId.systemDefault() kasama ng withZoneSameInstant.
  • Para sa JSON serialization, kinakailangan ang custom na adapter — gamitin ang Kotlinx Serialization o Jackson JavaTimeModule.

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din