Mert Özen Her Satırda Daha İleri
Frontend JavaScript Temel — #18

JavaScript Date Temelleri: Tarih Oluşturmak, Hesaplamak ve Biçimlendirmek

Mert Özen 3 Kas 2026 8 dk 5 görüntülenme
JavaScript Date Temelleri: Tarih Oluşturmak, Hesaplamak ve Biçimlendirmek

Date object'inin aslında bir timestamp olduğunu görüyor; tarih oluşturmayı, sıfırdan başlayan ay tuzağını, get/set method'larını, iki tarih arası farkı ve toLocaleDateString ile Intl.DateTimeFormat biçimlendirmesini öğreniyoruz.

Sürüm notu: Bu yazıdaki kodlar ES2026 ve güncel tarayıcılar (Chrome, Edge, Firefox, Safari) içindir.

Önce Tahmin Et

Bir tarih oluşturuyoruz ve ayını, gününü soruyoruz:

const date = new Date(2026, 1, 30);
console.log(date.getMonth(), date.getDate());

Konsola ne yazılır? İpucu vermeyeceğim ama iki ayrı tuzak var. Okumaya devam etmeden önce bir tahmin yap. Cevap, ayların neden sıfırdan başladığını konuştuğumuz bölümde.

Date Aslında Bir Sayıdır: Timestamp ve Epoch

JavaScript'te tarih ve saatle çalışmanın yerleşik yolu Date object'i. Onu anlamanın en kolay yolu şu: bir Date, içinde tek bir sayı taşıyan bir object'tir. O sayı da 1 Ocak 1970, saat 00:00:00 UTC'den bu yana geçen milisaniye. Bu başlangıç anına epoch, o sayıya da timestamp denir.

1970'te çalıştırılmış ve hiç durmamış dev bir kronometre düşün. Her Date, o kronometreden alınmış bir okumadır. Gün, ay, saat gibi insanların anladığı bilgiler bu sayıdan hesaplanır. Bunu konsola yapıştırıp dene:

const now = new Date();
console.log(now);           // şu anki tarih ve saat
console.log(now.getTime()); // e.g. 1793700000000
console.log(Date.now());    // object oluşturmadan aynı sayı

console.log(new Date(0).toISOString()); // "1970-01-01T00:00:00.000Z"

Date.now() özellikle süre ölçmek için kullanışlıdır: bir işin başında ve sonunda alıp farkına bakarsın. Tarihin bir sayı olması, ilerleyen bölümlerde iki tarih arasındaki farkı hesaplamayı da çok kolaylaştıracak.

Tarih Oluşturmanın Yolları

new Date'i birkaç farklı biçimde çağırabilirsin:

new Date();                         // şu an
new Date(1793700000000);            // timestamp'ten
new Date(2026, 0, 15);              // 15 Ocak 2026, 00:00, yerel saat
new Date(2026, 0, 15, 14, 30);      // 15 Ocak 2026, 14:30, yerel saat
new Date("2026-01-15T14:30:00");    // ISO biçiminde string, yerel saat

Parça parça verdiğin biçim, yani yıl, ay, gün, saat ve dakika, en öngörülebilir olanıdır. String ile oluşturmak da yaygın ama burada dikkat etmen gereken incelikler var:

new Date("2026-01-15T00:00").getHours(); // 0: saat yazılınca yerel saat
new Date("2026-01-15").getHours();       // Türkiye'de 3: sadece tarih UTC sayılır
new Date("15.01.2026");                  // standart dışı biçim: güvenme
new Date("hello");                       // Invalid Date

Şaşırtıcı olan ikinci satır. Standarda göre sadece tarih içeren "2026-01-15" gibi bir string UTC gece yarısı olarak yorumlanır, saati de olan "2026-01-15T00:00" ise yerel saat olarak. Türkiye UTC'nin üç saat ilerisinde olduğu için UTC gece yarısı burada saat 03:00'e denk gelir. Batıda, örneğin Amerika'da yaşayan bir kullanıcı için ise aynı string bir önceki günün akşamına düşer.

