Panduan Definitif untuk Manipulasi DateTime

Diterbitkan: 2022-03-11

Sebagai pengembang perangkat lunak, Anda tidak dapat lari dari manipulasi tanggal. Hampir setiap aplikasi yang dibuat oleh pengembang akan memiliki beberapa komponen di mana tanggal/waktu perlu diperoleh dari pengguna, disimpan dalam database, dan ditampilkan kembali kepada pengguna.

Tanyakan kepada programmer mana pun tentang pengalaman mereka menangani tanggal dan zona waktu dan mereka mungkin akan berbagi beberapa cerita perang. Menangani bidang tanggal dan waktu tentu saja bukan ilmu roket tetapi seringkali bisa membosankan dan rawan kesalahan.

Ada ratusan artikel tentang topik di luar sana, namun, sebagian besar terlalu akademis, berfokus pada detail seluk beluk, atau terlalu tambal sulam, memberikan potongan kode pendek tanpa banyak penjelasan yang menyertainya. Panduan mendalam tentang manipulasi DateTime ini akan membantu Anda memahami konsep pemrograman dan praktik terbaik yang relevan dengan waktu dan tanggal tanpa harus menelusuri lautan informasi tentang topik ini.

Dalam artikel ini, saya akan membantu Anda berpikir jernih tentang bidang tanggal dan waktu dan menyarankan beberapa praktik terbaik yang dapat membantu Anda menghindari neraka tanggal/waktu. Di sini kita akan menjelajahi beberapa konsep utama yang diperlukan untuk memanipulasi nilai tanggal dan waktu dengan benar, format yang nyaman untuk menyimpan nilai DateTime dan mentransfernya melalui API, dan banyak lagi.

Sebagai permulaan, jawaban yang tepat untuk kode produksi hampir selalu menggunakan perpustakaan yang tepat daripada menggulirkan milik Anda sendiri. Potensi kesulitan dengan perhitungan DateTime yang dibahas dalam artikel ini hanyalah puncak gunung es, tetapi masih berguna untuk diketahui, dengan atau tanpa perpustakaan.

Perpustakaan DateTime Membantu Jika Anda Memahaminya dengan Benar

Perpustakaan tanggal membantu dalam banyak hal untuk membuat hidup Anda lebih mudah. Mereka sangat menyederhanakan penguraian tanggal, operasi aritmatika dan logika tanggal, dan pemformatan tanggal. Anda dapat menemukan perpustakaan tanggal yang andal untuk ujung depan dan ujung belakang untuk melakukan sebagian besar pekerjaan berat untuk Anda.

Namun, kami sering menggunakan pustaka tanggal tanpa memikirkan cara kerja tanggal/waktu. Tanggal/waktu adalah konsep yang kompleks. Bug yang muncul karena pemahaman yang salah bisa sangat sulit untuk dipahami dan diperbaiki, bahkan dengan bantuan perpustakaan tanggal. Sebagai seorang programmer, Anda perlu memahami dasar-dasarnya dan dapat menghargai masalah yang dipecahkan oleh perpustakaan tanggal untuk memanfaatkannya sebaik mungkin.

Juga, perpustakaan tanggal/waktu hanya dapat membawa Anda sejauh ini. Semua pustaka tanggal bekerja dengan memberi Anda akses ke struktur data yang nyaman untuk mewakili DateTime. Jika Anda mengirim dan menerima data melalui REST API, pada akhirnya Anda perlu mengonversi tanggal menjadi string dan sebaliknya karena JSON tidak memiliki struktur data asli untuk mewakili DateTime. Konsep yang telah saya uraikan di sini akan membantu Anda menghindari beberapa masalah umum yang mungkin muncul saat melakukan transformasi date-to-string dan string-to-date ini.

Catatan: Meskipun saya telah menggunakan JavaScript sebagai bahasa pemrograman yang dibahas dalam artikel ini, ini adalah konsep umum yang berlaku, sebagian besar, untuk hampir semua bahasa pemrograman dan pustaka tanggalnya. Jadi, bahkan jika Anda belum pernah menulis sebaris JavaScript sebelumnya, silakan lanjutkan membaca karena saya hampir tidak mengasumsikan pengetahuan sebelumnya tentang JavaScript dalam artikel ini.

Standarisasi Waktu

DateTime adalah titik waktu yang sangat spesifik. Mari kita pikirkan ini. Saat saya menulis artikel ini, jam di laptop saya menunjukkan 21 Juli 13:29. Inilah yang kami sebut “waktu setempat”, waktu yang saya lihat di jam dinding di sekitar saya dan di jam tangan saya.

