Tipe Tanggal dan Waktu

DATE, DATETIME versus TIMESTAMP, dan perilaku zona waktu yang memengaruhi penyimpanan.

Daftar isi

Tipe tanggal dan waktu menyimpan titik waktu atau durasi, dan perilakunya berbeda dalam hal rentang serta penanganan zona waktu. Topik ini membahas DATE, TIME, DATETIME, dan TIMESTAMP, membandingkan DATETIME dengan TIMESTAMP secara langsung, lalu menunjukkan bagaimana zona waktu sesi mengubah nilai yang dibaca dari kolom TIMESTAMP. Semua demonstrasi memakai nilai literal tanggal yang tetap, sehingga keluarannya dapat direproduksi tanpa bergantung pada waktu eksekusi.

Tipe yang tersedia dan rentangnya#

MySQL menyediakan beberapa tipe waktu dengan kebutuhan penyimpanan dan rentang yang berbeda, sebagaimana dirangkum tabel berikut.

TipePenyimpananRentang
DATE3 byte1000-01-01 sampai 9999-12-31
TIME3 byte-838:59:59 sampai 838:59:59
DATETIME8 byte1000-01-01 00:00:00 sampai 9999-12-31 23:59:59
TIMESTAMP4 byte1970-01-01 00:00:01 sampai 2038-01-19 03:14:07 UTC
YEAR1 byte1901 sampai 2155

Rentang TIME yang negatif dan melebihi dua puluh empat jam memang terlihat tidak lazim, tetapi tipe ini memang dimaksudkan untuk durasi dan bukan jam pada hari tertentu. Kolom DATE dan TIME menyimpan bagian tanggal dan bagian jam secara terpisah, seperti pada tabel sesi ujian berikut. Skema yang sama juga membuat tabel pengukuran suhu dengan dua kolom waktu sekaligus.

sql skema-waktu.sql
-- tidak-dijalankan
DROP DATABASE IF EXISTS latihan_tipe;
CREATE DATABASE latihan_tipe;
USE latihan_tipe;

CREATE TABLE sesi_ujian (
  id INT PRIMARY KEY,
  tanggal DATE NOT NULL,
  mulai TIME NOT NULL
);

INSERT INTO sesi_ujian VALUES
  (1, '2026-01-15', '08:00:00'),
  (2, '2026-01-15', '13:30:00');

CREATE TABLE pengukuran (
  id INT PRIMARY KEY,
  dicatat_datetime DATETIME NOT NULL,
  dicatat_timestamp TIMESTAMP NOT NULL,
  suhu DECIMAL(5,2) NOT NULL
);

SET SESSION time_zone = '+07:00';
INSERT INTO pengukuran VALUES
  (1, '2026-01-15 08:00:00', '2026-01-15 08:00:00', 24.50);
bash
# membuat ulang basis data latihan dan tabelnya
mysql -u root < skema-waktu.sql
bash
# menampilkan tanggal dan jam pada kolom terpisah
mysql -t -u root latihan_tipe -e "SELECT id, tanggal, mulai FROM sesi_ujian;"
text Keluaran
+----+------------+----------+
| id | tanggal    | mulai    |
+----+------------+----------+
|  1 | 2026-01-15 | 08:00:00 |
|  2 | 2026-01-15 | 13:30:00 |
+----+------------+----------+

Nilai literal '2026-01-15' dan '08:00:00' masuk tanpa penyesuaian karena DATE dan TIME tidak terikat zona waktu sama sekali. Format masukan yang diterima server cukup luas, tetapi format ISO seperti YYYY-MM-DD dan HH:MM:SS adalah bentuk yang paling aman ditulis pada skrip karena tidak ambigu.

Tipe TIME, DATETIME, dan TIMESTAMP juga menerima presisi fraksi detik melalui deklarasi seperti DATETIME(3) atau TIMESTAMP(6), yang menyimpan tiga sampai enam digit di belakang detik dengan tambahan penyimpanan sesuai presisinya. Tanpa deklarasi tersebut, nilai yang dimasukkan lengkap dengan fraksi detiknya dibulatkan ke detik terdekat. Topik ini memakai bentuk tanpa fraksi detik karena pencatatan sesi ujian dan pengukuran suhu pada latihan tidak membutuhkannya.

DATETIME versus TIMESTAMP#

DATETIME menyimpan tanggal dan jam persis seperti yang ditulis, tanpa makna zona waktu apa pun. TIMESTAMP menyimpan waktunya dalam UTC secara internal, lalu mengonversinya dari dan ke zona waktu sesi setiap kali nilai ditulis atau dibaca. Zona waktu sesi mewarisi zona waktu global yang bernilai SYSTEM, yang berarti mengikuti zona waktu sistem operasi server. Nilai ketiga variabel berikut menunjukkan kondisi bawaan sandbox ini.

bash
# membaca pengaturan zona waktu server
mysql -t -u root -e "SELECT @@global.time_zone AS global_tz, @@session.time_zone AS session_tz, @@system_time_zone AS system_tz;"
text Keluaran
+-----------+------------+-----------+
| global_tz | session_tz | system_tz |
+-----------+------------+-----------+
| SYSTEM    | SYSTEM     | UTC       |
+-----------+------------+-----------+