"15.01.2026" gibi standart dışı biçimlerde sonuç tarayıcıya kalmıştır; kimi Invalid Date verir, kimi beklemediğin bir tarih üretir. Tarihi string'den oluşturacaksan ISO biçimini (YYYY-MM-DDTHH:mm) kullan. Geçersiz bir tarihi yakalamak için de daha önce gördüğümüz Number.isNaN işini görür: Number.isNaN(date.getTime()) ise tarih geçersizdir.

Ay Neden 0'dan Başlıyor?

Yukarıda 15 Ocak için new Date(2026, 0, 15) yazdığımızı fark ettin mi? Date'te aylar sıfırdan başlar: Ocak 0, Şubat 1, Aralık 11. Bu, dilin ilk günlerinde Java'nın tarih class'ından devralınan bir karar ve değiştirmek bütün web'i bozacağı için hep böyle kaldı. Bir mantığı da var: ay numarasını doğrudan bir isim array'inde index olarak kullanabilirsin.

const months = ["January", "February", "March", "April", "May", "June",
  "July", "August", "September", "October", "November", "December"];

const d = new Date(2026, 4, 19);
console.log(months[d.getMonth()]); // "May"

İşi karıştıran şey, kuralın her parçada aynı olmaması. Ay sıfırdan başlar ama ayın günü (getDate) 1'den başlar. Haftanın günü (getDay) yine sıfırdan başlar ve Pazar'dır.

Şimdi baştaki soru. new Date(2026, 1, 30) 30 Ocak değil, 30 Şubat demek. Birinci tuzak bu. İkinci tuzak: 2026'da Şubat 28 gün çeker ve Date taşan değerlere hata vermez, fazlasını bir sonraki aya aktarır. 30 Şubat, 28 Şubat'tan iki gün sonrası, yani 2 Mart olur. Konsolda 2 2 görürsün: Mart için 2, ayın günü için 2.

Bu taşma davranışı can sıkıcı olduğu kadar işe de yarar. Örneğin bir ayın son gününü bulmak için bir sonraki ayın sıfırıncı gününü istersin: new Date(2028, 2, 0).getDate() sana 2028 Şubat'ının kaç gün çektiğini söyler, yani 29.

get ve set Method'ları

Bir tarihin parçalarını get method'larıyla okursun:

const d = new Date(2026, 0, 15, 14, 30, 45);

d.getFullYear(); // 2026
d.getMonth();    // 0  (Ocak)
d.getDate();     // 15 (ayın günü)
d.getDay();      // 4  (Perşembe; 0 = Pazar)
d.getHours();    // 14
d.getMinutes();  // 30
d.getSeconds();  // 45

Her birinin bir set karşılığı da var. set method'ları tarihi yerinde değiştirir ve taşmayı yine kendisi halleder. "Bugünden 30 gün sonrası" gibi hesaplar bu sayede tek satır:

const due = new Date(2026, 0, 15);
due.setDate(due.getDate() + 30);
console.log(due.toLocaleDateString("tr-TR")); // "14.02.2026"

const copy = new Date(due); // aynı anın bağımsız bir kopyası
copy.setFullYear(2027);
console.log(due.getFullYear(), copy.getFullYear()); // 2026 2027

30 gün eklediğimizde Ocak bitti, hesap kendiliğinden Şubat'a geçti. İkinci kısım önemli bir alışkanlığı gösteriyor: Date değiştirilebilir bir object. Orijinal tarihe dokunmadan bir hesap yapacaksan önce new Date(oldDate) ile kopyala. Bu method'ların bir de UTC sürümleri var (getUTCHours, setUTCDate gibi); yerel saat yerine UTC'ye göre çalışırlar.

İki Tarih Arasındaki Fark

