---
title: "Tarih ve Zaman API'si"
source: 'https://academia.sh/tr/kurslar/java-standart-kutuphane/tarih-ve-zaman-apisi'
course: 'Standart Kütüphane ve Akışlar'
language: tr
updated: '2026-08-17T18:09:43+00:00'
license: 'CC BY-SA 4.0'
---

# Tarih ve Zaman API'si

Üç tipin (anlık, yerel tarih-saat, bölgesel tarih-saat) 'aynı anı mı gösteriyor' sorusunu doğru yanıtlayıp yanıtlayamadığı koşturularak ölçülür. Saat dilimi verilmeden karşılaştırmanın sessizce yanlış sonuç verdiği ve yaz saati geçişindeki boşluk ile çakışmanın API'nin kural gereği seçtiği yer olduğu gösterilir.

Önceki ders bir nesnenin bayta çevrildiğinde yapıcının hiç çalışmadığını ölçtü — orada
zaman hiç karışmadı, çünkü ölçülen her şey belleğe alınmış sabit bir bayt dizisiydi. Bir
sistemin taşıdığı en yaygın veri türlerinden biri zamanın kendisidir, ve zaman üç ayrı tiple
temsil edilir: `Instant` bir anlıktır, `LocalDateTime` bir yerel tarih-saattir,
`ZonedDateTime` bir bölgesel tarih-saattir. Bu üç tip aynı bilgiyi taşımaz — biri saat
dilimini hiç bilmez, biri her zaman bilir — ve bu ders o farkın nerede önemli olduğunu ölçer.

Kursun sorusu değişmedi: bir garanti — burada "bu iki değer aynı anı mı gösteriyor" sorusuna
doğru yanıt verme garantisi — hangi tarafın elinde? Yanıt bu kez bir sınıf seçimi değil, bir
**tip** seçimidir. Üç bölüm sırayla ilerliyor. İlki üç tipi aynı soruyla sınıyor. İkincisi bu
sorunun yanlış yanıtlandığı somut bir çağıran hatasını ölçüyor. Üçüncüsü ise saat dilimi
eklemenin bile her soruyu yanıtlamadığı bir sınır durumu — yaz saati geçişini — ölçüyor.

Bu üçü, bir önceki dersin ölçtüğü şeyin bir başka biçimidir. Dizileştirme dersinde bir bayt
dizisinin **hangi bilgiyi taşıdığı** ölçülmüştü; burada da aynı soru soruluyor, ama nesne
değil zaman temsil ediliyor. Bir `LocalDateTime`, tıpkı alan adı değiştirilmiş bir kayıt
gibi, sahip olmadığı bir bilgiyi taşıyormuş gibi kullanıldığında sessizce yanlış sonuç
üretiyor — fark, orada bir dizileştirme sürüm kimliğinin, burada bir saat diliminin eksik
olması.

**Belirlenimcilik notu:** bu derste gerçek saat hiç okunmaz. Bütün örnekler sabit bir
`Instant` üzerine ya da sabit bir metinden ayrıştırılmış bir tarihe kurulur; yaz saati
geçişleri de sistemin kendi saat dilimi verisinden **programatik olarak keşfedilir**, tarihi
düzyazıda iddia edilmez.

## Üç Tip, Aynı Soru

- **IO22** — Tek bir `Instant` iki ayrı saat dilimine (İstanbul, New York) uygulanıp iki
  `ZonedDateTime` üretiliyor; bu iki değer **aynı anı** gösteriyor ama farklı yerel saatler
  taşıyor. Dört ayrı yöntem aynı soruyu ("bu ikisi aynı an mı") yanıtlamaya çalışıyor:
  `Instant.equals`, `LocalDateTime.equals`, `ZonedDateTime.equals`, `ZonedDateTime.isEqual`.

```java
// UcTip.java — uc tipten hangisi "ayni ani mi gosteriyor" sorusunu dogru yanitliyor
import java.time.*;

public class UcTip {
    public static void main(String[] args) {
        Instant an = Instant.parse("2026-06-15T13:00:00Z");

        ZonedDateTime istanbul = an.atZone(ZoneId.of("Europe/Istanbul"));
        ZonedDateTime newYork = an.atZone(ZoneId.of("America/New_York"));
        LocalDateTime istanbulYerel = istanbul.toLocalDateTime();
        LocalDateTime newYorkYerel = newYork.toLocalDateTime();

        System.out.println("istanbul yerel saat: " + istanbulYerel);
        System.out.println("newyork  yerel saat: " + newYorkYerel);
        System.out.println();

        System.out.printf("%-40s%s%n", "soru: ayni ani mi gosteriyor", "yanit");
        System.out.printf("%-40s%s%n", "Instant.equals",
                an.equals(Instant.parse("2026-06-15T13:00:00Z")));
        System.out.printf("%-40s%s%n", "LocalDateTime.equals (yerel saatler)",
                istanbulYerel.equals(newYorkYerel));
        System.out.printf("%-40s%s%n", "ZonedDateTime.equals (bolge+yerel+ofset)",
                istanbul.equals(newYork));
        System.out.printf("%-40s%s%n", "ZonedDateTime.isEqual (yalniz an)",
                istanbul.isEqual(newYork));
    }
}
```

