Kolom kategori dan kunci penggabungan kini seragam, tetapi catatan pemeriksaan awal kita masih menyimpan satu baris yang tertunda: kolom tanggal. Sistem kasir lama Toko Kopi Kita mengekspor 15/01/2026, sistem baru menulis 2026-01-16, dan keduanya tersimpan sebagai teks yang tidak bisa diurutkan secara kronologis. Lebih berbahaya lagi, teks tanggal sering kali bisa diurai dengan cara yang keliru tanpa satu pun pesan galat. Pada chapter ini kita mem-parse tanggal dengan pd.to_datetime
memakai format eksplisit, mengurai komponen tahun, bulan, dan hari lewat aksesor .dt, lalu menangani kolom yang memuat dua format sekaligus.
Tanggal Teks dan Jebakan Ambiguitas #
Tanggal tertulis 05/01/2026 bermakna 5 Januari bagi kasir di Indonesia, tetapi 1 Mei bagi pembaca format Amerika. Ketika kita menyerahkan teks ambigu seperti itu pada pd.to_datetime tanpa petunjuk apa pun, pandas harus menebak — dan tebakan bawaannya memprioritaskan bulan di depan. Mari kita lihat betapa senyapnya salah tebak itu.
import pandas as pd
tanggal_teks = pd.Series(["05/01/2026", "06/01/2026", "07/01/2026"])
print(tanggal_teks.tolist())
tanggal = pd.to_datetime(tanggal_teks)
print(tanggal.tolist())Output yang Dihasilkan:
['05/01/2026', '06/01/2026', '07/01/2026']
[Timestamp('2026-05-01 00:00:00'), Timestamp('2026-06-01 00:00:00'), Timestamp('2026-07-01 00:00:00')]Mari kita telaah kejadian di atas:
- Baris 3 (
pd.Series([...])): tiga tanggal yang dimaksudkan kasir adalah 5, 6, dan 7 Januari 2026. Ketiganya ambigu karena angka pertamanya bisa dibaca sebagai bulan maupun hari. - Baris 6 (
pd.to_datetime(tanggal_teks)): tanpa parameter format, pandas memakai gaya bulan-dulu. Teks05/01/2026diurai sebagai 5 Mei,06/01/2026sebagai 5 Juni,07/01/2026sebagai 5 Juli. - Tidak ada galat yang muncul: hasil penguraian keliru, tetapi proses berjalan lancar dan kolom bertipe datetime. Kesalahan semacam ini baru ketahuan ketika laporan menampilkan penjualan bulan Mei pada bulan Januari.
Parsing dengan Format Eksplisit #
Solusi baku untuk penguraian tanggal adalah menyebut formatnya lewat parameter format, ditulis dengan kode persen yang membaca potongan teks. Daftar berikut merangkum kode yang paling sering kita perlukan.
| Kode | Arti | Contoh pada 15 Januari 2026 |
|---|---|---|
%d | hari dalam bulan, dua digit | 15 |
%m | bulan sebagai angka, dua digit | 01 |
%Y | tahun empat digit | 2026 |
%y | tahun dua digit | 26 |
%H dan %M | jam dan menit, dua digit | 08 dan 30 |
%b | nama bulan singkat bahasa Inggris | Jan |
Kolom kasir kita ditulis hari-dulu-bulan-tahun, sehingga formatnya %d/%m/%Y. Format eksplisit ini juga bekerja untuk tanggal yang tadinya ambigu.
import pandas as pd
tanggal_teks = pd.Series(["05/01/2026", "06/01/2026",
"07/01/2026", "15/01/2026"])
tanggal = pd.to_datetime(tanggal_teks, format="%d/%m/%Y")
print(tanggal.tolist())
print("Tipe kolom:", tanggal.dtype)Output yang Dihasilkan:
[Timestamp('2026-01-05 00:00:00'), Timestamp('2026-01-06 00:00:00'), Timestamp('2026-01-07 00:00:00'), Timestamp('2026-01-15 00:00:00')]
Tipe kolom: datetime64[ns]Mari kita telaah hasilnya:
- Baris 6 (
pd.to_datetime(..., format="%d/%m/%Y")): setiap teks diurai persis mengikuti pola hari-bulan-tahun.05/01/2026kini benar terbaca sebagai 5 Januari — ambigu tidak ada lagi karena pola kita yang menentukan. dtypehasil:datetime64[ns]— tipe khusus pandas untuk penanda waktu. Sejak kolom bertipe ini, urutan kronologis, selisih hari, dan penguraian komponen menjadi operasi yang sah.
Sebaliknya, format yang keliru memberi galat yang jelas — dan itu justru baik. Galat eksplisit menghentikan kita sebelum data masuk lebih jauh ke dalam analisis.
import pandas as pd
tanggal_teks = pd.Series(["05/01/2026", "15/01/2026"])
try:
salah = pd.to_datetime(tanggal_teks, format="%m/%d/%Y")
print(salah.tolist())
except ValueError as galat:
print("Galat:", galat)Output yang Dihasilkan:
Galat: time data "15/01/2026" doesn't match format "%m/%d/%Y", at position 1. You might want to try:
- passing `format` if your strings have a consistent format;
- passing `format='ISO8601'` if your strings are all ISO8601 but not necessarily in exactly the same format;
- passing `format='mixed'`, and the format will be inferred for each element individually. You might want to use `dayfirst` alongside this.Mari kita baca galatnya:
- Baris 6 (
format="%m/%d/%Y"): pola bulan-dulu-hari diterapkan pada kolom hari-dulu-bulan. Teks pertama05/01/2026lolos karena polosnya cocok (bulan 05, hari 01), tetapi teks kedua15/01/2026menabrak: tidak ada bulan ke-15. - Pesan
ValueError: pandas menyebut posisi baris yang gagal dan menyodorkan tiga jalan keluar, termasukformat='mixed'untuk kolom yang formatnya berubah-ubah — kasus yang kita tangani pada bagian akhir chapter ini. try/except: pola yang sudah menjadi kebiasaan kita sejak chapter Memperbaiki Tipe Data — tangkap galat, cetak pesannya, lalu putuskan perbaikannya dengan sadar.
Mengurai Komponen Tanggal dengan Aksesor .dt #
Setelah kolom bertipe datetime64, aksesor .dt membuka seluruh komponen tanggal: .dt.year, .dt.month, .dt.day, sampai nama hari. Komponen-komponen ini adalah bahan baku analisis musiman — penjualan per bulan, ramai hari apa, pada jam berapa.
import pandas as pd
penjualan = pd.DataFrame({
"tanggal": ["15/01/2026", "16/01/2026", "17/01/2026", "18/01/2026"],
"terjual": [3, 2, 2, 4],
})
penjualan["tanggal"] = pd.to_datetime(penjualan["tanggal"], format="%d/%m/%Y")
penjualan["tahun"] = penjualan["tanggal"].dt.year
penjualan["bulan"] = penjualan["tanggal"].dt.month
penjualan["hari"] = penjualan["tanggal"].dt.day
penjualan["nama_hari"] = penjualan["tanggal"].dt.day_name()
print(penjualan)
print()
print(penjualan["tanggal"].dt.month_name().tolist())Output yang Dihasilkan:
tanggal terjual tahun bulan hari nama_hari
0 2026-01-15 3 2026 1 15 Thursday
1 2026-01-16 2 2026 1 16 Friday
2 2026-01-17 2 2026 1 17 Saturday
3 2026-01-18 4 2026 1 18 Sunday
['January', 'January', 'January', 'January']Mari kita telaah komponen-komponennya:
- Baris 7 (
pd.to_datetime(..., format=...)): kolom teks berubah menjadidatetime64lebih dulu — aksesor.dthanya bekerja pada kolom bertipe ini; pada kolom teks ia menolak bekerja. - Baris 9-12 (
.dt.year,.dt.month,.dt.day,.dt.day_name()): setiap pemanggilan menghasilkan kolom baru berisi komponen tanggal.day_name()danmonth_name()mengembalikan nama bahasa Inggris —January,Thursday— sesuai konvensi pustaka standar. - Nama berbahasa Inggris: bila laporan membutuhkan
Kamisalih-alihThursday, kita terjemahkan dengan kamus pengganti persis seperti kamuscashketunaipada chapter Menangani Data Tidak Konsisten — pola keputusan yang sama, objek yang berbeda.
Menyeragamkan Bentuk Keluaran dengan strftime #
Arah kebalikan dari parsing adalah pemformatan: mengubah tanggal menjadi teks dengan strftime, misalnya saat tampilan laporan ditentukan pihak lain. Manfaat sekundernya lebih penting: format %Y-%m-%d (baku ISO 8601) mengurutkan benar bahkan sebagai teks — itulah mengapa format ini menjadi pilihan penyimpanan.
import pandas as pd
tanggal_teks = pd.Series(["15/01/2026", "02/02/2026", "20/01/2026"])
print("Urutan sebagai teks :", sorted(tanggal_teks.tolist()))
tanggal = pd.to_datetime(tanggal_teks, format="%d/%m/%Y")
urut = tanggal.sort_values()
print("Urutan sebagai tanggal:", urut.dt.strftime("%d/%m/%Y").tolist())
print("Bentuk baku ISO :", tanggal.dt.strftime("%Y-%m-%d").tolist())Output yang Dihasilkan:
Urutan sebagai teks : ['02/02/2026', '15/01/2026', '20/01/2026']
Urutan sebagai tanggal: ['15/01/2026', '20/01/2026', '02/02/2026']
Bentuk baku ISO : ['2026-01-15', '2026-02-02', '2026-01-20']Mari kita telaah urutannya:
- Baris 4 (
sorted(tanggal_teks.tolist())): sebagai teks,02/02/2026menempati urutan pertama karena karakter0lebih kecil dari1. Urutan kronologis rusak — 2 Februari tampil sebelum 15 Januari. - Baris 7-8 (
sort_values()+strftime): pada kolom datetime,sort_values()mengurutkan menurut waktu yang sesungguhnya.strftime("%d/%m/%Y")kemudian menyusun teks laporan sesuai permintaan, sementara urutan kronologis tetap terjaga. - Baris 9 (
strftime("%Y-%m-%d")): bentuk ISO menaruh tahun di depan sehingga urutan teks sama dengan urutan waktu. Simpan tanggal dalam bentuk ini bila berkas harus dibaca sistem lain.
Dua Format dalam Satu Kolom dan Nilai NaT #
Skenario yang paling sering kita jumpai: migrasi sistem membuat satu kolom memuat dua format. Kolom gabungan di bawah memuat gaya kasir lama (15/01/2026), gaya sistem baru (2026-01-16), dan satu tanggal mustahil (29/02/2026 — 2026 bukan tahun kabisat).
import pandas as pd
tanggal_campur = pd.Series(["15/01/2026", "2026-01-16",
"17/01/2026", "29/02/2026"])
paksa = pd.to_datetime(tanggal_campur, format="%d/%m/%Y", errors="coerce")
print(paksa.tolist())
print()
def ke_tanggal(teks):
for pola in ("%d/%m/%Y", "%Y-%m-%d"):
try:
return pd.to_datetime(teks, format=pola)
except ValueError:
continue
return pd.NaT
hasil = tanggal_campur.apply(ke_tanggal)
print(hasil.tolist())
print("Nilai yang gagal diurai:", hasil.isna().sum())Output yang Dihasilkan:
[Timestamp('2026-01-15 00:00:00'), NaT, Timestamp('2026-01-17 00:00:00'), NaT]
[Timestamp('2026-01-15 00:00:00'), Timestamp('2026-01-16 00:00:00'), Timestamp('2026-01-17 00:00:00'), NaT]
Nilai yang gagal diurai: 1Mari kita telaah dua pendekatan itu:
- Baris 6 (
errors="coerce"): dengan satu format saja, teks2026-01-16yang tidak cocok pola langsung menjadiNaT, begitu pula tanggal mustahil29/02/2026. Pendekatan ini jujur: dua nilai hilang, selanjutnya ditangani sebagai missing values. - Baris 10-16 (
ke_tanggal): fungsi ini mencoba dua pola secara berurutan memakaitry/except— gaya lama lebih dulu, gaya baru menyusul. Teks2026-01-16gagal pada pola pertama, lalu berhasil pada pola kedua; hanya tanggal mustahil yang tetapNaT. - Baris 18 (
apply(ke_tanggal)): fungsi diterapkan pada tiap nilai kolom. Hasilnya tiga tanggal sah terurai dan satuNaT— satu-satunya nilai yang memang tidak bisa diselamatkan format apa pun.
Rangkuman #
- Penguraian tanpa format membuat pandas menebak; untuk teks ambigu, tebakan bawaannya memprioritaskan bulan sehingga
05/01/2026terbaca 5 Mei. pd.to_datetimedenganformat="%d/%m/%Y"mengurai tanggal gaya Indonesia secara eksplisit; format keliru memunculkanValueErroryang jelas.- Aksesor
.dtmengurai komponen —.dt.year,.dt.month,.dt.day,.dt.day_name()— untuk analisis musiman. strftimemenyusun teks tanggal untuk laporan; bentuk%Y-%m-%d(ISO) mengurutkan benar bahkan sebagai teks.- Kolom dua format diurai dengan fungsi percoba dua pola;
errors="coerce"dan tanggal mustahil menghasilkanNaTyang ditangani seperti missing values.
Kolom tanggal kini bertipe datetime64 dan komponennya siap dianalisis. Catatan pemeriksaan awal kita tinggal satu baris yang belum selesai: harga 240000 untuk segelas kopi susu — nilai yang menjulang jauh di atas kawanannya. Nilai seperti itu disebut pencilan, dan menentukan ia salah ketik atau transaksi sah membutuhkan alat deteksi. Kita bawa kasus itu ke chapter berikutnya, Menangani Outlier
.