DateTime Manipülasyonu İçin Kesin Kılavuz
Yayınlanan: 2022-03-11Bir yazılım geliştiricisi olarak tarih manipülasyonundan kaçamazsınız. Bir geliştiricinin oluşturduğu hemen hemen her uygulama, tarih/saatin kullanıcıdan alınması, bir veritabanında saklanması ve kullanıcıya geri gösterilmesi gereken bazı bileşenlere sahip olacaktır.
Herhangi bir programcıya deneyimlerini ele alma tarihlerini ve saat dilimlerini sorun ve muhtemelen bazı savaş hikayelerini paylaşacaklar. Tarih ve saat alanlarını işlemek kesinlikle roket bilimi değildir, ancak çoğu zaman sıkıcı ve hataya açık olabilir.
Bu konuyla ilgili yüzlerce makale var, ancak çoğu ya çok akademik, küçük ayrıntılara odaklanıyor ya da çok düzensiz, onlara eşlik eden çok fazla açıklama olmadan kısa kod parçacıkları sağlıyor. DateTime manipülasyonuna yönelik bu derinlemesine kılavuz, bu konuyla ilgili bir bilgi denizinde gezinmek zorunda kalmadan, saat ve tarihle ilgili programlama kavramlarını ve en iyi uygulamaları anlamanıza yardımcı olacaktır.
Bu makalede, tarih ve saat alanları hakkında net bir şekilde düşünmenize yardımcı olacağım ve tarih/saat cehenneminden kaçınmanıza yardımcı olabilecek bazı en iyi uygulamaları önereceğim. Burada tarih ve saat değerlerini doğru bir şekilde işlemek için gerekli olan bazı temel kavramları, DateTime değerlerini depolamak ve API'ler üzerinden aktarmak için uygun biçimleri ve daha fazlasını keşfedeceğiz.
Yeni başlayanlar için, üretim kodu için doğru cevap, hemen hemen her zaman kendinizinkini almak yerine uygun bir kitaplık kullanmaktır. Bu makalede tartışılan DateTime hesaplamasıyla ilgili olası zorluklar, buzdağının yalnızca görünen kısmıdır, ancak yine de bir kitaplık olsun ya da olmasın hakkında bilgi sahibi olmanıza yardımcı olur.
DateTime Kitaplıkları, Onları Doğru Anlarsanız Yardımcı Olur
Tarih kitaplıkları hayatınızı kolaylaştırmak için birçok yönden yardımcı olur. Tarih ayrıştırmayı, tarih aritmetiği ve mantıksal işlemleri ve tarih biçimlendirmesini büyük ölçüde basitleştirirler. Ağır işlerin çoğunu sizin yerinize yapmak üzere hem ön uç hem de arka uç için güvenilir bir tarih kitaplığı bulabilirsiniz.
Ancak, tarih/saatin gerçekte nasıl çalıştığını düşünmeden genellikle tarih kitaplıklarını kullanırız. Tarih/saat karmaşık bir kavramdır. Yanlış anlaşılması nedeniyle ortaya çıkan hataları, tarih kitaplıkları yardımıyla bile anlamak ve düzeltmek son derece zor olabilir. Bir programcı olarak, temelleri anlamanız ve onlardan en iyi şekilde yararlanmak için kütüphanelerin çözdüğü sorunları takdir edebilmeniz gerekir.
Ayrıca tarih/saat kitaplıkları sizi yalnızca bir yere kadar götürebilir. Tüm tarih kitaplıkları, bir DateTime'ı temsil edecek uygun veri yapılarına erişmenizi sağlayarak çalışır. Bir REST API aracılığıyla veri gönderiyor ve alıyorsanız, JSON'un DateTime'ı temsil edecek yerel bir veri yapısı olmadığından, sonunda tarihi bir dizeye dönüştürmeniz ve bunun tersi gerekir. Burada ana hatlarıyla belirttiğim kavramlar, bu tarihten dizeye ve dizeden bugüne dönüşümleri yaparken ortaya çıkabilecek bazı yaygın sorunlardan kaçınmanıza yardımcı olacaktır.
Not: Bu makalede tartışılan programlama dili olarak JavaScript kullanmış olsam da, bunlar büyük ölçüde hemen hemen tüm programlama dilleri ve tarih kitaplıkları için geçerli olan genel kavramlardır. Bu nedenle, daha önce bir satır JavaScript yazmamış olsanız bile, makalede önceden JavaScript bilgisi olduğunu pek varsaymadığım için okumaya devam etmekten çekinmeyin.
Zamanı Standartlaştırmak
Bir DateTime, zaman içinde çok özel bir noktadır. Bunu düşünelim. Bu makaleyi karalarken, dizüstü bilgisayarımdaki saat 21 Temmuz 13:29'u gösteriyor. Çevremdeki duvar saatlerinde ve kol saatimde gördüğüm saate “yerel saat” diyoruz.
Birkaç dakika ver ya da ayır, arkadaşımdan saat 15:00'te yakındaki bir kafede benimle buluşmasını istersem, onu aşağı yukarı o saatte orada görmeyi bekleyebilirim. Aynı şekilde, örneğin “bir buçuk saat sonra buluşalım” desem bir karışıklık olmaz. Aynı şehirde veya saat diliminde yaşayan insanlarla rutin olarak bu şekilde zaman hakkında konuşuruz.
Farklı bir senaryo düşünelim: İsveç Uppsala'da yaşayan bir arkadaşımla saat 17.00'de konuşmak istediğimi söylemek istiyorum. Ona bir mesaj gönderdim, "Hey Anton, 17:00'de konuşalım." Hemen “Senin zamanın mı, benim zamanın mı?” cevabını alıyorum.
Anton bana UTC+01:00 olan Orta Avrupa saat diliminde yaşadığını söyledi. UTC+05:45'te yaşıyorum. Bu, yaşadığım yerde 17:00 olduğu zaman, 17:00 - 05:45 = 11:15 UTC, yani Uppsala'da 11:15 UTC + 01:00 = 12:15 PM, her ikisi için de mükemmel bizim.
Ayrıca, saat dilimi (Orta Avrupa Saati) ile saat dilimi farkı (UTC+05:45) arasındaki farkın da farkında olun. Ülkeler, politik nedenlerle de Yaz Saati Uygulaması için zaman dilimi farklarını değiştirmeye karar verebilirler. Hemen hemen her yıl, en az bir ülkede kurallarda bir değişiklik olur, yani bu kuralları içeren herhangi bir kod güncel tutulmalıdır - uygulamanızın her katmanı için bunun için kod tabanınızın neye bağlı olduğunu düşünmeye değer.
Bu, çoğu durumda saat dilimleriyle yalnızca ön ucun ilgilenmesini önermemizin bir başka iyi nedenidir. Olmadığında, veritabanı motorunuzun kullandığı kurallar ön veya arka ucunuzdakilerle eşleşmediğinde ne olur?
Kullanıcıya göre ve evrensel olarak kabul edilen bir standarda göre zamanın iki farklı versiyonunu yönetme sorunu, hassaslığın anahtar olduğu ve bir saniyenin bile büyük bir fark yaratabileceği programlama dünyasında daha da zordur. Bu sorunları çözmeye yönelik ilk adım, DateTime'ı UTC'de depolamaktır.
Formatı Standartlaştırmak
Saati standart hale getirmek harika çünkü sadece UTC saatini kaydetmem gerekiyor ve kullanıcının saat dilimini bildiğim sürece her zaman onların saatine dönüştürebiliyorum. Tersine, bir kullanıcının yerel saatini ve saat dilimini biliyorsam, bunu UTC'ye dönüştürebilirim.
Ancak tarihler ve saatler birçok farklı biçimde belirtilebilir. Tarih için “30 Temmuz” veya “30 Temmuz” veya “7/30” (ya da yaşadığınız yere bağlı olarak 30/7) yazabilirsiniz. Zaman için “9:30 PM” veya “2130” yazabilirsiniz.
Dünyanın her yerinden bilim adamları bu sorunu çözmek için bir araya geldiler ve kısa ve kesin olduğu için programcıların gerçekten sevdiği zamanı tanımlamak için bir formata karar verdiler. ISO-8601 genişletilmiş biçiminin basitleştirilmiş bir sürümü olan "ISO tarih biçimi" olarak adlandırmayı seviyoruz ve şöyle görünüyor:
00:00 veya UTC için, bunun yerine Zulu zamanı anlamına gelen ve UTC'nin başka bir adı olan “Z”yi kullanırız.
JavaScript'te Tarih Manipülasyonu ve Aritmetik
En iyi uygulamalarla başlamadan önce, sözdizimini ve genel kavramları kavramak için JavaScript kullanarak tarih işlemeyi öğreneceğiz. JavaScript kullanmamıza rağmen, bu bilgileri en sevdiğiniz programlama diline kolayca uyarlayabilirsiniz.
Çoğu geliştiricinin karşılaştığı yaygın tarihle ilgili sorunları çözmek için tarih aritmetiğini kullanacağız.
Amacım, bir dizeden bir tarih nesnesi oluşturma ve bileşenleri birinden çıkarma konusunda sizi rahat ettirmektir. Bu, bir tarih kitaplığının size yardımcı olabileceği bir şeydir, ancak bunun perde arkasında nasıl yapıldığını anlamak her zaman daha iyidir.
Ellerimizi tarih/saat ile kirlettikten sonra, karşılaştığımız sorunları düşünmek, en iyi uygulamaları çıkarmak ve ilerlemek daha kolay. En iyi uygulamalara geçmek istiyorsanız, bunu yapmaktan çekinmeyin, ancak en azından aşağıdaki tarih-aritmetik bölümüne göz atmanızı şiddetle tavsiye ederim.
JavaScript Tarih Nesnesi
Programlama dilleri hayatımızı kolaylaştıran faydalı yapılar içerir. JavaScript Date
nesnesi böyle bir şeydir. Geçerli tarih ve saati almak, bir değişkende bir tarih depolamak, tarih aritmetiği yapmak ve tarihi kullanıcının yerel ayarına göre biçimlendirmek için uygun yöntemler sunar.
Tarayıcı uygulamaları arasındaki farklılıklar ve Yaz Saati Uygulamasının (DST) yanlış işlenmesi nedeniyle, kritik görev uygulamaları için Date nesnesine bağlı olarak önerilmez ve muhtemelen Luxon, date-fns veya dayjs gibi bir DateTime kitaplığı kullanmanız gerekir. (Ne kullanırsanız kullanın, bir zamanlar popüler olan Moment.js'den kaçının - kodda göründüğü gibi genellikle moment
olarak adlandırılır - artık kullanımdan kaldırılmıştır.)
Ancak eğitim amaçlı olarak, JavaScript'in DateTime'ı nasıl işlediğini öğrenmek için Date() nesnesinin sağladığı yöntemleri kullanacağız.
Güncel Tarih Alma
const currentDate = new Date();
Date yapıcısına hiçbir şey iletmezseniz, döndürülen tarih nesnesi geçerli tarih ve saati içerir.
Daha sonra, yalnızca tarih bölümünü aşağıdaki gibi çıkarmak için biçimlendirebilirsiniz:
const currentDate = new Date(); const currentDayOfMonth = currentDate.getDate(); const currentMonth = currentDate.getMonth(); // Be careful! January is 0, not 1 const currentYear = currentDate.getFullYear(); const dateString = currentDayOfMonth + "-" + (currentMonth + 1) + "-" + currentYear; // "27-11-2020"
Not: “Ocak 0” tuzağı yaygındır ancak evrensel değildir. Kullanmaya başlamadan önce herhangi bir dilin (veya yapılandırma formatının: örneğin, cron özellikle 1 tabanlıdır) belgelerini iki kez kontrol etmeye değer.
Geçerli Zaman Damgasını Alma
Bunun yerine geçerli zaman damgasını almak istiyorsanız, yeni bir Date nesnesi oluşturabilir ve getTime() yöntemini kullanabilirsiniz.
const currentDate = new Date(); const timestamp = currentDate.getTime();
JavaScript'te zaman damgası, 1 Ocak 1970'den bu yana geçen milisaniye sayısıdır.
<IE8'i desteklemeyi düşünmüyorsanız, yeni bir Date nesnesi oluşturmak zorunda kalmadan zaman damgasını doğrudan almak için Date.now()
'u kullanabilirsiniz.
Tarih Ayrıştırma
Bir dizeyi bir JavaScript tarih nesnesine dönüştürmek farklı şekillerde yapılır.
Date nesnesinin yapıcısı çok çeşitli tarih biçimlerini kabul eder:
const date1 = new Date("Wed, 27 July 2016 13:30:00"); const date2 = new Date("Wed, 27 July 2016 07:45:00 UTC"); const date3 = new Date("27 July 2016 13:30:00 UTC+05:45");
JS herhangi bir tarih için haftanın gününü belirleyebildiğinden, haftanın gününü eklemeniz gerekmediğini unutmayın.
Ayrıca yılı, ayı, günü, saati, dakikayı ve saniyeyi ayrı bağımsız değişkenler olarak iletebilirsiniz:
const date = new Date(2016, 6, 27, 13, 30, 0);
Elbette, her zaman ISO tarih biçimini kullanabilirsiniz:
const date = new Date("2016-07-27T07:45:00Z");
Ancak, saat dilimini açıkça belirtmezseniz sorun yaşayabilirsiniz!
const date1 = new Date("25 July 2016"); const date2 = new Date("July 25, 2016");
Bunlardan herhangi biri size 25 Temmuz 2016 00:00:00 yerel saatini verecektir.
ISO biçimini kullanırsanız, saat ve saat dilimini değil, yalnızca tarihi verseniz bile, saat dilimini UTC olarak otomatik olarak kabul edecektir.
Bu şu demek:
new Date("25 July 2016").getTime() !== new Date("2016-07-25").getTime() new Date("2016-07-25").getTime() === new Date("2016-07-25T00:00:00Z").getTime()
Tarih Biçimlendirme
Neyse ki, modern JavaScript, tarih biçimlendirmesini basit bir işlem haline getiren standart Intl
ad alanında yerleşik bazı kullanışlı uluslararasılaştırma işlevlerine sahiptir.
Bunun için iki nesneye ihtiyacımız olacak: çıktı tercihlerimizle başlatılan bir Date
ve bir Intl.DateTimeFormat
. Amerikan (A/G/YYYY) biçimini kullanmak istediğimizi varsayarsak, bu şöyle görünür:
const firstValentineOfTheDecade = new Date(2020, 1, 14); // 1 for February const enUSFormatter = new Intl.DateTimeFormat('en-US'); console.log(enUSFormatter.format(firstValentineOfTheDecade)); // 2/14/2020
Bunun yerine Hollandaca (G/A/YYYY) biçimini isteseydik, DateTimeFormat
yapıcısına farklı bir kültür kodu iletirdik:
const nlBEFormatter = new Intl.DateTimeFormat('nl-BE'); console.log(nlBEFormatter.format(firstValentineOfTheDecade)); // 14/2/2020
Veya Amerikan biçiminin daha uzun bir biçimi, ay adının yazılı olduğu şekilde:
const longEnUSFormatter = new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric', }); console.log(longEnUSFormatter.format(firstValentineOfTheDecade)); // February 14, 2020
Şimdi, eğer ayın günü için uygun bir sıra biçimi istiyorsak - yani, sadece "14" yerine "14." - bunun ne yazık ki biraz geçici bir çözüme ihtiyacı var, çünkü bu yazı itibariyle day
'ın tek geçerli değerleri "numeric"
veya "2-digit"
. Bunun için Intl
başka bir bölümünden yararlanmak için Flavio Copes'in Mathias Bynens kodunun sürümünü ödünç alarak, formatToParts()
aracılığıyla ayın gününü özelleştirebiliriz:
const pluralRules = new Intl.PluralRules('en-US', { type: 'ordinal' }) const suffixes = { 'one': 'st', 'two': 'nd', 'few': 'rd', 'other': 'th' } const convertToOrdinal = (number) => `${number}${suffixes[pluralRules.select(number)]}` // At this point: // convertToOrdinal("1") === "1st" // convertToOrdinal("2") === "2nd" // etc. const extractValueAndCustomizeDayOfMonth = (part) => { if (part.type === "day") { return convertToOrdinal(part.value); } return part.value; }; console.log( longEnUSFormatter.formatToParts(firstValentineOfTheDecade) .map(extractValueAndCustomizeDayOfMonth) .join("") ); // February 14th, 2020
Ne yazık ki formatToParts
, bu yazı itibariyle Internet Explorer (IE) tarafından hiç desteklenmemektedir, ancak diğer tüm masaüstü, mobil ve arka uç (ör. Node.js) teknolojilerinde destek vardır. IE'yi desteklemesi gerekenler ve kesinlikle sıra sayılarına ihtiyaç duyanlar için aşağıdaki yan not (veya daha iyisi, uygun bir tarih kitaplığı) bir cevap sağlar.
Sürüm 11'den önce IE gibi daha eski tarayıcıları desteklemeniz gerekiyorsa, Python veya PHP'de strftime
gibi standart tarih biçimlendirme işlevleri olmadığından JavaScript'te tarih biçimlendirme daha zordur.
Örneğin PHP'de, strftime("Today is %b %d %Y %X", mktime(5,10,0,12,30,99))
size Today is Dec 30 1999 05:10:00
.
Tarihi farklı biçimlerde almak için başında %
ile başlayan farklı bir harf kombinasyonu kullanabilirsiniz. (Dikkat edin, her dil her harfe aynı anlamı vermez - özellikle 'M' ve 'm' dakikalar ve aylar için yer değiştirebilir.)
Kullanmak istediğiniz formattan eminseniz, yukarıda ele aldığımız JavaScript işlevlerini kullanarak tek tek bitleri çıkarmak ve kendiniz bir dize oluşturmak en iyisidir.
var currentDate = new Date (); var date = currentDate.getDate(); var month = currentDate.getMonth(); var year = currentDate.getFullYear();
Tarihi AA/GG/YYYY formatında şu şekilde alabiliriz:
var monthDateYear = (month+ 1 ) + "/" + date + "/" + year;
Bu çözümle ilgili sorun, tarihlere tutarsız bir uzunluk verebilmesidir, çünkü ayın bazı ayları ve günleri tek basamaklı ve diğerleri çift basamaklıdır. Bu, örneğin, tarihi bir tablo sütununda görüntülüyorsanız, tarihler aynı hizada olmadığından sorunlu olabilir.
Başına 0 ekleyen bir “pad” işlevi kullanarak bunu ele alabiliriz.
function pad ( n ) { return n< 10 ? '0' +n : n; }
Şimdi, aşağıdakileri kullanarak AA/GG/YYYY biçiminde doğru tarihi alıyoruz:
var mmddyyyy = pad(month + 1 ) + "/" + pad(date) + "/" + year;
Bunun yerine GG-AA-YYYY istiyorsak, süreç benzer:
var ddmmyyyy = pad(date) + "-" + pad(month + 1 ) + "-" + year;
Bahsi yükseltelim ve tarihi “Ay Tarih, Yıl” formatında yazdırmaya çalışalım. Ay dizinlerinin adlarla eşleştirilmesine ihtiyacımız olacak:
var monthNames = [ "January" , "February" , "March" , "April" , "May" , "June" , "July" , "August" , "September" , "October" , "November" , "December" ]; var dateWithFullMonthName = monthNames[month] + " " + pad(date) + ", " + year;
Bazı insanlar tarihi 1 Ocak 2013 olarak göstermeyi sever. Sorun değil, tek ihtiyacımız olan 1 için 1., 12 için 12 ve 103 için 103, vb. döndüren bir yardımcı işlev ordinal
sayısıdır ve gerisi basittir:
var ordinalDate = ordinal(date) + " " + monthNames[month] + ", " + year;
Tarih nesnesinden haftanın gününü belirlemek kolaydır, o yüzden şunu ekleyelim:
var daysOfWeek = [ "Sun" , "Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" ]; ordinalDateWithDayOfWeek = daysOfWeek[currentDate.getDay()] + ", " + ordinalDate;
Buradaki daha büyük nokta, tarihten alınan sayıları aldığınızda, biçimlendirme çoğunlukla dizelerle ilgilidir.
Tarih Formatını Değiştirme
Bir tarihi nasıl ayrıştıracağınızı ve biçimlendireceğinizi öğrendikten sonra, bir tarihi bir biçimden diğerine değiştirmek yalnızca ikisini birleştirme meselesidir.
Örneğin, 21 Temmuz 2013 biçiminde bir tarihiniz varsa ve biçimi 21-07-2013 olarak değiştirmek istiyorsanız, bunu şu şekilde gerçekleştirebilirsiniz:
const myDate = new Date("Jul 21, 2013"); const dayOfMonth = myDate.getDate(); const month = myDate.getMonth(); const year = myDate.getFullYear(); function pad(n) { return n<10 ? '0'+n : n } const ddmmyyyy = pad(dayOfMonth) + "-" + pad(month + 1) + "-" + year; // "21-07-2013"
JavaScript Tarih Nesnesinin Yerelleştirme İşlevlerini Kullanma
Yukarıda tartıştığımız tarih biçimlendirme yöntemleri çoğu uygulamada çalışmalıdır, ancak tarihin biçimlendirmesini gerçekten yerelleştirmek istiyorsanız, Date
nesnesinin toLocaleDateString()
yöntemini kullanmanızı öneririm:
const today = new Date().toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric', });
…bize 26 Jul 2016
gibi bir şey verir.