```
istanbul yerel saat: 2026-06-15T16:00
newyork  yerel saat: 2026-06-15T09:00

soru: ayni ani mi gosteriyor            yanit
Instant.equals                          true
LocalDateTime.equals (yerel saatler)    false
ZonedDateTime.equals (bolge+yerel+ofset)false
ZonedDateTime.isEqual (yalniz an)       true
```

İki `ZonedDateTime` **aynı** `Instant`'tan üretildi, yani ikisi de birebir aynı anı gösteriyor
— biri saat 16.00, öteki saat 09.00 gösterse bile. Dört yöntemden ikisi bunu doğru söylüyor:
`Instant.equals` (zaten aynı anlığı karşılaştırıyor) ve `ZonedDateTime.isEqual` (yalnız
temsil ettiği anı karşılaştırmak için özel olarak yazılmış bir yöntem). `LocalDateTime.equals`
`false` diyor, ama bu soruyu yanlış yanıtlamıyor — **hiç yanıtlayamıyor**, çünkü elindeki
değerlerin hangi saat dilimine ait olduğuna dair hiçbir bilgi yok; yalnız iki takvim
okumasının birbirine eşit olup olmadığını söylüyor. En şaşırtıcı satır üçüncüsü:
`ZonedDateTime.equals`, elinde saat dilimi bilgisi olmasına rağmen `false` diyor, çünkü o
yöntem "aynı an mı" sorusunu değil, "bölge, yerel saat ve ofset birebir aynı mı" sorusunu
yanıtlıyor. Aynı tipte iki yöntem var ve ikisi **farklı** sorulara yanıt veriyor; doğru
yöntemi seçmek çağırana kalıyor.

Bu dördü, üç tipin taşıdığı bilginin doğrudan bir sonucudur. `Instant` bir sayı çizgisindeki
tek bir noktadır ve iki nokta aynı yerdeyse `equals` bunu doğrudan söyler; başka bir sorusu
yoktur. `ZonedDateTime` bu noktayı bir de takvim okumasıyla birlikte taşır — hangi bölgede,
hangi yerel saatte olduğunu — ve `equals` bu **ek** bilgiyi de karşılaştırmaya katar; iki
temsil aynı anı gösterse bile bölgeleri farklıysa `equals` onları ayrı sayar, çünkü o iki
değeri **birebir aynı temsil** olarak tanımlar, aynı an olarak değil. `isEqual` bilerek bu
ek bilgiyi göz ardı eder ve yalnız temsil edilen ana bakar. `LocalDateTime` ise bu ek
bilginin **kendisini hiç taşımaz**; elinde karşılaştıracağı bir bölge, bir ofset yoktur, bu
yüzden sorulan soruyu değil, sorabileceği tek soruyu — iki takvim okuması eşit mi — yanıtlar.

## Çağıranın Kuralı: Saat Dilimi Vermeden Karşılaştırma

- **IO23** — İki kayıt aynı yerel saat değerini taşıyor (`09:00`) ama biri İstanbul'da,
  biri New York'ta girilmiş; hangi saat diliminde girildiği bilgisi kayıtla birlikte
  **tutulmuyor**. Bu bilgi olmadan yapılan bir karşılaştırma, bilgi verilerek yapılan bir
  karşılaştırmayla yan yana koşturuluyor.

Bu durum uydurma değildir: bir randevu sistemi, bir günlük kaydı, bir zamanlanmış görev —
zamanı kullanıcının kendi yerel saatiyle alan her sistem aynı seçimle karşılaşır. Saat
dilimini kayıtla birlikte saklamak bir tasarım kararıdır, dilin kendisi bunu dayatmaz;
`LocalDateTime` bilinçli olarak saat dilimsiz bir tip olarak var eder, çünkü çoğu zaman
gerçekten de yalnız bir takvim okuması yeterlidir. Bedel yalnız o okumayı "aynı an" anlamında
kullanmaya kalkışıldığında ortaya çıkıyor.