Tarih özünde bir sayı olduğu için iki tarihi birbirinden çıkarabilirsin. Sonuç milisaniye cinsinden bir sayıdır. Kaydet ve aç:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>New Year Countdown</title>
</head>
<body>
  <p id="today"></p>
  <p id="remaining"></p>
  <script>
    const today = new Date();
    const newYear = new Date(today.getFullYear() + 1, 0, 1);

    const diffMs = newYear - today;
    const oneDayMs = 1000 * 60 * 60 * 24;
    const days = Math.ceil(diffMs / oneDayMs);

    document.getElementById("today").textContent =
      "Today: " + today.toLocaleDateString("en-US", { dateStyle: "full" });
    document.getElementById("remaining").textContent = `${days} days until New Year.`;
  </script>
</body>
</html>

newYear - today satırında motor iki tarihi de sayıya çevirir ve çıkarır; daha önce gördüğümüz coercion burada işimize yarıyor. Milisaniyeyi bir günün milisaniyesine bölünce gün sayısı çıkar. Bugünün saati de işin içinde olduğu için sonuç küsuratlı gelir, biz de Math.ceil ile yukarı yuvarlıyoruz.

Karşılaştırma da aynı mantıkla çalışır: date1 < date2 beklediğin gibi sonuç verir. Ama === ile iki tarihi karşılaştırmak, aynı anı gösterseler bile false döner. Daha önce gördüğümüz object referansı kuralı: iki ayrı object, iki ayrı referans. Eşitlik için date1.getTime() === date2.getTime() yaz.

Bir uyarı: yaz saati uygulaması olan ülkelerde bir gün 23 ya da 25 saat sürebilir ve milisaniye hesabı bir saat kayabilir. Türkiye 2016'dan beri yaz saatine geçmiyor ama kullanıcıların başka ülkelerden de gelebilir. Bu konuyu Orta seride detaylandıracağız.

Tarihi Okunur Hâle Getirmek: toLocaleDateString ve Intl.DateTimeFormat

Bir tarihi kullanıcıya göstermek için ay isimlerini kendin yazmana gerek yok. Tarayıcı her dil için biçimlendirme kurallarını zaten biliyor:

const d = new Date(2026, 0, 15, 14, 30);

d.toLocaleDateString("tr-TR"); // "15.01.2026"
d.toLocaleDateString("tr-TR", {
  weekday: "long", year: "numeric", month: "long", day: "numeric"
}); // "15 Ocak 2026 Perşembe"
d.toLocaleTimeString("tr-TR"); // "14:30:00"
d.toLocaleDateString("en-US"); // "1/15/2026"
d.toLocaleDateString("de-DE"); // "15.1.2026"

İlk parametre dil ve bölge kodu, ikincisi neyin nasıl gösterileceğini söyleyen bir seçenek object'i. Aynı tarih ABD'de ay önce, Almanya'da noktalı ve başında sıfır olmadan yazılıyor. Kendin bir biçim uydurmak yerine bunu tarayıcıya bırakmak hem daha az kod hem de daha az hata demek.

Arka planda bu işi Intl.DateTimeFormat yapar. Çok sayıda tarihi aynı biçimde göstereceksen bir biçimlendirici oluşturup tekrar tekrar kullanmak daha verimlidir:

const longFormat = new Intl.DateTimeFormat("tr-TR", { dateStyle: "long" });
console.log(longFormat.format(d)); // "15 Ocak 2026"

const fullFormat = new Intl.DateTimeFormat("tr-TR", { dateStyle: "full", timeStyle: "short" });
console.log(fullFormat.format(d)); // "15 Ocak 2026 Perşembe 14:30"

console.log(d.toISOString()); // Türkiye'de "2026-01-15T11:30:00.000Z"

Tarayıcılar arasında çıktıda küçük farklar olabilir, bu normal. Tarihi bir insana değil de bir sunucuya ya da bir JSON dosyasına göndereceksen toISOString() kullan. Her zaman UTC'dir, sondaki Z da bunu söyler. Daha önce gördüğümüz JSON.stringify de tarihleri bu biçime çevirir. Intl.DateTimeFormat'ın diğer seçeneklerini ve "3 gün önce" gibi göreceli zaman ifadelerini Orta seride göreceğiz.