Yerel ayarı 'en-US' olarak değiştirmek, bunun yerine “26 Tem 2016” değerini verir. Biçimlendirmenin nasıl değiştiğine dikkat edin, ancak görüntüleme seçenekleri hala aynı - çok kullanışlı bir özellik. Önceki bölümde gösterildiği gibi, daha yeni Intl.DateTimeFormat
tabanlı teknik buna çok benzer şekilde çalışır, ancak bir biçimlendirici nesnesini yeniden kullanmanıza izin verir, böylece seçenekleri yalnızca bir kez ayarlamanız gerekir.
toLocaleDateString()
ile çıktı bilgisayarınızda iyi görünse bile her zaman biçimlendirme seçeneklerini geçmek iyi bir alışkanlıktır. Bu, kullanıcı arayüzünü gerçekten uzun ay adlarıyla beklenmedik yerel ayarlara girmekten veya kısa adlar nedeniyle garip görünmekten koruyabilir.
Bunun yerine tam ayı “Temmuz” istersem, seçeneklerdeki ay parametresini “uzun” olarak değiştirmekten başka bir şey yapmıyorum. JavaScript benim için her şeyi hallediyor. tr-US için şimdi 26 Temmuz 2016'yı alıyorum.
Not: Tarayıcının kullanıcının yerel ayarını otomatik olarak kullanmasını istiyorsanız, ilk parametre olarak “undefined” iletebilirsiniz.
Tarihin sayısal versiyonunu göstermek istiyorsanız ve farklı yerel ayarlar için AA/GG/YYYY ile GG/AA/YYYY ile uğraşmak istemiyorsanız, aşağıdaki basit çözümü öneririm:
const today = new Date().toLocaleDateString(undefined, { day: 'numeric', month: 'numeric', year: 'numeric', });
Bilgisayarımda bu, 7/26/2016
çıktısını verir. Ay ve tarihin iki basamaklı olduğundan emin olmak istiyorsanız, seçenekleri değiştirmeniz yeterlidir:
const today = new Date().toLocaleDateString(undefined, { day: '2-digit', month: '2-digit', year: 'numeric', });
Bu 07/26/2016
çıktısını verir. Tam istediğimiz şey!
Hem saatin hem de tarihin görüntülenme şeklini yerelleştirmek için diğer ilgili işlevleri de kullanabilirsiniz:
kod | Çıktı | Tanım |
---|---|---|
| "04:21:38" | Yalnızca zamanın yerelleştirilmiş sürümünü görüntüle |
| "04:21:38" | Sağlanan seçeneklere göre yerelleştirilmiş saati görüntüleyin |
| "22.07.2016, 04:21:38" | Kullanıcının yerel ayarı için tarih ve saati görüntüle |
| "22.07.2016, 04:21" | Sağlanan seçeneklere göre yerelleştirilmiş tarih ve saati görüntüleyin |
Göreli Tarih ve Saatleri Hesaplama
İşte bir JavaScript Tarihine 20 gün eklemeye bir örnek (yani, bilinen bir tarihten 20 gün sonraki tarihi bulma):
const myDate = new Date("July 20, 2016 15:00:00"); const nextDayOfMonth = myDate.getDate() + 20; myDate.setDate(nextDayOfMonth); const newDate = myDate.toLocaleString();
Orijinal tarih nesnesi artık 20 Temmuz'dan 20 gün sonraki bir tarihi temsil ediyor ve newDate
, o tarihi temsil eden yerelleştirilmiş bir dize içeriyor. Tarayıcımda, newDate
"8/9/2016, 3:00:00 PM" içeriyor.
Göreli zaman damgalarını tam günlerden daha kesin bir farkla hesaplamak için, belirli bir dönemden, yani 1 Ocak 1970'ten bu yana geçen milisaniye sayısını temsil eden tam sayılarla çalışmak için Date.getTime()
ve Date.setTime()
'ı kullanabilirsiniz. örneğin, şu andan 17 saat sonrasını bilmek istiyorsanız:
const msSinceEpoch = (new Date()).getTime(); const seventeenHoursLater = new Date(msSinceEpoch + 17 * 60 * 60 * 1000);
Tarihleri Karşılaştırma
Tarihle ilgili diğer her şeyde olduğu gibi, tarihleri karşılaştırmanın da kendine göre zorlukları vardır.
İlk olarak, tarih nesneleri oluşturmamız gerekiyor. Neyse ki, <, >, <= ve >= hepsi çalışıyor. 19 Temmuz 2014 ile 18 Temmuz 2014'ü karşılaştırmak şu kadar kolay:
const date1 = new Date("July 19, 2014"); const date2 = new Date("July 28, 2014"); if(date1 > date2) { console.log("First date is more recent"); } else { console.log("Second date is more recent"); }
Aynı tarihi temsil eden iki tarih nesnesi hala iki farklı tarih nesnesi olduğundan ve eşit olmayacaklarından, eşitliği kontrol etmek daha zordur. Tarih dizelerini karşılaştırmak kötü bir fikirdir, çünkü örneğin "20 Temmuz 2014" ve "20 Temmuz 2014" aynı tarihi temsil eder ancak farklı dize temsillerine sahiptir. Aşağıdaki pasaj ilk noktayı göstermektedir:
const date1 = new Date("June 10, 2003"); const date2 = new Date(date1); const equalOrNot = date1 == date2 ? "equal" : "not equal"; console.log(equalOrNot);
Bu çıktı not equal
.
Bu özel durum, tarihlerin (zaman damgaları) tam sayı eşdeğerlerini aşağıdaki gibi karşılaştırarak düzeltilebilir:
date1.getTime() == date2.getTime()
Bu örneği bir çok yerde gördüm ama beğenmedim çünkü genelde başka bir tarih nesnesinden bir tarih nesnesi yaratmıyorsunuz. Bu nedenle, örneğin yalnızca akademik bir bakış açısıyla önemli olduğunu hissediyorum. Ayrıca, bu, her iki Date nesnesinin de aynı saniyeye atıfta bulunmasını gerektirir, ancak yalnızca aynı güne, saate veya dakikaya atıfta bulunup bulunmadıklarını bilmek isteyebilirsiniz.
Daha pratik bir örneğe bakalım. Kullanıcının girdiği doğum gününün bir API'den aldığınız şanslı tarihle aynı olup olmadığını karşılaştırmaya çalışıyorsunuz.
const userEnteredString = "12/20/1989"; // MM/DD/YYYY format const dateStringFromAPI = "1989-12-20T00:00:00Z"; const dateFromUserEnteredString = new Date(userEnteredString) const dateFromAPIString = new Date(dateStringFromAPI); if (dateFromUserEnteredString.getTime() == dateFromAPIString.getTime()) { transferOneMillionDollarsToUserAccount(); } else { doNothing(); }
Her ikisi de aynı tarihi temsil ediyor ama maalesef kullanıcınız milyon doları alamayacak.
Sorun şu: JavaScript, aksi açıkça belirtilmediği sürece, her zaman tarayıcının sağladığı saat dilimini varsayar.
Bu benim için new Date ("12/20/1989")
1989-12-20T00:00:00+5:45 veya 1989-12-19T18:15:00Z tarihini oluşturacağı anlamına gelir ve bu tarih ile aynı değildir. 1989-12-20T00:00:00Z zaman damgası açısından.
Mevcut bir tarih nesnesinin yalnızca saat dilimini değiştirmek mümkün değildir, bu nedenle hedefimiz artık yerel saat dilimi yerine UTC ile yeni bir tarih nesnesi oluşturmaktır.
Tarih nesnesini oluştururken kullanıcının saat dilimini yok sayacağız ve UTC'yi kullanacağız. Bunu yapmanın iki yolu vardır:
- Kullanıcı giriş tarihinden itibaren ISO biçimli bir tarih dizesi oluşturun ve bunu bir Date nesnesi oluşturmak için kullanın. Bir Date nesnesi oluşturmak için geçerli bir ISO tarih formatı kullanırken UTC ile yerelin amacını çok net hale getirmek.
const userEnteredDate = "12/20/1989"; const parts = userEnteredDate.split("/"); const userEnteredDateISO = parts[2] + "-" + parts[0] + "-" + parts[1]; const userEnteredDateObj = new Date(userEnteredDateISO + "T00:00:00Z"); const dateFromAPI = new Date("1989-12-20T00:00:00Z"); const result = userEnteredDateObj.getTime() == dateFromAPI.getTime(); // true
Bu, varsayılan olarak gece yarısına (yani 00:00:00Z) geçeceğinden beri süreyi belirtmezseniz de çalışır:
const userEnteredDate = new Date("1989-12-20"); const dateFromAPI = new Date("1989-12-20T00:00:00Z"); const result = userEnteredDate.getTime() == dateFromAPI.getTime(); // true
Unutmayın: Tarih oluşturucuya YYYY-AA-GG'nin doğru ISO tarih biçiminde bir dize iletilirse, UTC'yi otomatik olarak varsayar.
- JavaScript, bir tarihin UTC zaman damgasını almak için kullanabileceğiniz düzgün bir Date.UTC() işlevi sağlar. Bileşenleri tarihten çıkartıp fonksiyona aktarıyoruz.
const userEnteredDate = new Date("12/20/1989"); const userEnteredDateTimeStamp = Date.UTC(userEnteredDate.getFullYear(), userEnteredDate.getMonth(), userEnteredDate.getDate(), 0, 0, 0); const dateFromAPI = new Date("1989-12-20T00:00:00Z"); const result = userEnteredDateTimeStamp == dateFromAPI.getTime(); // true ...
İki Tarih Arasındaki Farkı Bulma
Karşılaşacağınız yaygın bir senaryo, iki tarih arasındaki farkı bulmaktır.
İki kullanım durumunu tartışıyoruz:
İki Tarih Arasındaki Gün Sayısını Bulma
Her iki tarihi de UTC zaman damgasına dönüştürün, farkı milisaniye cinsinden bulun ve eşdeğer günleri bulun.
const dateFromAPI = "2016-02-10T00:00:00Z"; const now = new Date(); const datefromAPITimeStamp = (new Date(dateFromAPI)).getTime(); const nowTimeStamp = now.getTime(); const microSecondsDiff = Math.abs(datefromAPITimeStamp - nowTimeStamp); // Math.round is used instead of Math.floor to account for certain DST cases // Number of milliseconds per day = // 24 hrs/day * 60 minutes/hour * 60 seconds/minute * 1000 ms/second const daysDiff = Math.round(microSecondsDiff / (1000 * 60 * 60 * 24)); console.log(daysDiff);
Kullanıcının Yaşını Doğum Tarihinden Bulma
const birthDateFromAPI = "12/10/1989";
Not: Standart olmayan bir formatımız var. Bunun 12 Ekim mi yoksa 10 Aralık mı olduğunu belirlemek için API belgesini okuyun. Buna göre ISO formatına geçin.
const parts = birthDateFromAPI.split("/"); const birthDateISO = parts[2] + "-" + parts[0] + "-" + parts[1]; const birthDate = new Date(birthDateISO); const today = new Date(); let age = today.getFullYear() - birthDate.getFullYear(); if(today.getMonth() < birthDate.getMonth()) { age--; } if(today.getMonth() == birthDate.getMonth() && today.getDate() < birthDate.getDate()) { age--; }
Bu kodu yazmanın daha kısa yolları olduğunu biliyorum ama mantığın netliği nedeniyle bu şekilde yazmayı seviyorum.
Randevu Cehennemden Kurtulmak İçin Öneriler
Artık tarih aritmetiği konusunda rahat olduğumuza göre, izlenecek en iyi uygulamaları ve bunları izlemenin nedenlerini anlayabilecek durumdayız.
Kullanıcıdan DateTime Alma
Kullanıcıdan tarih ve saati alıyorsanız, büyük olasılıkla yerel DateTime'ı arıyorsunuz. Date
yapıcısının tarihi birkaç farklı şekilde kabul edebileceğini date aritmetiği bölümünde gördük.
Herhangi bir karışıklığı ortadan kaldırmak için, geçerli bir ayrıştırılabilir biçimde tarihiniz olsa bile, her zaman new Date(year, month, day, hours, minutes, seconds, milliseconds)
biçimini kullanarak bir tarih oluşturmanızı öneririm. Ekibinizdeki tüm programcılar bu basit kuralı takip ederse, Date
yapıcısında olabildiğince açık olduğundan, kodu uzun vadede korumak son derece kolay olacaktır.
İşin güzel yanı, son dört parametreden herhangi birini sıfırlarsa çıkarmanıza izin veren varyasyonları kullanabilmenizdir; yani, new Date(2012, 10, 12)
new Date(2012, 10, 12, 0, 0, 0, 0)
ile aynıdır, çünkü belirtilmemiş parametreler varsayılan olarak sıfırdır.
Örneğin, size 2012-10-12 tarihini ve 12:30 saatini veren bir tarih ve saat seçici kullanıyorsanız, parçaları çıkarabilir ve aşağıdaki gibi yeni bir Date nesnesi oluşturabilirsiniz:
const dateFromPicker = "2012-10-12"; const timeFromPicker = "12:30"; const dateParts = dateFromPicker.split("-"); const timeParts = timeFromPicker.split(":"); const localDate = new Date(dateParts[0], dateParts[1]-1, dateParts[2], timeParts[0], timeParts[1]);
ISO tarih biçiminde olmadığı sürece bir dizeden tarih oluşturmaktan kaçının. Bunun yerine Tarih(yıl, ay, tarih, saat, dakika, saniye, mikrosaniye) yöntemini kullanın.
Sadece Tarih Alma
Yalnızca tarihi, örneğin bir kullanıcının doğum tarihini alıyorsanız, UTC'ye dönüştürüldüğünde tarihin ileri veya geri kaymasına neden olabilecek saat dilimi bilgilerini ortadan kaldırmak için biçimi geçerli ISO tarih biçimine dönüştürmek en iyisidir. Örneğin:
const dateFromPicker = "12/20/2012"; const dateParts = dateFromPicker.split("/"); const ISODate = dateParts[2] + "-" + dateParts[0] + "-" + dateParts[1]; const birthDate = new Date(ISODate).toISOString();
Unutmuş olmanız durumunda, geçerli ISO tarih biçiminde (YYYY-AA-GG) girişi olan bir Date
nesnesi oluşturursanız, varsayılan olarak tarayıcının saat dilimine ayarlanmak yerine UTC'ye varsayılan olur.
Tarihin Saklanması
DateTime'ı her zaman UTC'de saklayın. Arka uca her zaman bir ISO tarih dizesi veya zaman damgası gönderin.
Nesiller boyu bilgisayar programcıları, kullanıcıya doğru yerel saati göstermeye çalışan acı deneyimlerden sonra bu basit gerçeği fark ettiler. Yerel saati arka uçta saklamak kötü bir fikirdir, yerel saate dönüştürmeyi ön uçta tarayıcının yapmasına izin vermek daha iyidir.
Ayrıca, arka uca asla “20 Temmuz 1989 12:10 PM” gibi bir DateTime dizesi göndermemeniz gerektiği de açıkça belirtilmelidir. Saat dilimini de gönderseniz bile, diğer programcıların niyetinizi anlaması ve tarihi doğru bir şekilde ayrıştırması ve saklaması için harcadığınız çabayı artırıyorsunuz.
Yerel DateTime'ı UTC'ye dönüştürmek için Date nesnesinin toISOString()
veya toJSON()
yöntemlerini kullanın.
const dateFromUI = "12-13-2012"; const timeFromUI = "10:20"; const dateParts = dateFromUI.split("-"); const timeParts = timeFromUI.split(":"); const date = new Date(dateParts[2], dateParts[0]-1, dateParts[1], timeParts[0], timeParts[1]); const dateISO = date.toISOString(); $.post("http://example.com/", {date: dateISO}, ...)
Tarih ve Saati Görüntüleme
- Bir REST API'sinden zaman damgasını veya ISO formatlı tarihi alın.
- Bir
Date
nesnesi oluşturun. - Yerel saati görüntülemek için
toLocaleString()
veyatoLocaleDateString()
vetoLocaleTimeString()
yöntemlerini veya bir tarih kitaplığını kullanın.
const dateFromAPI = "2016-01-02T12:30:00Z"; const localDate = new Date(dateFromAPI); const localDateString = localDate.toLocaleDateString(undefined, { day: 'numeric', month: 'short', year: 'numeric', }); const localTimeString = localDate.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit', });
Yerel Saati Ne Zaman Saklamalısınız?
"Bazen bir olayın meydana geldiği saat dilimini bilmek önemlidir ve tek bir zaman dilimine dönüştürmek bu bilgiyi geri alınamaz bir şekilde yok eder.
"Bir pazarlama promosyonu yapıyorsanız ve hangi müşterilerin öğle yemeği saatinde sipariş verdiğini bilmek istiyorsanız, öğlen GMT'de verilmiş gibi görünen bir sipariş, aslında New York'ta kahvaltıda verildiğinde pek yardımcı olmaz."
Bu tür bir durumla karşılaşırsanız, yerel zamandan da tasarruf etmek daha akıllıca olacaktır. Her zamanki gibi, tarihi ISO formatında oluşturmak istiyoruz, ancak önce saat dilimi ofsetini bulmamız gerekiyor.
Date nesnesinin getTimeZoneOffset()
işlevi, bize belirli bir yerel saate eklendiğinde eşdeğer UTC saatini veren dakika sayısını söyler. Bunu (+-)ss:mm biçimine dönüştürmenizi öneririm çünkü bunun bir saat dilimi farkı olduğunu daha açık hale getirir.
const now = new Date(); const tz = now.gettime zoneOffset();
+05:45 saat dilimim için -345 alıyorum, bu yalnızca zıt işaret değil, -345 gibi bir sayı bir arka uç geliştirici için tamamen kafa karıştırıcı olabilir. Yani bunu +05:45'e çeviriyoruz.
const sign = tz > 0 ? "-" : "+"; const hours = pad(Math.floor(Math.abs(tz)/60)); const minutes = pad(Math.abs(tz)%60); const tzOffset = sign + hours + ":" + minutes;
Şimdi değerlerin geri kalanını alıyoruz ve yerel DateTime'ı temsil eden geçerli bir ISO dizesi oluşturuyoruz.
const localDateTime = now.getFullYear() + "-" + pad(now.getMonth()+1) + "-" + pad(now.getDate()) + "T" + pad(now.getHours()) + ":" + pad(now.getMinutes()) + ":" + pad(now.getSeconds());
İsterseniz UTC ve yerel tarihleri bir nesneye sarabilirsiniz.
const eventDate = { utc: now.toISOString(), local: localDateTime, tzOffset: tzOffset, }
Şimdi, arka uçta, olayın yerel saatle öğleden önce olup olmadığını öğrenmek istiyorsanız, tarihi ayrıştırabilir ve basitçe getHours()
işlevini kullanabilirsiniz.
const localDateString = eventDate.local; const localDate = new Date(localDateString); if(localDate.getHours() < 12) { console.log("Event happened before noon local time"); }
Burada tzOffset
kullanmadık, ancak gelecekte hata ayıklama amacıyla ihtiyaç duyabileceğimiz için onu hala saklıyoruz. Aslında yalnızca saat dilimi farkını ve yalnızca UTC saatini gönderebilirsiniz. Ancak yerel saati de saklamayı seviyorum çünkü sonunda tarihi bir veritabanında saklamanız gerekecek ve yerel saati ayrı olarak saklamak, yerel tarihi almak için hesaplamalar yapmak zorunda kalmadan doğrudan bir alana dayalı sorgulama yapmanızı sağlıyor.
Bazen, yerel saat dilimi depolanmış olsa bile, tarihleri belirli bir saat diliminde görüntülemek istersiniz. Örneğin, olayların saatleri, sanalsa geçerli kullanıcının saat diliminde veya sanal değilse fiziksel olarak gerçekleşecekleri saat diliminde daha anlamlı olabilir. Her durumda, önceden açık saat dilimi adlarıyla biçimlendirme için yerleşik çözümlere bakmakta fayda var.
Sunucu ve Veritabanı Yapılandırması
Sunucularınızı ve veritabanlarınızı her zaman UTC saat dilimini kullanacak şekilde yapılandırın. (UTC ve GMT'nin aynı şey olmadığına dikkat edin - örneğin GMT, yaz aylarında BST'ye geçiş anlamına gelebilir, oysa UTC asla yapmaz.)
Özellikle istenmeden yapıldıklarında, zaman dilimi dönüşümlerinin ne kadar acı verici olabileceğini zaten gördük. Her zaman UTC DateTime göndermek ve sunucularınızı UTC saat diliminde olacak şekilde yapılandırmak hayatınızı kolaylaştırabilir. Herhangi bir saat dilimi dönüştürmesi yapmak zorunda olmadığı için arka uç kodunuz çok daha basit ve temiz olacaktır. DateTime data coming in from servers across the world can be compared and sorted effortlessly.
Code in the back end should be able to assume the time zone of the server to be UTC (but should still have a check in place to be sure). A simple configuration check saves having to think about and code for conversions every time new DateTime code is written.
It's Time for Better Date Handling
Date manipulation is a hard problem. The concepts behind the practical examples in this article apply beyond JavaScript, and are just the beginning when it comes to properly handling DateTime data and calculations. Plus, every helper library will come with its own set of nuances—which is even true of the eventual official standard support{target=”_blank”} for these types of operations.
The bottom line is: Use ISO on the back end, and leave the front end to format things properly for the user. Professional programmers will be aware of some of the nuances, and will (all the more decidedly) use well-supported DateTime libraries on both the back end and the front end. Built-in functions on the database side are another story, but hopefully this article gives enough background to make wiser decisions in that context, too.