JavaScript'te zaman biçimlendirme ve dönüşümlerinin sürpriz yaratmaması için öğrenin: zaman damgaları, ISO stringler, zaman dilimleri, DST, ayrıştırma kuralları ve güvenilir desenler.

JavaScript zaman hataları nadiren "saat yanlış" gibi belirgin olur. Genellikle küçük, kafa karıştıran kaymalar şeklinde görünürler: laptopunuzda doğru görünen bir tarih meslektaşınızın makinesinde yanlış olabilir; bir API yanıtı farklı bir zaman diliminde render edilince bozulabilir; veya mevsimsel bir değişiklik etrafında "bir eksik" raporu oluşabilir.
Genellikle şu durumlardan biri (veya birkaçı) göze çarpar:
+02:00) farklıdır.Büyük sorunlardan biri, zaman kelimesinin farklı kavramlara işaret edebilmesidir:
JavaScript'in yerleşik Date objesi bu kavramların hepsini kapsamaya çalışır, ancak öncelikle bir anı temsil eder ve sürekli olarak yerel gösterime doğru sizi yönlendirir; bu da istem dışı dönüşümleri kolaylaştırır.
Bu rehber niyetli olarak pratiktir: tarayıcılar ve sunucular arasında öngörülebilir dönüşümler nasıl sağlanır, ISO 8601 gibi daha güvenli formatlar nasıl seçilir ve klasik tuzaklar (saniye vs milisaniye, UTC vs yerel, ayrıştırma farkları) nasıl tespit edilir. Amaç teori değil—daha az "neden kaydı?" sürprizidir.
JavaScript zaman hataları genelde birbirinin yerine kullanılabilir gibi görünen ama gerçekte olmayan temsil biçimlerinin karıştırılmasıyla başlar.
1) Epoch milisaniyeleri (sayı)
1735689600000 gibi bir sayı genellikle "1970-01-01T00:00:00Z'den bu yana milisaniyeler" demektir. Formatlama veya zaman dilimi içermeyen bir anı temsil eder.
2) Date nesnesi (bir anın sarmalayıcısı)
Bir Date, aynı türde bir anı zaman damgası olarak saklar. Kafa karıştıran nokta: bir Date'i yazdırdığınızda, JavaScript ortamdaki yerel kuralları kullanarak formatlar—istisna istemezseniz yerel gösterim olur.
3) Biçimlendirilmiş string (insan gösterimi)
"2025-01-01", "01/01/2025 10:00" veya "2025-01-01T00:00:00Z" gibi stringler tek bir şey değiller. Bazıları açık ve nettir (Z ile ISO 8601), bazıları locale'e bağlıdır, bazıları ise hiç zaman dilimi içermez.
Aynı an zaman dilimine göre farklı görünebilir:
const instant = new Date("2025-01-01T00:00:00Z");
instant.toLocaleString("en-US", { timeZone: "UTC" });
// "1/1/2025, 12:00:00 AM"
instant.toLocaleString("en-US", { timeZone: "America/Los_Angeles" });
// "12/31/2024, 4:00:00 PM" (bir önceki gün)
Uygulamanız ve API'leriniz genelinde tek bir dahili temsil (genellikle epoch milisaniyeleri veya UTC ISO 8601) seçin ve ona sadık kalın. Date ve biçimlendirilmiş stringlere dönüşümü sadece sınırda yapın: giriş ayrıştırma ve UI gösterimi esnasında.
“Timestamp” genellikle epoch zamanı (Unix zamanı) anlamına gelir: 1970-01-01 00:00:00 UTC'den bu yana geçen süre. Sorun şu: farklı sistemler farklı birimlerde sayar.
JavaScript Date çoğu kafa karışıklığının kaynağıdır çünkü milisaniye kullanır. Birçok API, veritabanı ve log ise saniye kullanır.
17040672001704067200000Aynı an; fakat milisaniye versiyonunda üç ekstra rakam vardır.
Birimlerin açıkça görülmesi için çarpma/bölme kullanın:
// seconds -> Date
const seconds = 1704067200;
const d1 = new Date(seconds * 1000);
// milliseconds -> Date
const ms = 1704067200000;
const d2 = new Date(ms);
// Date -> seconds
const secondsOut = Math.floor(d2.getTime() / 1000);
// Date -> milliseconds
const msOut = d2.getTime();
Date()'e geçirmekMakul gibi görünür ama yanlıştır:
const ts = 1704067200; // seconds
const d = new Date(ts); // WRONG: treated as milliseconds
Sonuç 1970 civarında bir tarih olacaktır, çünkü 1,704,067,200 milisaniye epoch'tan sadece yaklaşık 19 gün sonradır.
Hangi birimde olduğunu bilmediğinizde basit koruyucu önlemler ekleyin:
function asDateFromUnknownEpoch(x) {
// crude heuristic: seconds are ~1e9-1e10, milliseconds are ~1e12-1e13
if (x < 1e11) return new Date(x * 1000); // assume seconds
return new Date(x); // assume milliseconds
}
const input = Number(valueFromApi);
console.log({ input, digits: String(Math.trunc(input)).length });
console.log('as ISO:', asDateFromUnknownEpoch(input).toISOString());
"digits" sayısı ~10 ise muhtemelen saniyedir. ~13 ise milisaniyedir. Hata ayıklarken toISOString() yazdırmak birim hatalarını hemen fark etmenize yardımcı olur.
JavaScript Date kafa karıştırıcı olabilir çünkü bir anı saklar, ama o anı farklı zaman dilimlerinde sunabilir. İçeride bir Date, esasen "Unix epoch'tan (1970-01-01T00:00:00Z) bu yana milisaniyeler"dir. Kayma, JavaScript'e o anı yerel zaman (bilgisayar/sunucu ayarlarına göre) veya UTC olarak formatlamasını söylediğinizde ortaya çıkar.
Birçok Date API'sinin hem yerel hem UTC varyantları vardır. Aynı an için farklı sayılar döndürürler:
const d = new Date('2025-01-01T00:30:00Z');
d.getHours(); // saat, *yerel* zaman diliminde
d.getUTCHours(); // saat, UTC olarak
d.toString(); // yerel zaman string'i
d.toISOString(); // UTC (her zaman Z ile biter)
Makineniz New York'taysa (UTC-5), o UTC zamanı yerelde önceki günün "19:30"u olarak görünebilir. UTC'ye ayarlı bir sunucuda ise "00:30" olacaktır. Aynı an, farklı gösterim.
Loglar sıklıkla Date#toString() veya bir Date'i dolaylı olarak interpolasyonla kullanır; bu yerel zaman dilimini kullanır. Bu, aynı kodun laptopunuzda, CI'da ve üretimde farklı zaman damgaları yazdırabileceği anlamına gelir.
Zamanı UTC olarak saklayın ve iletin (ör. epoch milisaniyeleri veya toISOString()). Gösterimi yalnızca kullanıcı için locale'a göre yapın:
toISOString() veya epoch milisaniyeleri tercih edinIntl.DateTimeFormat ile formatlayınHızlı geliştirme yapıyorsanız (ör. Koder.ai ile hızlı uygulama üretimi), bunu API kontratlarınıza erken dahil etmek işinizi kolaylaştırır: alanlara net adlar verin (createdAtMs, createdAtIso) ve sunucu (Go + PostgreSQL) ile istemci (React) arasında her alanın neyi temsil ettiği konusunda tutarlı olun.
Tarayıcı, sunucu ve veritabanı arasında tarih/zaman göndermeniz gerekiyorsa, ISO 8601 stringleri varsayılan olarak en güvenlisidir. Açık, yaygın destekli ve (en önemlisi) zaman dilimi bilgisi taşırlar.
İki iyi değiş tokuş formatı:
2025-03-04T12:30:00Z2025-03-04T12:30:00+02:00"Z" ne anlama gelir?
Z Zulu zamanı, yani UTC anlamına gelir. Dolayısıyla 2025-03-04T12:30:00Z UTC'de 12:30 demektir.
+02:00 gibi ofsetler ne zaman önemlidir?
Ofsetler, bir etkinliğin yerel zaman bağlamına bağlı olduğu durumlarda (randevular, rezervasyonlar, mağaza açılış saatleri) çok önemlidir. 2025-03-04T12:30:00+02:00, UTC'den iki saat ileri olan bir anı tarif eder ve 2025-03-04T12:30:00Z ile aynı an değildir.
03/04/2025 gibi stringler tuzaktır: Mart 4 mü yoksa 3 Nisan mı? Farklı kullanıcılar ve ortamlar farklı şekilde yorumlayabilir. 2025-03-04 (ISO tarih) veya tam bir ISO datetime tercih edin.
const iso = "2025-03-04T12:30:00Z";
const d = new Date(iso);
const back = d.toISOString();
console.log(iso); // 2025-03-04T12:30:00Z
console.log(back); // 2025-03-04T12:30:00.000Z
Bu "geri dönüşüm" davranışı API'ler için istediğiniz şeydir: tutarlı, öngörülebilir ve zaman dilimini göz önünde bulundurur.
Date.parse() kullanışlı gelebilir: bir string ver, bir zaman damgası alırsın. Sorun şu ki, ISO 8601 açıkça belirtilmemiş her şey için ayrıştırma tarayıcı sezgilerine dayanabilir. Bu sezgiler motorlar ve sürümler arasında farklılık gösterdi; aynı girdi farklı ortamlarda farklı yorumlanabilir (veya hiç ayrıştırılamayabilir).
Date.parse() değişkenlik gösterirJavaScript yalnızca ISO 8601 tarzı stringler için ayrıştırmayı güvenilir şekilde standartlaştırır (hatta burada bile zaman dilimi detayları önem taşır). "Dost canlısı" formatlar—ör. "03/04/2025", "March 4, 2025" veya "2025-3-4"—için tarayıcılar:
Eğer kesin string şeklini tahmin edemiyorsanız, sonucu tahmin edemezsiniz.
YYYY-MM-DDYaygın bir tuzak, "YYYY-MM-DD" (örneğin "2025-01-15") formudur. Birçok geliştirici bunun yerel gece yarısı olarak yorumlanmasını bekler. Ancak bazı ortamlar bu formu UTC gece yarısı olarak ele alır.
Bu fark önemlidir: UTC gece yarısı yerel zamana çevrildiğinde negatif zaman dilimlerinde (örn. Amerika) bir önceki gün olabilir veya saat beklenmedik şekilde kayabilir. Bu, "tarihim neden bir gün kaydı?" hatalarının sık görülen bir sebebidir.
Sunucu/API girişi için:
2025-01-15T13:45:00Z veya 2025-01-15T13:45:00+02:00."YYYY-MM-DD") olarak saklayın ve isterseniz ayrıca hangi zaman diliminin kastedildiğini tanımlayın.Kullanıcı girişi için:
03/04/2025 gibi belirsiz formatları kabul etmeyin.Date.parse()'in "anlamasını" beklemek yerine şu desenlerden birini seçin:
new Date(year, monthIndex, day) ile yerel tarihler oluşturun).Zaman verisi kritikse, "makinemde ayrışıyor" yeterli değildir—ayrıştırma kurallarınızı açık ve tutarlı hale getirin.
Kullanıcıların beklediği şekilde bir tarih/zaman göstermek istiyorsanız, JavaScript'te en iyi araç Intl.DateTimeFormat'tır. Kullanıcının locale kurallarını (sıra, ayırıcılar, ay isimleri) kullanır ve month + '/' + day gibi kırılgan elle birleştirmelerden kaçınır.
Elle biçimlendirme sıklıkla ABD stili çıktı sabitler, baştaki sıfırı unutur veya 24/12 saat karışıklığına sebep olur. Intl.DateTimeFormat ayrıca hangi zaman dilimini gösterdiğinizi açıkça belirtmenizi sağlar—veriniz UTC'de saklansa bile UI'nın kullanıcının yerel zamanını yansıtması gerektiğinde kritik önemdedir.
"Güzelce göster" için dateStyle ve timeStyle en basit olanlardır:
const d = new Date('2025-01-05T16:30:00Z');
// Kullanıcının locale'i + kullanıcının yerel zaman dilimi
console.log(new Intl.DateTimeFormat(undefined, {
dateStyle: 'medium',
timeStyle: 'short'
}).format(d));
// Belirli bir zaman dilimini zorla (etkinlik zamanları için harika)
console.log(new Intl.DateTimeFormat('en-GB', {
dateStyle: 'full',
timeStyle: 'short',
timeZone: 'UTC'
}).format(d));
Saat döngüsünü (12/24 saat) tutarlı yapmak isterseniz hour12 kullanın:
console.log(new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: '2-digit',
hour12: true
}).format(d));
UI'nizdeki her "zaman türü" için tek bir biçimlendirme fonksiyonu seçin (mesaj zamanı, log girişi, etkinlik başlangıcı) ve timeZone kararını kasıtlı yapın:
Bu, kırılgan özel format stringleri tutmak zorunda kalmadan tutarlı, locale-dostu çıktı sağlar.
DST, bir zaman diliminin UTC ofsetini (genellikle bir saat) belirli tarihlerde değiştirdiği zamandır. Zorlayıcı kısmı: DST sadece ofseti değiştirmez—bazı yerel saatlerin varlığını değiştirir.
Saatler ileri alındığında, bazı yerel zaman aralıkları hiç olmaz. Örneğin, birçok bölgede saat 01:59'dan 03:00'a atlar, bu yüzden 02:30 yerel zamanı "olmayan" bir zamandır.
Saatler geri alındığında, bazı yerel zamanlar iki kez yaşanır. Örneğin 01:30 hem öne hem arkaya alınmadan önce hem de sonra gerçekleşebilir; aynı duvar saati iki farklı ana işaret edebilir.
DST sınırlarında bunlar eşdeğer değildir:
Eğer bugün DST başlıyorsa, "yarın 9:00" sadece 23 saat uzak olabilir. DST bitiyorsa 25 saat uzak olabilir.
// Senaryo: "yarın aynı yerel saatte" planlama
const d = new Date(2025, 2, 8, 9, 0); // 8 Mar, yerel 9:00
const plus24h = new Date(d.getTime() + 24 * 60 * 60 * 1000);
const nextDaySameLocal = new Date(d);
nextDaySameLocal.setDate(d.getDate() + 1);
// DST etrafında plus24h ile nextDaySameLocal 1 saat farklı olabilir.
setHours neden sizi şaşırtırdate.setHours(2, 30, 0, 0) gibi bir şey yaparsanız ve o gün "ileri alınmış"sa, JavaScript bunu geçerli bir zamana normalize edebilir (çoğunlukla 03:30), çünkü 02:30 yerel olarak yoktur.
setDate) kullanılarak yapılmalıdır, milisaniye eklemek yerine.Z tercih edin ki an net olsun.Sıkça yapılan bir hata, Date'i takvim anı olmayan bir şeyi temsil etmek için kullanmaktır.
Zaman damgası "bu ne zaman oldu?" sorusunu cevaplar (ör. 2025-12-23T10:00:00Z). Süre "ne kadar sürdü?" sorusunu cevaplar (ör. "3 dakika 12 saniye"). Bunlar farklı kavramlardır; karıştırılması kafa karıştıran aritmetiğe ve beklenmeyen zaman dilimi/DST etkilerine yol açar.
Date süreler için yanlış araçtırDate her zaman epoch'a göre bir anı temsil eder. "90 saniye"yi bir Date ile saklarsanız, aslında "1970-01-01 artı 90 saniye" saklıyorsunuzdur; formatlandığında 01:01:30 olarak gösterilebilir, bir saat kayabilir veya istemediğiniz bir tarih görünebilir.
Süreler için şu tercihleri kullanın:
HH:mm:ss'ye çevirmeGeri sayım ve medya uzunlukları için basit bir formatlayıcı:
function formatHMS(totalSeconds) {
const s = Math.max(0, Math.floor(totalSeconds));
const hh = String(Math.floor(s / 3600)).padStart(2, "0");
const mm = String(Math.floor((s % 3600) / 60)).padStart(2, "0");
const ss = String(s % 60).padStart(2, "0");
return `${hh}:${mm}:${ss}`;
}
formatHMS(75); // "00:01:15" (geri sayım)
formatHMS(5423); // "01:30:23" (medya süresi)
Dakikadan dönüştürüyorsanız önce çarpın (minutes * 60) ve render edene kadar sayısal değeri koruyun.
JavaScript'te zamanları karşılaştırırken en güvenli yaklaşım formatlanmış metinler yerine sayıları karşılaştırmaktır. Bir Date nesnesi temelde bir epoch milisaniyesi sayısının sarmalayıcısıdır; karşılaştırmaların "sayı vs sayı" olarak bitmesini istersiniz.
Güvenilir karşılaştırma için getTime() (veya aynı sayıyı döndüren Date.valueOf()) kullanın:
const a = new Date('2025-01-10T12:00:00Z');
const b = new Date('2025-01-10T12:00:01Z');
if (a.getTime() < b.getTime()) {
// a daha önce
}
// Ayrıca çalışır:
if (+a < +b) {
// unary + valueOf()'u çağırır
}
"1/10/2025, 12:00 PM" gibi formatlanmış stringleri karşılaştırmaktan kaçının—bunlar locale'e bağımlıdır ve doğru sıralamaz. Aynı formatta ve aynı zaman diliminde olan ISO 8601 stringleri (...Z) leksikografik olarak sıralanabilir; bu bir istisnadır.
Zamana göre sıralama, epoch milisaniyelerine göre sıralanırsa basittir:
items.sort((x, y) => new Date(x.createdAt).getTime() - new Date(y.createdAt).getTime());
Aralıkla filtreleme de aynı fikir:
const start = new Date('2025-01-01T00:00:00Z').getTime();
const end = new Date('2025-02-01T00:00:00Z').getTime();
const inRange = items.filter(i => {
const t = new Date(i.createdAt).getTime();
return t >= start && t < end;
});
"Günün başı" yerel mi yoksa UTC mi kastettiğinize bağlıdır:
// Yerel gün başı/sonu
const d = new Date(2025, 0, 10); // Yerelde 10 Ocak
const localStart = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
const localEnd = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999);
// UTC gün başı/sonu
const utcStart = new Date(Date.UTC(2025, 0, 10, 0, 0, 0, 0));
const utcEnd = new Date(Date.UTC(2025, 0, 10, 23, 59, 59, 999));
Erken bir tanımı seçin ve karşılaştırma ile aralık mantığınızda ona sadık kalın.
Zaman hataları rastgele hissedilir ta ki elinizde ne olduğunu (zaman damgası? string? Date?) ve kaymanın nerede meydana geldiğini (ayrıştırma, zaman dilimi dönüşümü, formatlama) tespit edene kadar.
İşinize genellikle aynı değeri üç farklı şekilde loglamak başlar. Bu, birimin saniye/milisaniye, yerel/UTC veya string ayrıştırma hatası olup olmadığını hızlıca gösterir.
console.log('raw input:', input);
const d = new Date(input);
console.log('toISOString (UTC):', d.toISOString());
console.log('toString (local):', d.toString());
console.log('timezone offset (min):', d.getTimezoneOffset());
Ne aramalı:
toISOString() çok farklı (ör. 1970 veya çok uzak bir gelecek), saniye vs milisaniye şüphesi vardır.toISOString() doğru ama toString() "kaymış" ise yerel zaman dilimi gösterimi sorunu görüyorsunuz.getTimezoneOffset() tarih değiştikçe değişiyorsa yaz saati uygulaması ile karşılaşıyorsunuz demektir.Birçok "bende çalışıyor" raporu farklı ortam varsayımlarından kaynaklanır.
console.log(Intl.DateTimeFormat().resolvedOptions());
console.log('TZ:', process.env.TZ);
console.log(Intl.DateTimeFormat().resolvedOptions().timeZone);
Sunucunuz UTC'de çalışırken laptopunuz yerel bir bölgedeyse, formatlanmış çıktı timeZone açık belirtilmediği sürece farklı olur.
DST sınırları ve "uç" zamanlar etrafında ünite testleri oluşturun:
23:30 → 00:30 geçişleriHızlı iterasyon yapıyorsanız, bu testleri iskeletinize ekleyin. Örneğin React + Go uygulaması üretirken Koder.ai ile küçük bir "zaman kontratı" test paketi eklemek regreksiyonların dağıtımdan önce yakalanmasını sağlar.
"2025-03-02 10:00" gibi belirsiz stringler yok.locale ve (gerekirse) timeZone belirtir.JavaScript'te güvenilir zaman işleme büyük ölçüde bir "gerçek kaynak" seçip saklamadan gösterime kadar tutarlı olmaktır.
UTC'de saklayın ve hesaplayın. Kullanıcıya yönelik yerel zamanı yalnızca sunum detayı olarak ele alın.
Sistemler arası iletimde ISO 8601 stringleri ofsetle birlikte gönderin (tercihen Z). Numerik epoch göndermeniz gerekiyorsa, alan adıyla birimi belgeleyin ve tutarlı olun (createdAtMs gibi). UI için Intl.DateTimeFormat (veya toLocaleString) ile biçimlendirin; deterministik çıktı gerekliyse açık timeZone verin.
Z içeren ISO 8601 tercih edin (2025-12-23T10:15:00Z). Epoch kullanıyorsanız alan adı createdAtMs gibi bir isimle birimi belli edin.Tekrarlayan etkinlikler, karmaşık zaman dilimi kuralları, DST-güvenli aritmetik ("yarın aynı yerel zaman") veya tutarsız girdilerden çokça ayrıştırma gerekiyorsa özel bir tarih-saat kütüphanesi düşünün. Değer, daha temiz API'ler ve daha az uç durum hatasında yatar.
Eğer daha derine inmek isterseniz, zamanla ilgili daha fazla rehbere /blog sayfasında göz atabilirsiniz. Araç veya destek seçeneklerini değerlendiriyorsanız, /pricing bölümüne bakın.