Beri atau luangkan beberapa menit, jika saya meminta teman saya untuk menemui saya di kafe terdekat pada pukul 15:00, saya dapat berharap untuk melihatnya di sana kira-kira pada waktu itu. Demikian pula, tidak akan ada kebingungan jika sebaliknya saya berkata, misalnya, “mari kita bertemu dalam satu setengah jam.” Kami secara rutin membicarakan waktu seperti ini dengan orang-orang yang tinggal di kota atau zona waktu yang sama.

Mari kita pikirkan skenario yang berbeda: Saya ingin memberi tahu seorang teman yang tinggal di Uppsala, Swedia bahwa saya ingin berbicara dengannya pada jam 5 sore. Saya mengiriminya pesan, "Hei Anton, mari kita bicara jam 5 sore." Saya langsung mendapat tanggapan, “Waktu Anda atau waktu saya?”

Anton memberi tahu saya bahwa dia tinggal di zona waktu Eropa Tengah yaitu UTC+01:00. Saya tinggal di UTC+05:45. Ini berarti ketika jam 5 sore di tempat saya tinggal, itu adalah jam 5 sore - 05:45 = 11:15 UTC, yang berarti 11:15 UTC + 01:00 = 12:15 di Uppsala, cocok untuk keduanya dari kita.

Perhatikan juga perbedaan antara zona waktu (Waktu Eropa Tengah) dan offset zona waktu (UTC+05:45). Negara dapat memutuskan untuk mengubah offset zona waktu mereka untuk Waktu Musim Panas karena alasan politik juga. Hampir setiap tahun ada perubahan pada aturan di setidaknya satu negara, artinya kode apa pun dengan aturan ini harus selalu diperbarui—ada baiknya mempertimbangkan apa basis kode Anda bergantung pada ini untuk setiap tingkat aplikasi Anda.

Itu alasan bagus lainnya, kami akan merekomendasikan bahwa hanya front end yang berurusan dengan zona waktu dalam banyak kasus. Jika tidak, apa yang terjadi ketika aturan yang digunakan mesin database Anda tidak cocok dengan aturan yang ada di depan atau belakang Anda?

Masalah mengelola dua versi waktu yang berbeda ini, relatif terhadap pengguna dan relatif terhadap standar yang diterima secara universal, sulit, terlebih lagi di dunia pemrograman di mana presisi adalah kuncinya dan bahkan satu detik dapat membuat perbedaan besar. Langkah pertama untuk memecahkan masalah ini adalah menyimpan DateTime di UTC.

Standarisasi Format

Menstandarkan waktu sangat bagus karena saya hanya perlu menyimpan waktu UTC dan selama saya mengetahui zona waktu pengguna, saya selalu dapat mengonversi ke waktu mereka. Sebaliknya, jika saya mengetahui waktu lokal pengguna dan mengetahui zona waktu mereka, saya dapat mengonversinya ke UTC.

Tetapi tanggal dan waktu dapat ditentukan dalam berbagai format. Untuk tanggalnya, Anda bisa menulis “30 Juli” atau “30 Juli” atau “30/7” (atau 30/7, tergantung di mana Anda tinggal). Untuk waktu, Anda bisa menulis “9:30 PM” atau “2130”.

Para ilmuwan di seluruh dunia berkumpul untuk mengatasi masalah ini dan memutuskan format untuk menggambarkan waktu yang sangat disukai programmer karena singkat dan tepat. Kami suka menyebutnya "format tanggal ISO," yang merupakan versi sederhana dari format diperpanjang ISO-8601 dan terlihat seperti ini:

Gambar yang menunjukkan versi sederhana dari format perluasan ISO-8601 yang disebut format tanggal ISO.

Untuk 00:00 atau UTC, kami menggunakan "Z", yang berarti waktu Zulu, nama lain untuk UTC.

Manipulasi Tanggal dan Aritmatika dalam JavaScript

Sebelum kita mulai dengan praktik terbaik, kita akan belajar tentang manipulasi tanggal menggunakan JavaScript untuk memahami sintaks dan konsep umum. Meskipun kami menggunakan JavaScript, Anda dapat menyesuaikan informasi ini dengan bahasa pemrograman favorit Anda dengan mudah.

Kami akan menggunakan aritmatika tanggal untuk memecahkan masalah umum terkait tanggal yang dihadapi sebagian besar pengembang.

Tujuan saya adalah membuat Anda nyaman membuat objek tanggal dari string dan mengekstrak komponen dari satu. Ini adalah sesuatu yang perpustakaan tanggal dapat membantu Anda, tetapi selalu lebih baik untuk memahami bagaimana hal itu dilakukan di belakang layar.