```java
// SaatDilimiEksik.java — saat dilimi verilmeden karsilastirma sessizce yanlis sonuc verir
import java.time.*;

public class SaatDilimiEksik {
    static boolean naifAyniZaman(LocalDateTime a, LocalDateTime b) {
        return a.equals(b);
    }

    static boolean dogruAyniZaman(LocalDateTime a, ZoneId aBolge, LocalDateTime b, ZoneId bBolge) {
        return a.atZone(aBolge).toInstant().equals(b.atZone(bBolge).toInstant());
    }

    public static void main(String[] args) {
        LocalDateTime aKaydi = LocalDateTime.of(2026, 6, 15, 9, 0);
        LocalDateTime bKaydi = LocalDateTime.of(2026, 6, 15, 9, 0);
        ZoneId aBolge = ZoneId.of("Europe/Istanbul");
        ZoneId bBolge = ZoneId.of("America/New_York");

        System.out.println("a kaydi (yerel): " + aKaydi + " (Istanbul)");
        System.out.println("b kaydi (yerel): " + bKaydi + " (New York)");
        System.out.println("naif karsilastirma (saat dilimi vermeden): "
                + naifAyniZaman(aKaydi, bKaydi));
        System.out.println("dogru karsilastirma (saat dilimi verilerek): "
                + dogruAyniZaman(aKaydi, aBolge, bKaydi, bBolge));

        Duration fark = Duration.between(aKaydi.atZone(aBolge).toInstant(),
                bKaydi.atZone(bBolge).toInstant());
        System.out.println("gercek fark: " + fark.toHours() + " saat");
    }
}
```

```
a kaydi (yerel): 2026-06-15T09:00 (Istanbul)
b kaydi (yerel): 2026-06-15T09:00 (New York)
naif karsilastirma (saat dilimi vermeden): true
dogru karsilastirma (saat dilimi verilerek): false
gercek fark: 7 saat
```

Naif karşılaştırma "aynı zaman" diyor, çünkü iki `LocalDateTime` değeri gerçekten eşit — ikisi
de `09:00` okuyor. Ama gerçekte aralarında **yedi saatlik** bir fark var. Bu kütüphanenin bir
kusuru değil: `LocalDateTime.equals` zaten yalnız iki takvim okumasını karşılaştırdığını
söylüyor, başka bir söz vermiyor. Kusur, saat dilimi bilgisini **hiç taşımayan** bir tipi,
sanki taşıyormuş gibi kullanmakta. Doğru karşılaştırma her iki tarafa da saat dilimi
ekleyip `Instant`'a çevirdikten sonra karşılaştırıyor ve doğru yanıtı — `false`, ve altında
yatan yedi saatlik farkı — veriyor. Bu, bir önceki bölümün doğal sonucu: `LocalDateTime`
"aynı an mı" sorusunu yanıtlayamayan tipti; burada aynı eksiklik bir kayıt karşılaştırma
hatası olarak somutlaşıyor.

Bu kural derleyici tarafından hiç zorlanmıyor. `naifAyniZaman` iki `LocalDateTime` alıp bir
`boolean` döndüren, tamamen geçerli, tipi uyan bir yöntemdir; derleyici onun **anlamsal**
olarak yanlış bir soruya yanlış bir yanıt verdiğini bilemez, çünkü elindeki tek bilgi iki
parametrenin de aynı sınıftan olduğudur. Sonuç, önceki derste ölçülen kusurla aynı
biçimdedir: hata çalışma zamanında bile bir istisna olarak görünmez, yalnız yanlış bir
`boolean` olarak sessizce geçer. `Duration.between` çağrısı bu farkı görünür kılmak için
her iki tarafın da açıkça bir `Instant`'a çevrilmesini gerektiriyor — yani doğru
karşılaştırmayı yazmanın bedeli, saat dilimini **unutmamaktır**, unutulduğunda derleyicinin
hiçbir uyarı vermeyeceğini bilerek unutmamaktır.

## Yaz Saati Geçişinde Boşluk ve Çakışma

- **IO24** — Sistemin New York saat dilimi kuralları, sabit bir başlangıç anından sonraki
  ilk iki geçiş için sorgulanıyor. İlk geçiş bir **boşluktur** (bir saatlik bir yerel saat
  aralığı hiç yaşanmaz), ikincisi bir **çakışmadır** (bir saatlik bir yerel saat aralığı
  iki kez yaşanır). Hangi tarihte olduğu düzyazıda iddia edilmiyor; program kendi bulduğu
  tarihi kullanıyor.