Bentuk zona waktu yang selalu tersedia adalah offset seperti +07:00 beserta kata SYSTEM, sedangkan nama seperti Asia/Jakarta hanya dapat dipakai setelah tabel zona waktu dimuat ke server. Offset sudah memenuhi kebutuhan demonstrasi berikut, yang membaca baris pengukuran yang sama dari dua sesi dengan zona waktu berbeda.

bash
# membaca baris yang sama dari dua zona waktu sesi berbeda
mysql -t -u root latihan_tipe <<'SQL'
SET SESSION time_zone = '+07:00';
SELECT @@session.time_zone AS zona_aktif, dicatat_datetime, dicatat_timestamp
FROM pengukuran WHERE id = 1;
SET SESSION time_zone = '+00:00';
SELECT @@session.time_zone AS zona_aktif, dicatat_datetime, dicatat_timestamp
FROM pengukuran WHERE id = 1;
SQL
text Keluaran
+------------+---------------------+---------------------+
| zona_aktif | dicatat_datetime    | dicatat_timestamp   |
+------------+---------------------+---------------------+
| +07:00     | 2026-01-15 08:00:00 | 2026-01-15 08:00:00 |
+------------+---------------------+---------------------+
+------------+---------------------+---------------------+
| zona_aktif | dicatat_datetime    | dicatat_timestamp   |
+------------+---------------------+---------------------+
| +00:00     | 2026-01-15 08:00:00 | 2026-01-15 01:00:00 |
+------------+---------------------+---------------------+

Kolom dicatat_datetime tetap menampilkan 2026-01-15 08:00:00 pada kedua sesi, sedangkan kolom dicatat_timestamp menampilkan 08:00:00 pada sesi +07:00 lalu berubah menjadi 01:00:00 pada sesi +00:00. Konversi itu bukan perubahan data, melainkan penggambaran ulang nilai UTC yang tersimpan menurut zona waktu sesi. Akibat praktisnya, TIMESTAMP tepat untuk peristiwa yang harus sejajar antarzona waktu seperti log dan jejak audit, sedangkan DATETIME tepat untuk waktu yang memang dinyatakan pada zona tertentu, misalnya jam buka perpustakaan.

Kesalahan umum#

Kesalahan pada tipe waktu sering baru ketahuan ketika data lintas zona waktu atau tanggal jauh di masa depan diproses. Tiga kesalahan berikut paling sering ditemukan beserta perbaikannya.

  1. Memilih TIMESTAMP untuk tanggal lahir atau tanggal sejarah. Rentang empat byte TIMESTAMP berhenti pada 2038-01-19 03:14:07 UTC, sehingga tanggal yang lebih jauh tidak pernah dapat tersimpan. Percobaan berikut memasukkan nilai 2039 pada kedua kolom waktu sekaligus.
bash
# mencoba memasukkan tanggal melewati rentang TIMESTAMP
mysql -u root latihan_tipe -e "INSERT INTO pengukuran VALUES (2, '2039-01-01 00:00:00', '2039-01-01 00:00:00', 20.00);"
text Keluaran
ERROR 1292 (22007) at line 1: Incorrect datetime value: '2039-01-01 00:00:00' for column 'dicatat_timestamp' at row 1

Error 1292 menunjuk kolom dicatat_timestamp, sedangkan nilai 2039 pada kolom dicatat_datetime tidak bermasalah. Perbaikannya adalah memakai DATETIME untuk tanggal yang melewati rentang tersebut dan menyisakan TIMESTAMP untuk peristiwa yang membutuhkan konversi zona waktu.

  1. Memasukkan tanggal yang tidak ada dalam kalender. Nilai 2026-02-30 berbentuk tanggal yang sah secara teks tetapi tidak ada dalam kalender, dan server menolaknya dalam strict mode.
bash
# mencoba memasukkan tanggal 30 Februari yang tidak ada
mysql -u root latihan_tipe -e "INSERT INTO pengukuran VALUES (3, '2026-02-30 00:00:00', '2026-02-28 00:00:00', 21.00);"
text Keluaran
ERROR 1292 (22007) at line 1: Incorrect datetime value: '2026-02-30 00:00:00' for column 'dicatat_datetime' at row 1

Perbaikannya adalah memvalidasi tanggal pada aplikasi sebelum dikirim, dan pesan error 1292 semacam ini patut diperiksa sungguhan karena menunjuk nilai yang tidak pernah tersimpan.

  1. Menganggap DATETIME ikut dikonversi antarzona waktu. Kolom DATETIME tidak menyimpan informasi zona waktu, sehingga nilai 08:00:00 yang ditulis dari satu zona akan tetap terbaca 08:00:00 dari zona mana pun. Apabila makna zonanya penting, simpan zona atau offsetnya sebagai kolom terpisah, atau gunakan TIMESTAMP agar konversi dilakukan server.

Ringkasan#

  • DATE dan TIME menyimpan tanggal dan durasi tanpa makna zona waktu, sedangkan DATETIME menyimpan jam persis seperti yang ditulis.
  • TIMESTAMP menyimpan UTC secara internal dan dikonversi menurut zona waktu sesi saat ditulis atau dibaca, sehingga nilainya tampak berubah antarsesi.
  • Rentang TIMESTAMP berhenti pada 2038-01-19 03:14:07 UTC, sehingga tanggal di luar itu harus memakai DATETIME.
  • Literal tanggal yang tidak ada seperti 2026-02-30 ditolak server dalam strict mode dengan error 1292.

Referensi#