Setelah tangan kita kotor dengan tanggal/waktu, akan lebih mudah untuk memikirkan masalah yang kita hadapi, mengekstrak praktik terbaik, dan melanjutkan. Jika Anda ingin melompat ke praktik terbaik, jangan ragu untuk melakukannya, tetapi saya sangat menyarankan Anda untuk setidaknya membaca sekilas bagian aritmatika tanggal di bawah ini.

Objek Tanggal JavaScript

Bahasa pemrograman berisi konstruksi yang berguna untuk membuat hidup kita lebih mudah. Objek Date JavaScript adalah salah satunya. Ini menawarkan metode yang mudah untuk mendapatkan tanggal dan waktu saat ini, menyimpan tanggal dalam variabel, melakukan aritmatika tanggal, dan memformat tanggal berdasarkan lokasi pengguna.

Karena perbedaan antara implementasi browser dan penanganan Daylight Savings Time (DST) yang salah, tidak disarankan bergantung pada objek Date untuk aplikasi penting dan Anda mungkin harus menggunakan pustaka DateTime seperti Luxon, date-fns, atau dayjs. (Apa pun yang Anda gunakan, hindari Moment.js yang dulu populer—sering hanya disebut moment , seperti yang muncul dalam kode—karena sekarang sudah tidak digunakan lagi.)

Tetapi untuk tujuan pendidikan, kita akan menggunakan metode yang disediakan oleh objek Date() untuk mempelajari bagaimana JavaScript menangani DateTime.

Mendapatkan Tanggal Saat Ini

 const currentDate = new Date();

Jika Anda tidak meneruskan apa pun ke konstruktor Tanggal, objek tanggal yang dikembalikan berisi tanggal dan waktu saat ini.

Anda kemudian dapat memformatnya untuk mengekstrak hanya bagian tanggal sebagai berikut:

 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"

Catatan: Jebakan “Januari adalah 0” umum terjadi tetapi tidak universal. Sebaiknya periksa ulang dokumentasi bahasa apa pun (atau format konfigurasi: misalnya, cron terutama berbasis 1) sebelum Anda mulai menggunakannya.

Mendapatkan Stempel Waktu Saat Ini

Jika Anda ingin mendapatkan stempel waktu saat ini, Anda dapat membuat objek Date baru dan menggunakan metode getTime().

 const currentDate = new Date(); const timestamp = currentDate.getTime();

Dalam JavaScript, cap waktu adalah jumlah milidetik yang telah berlalu sejak 1 Januari 1970.

Jika Anda tidak bermaksud untuk mendukung <IE8, Anda dapat menggunakan Date.now() untuk langsung mendapatkan cap waktu tanpa harus membuat objek Date baru.

Mengurai Tanggal

Mengonversi string ke objek tanggal JavaScript dilakukan dengan cara yang berbeda.

Konstruktor objek Date menerima berbagai format tanggal:

 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");

Perhatikan bahwa Anda tidak perlu memasukkan hari dalam seminggu karena JS dapat menentukan hari dalam seminggu untuk tanggal apa pun.

Anda juga dapat memasukkan tahun, bulan, hari, jam, menit, dan detik sebagai argumen terpisah:

 const date = new Date(2016, 6, 27, 13, 30, 0);

Tentu saja, Anda selalu dapat menggunakan format tanggal ISO:

 const date = new Date("2016-07-27T07:45:00Z");

Namun, Anda dapat mengalami masalah jika Anda tidak memberikan zona waktu secara eksplisit!

 const date1 = new Date("25 July 2016"); const date2 = new Date("July 25, 2016");

Salah satu dari ini akan memberi Anda 25 Juli 2016 00:00:00 waktu setempat.

Jika Anda menggunakan format ISO, bahkan jika Anda hanya memberikan tanggal dan bukan waktu dan zona waktu, itu akan secara otomatis menerima zona waktu sebagai UTC.

Ini berarti bahwa:

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

Memformat Tanggal

Untungnya, JavaScript modern memiliki beberapa fungsi internasionalisasi yang nyaman yang dibangun ke dalam namespace Intl standar yang membuat pemformatan tanggal menjadi operasi yang mudah.

Untuk ini, kita memerlukan dua objek: a Date dan Intl.DateTimeFormat , yang diinisialisasi dengan preferensi output kita. Misalkan kita ingin menggunakan format Amerika (M/D/YYYY), ini akan terlihat seperti:

 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