Önceki iki bölüm saat dilimini **eksik** bir durumu ele aldı; bu bölüm saat dilimi tamsa bile
sınırın nerede olduğunu ölçüyor. `ZoneId.getRules()` bir bölgenin bütün geçiş bilgisini
taşıyan bir `ZoneRules` nesnesi döndürüyor; bu bilgi kodun içinde yazılı bir sabit değil,
sanal makinenin kendi saat dilimi verisinden okunuyor. `nextTransition` bir andan sonraki
ilk geçişi buluyor — sonucu hangi anın verildiğine bağlı, ama bu bağımlılık ölçümü
belirsizleştirmiyor, çünkü başlangıç anı burada da sabit bir metinden ayrıştırılıyor.

```java
// KuralKesif.java — yaz saati gecisleri sistemin kendi saat dilimi verisinden kesfedilir
import java.time.*;
import java.time.zone.*;

public class KuralKesif {
    public static void main(String[] args) {
        ZoneId bolge = ZoneId.of("America/New_York");
        ZoneRules kurallar = bolge.getRules();
        Instant baslangic = Instant.parse("2020-01-01T00:00:00Z");

        ZoneOffsetTransition ilkGecis = kurallar.nextTransition(baslangic);
        ZoneOffsetTransition ikinciGecis = kurallar.nextTransition(ilkGecis.getInstant());

        System.out.println("ilk gecis bosluk mu: " + ilkGecis.isGap());
        System.out.println("ilk gecis oncesi/sonrasi yerel saat: "
                + ilkGecis.getDateTimeBefore() + " / " + ilkGecis.getDateTimeAfter());
        System.out.println("ikinci gecis cakisma mi: " + ikinciGecis.isOverlap());
        System.out.println("ikinci gecis oncesi/sonrasi yerel saat: "
                + ikinciGecis.getDateTimeBefore() + " / " + ikinciGecis.getDateTimeAfter());

        LocalDateTime boslukIcinde = ilkGecis.getDateTimeBefore().plusMinutes(30);
        ZonedDateTime boslukSonuc = boslukIcinde.atZone(bolge);
        System.out.println();
        System.out.println("bosluktaki yerel saat: " + boslukIcinde);
        System.out.println("varsayilan cozum: " + boslukSonuc);
        System.out.println("yerel saat ileri kaydirildi mi: "
                + !boslukSonuc.toLocalDateTime().equals(boslukIcinde));

        LocalDateTime cakismaIcinde = ikinciGecis.getDateTimeAfter();
        ZonedDateTime erkenOfset = cakismaIcinde.atZone(bolge);
        ZonedDateTime gecOfset = erkenOfset.withLaterOffsetAtOverlap();
        System.out.println();
        System.out.println("cakismadaki yerel saat: " + cakismaIcinde);
        System.out.println("varsayilan cozum (erken ofset): " + erkenOfset);
        System.out.println("gec ofset istendiginde: " + gecOfset);
        System.out.println("yerel saat ayni, aninlar ayni mi: "
                + erkenOfset.toLocalDateTime().equals(gecOfset.toLocalDateTime()) + ", "
                + erkenOfset.toInstant().equals(gecOfset.toInstant()));
    }
}
```

```
ilk gecis bosluk mu: true
ilk gecis oncesi/sonrasi yerel saat: 2020-03-08T02:00 / 2020-03-08T03:00
ikinci gecis cakisma mi: true
ikinci gecis oncesi/sonrasi yerel saat: 2020-11-01T02:00 / 2020-11-01T01:00

bosluktaki yerel saat: 2020-03-08T02:30
varsayilan cozum: 2020-03-08T03:30-04:00[America/New_York]
yerel saat ileri kaydirildi mi: true

cakismadaki yerel saat: 2020-11-01T01:00
varsayilan cozum (erken ofset): 2020-11-01T01:00-04:00[America/New_York]
gec ofset istendiginde: 2020-11-01T01:00-05:00[America/New_York]
yerel saat ayni, aninlar ayni mi: true, false
```

Program kendi başlangıç anından itibaren sistemin saat dilimi kurallarını tarayıp iki geçiş
buluyor: biri **boşluk**, biri **çakışma**. Boşlukta saat 02.00'den 03.00'e doğrudan atlıyor
— `02:30` diye bir yerel saat o bölgede o gün hiç yaşanmıyor. Bu yüzden `02:30`'u o bölgeye
bağladığımızda API onu **olduğu gibi** kabul etmiyor; boşluğun uzunluğu kadar ileri kaydırıp
`03:30`'a taşıyor. Çakışmada ise tam tersi olur: `01:00` o gün **iki kez** yaşanıyor, biri
yaz saatiyle (ofset −4), biri kışla (ofset −5). API bir seçim yapmak zorunda ve varsayılan
olarak **erken** ofseti seçiyor; `withLaterOffsetAtOverlap` çağrıldığında aynı yerel saat
bu kez bir saat **sonraki** gerçek ana bağlanıyor — yerel saat değişmiyor, ama gösterdiği
an değişiyor.

