Tanggal yang tersimpan sebagai DATE bisa dibaca apa adanya, tetapi kebutuhan nyata selalu lebih dari itu: laporan perlu label “Kamis, 15 Januari 2026”, proses pesanan perlu tanggal jatuh tempo tujuh hari ke depan, dan dashboard perlu selisih hari sejak pemesanan. Keluarga fungsi tanggal dan waktu adalah keluarga fungsi terbesar di MySQL, dan di chapter ini kita berlatih anggotanya yang paling sering dipakai. Fokus kita pada fungsi yang deterministik — menerima tanggal literal seperti '2026-01-15' dan mengembalikan hasil yang selalu sama — sementara fungsi waktu saat ini seperti NOW() kita kenal lewat contoh yang berjalan tanpa dibandingkan keluarannya.
Menyiapkan Tabel Latihan #
Kita pakai tabel pesanan berisi tanggal literal Januari sampai Maret 2026. Semua tanggal ditulis dalam format ISO '2026-01-15' — satu-satunya format yang dikenali tanpa ambigu oleh MySQL di seluruh pengaturan regional:
DROP DATABASE IF EXISTS belajar_fungsi;
CREATE DATABASE belajar_fungsi;
USE belajar_fungsi;Output yang Dihasilkan:
Query OK, 0 rows affected, 1 warning (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Database changedCREATE TABLE pesanan (
id INT AUTO_INCREMENT PRIMARY KEY,
pelanggan VARCHAR(40) NOT NULL,
tanggal_pesan DATE NOT NULL
);
INSERT INTO pesanan (pelanggan, tanggal_pesan) VALUES
('Budi', '2026-01-15'),
('Siti', '2026-01-20'),
('Rina', '2026-02-01'),
('Tono', '2026-02-14'),
('Dewi', '2026-03-05'),
('Agus', '2026-03-10');Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 6 rows affected (0.00 sec)
Records: 6 Duplicates: 0 Warnings: 0Kolom tanggal_pesan bertipe DATE, bukan VARCHAR. Keputusan ini yang membuat semua fungsi pada chapter ini bekerja: fungsi tanggal memakai nilai bertipe DATE secara langsung, sedangkan teks harus dikonversi dulu — dan konversi itu akan kita bahas pada chapter Fungsi Konversi Tipe Data.
Membaca Waktu Saat Ini #
MySQL menyediakan CURRENT_DATE() (alias CURDATE()), CURRENT_TIME() (alias CURTIME()), dan NOW() untuk membaca jam dinding server:
-- @check
SELECT CURRENT_DATE() AS hari_ini,
CURRENT_TIME() AS jam_sekarang,
NOW() AS waktu_lengkap;Output yang Dihasilkan:
+------------+--------------+---------------------+
| hari_ini | jam_sekarang | waktu_lengkap |
+------------+--------------+---------------------+
| 2026-10-14 | 09:09:00 | 2026-10-14 09:09:00 |
+------------+--------------+---------------------+
1 row in set (0.00 sec)Nilai di atas adalah potret layar pada salah satu eksekusi — angkanya pasti berbeda ketika kita menjalankan query yang sama beberapa detik kemudian. Karena sifat itu, contoh-contoh berikutnya memakai tanggal literal agar hasilnya dapat dibicarakan dengan pasti. Perlu dicatat pula bahwa NOW() membaca jam server, bukan jam komputer kita; bila aplikasi dan server berada di zona waktu berbeda, hasil NOW() bisa terasa “salah” padahal tidak.
Mengekstrak Bagian Tanggal #
YEAR(), MONTH(), dan DAY() memecah tanggal menjadi komponen angka, HOUR() membaca jam dari nilai waktu, DAYNAME() dan MONTHNAME() mengembalikan nama dalam bahasa Inggris:
SELECT YEAR('2026-01-15') AS tahun,
MONTH('2026-01-15') AS bulan,
DAY('2026-01-15') AS tanggal,
HOUR('13:45:30') AS jam;Output yang Dihasilkan:
+-------+-------+---------+------+
| tahun | bulan | tanggal | jam |
+-------+-------+---------+------+
| 2026 | 1 | 15 | 13 |
+-------+-------+---------+------+
1 row in set (0.00 sec)Fungsi EXTRACT(unit FROM nilai) menjadi alternatif yang lebih ringkas ketika kita butuh gabungan bagian — misalnya YEAR_MONTH mengembalikan tahun dan bulan sebagai satu angka, seperti 202603 untuk Maret 2026:
SELECT EXTRACT(YEAR_MONTH FROM '2026-03-05') AS tahun_bulan;Output yang Dihasilkan:
+-------------+
| tahun_bulan |
+-------------+
| 202603 |
+-------------+
1 row in set (0.00 sec)Penerapan pada tabel: melihat nama hari dan nama bulan setiap pesanan:
SELECT pelanggan, tanggal_pesan,
DAYNAME(tanggal_pesan) AS nama_hari,
MONTHNAME(tanggal_pesan) AS nama_bulan
FROM pesanan
ORDER BY id;Output yang Dihasilkan:
+-----------+---------------+-----------+------------+
| pelanggan | tanggal_pesan | nama_hari | nama_bulan |
+-----------+---------------+-----------+------------+
| Budi | 2026-01-15 | Thursday | January |
| Siti | 2026-01-20 | Tuesday | January |
| Rina | 2026-02-01 | Sunday | February |
| Tono | 2026-02-14 | Saturday | February |
| Dewi | 2026-03-05 | Thursday | March |
| Agus | 2026-03-10 | Tuesday | March |
+-----------+---------------+-----------+------------+
6 rows in set (0.00 sec)Hasilnya berbahasa Inggris — Thursday, January — karena itulah bawaan server. Kita tidak perlu menerjemahkannya manual di aplikasi; ada pengaturan yang mengurusnya, dan itu topik bagian berikutnya.
Memformat Tanggal dengan DATE_FORMAT #
DATE_FORMAT(tanggal, pola) mengubah tanggal menjadi teks sesuai pola yang kita susun dari penentu format. Penentu yang paling sering dipakai: %Y (tahun 4 digit), %m (bulan 2 digit), %d (tanggal 2 digit), %W (nama hari lengkap), dan %M (nama bulan lengkap):
SELECT DATE_FORMAT('2026-01-15', '%W, %d %M %Y') AS tanggal_lengkap;Output yang Dihasilkan:
+---------------------------+
| tanggal_lengkap |
+---------------------------+
| Thursday, 15 January 2026 |
+---------------------------+
1 row in set (0.00 sec)Hasilnya Thursday, 15 January 2026. Untuk mendapatkannya dalam bahasa Indonesia, kita cukup mengganti variabel sesi lc_time_names — MySQL ternyata membawa locale id_ID di dalam pemasangannya:
SET SESSION lc_time_names = 'id_ID';
SELECT DATE_FORMAT('2026-01-15', '%W, %d %M %Y') AS tanggal_indonesia;Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
+------------------------+
| tanggal_indonesia |
+------------------------+
| Kamis, 15 Januari 2026 |
+------------------------+
1 row in set (0.00 sec)Kini hasilnya Kamis, 15 Januari 2026. Dua statement di atas sengaja ditempatkan dalam satu blok yang sama karena SET SESSION hanya berlaku pada sesi yang menjalankannya — ketika sesi berakhir, pengaturan kembali ke bawaan en_US. Fungsi DAYNAME dan MONTHNAME juga ikut berubah mengikuti lc_time_names, sehingga kombinasi DAYNAME + lc_time_names menjadi cara ringkas menampilkan hari dalam bahasa Indonesia.
Menghitung Selisih Waktu #
DATEDIFF(akhir, awal) menghitung selisih dua tanggal dalam satuan hari, dengan mengabaikan komponen jam. TIMESTAMPDIFF(unit, awal, akhir) lebih umum: satuan bisa YEAR, MONTH, DAY, HOUR, sampai MINUTE:
SELECT DATEDIFF('2026-03-01', '2026-01-15') AS selisih_hari,
TIMESTAMPDIFF(MONTH, '2026-01-15', '2026-03-01') AS selisih_bulan,
TIMESTAMPDIFF(HOUR, '2026-01-15 08:00:00', '2026-01-15 13:30:00') AS selisih_jam;Output yang Dihasilkan:
+--------------+---------------+-------------+
| selisih_hari | selisih_bulan | selisih_jam |
+--------------+---------------+-------------+
| 45 | 1 | 5 |
+--------------+---------------+-------------+
1 row in set (0.00 sec)Perhatikan urutan argumennya yang tidak simetris: DATEDIFF menghitung argumen pertama dikurangi argumen kedua, sedangkan TIMESTAMPDIFF menghitung argumen kedua dikurangi argumen pertama. Selisih 45 hari dan 1 bulan penuh pada contoh di atas sama-sama benar meski angkanya berbeda — TIMESTAMPDIFF(MONTH, ...) menghitung jumlah batas bulan yang terlewati. Penerapan pada tabel, misalnya menghitung sisa hari menuju akhir tahun:
SELECT pelanggan, tanggal_pesan,
DATEDIFF('2026-12-31', tanggal_pesan) AS sisa_hari_tahun
FROM pesanan
ORDER BY id;Output yang Dihasilkan:
+-----------+---------------+-----------------+
| pelanggan | tanggal_pesan | sisa_hari_tahun |
+-----------+---------------+-----------------+
| Budi | 2026-01-15 | 350 |
| Siti | 2026-01-20 | 345 |
| Rina | 2026-02-01 | 333 |
| Tono | 2026-02-14 | 320 |
| Dewi | 2026-03-05 | 301 |
| Agus | 2026-03-10 | 296 |
+-----------+---------------+-----------------+
6 rows in set (0.00 sec)Menambah dan Mengurangi Waktu dengan INTERVAL #
DATE_ADD(tanggal, INTERVAL n satuan) menggeser tanggal ke depan dan DATE_SUB ke belakang. Bentuk intervalnya kaya: DAY, WEEK, MONTH, YEAR, HOUR, MINUTE, sampai kombinasi seperti MINUTE_SECOND:
SELECT DATE_ADD('2026-01-15', INTERVAL 1 MONTH) AS tambah_bulan,
DATE_ADD('2026-01-15', INTERVAL 45 DAY) AS tambah_hari,
DATE_SUB('2026-01-15', INTERVAL 2 WEEK) AS kurang_dua_minggu;Output yang Dihasilkan:
+--------------+-------------+-------------------+
| tambah_bulan | tambah_hari | kurang_dua_minggu |
+--------------+-------------+-------------------+
| 2026-02-15 | 2026-03-01 | 2026-01-01 |
+--------------+-------------+-------------------+
1 row in set (0.00 sec)Penerapan yang sering kita jumpai di aplikasi nyata adalah menghitung tanggal jatuh tempo — di sini pesanan harus dikirim paling lambat tujuh hari setelah dipesan:
SELECT pelanggan, tanggal_pesan,
DATE_ADD(tanggal_pesan, INTERVAL 7 DAY) AS batas_kirim
FROM pesanan
ORDER BY id;Output yang Dihasilkan:
+-----------+---------------+-------------+
| pelanggan | tanggal_pesan | batas_kirim |
+-----------+---------------+-------------+
| Budi | 2026-01-15 | 2026-01-22 |
| Siti | 2026-01-20 | 2026-01-27 |
| Rina | 2026-02-01 | 2026-02-08 |
| Tono | 2026-02-14 | 2026-02-21 |
| Dewi | 2026-03-05 | 2026-03-12 |
| Agus | 2026-03-10 | 2026-03-17 |
+-----------+---------------+-------------+
6 rows in set (0.00 sec)Semua hasil berupa nilai DATE yang sah — DATE_ADD sudah menghitung pergantian bulan dan tahun dengan benar, sesuatu yang rawan salah bila dihitung manual dengan operasi angka biasa. Selisih dan pergeseran waktu seperti ini juga landasan bagi klausa WHERE tanggal_pesan >= DATE_SUB(...) untuk memfilter rentang waktu, yang akan kita latih di section Filtering dan Sorting.
Membersihkan Database Latihan #
DROP DATABASE belajar_fungsi;Output yang Dihasilkan:
Query OK, 1 row affected (0.00 sec)Kita sudah bisa memformat, membedah, menggeser, dan menghitung jarak antar tanggal. Namun di lapangan data sering datang dengan tipe yang tidak kita harapkan — angka tersimpan sebagai teks hasil impor CSV, atau tanggal yang dikirim aplikasi sebagai string. Chapter berikutnya membahas CAST dan CONVERT: cara mengubah tipe data secara eksplisit di dalam query, termasuk jebakan konversi implisit yang berjalan senyap.