Saat Dilimine Kısa Bir Not

Bir Date'in içindeki sayı dünyanın her yerinde aynı anı gösterir. getHours ya da toLocaleTimeString gibi method'lar ise o anı, kodun çalıştığı bilgisayarın saat dilimine göre yorumlar. Aynı kod İstanbul'da 14:30, Londra'da 11:30 gösterir. Başka bir saat dilimindeki karşılığı görmek için timeZone seçeneğini kullanabilirsin:

const moment = new Date(2026, 0, 15, 14, 30); // Türkiye'de oluşturulduğunu varsayalım

const timeIn = (zone) =>
  new Intl.DateTimeFormat("tr-TR", { timeStyle: "short", timeZone: zone }).format(moment);

console.log(timeIn("Europe/Istanbul")); // "14:30"
console.log(timeIn("Europe/London"));   // "11:30"
console.log(timeIn("Asia/Tokyo"));      // "20:30"

console.log(Intl.DateTimeFormat().resolvedOptions().timeZone); // e.g. "Europe/Istanbul"

Date'in eksikleri zamanla iyice belirginleşti: değiştirilebilir olması, sıfırdan başlayan aylar, saat dilimleriyle zahmetli çalışma. Bunları çözmek için tasarlanan Temporal API'sinin ES2027 ile dile girmesi bekleniyor. Chrome ve Firefox destekliyor, Safari henüz desteklemiyor. Temporal'ı İleri seride göreceğiz; o zamana kadar Date ve Intl her tarayıcıda çalışan yoldur.

Sık Düşülen Tuzaklar

getYear kullanmak

getYear() eski ve kullanımdan kalkmış bir method; 2026 için 126 döndürür, çünkü 1900'den bu yana geçen yılı verir. Her zaman getFullYear() kullan.

getDay ile getDate'i karıştırmak

İsimleri benzer, anlamları farklı. getDate ayın günüdür (1–31), getDay haftanın günü (0–6, Pazar 0). "Ayın 15'i" diye getDay okursan Perşembe'nin numarasını, yani 4'ü alırsın.

Tarihe + ile süre eklemek

Çıkarma tarihi sayıya çevirir ama + öyle yapmaz: new Date() + 1000 bir string üretir, tarihin metninin sonuna "1000" eklenir. Milisaniye eklemek istiyorsan new Date(date.getTime() + 1000) yaz.

Ortak bir tarihi farkında olmadan değiştirmek

Bir tarihi bir fonksiyona verip içinde setDate çağırırsan, dışarıdaki orijinal tarih de değişir; ikisi aynı object'tir. Hesaplamaya başlamadan önce kopyala.

Küçük Bir Deneme

★☆☆ Konsolda bugünün tarihini "en-US" ile "Tuesday, November 3, 2026" biçiminde yazdır. Sonra aynısını "tr-TR" ile dene ve farka bak.

★★☆ Çalıştırmadan önce tahmin et, sonra dene:

const a = new Date(2026, 0, 31);
a.setMonth(1);
console.log(a.getMonth(), a.getDate());

★★★ Bir yaş hesaplayıcı yap: type="date" bir input'tan doğum tarihini al ve kullanıcının kaç yaşında olduğunu sayfada göster. İpucu: bu input'un value'su "1995-06-21" gibi bir string'dir ve new Date'e doğrudan verirsen UTC tuzağına düşersin. String'i split("-") ile parçala ve new Date(year, month - 1, day) kullan. Doğum günü bu yıl henüz gelmediyse yaşı bir eksik saymayı unutma.

Son alıştırmada bir metni bilinen bir kalıba göre parçalara ayırdık. Peki kalıp bu kadar düzenli değilse? Bir metnin içinde telefon numarası aramak, bir e-postanın doğru biçimde olup olmadığını kontrol etmek ya da bütün tarihleri tek hamlede bulmak için daha güçlü bir araca ihtiyacın var. Bir sonraki yazıda düzenli ifadelerle tanışacağız.