Jika sebaliknya kami menginginkan format Belanda (D/M/YYYY), kami hanya akan meneruskan kode budaya yang berbeda ke konstruktor DateTimeFormat :

 const nlBEFormatter = new Intl.DateTimeFormat('nl-BE'); console.log(nlBEFormatter.format(firstValentineOfTheDecade)); // 14/2/2020

Atau format Amerika yang lebih panjang, dengan nama bulan yang dieja:

 const longEnUSFormatter = new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric', }); console.log(longEnUSFormatter.format(firstValentineOfTheDecade)); // February 14, 2020

Sekarang, jika kita menginginkan format ordinal yang tepat pada hari dalam bulan—yaitu, “14”, bukan hanya “14”—sayangnya ini membutuhkan sedikit solusi, karena satu-satunya nilai day yang valid pada tulisan ini adalah "numeric" atau "2-digit" . Meminjam kode Mathias Bynens versi Flavio Copes untuk memanfaatkan bagian lain dari Intl untuk ini, kita dapat menyesuaikan keluaran hari dalam sebulan melalui formatToParts() :

 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

Sayangnya, formatToParts tidak didukung oleh Internet Explorer (IE) sama sekali pada saat tulisan ini dibuat, tetapi semua teknologi desktop, seluler, dan back-end (yaitu Node.js) memiliki dukungan. Bagi mereka yang perlu mendukung IE dan benar-benar membutuhkan ordinal, sidenote di bawah ini (atau lebih baik, perpustakaan tanggal yang tepat) memberikan jawaban.

Jika Anda perlu mendukung browser lama seperti IE sebelum versi 11, pemformatan tanggal dalam JavaScript lebih sulit karena tidak ada fungsi pemformatan tanggal standar seperti strftime di Python atau PHP.

Dalam PHP misalnya, fungsi strftime("Today is %b %d %Y %X", mktime(5,10,0,12,30,99)) memberi Anda Today is Dec 30 1999 05:10:00 .

Anda dapat menggunakan kombinasi huruf yang berbeda yang diawali dengan % untuk mendapatkan tanggal dalam format yang berbeda. (Hati-hati, tidak setiap bahasa memberikan arti yang sama untuk setiap huruf—khususnya, 'M' dan 'm' dapat ditukar dalam hitungan menit dan bulan.)

Jika Anda yakin dengan format yang ingin Anda gunakan, yang terbaik adalah mengekstrak bit individu menggunakan fungsi JavaScript yang kami bahas di atas dan membuat string sendiri.

 var currentDate = new Date (); var date = currentDate.getDate(); var month = currentDate.getMonth(); var year = currentDate.getFullYear();

Kita bisa mendapatkan tanggal dalam format MM/DD/YYYY sebagai

 var monthDateYear = (month+ 1 ) + "/" + date + "/" + year;

Masalah dengan solusi ini adalah dapat memberikan panjang yang tidak konsisten pada tanggal karena beberapa bulan dan hari dalam sebulan adalah satu digit dan lainnya dua digit. Ini bisa menjadi masalah, misalnya, jika Anda menampilkan tanggal di kolom tabel, karena tanggalnya tidak berbaris.

Kita dapat mengatasinya dengan menggunakan fungsi "pad" yang menambahkan 0 di depan.

 function pad ( n ) { return n< 10 ? '0' +n : n; }

Sekarang, kami mendapatkan tanggal yang benar dalam format MM/DD/YYYY menggunakan:

 var mmddyyyy = pad(month + 1 ) + "/" + pad(date) + "/" + year;

Jika kita menginginkan DD-MM-YYYY sebagai gantinya, prosesnya serupa:

 var ddmmyyyy = pad(date) + "-" + pad(month + 1 ) + "-" + year;

Mari kita bertaruh dan mencoba untuk mencetak tanggal dalam format “Tanggal Bulan, Tahun”. Kami akan membutuhkan pemetaan indeks bulan ke nama:

 var monthNames = [ "January" , "February" , "March" , "April" , "May" , "June" , "July" , "August" , "September" , "October" , "November" , "December" ]; var dateWithFullMonthName = monthNames[month] + " " + pad(date) + ", " + year;

Beberapa orang suka menampilkan tanggal sebagai 1 Januari 2013. Tidak masalah, yang kita butuhkan hanyalah ordinal fungsi pembantu yang mengembalikan 1 untuk 1, 12 untuk 12, dan 103 untuk 103, dll., dan sisanya sederhana:

 var ordinalDate = ordinal(date) + " " + monthNames[month] + ", " + year;