İki durumun neden farklı sonuçlandığı geçişin kendi tanımından çıkıyor. Boşlukta saatler
gerçekten **atlıyor** — 02.00'den sonra bir sonraki gerçek an 03.00 olarak okunuyor — bu
yüzden aradaki yerel saatlerin karşılığı olan tek gerçek an, atlamanın **bittiği** yerdir; API
bu yüzden ileri kaydırıyor, geri değil. Çakışmada ise saatler **geri sarılıyor**: aynı yerel
okuma iki farklı gerçek anda beliriyor, ve aralarında bir tercih yapılması gerekiyor çünkü
her ikisi de eşit derecede geçerli. `getDateTimeBefore` ve `getDateTimeAfter`, bu ölçümde
kullanılan iki yöntem, geçişin **hangi yönde** olduğunu söylüyor: boşlukta ikinci değer
birinciden büyük, çakışmada küçük — geçişin bosluk mu çakışma mı olduğu buradan da
çıkarılabilir, `isGap`/`isOverlap` yalnız bunu adlandırıyor.

Bu bölümün sınırlayıcı ölçümü şudur: saat dilimi eklemek önceki bölümdeki hatayı çözüyor, ama
**her** soruyu yanıtlamıyor. Çakışma durumunda bölgeyle birlikte gelen yerel saat bile tek
başına yetmiyor — aynı yerel saat, aynı bölgede, iki ayrı gerçek ana karşılık gelebiliyor,
ve hangisinin kastedildiğini API'nin kendisi seçmek zorunda kalıyor. Bu seçim keyfi değil:
sistemin saat dilimi kuralları hangi yerel saatlerin var olmadığını ve hangilerinin iki kez
var olduğunu tanımlıyor, API da bu kurala göre **bir tarafı** seçiyor. Seçimin kendisi
sabit ve belgelenmiş; ama çağıranın istediği taraf bu olmayabilir, ve bunu fark etmenin tek
yolu `isGap`/`isOverlap` gibi sorguları kullanmaktır.

## Özet

- `Instant` bir anlıktır ve saat dilimi taşımaz; `LocalDateTime` bir yerel tarih-saattir ve
  saat dilimi taşımaz; `ZonedDateTime` bir bölgesel tarih-saattir ve saat dilimi taşır.
- "Aynı anı mı gösteriyor" sorusunu `Instant.equals` ve `ZonedDateTime.isEqual` doğru
  yanıtlar; `LocalDateTime.equals` bu soruyu hiç yanıtlayamaz, `ZonedDateTime.equals` ise
  farklı bir soruyu (bölge, yerel saat, ofset birebir aynı mı) yanıtlar.
- Saat dilimi verilmeden karşılaştırılan iki yerel tarih-saat, aralarında gerçek bir fark
  varken bile eşit çıkabilir; kusur kütüphanede değil, tipin taşımadığı bilgiyi varsaymakta.
- Yaz saati geçişindeki boşlukta bir yerel saat aralığı hiç yaşanmaz ve API onu geçişin
  uzunluğu kadar ileri kaydırır; çakışmada bir yerel saat aralığı iki kez yaşanır ve API
  varsayılan olarak erken ofseti seçer.
- Sınırlayıcı ölçüm: saat dilimi eklemek her soruyu yanıtlamaz. Çakışan bir yerel saat, aynı
  bölgede bile iki ayrı gerçek ana karşılık gelebilir ve hangisinin kastedildiği yalnız
  `isGap`/`isOverlap` gibi sorgularla anlaşılabilir.
- Boşluk ile çakışma geçişin yönünden gelir: saatler ileri atladığında ortada kalan yerel
  saatler hiç yaşanmaz, saatler geri sarıldığında bir yerel saat aralığı iki kez yaşanır.

## Sonraki Adım

Bu derste ölçülen her şey sayısal ve takvimseldi. Ama standart kütüphanenin girdi-çıktı
katmanında bir de **metin** eşleştirme sorusu var: bir dizginin belli bir örüntüye uyup
uymadığını sormak. Sıradaki ve kursun son dersi düzenli ifadelere bakar — örüntünün derlenmiş
bir değer olduğunu, açgözlü ve isteksiz niceleyicinin aynı girdide ayrı yakalama ürettiğini
ve geri izlemenin adım sayısıyla ölçüldüğünü gösterir.