Sangat mudah untuk menentukan hari dalam seminggu dari objek tanggal, jadi mari tambahkan itu di:

 var daysOfWeek = [ "Sun" , "Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" ]; ordinalDateWithDayOfWeek = daysOfWeek[currentDate.getDay()] + ", " + ordinalDate;

Poin yang lebih besar di sini adalah, setelah Anda mendapatkan angka yang diekstraksi dari tanggal, pemformatan sebagian besar terkait dengan string.

Mengubah Format Tanggal

Setelah Anda mengetahui cara mengurai tanggal dan memformatnya, mengubah tanggal dari satu format ke format lainnya hanyalah masalah menggabungkan keduanya.

Misalnya, jika Anda memiliki tanggal dalam format 21 Juli 2013 dan ingin mengubah format menjadi 21-07-2013, dapat dilakukan seperti ini:

 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"

Menggunakan Fungsi Lokalisasi Objek Tanggal JavaScript

Metode pemformatan tanggal yang kita bahas di atas seharusnya berfungsi di sebagian besar aplikasi, tetapi jika Anda benar-benar ingin melokalkan pemformatan tanggal, saya sarankan Anda menggunakan metode toLocaleDateString() objek Date :

 const today = new Date().toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric', });

…memberi kita sesuatu seperti 26 Jul 2016 .

Mengubah lokal ke 'en-US' memberikan "26 Juli 2016" sebagai gantinya. Perhatikan bagaimana pemformatan berubah, tetapi opsi tampilan tetap sama—fitur yang sangat berguna. Seperti yang ditunjukkan di bagian sebelumnya, teknik berbasis Intl.DateTimeFormat yang lebih baru bekerja sangat mirip dengan ini, tetapi memungkinkan Anda menggunakan kembali objek formatter sehingga Anda hanya perlu mengatur opsi sekali.

Dengan toLocaleDateString() , merupakan kebiasaan yang baik untuk selalu meneruskan opsi pemformatan, meskipun outputnya terlihat bagus di komputer Anda. Ini dapat melindungi UI agar tidak melanggar lokal yang tidak terduga dengan nama bulan yang sangat panjang atau terlihat canggung karena nama yang pendek.

Jika saya ingin "Juli" sebulan penuh, yang saya lakukan hanyalah mengubah parameter bulan di opsi menjadi "panjang". JavaScript menangani semuanya untuk saya. Untuk en-US, saya sekarang mendapatkan 26 Juli 2016.

Catatan: Jika Anda ingin browser menggunakan lokal pengguna secara otomatis, Anda dapat meneruskan "undefined" sebagai parameter pertama.

Jika Anda ingin menampilkan versi numerik tanggal dan tidak ingin repot dengan MM/DD/YYYY vs. DD/MM/YYYY untuk lokal yang berbeda, saya sarankan solusi sederhana berikut:

 const today = new Date().toLocaleDateString(undefined, { day: 'numeric', month: 'numeric', year: 'numeric', });

Di komputer saya, ini menghasilkan 7/26/2016 . Jika Anda ingin memastikan bahwa bulan dan tanggal memiliki dua digit, cukup ubah opsi:

 const today = new Date().toLocaleDateString(undefined, { day: '2-digit', month: '2-digit', year: 'numeric', });

Ini keluaran 07/26/2016 . Hanya apa yang kami inginkan!

Anda juga dapat menggunakan beberapa fungsi terkait lainnya untuk melokalkan cara waktu dan tanggal ditampilkan:

Kode Keluaran Keterangan
 now.toLocaleTimeString()
"04:21:38" Tampilkan versi lokal hanya waktu
 now.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit', });
"04:21:38" Tampilkan waktu yang dilokalkan berdasarkan opsi yang disediakan
 now.toLocaleString()
"22/7/2016, 04:21:38" Tampilkan tanggal dan waktu untuk lokal pengguna
 now.toLocaleString(undefined, { day: 'numeric', month: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit', });
"22/7/2016, 04:21" Tampilkan tanggal dan waktu yang dilokalkan berdasarkan opsi yang disediakan

Menghitung Tanggal dan Waktu Relatif

Berikut ini contoh penambahan 20 hari ke Tanggal JavaScript (yaitu, mencari tahu tanggal 20 hari setelah tanggal yang diketahui):

 const myDate = new Date("July 20, 2016 15:00:00"); const nextDayOfMonth = myDate.getDate() + 20; myDate.setDate(nextDayOfMonth); const newDate = myDate.toLocaleString();

Objek tanggal asli sekarang mewakili tanggal 20 hari setelah 20 Juli dan newDate berisi string lokal yang mewakili tanggal tersebut. Di browser saya, newDate berisi "8/9/2016, 3:00:00 PM".

Untuk menghitung stempel waktu relatif dengan perbedaan yang lebih tepat daripada seluruh hari, Anda dapat menggunakan Date.getTime() dan Date.setTime() untuk bekerja dengan bilangan bulat yang mewakili jumlah milidetik sejak zaman tertentu—yaitu, 1 Januari 1970. Untuk contoh, jika Anda ingin tahu kapan 17 jam setelah sekarang:

 const msSinceEpoch = (new Date()).getTime(); const seventeenHoursLater = new Date(msSinceEpoch + 17 * 60 * 60 * 1000);

Membandingkan Tanggal

Seperti semua hal lain yang terkait dengan tanggal, membandingkan tanggal memiliki gotcha sendiri.

Pertama, kita perlu membuat objek tanggal. Untungnya, <, >, <=, dan >= semuanya berfungsi. Jadi membandingkan 19 Juli 2014 dan 18 Juli 2014 semudah:

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

Memeriksa kesetaraan lebih sulit, karena dua objek tanggal yang mewakili tanggal yang sama masih merupakan dua objek tanggal yang berbeda dan tidak akan sama. Membandingkan string tanggal adalah ide yang buruk karena, misalnya, "20 Juli 2014" dan "20 Juli 2014" mewakili tanggal yang sama tetapi memiliki representasi string yang berbeda. Cuplikan di bawah ini mengilustrasikan poin pertama:

 const date1 = new Date("June 10, 2003"); const date2 = new Date(date1); const equalOrNot = date1 == date2 ? "equal" : "not equal"; console.log(equalOrNot);

Ini akan menghasilkan not equal .

Kasus khusus ini dapat diperbaiki dengan membandingkan ekuivalen bilangan bulat dari tanggal (cap waktu mereka) sebagai berikut:

 date1.getTime() == date2.getTime()

Saya telah melihat contoh ini di banyak tempat, tetapi saya tidak menyukainya karena Anda biasanya tidak membuat objek tanggal dari objek tanggal lain. Jadi saya merasa bahwa contoh itu penting dari sudut pandang akademis saja. Juga, ini membutuhkan kedua objek Tanggal untuk merujuk ke detik yang sama persis, sedangkan Anda mungkin hanya ingin tahu apakah mereka merujuk ke hari atau jam atau menit yang sama.

Mari kita lihat contoh yang lebih praktis. Anda mencoba membandingkan apakah tanggal lahir yang dimasukkan pengguna sama dengan tanggal keberuntungan yang Anda peroleh dari API.

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

Keduanya mewakili tanggal yang sama tetapi sayangnya pengguna Anda tidak akan mendapatkan jutaan dolar.

Inilah masalahnya: JavaScript selalu menganggap zona waktu sebagai zona waktu yang disediakan browser kecuali ditentukan lain secara eksplisit.

Ini berarti, bagi saya, new Date ("12/20/1989") akan membuat tanggal 1989-12-20T00:00:00+5:45 atau 1989-12-19T18:15:00Z yang tidak sama dengan 1989-12-20T00:00:00Z dalam hal cap waktu.

Tidak mungkin mengubah hanya zona waktu dari objek tanggal yang ada, jadi target kami sekarang adalah membuat objek tanggal baru tetapi dengan UTC, bukan zona waktu lokal.

Kami akan mengabaikan zona waktu pengguna dan menggunakan UTC saat membuat objek tanggal. Ada dua cara untuk melakukannya:

  1. Buat string tanggal berformat ISO dari tanggal input pengguna dan gunakan untuk membuat objek Date. Menggunakan format tanggal ISO yang valid untuk membuat objek Date sambil memperjelas maksud UTC vs lokal.
 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

Ini juga berfungsi jika Anda tidak menentukan waktu karena itu akan default ke tengah malam (yaitu, 00:00:00Z):

 const userEnteredDate = new Date("1989-12-20"); const dateFromAPI = new Date("1989-12-20T00:00:00Z"); const result = userEnteredDate.getTime() == dateFromAPI.getTime(); // true

Ingat: Jika konstruktor tanggal dilewatkan string dalam format tanggal ISO yang benar dari YYYY-MM-DD, ia mengasumsikan UTC secara otomatis.

  1. JavaScript menyediakan fungsi Date.UTC() rapi yang dapat Anda gunakan untuk mendapatkan cap waktu UTC dari suatu tanggal. Kami mengekstrak komponen dari tanggal dan meneruskannya ke fungsi.
 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 ...

Menemukan Perbedaan Antara Dua Tanggal

Skenario umum yang akan Anda temui adalah menemukan perbedaan antara dua tanggal.

Kami membahas dua kasus penggunaan:

Mencari Jumlah Hari Antara Dua Tanggal

Ubah kedua tanggal menjadi stempel waktu UTC, temukan perbedaannya dalam milidetik, dan temukan hari yang setara.

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

Menemukan Usia Pengguna dari Tanggal Lahir

 const birthDateFromAPI = "12/10/1989";

Catatan: Kami memiliki format non-standar. Baca dokumen API untuk menentukan apakah ini berarti 12 Okt atau 10 Des. Ubah ke format ISO yang sesuai.

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

Saya tahu ada cara yang lebih ringkas untuk menulis kode ini, tetapi saya suka menulisnya dengan cara ini karena kejelasan logikanya.

Saran untuk Menghindari Kencan Neraka

Sekarang setelah kita merasa nyaman dengan aritmatika tanggal, kita berada dalam posisi untuk memahami praktik terbaik yang harus diikuti dan alasan untuk mengikutinya.

Mendapatkan DateTime dari Pengguna

Jika Anda mendapatkan tanggal dan waktu dari pengguna, kemungkinan besar Anda mencari DateTime lokal mereka. Kami melihat di bagian aritmatika tanggal bahwa konstruktor Date dapat menerima tanggal dalam beberapa cara berbeda.

Untuk menghilangkan kebingungan, saya selalu menyarankan untuk membuat tanggal menggunakan format new Date(year, month, day, hours, minutes, seconds, milliseconds) baru bahkan jika Anda sudah memiliki tanggal dalam format parsable yang valid. Jika semua pemrogram di tim Anda mengikuti aturan sederhana ini, akan sangat mudah untuk mempertahankan kode dalam jangka panjang karena ini sejelas mungkin dengan konstruktor Date .

Bagian kerennya adalah Anda dapat menggunakan variasi yang memungkinkan Anda menghilangkan salah satu dari empat parameter terakhir jika nilainya nol; yaitu, new Date(2012, 10, 12) sama dengan new Date(2012, 10, 12, 0, 0, 0, 0) karena parameter yang tidak ditentukan default ke nol.

Misalnya, jika Anda menggunakan pemilih tanggal dan waktu yang memberi Anda tanggal 12-12-12 dan waktu 12:30, Anda dapat mengekstrak bagian dan membuat objek Tanggal baru sebagai berikut:

 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]);

Cobalah untuk menghindari membuat tanggal dari string kecuali dalam format tanggal ISO. Gunakan metode Tanggal (tahun, bulan, tanggal, jam, menit, detik, mikrodetik) sebagai gantinya.

Mendapatkan Hanya Tanggal

Jika Anda hanya mendapatkan tanggal, misalnya tanggal lahir pengguna, yang terbaik adalah mengonversi format ke format tanggal ISO yang valid untuk menghilangkan informasi zona waktu apa pun yang dapat menyebabkan tanggal bergeser maju atau mundur saat dikonversi ke UTC. Sebagai contoh:

 const dateFromPicker = "12/20/2012"; const dateParts = dateFromPicker.split("/"); const ISODate = dateParts[2] + "-" + dateParts[0] + "-" + dateParts[1]; const birthDate = new Date(ISODate).toISOString();

Jika Anda lupa, jika Anda membuat objek Date dengan input dalam format tanggal ISO yang valid (YYYY-MM-DD), itu akan default ke UTC alih-alih default ke zona waktu browser.

Menyimpan Tanggal

Selalu simpan DateTime dalam UTC. Selalu kirim string tanggal ISO atau cap waktu ke bagian belakang.

Generasi pemrogram komputer telah menyadari kebenaran sederhana ini setelah pengalaman pahit mencoba menunjukkan waktu lokal yang benar kepada pengguna. Menyimpan waktu lokal di bagian belakang adalah ide yang buruk, lebih baik biarkan browser menangani konversi ke waktu lokal di bagian depan.

Juga, harus jelas bahwa Anda tidak boleh mengirim string DateTime seperti "20 Juli 1989 12:10 PM" ke bagian belakang. Bahkan jika Anda mengirim zona waktu juga, Anda meningkatkan upaya pemrogram lain untuk memahami niat Anda dan mengurai dan menyimpan tanggal dengan benar.

Gunakan metode toISOString() atau toJSON() dari objek Date untuk mengonversi DateTime lokal ke UTC.

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

Menampilkan Tanggal dan Waktu

  1. Dapatkan cap waktu atau tanggal berformat ISO dari REST API.
  2. Buat objek Date .
  3. Gunakan metode toLocaleString() atau toLocaleDateString() dan toLocaleTimeString() atau pustaka tanggal untuk menampilkan waktu lokal.
 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', });

Kapan Anda Harus Menyimpan Waktu Lokal Juga?

“Terkadang penting untuk mengetahui zona waktu di mana suatu peristiwa terjadi, dan mengonversi ke satu zona waktu akan menghapus informasi itu secara permanen.

“Jika Anda melakukan promosi pemasaran dan ingin tahu pelanggan mana yang memesan sekitar waktu makan siang, pesanan yang tampaknya telah dilakukan pada siang GMT tidak terlalu membantu ketika sebenarnya dilakukan saat sarapan di New York.”

Jika Anda menemukan situasi seperti ini, akan lebih bijaksana untuk menghemat waktu setempat juga. Seperti biasa, kami ingin membuat tanggal dalam format ISO, tetapi kami harus menemukan offset zona waktu terlebih dahulu.

Fungsi getTimeZoneOffset() objek Date memberi tahu kita jumlah menit yang ketika ditambahkan ke waktu lokal tertentu memberikan waktu UTC yang setara. Saya sarankan mengonversinya ke (+-) format jj:mm karena membuatnya lebih jelas bahwa ini adalah offset zona waktu.

 const now = new Date(); const tz = now.gettime zoneOffset();

Untuk zona waktu saya +05:45, saya mendapatkan -345, ini bukan hanya tanda yang berlawanan, tetapi angka seperti -345 mungkin benar-benar membingungkan bagi pengembang back-end. Jadi kita ubah ini menjadi +05:45.

 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;

Sekarang kita mendapatkan sisa nilai dan membuat string ISO valid yang mewakili DateTime lokal.

 const localDateTime = now.getFullYear() + "-" + pad(now.getMonth()+1) + "-" + pad(now.getDate()) + "T" + pad(now.getHours()) + ":" + pad(now.getMinutes()) + ":" + pad(now.getSeconds());

Jika mau, Anda bisa membungkus UTC dan tanggal lokal dalam sebuah objek.

 const eventDate = { utc: now.toISOString(), local: localDateTime, tzOffset: tzOffset, }

Sekarang, di bagian belakang, jika Anda ingin mengetahui apakah acara tersebut terjadi sebelum tengah hari waktu setempat, Anda dapat mengurai tanggal dan cukup menggunakan fungsi getHours() .

 const localDateString = eventDate.local; const localDate = new Date(localDateString); if(localDate.getHours() < 12) { console.log("Event happened before noon local time"); }

Kami tidak menggunakan tzOffset di sini, tetapi kami masih menyimpannya karena kami mungkin memerlukannya di masa mendatang untuk keperluan debugging. Anda sebenarnya bisa saja mengirim offset zona waktu dan waktu UTC saja. Tetapi saya juga suka menyimpan waktu lokal karena Anda pada akhirnya harus menyimpan tanggal dalam database dan menyimpan waktu lokal secara terpisah memungkinkan Anda untuk langsung melakukan kueri berdasarkan bidang daripada harus melakukan perhitungan untuk mendapatkan tanggal lokal.

Terkadang, bahkan dengan zona waktu lokal yang disimpan, Anda ingin menampilkan tanggal dalam zona waktu tertentu. Misalnya, waktu untuk acara mungkin lebih masuk akal di zona waktu pengguna saat ini jika mereka virtual, atau di zona waktu tempat mereka akan berlangsung secara fisik, jika tidak. Bagaimanapun, ada baiknya melihat sebelumnya pada solusi yang sudah ada untuk pemformatan dengan nama zona waktu yang eksplisit.

Konfigurasi Server dan Basis Data

Selalu konfigurasikan server dan database Anda untuk menggunakan zona waktu UTC. (Perhatikan bahwa UTC dan GMT bukanlah hal yang sama—GMT, misalnya, mungkin menyiratkan peralihan ke BST selama musim panas, sedangkan UTC tidak akan pernah.)

Kami telah melihat seberapa banyak konversi zona waktu yang menyakitkan, terutama ketika itu tidak disengaja. Selalu mengirim UTC DateTime dan mengonfigurasi server Anda agar berada di zona waktu UTC dapat membuat hidup Anda lebih mudah. Kode back-end Anda akan jauh lebih sederhana dan bersih karena tidak perlu melakukan konversi zona waktu. 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.

Related: Buggy JavaScript Code: The 10 Most Common Mistakes JavaScript Developers Make