Selama ini kita bekerja dengan Timestamp, titik waktu yang presisi hingga pecahan detik. Dua kebutuhan analisis berbeda belum tersentuh: meringkas data dalam rentang utuh seperti “satu bulan” atau “satu kuartal”, dan menyimpan waktu bersama zona waktunya agar data dari berbagai wilayah dapat disandingkan. pandas menyediakan Period dan period_range() untuk kebutuhan pertama, serta pasangan tz_localize() dan tz_convert() untuk kebutuhan kedua. Chapter ini membahas keduanya, termasuk perbedaan alias frekuensi Period yang sering menjebak dan jebakan daylight saving time (DST).
Period: Rentang Waktu Utuh #
pd.Period("2026-01", freq="M") mewakili seluruh bulan Januari 2026, bukan titik 1 Januari pukul 00:00. Objek ini punya awal dan akhir yang bisa diperiksa lewat start_time dan end_time, serta mendukung aritmetika sederhana: bulan + 1 menghasilkan periode Februari tanpa perlu menghitung jumlah hari. Saat meringkas data bulanan atau kuartalan, label periode seperti ini lebih tepat daripada Timestamp, karena maknanya benar-benar satu rentang.
import pandas as pd
bulan = pd.Period("2026-01", freq="M")
print("Satu periode bulanan:", repr(bulan))
print("Awal periode :", bulan.start_time)
print("Akhir periode:", bulan.end_time)
print("Bulan berikutnya :", bulan + 1)
print("Bulan sebelumnya :", bulan - 1)
triwulan = pd.period_range("2026-01", periods=4, freq="Q")
print("\nEmpat kuartal 2026:", list(triwulan))
print("Kuartal pertama diubah ke bulan terakhir:", triwulan[0].asfreq("M", how="end"))
tanggal = pd.date_range("2026-01-01", "2026-01-07", freq="D")
print("\nDatetimeIndex 7 hari dikelompokkan ke periode bulan:")
print(tanggal.to_period("M"))
stempel = pd.period_range("2026-01", periods=3, freq="M").to_timestamp()
print("\nPeriode bulanan kembali ke Timestamp awal bulan:")
print(stempel)
print("\nMencoba alias ME yang salah untuk Period:")
try:
pd.Period("2026-01", freq="ME")
except ValueError as exc:
print(exc)Output yang Dihasilkan:
Satu periode bulanan: Period('2026-01', 'M')
Awal periode : 2026-01-01 00:00:00
Akhir periode: 2026-01-31 23:59:59.999999
Bulan berikutnya : 2026-02
Bulan sebelumnya : 2025-12
Empat kuartal 2026: [Period('2026Q1', 'Q-DEC'), Period('2026Q2', 'Q-DEC'), Period('2026Q3', 'Q-DEC'), Period('2026Q4', 'Q-DEC')]
Kuartal pertama diubah ke bulan terakhir: 2026-03
DatetimeIndex 7 hari dikelompokkan ke periode bulan:
PeriodIndex(['2026-01', '2026-01', '2026-01', '2026-01', '2026-01', '2026-01',
'2026-01'],
dtype='period[M]')
Periode bulanan kembali ke Timestamp awal bulan:
DatetimeIndex(['2026-01-01', '2026-02-01', '2026-03-01'], dtype='datetime64[us]', freq='MS')
Mencoba alias ME yang salah untuk Period:
Invalid frequency: ME. Failed to parse with error message: ValueError("for Period, please use 'M' instead of 'ME'")Mari kita telaah baris-baris kuncinya:
- Baris 3 (
pd.Period): satu periode bulanan dibuat;repr-nyaPeriod('2026-01', 'M')menunjukkan periode dan frekuensinya. - Baris 5-6 (
start_time/end_time): akhir periode adalah2026-01-31 23:59:59.999999, batas terakhir resolusi mikrodetik yang mencakup segenap detik hari itu. - Baris 7-8 (aritmetika): penambahan dan pengurangan bilangan bulat berpindah antarperiode, aman lintas panjang bulan.
- Baris 10-12 (
period_range): empat kuartal dibangun sekaligus denganfreq="Q";asfreq("M", how="end")mengubah kuartal menjadi bulan terakhirnya,2026-03. - Baris 14-16 (
to_period): DatetimeIndex tujuh hari dikelompokkan menjadi PeriodIndex bulanan, semua anggota jatuh ke2026-01. - Baris 18-20 (
to_timestamp): PeriodIndex bulanan dikembalikan menjadi DatetimeIndex yang berisi awal tiap bulan.
Alias Period: M, Bukan ME #
Perhatikan dua baris terakhir skrip: mencoba freq="ME" memunculkan ValueError dengan pesan for Period, please use 'M' instead of 'ME'. Pesan itu menegaskan perbedaan yang penting pada pandas modern. Alias ME dan YE berlaku untuk alat berbasis Timestamp seperti date_range() dan resample(), sedangkan Period tetap memakai M, Y, dan Q — kepanjangan ME (month end) tidak relevan bagi Period karena rentang utuh bulan tidak membedakan awal dan akhir. Bila skrip lama Anda mencampur keduanya, kesalahan ini muncul justru saat berpindah ke Period.
Menambahkan Zona Waktu dengan tz_localize #
Timestamp hasil pembacaan data biasanya naive: menunjukkan jam tanpa menyebut zona. tz_localize() memberi zona tanpa mengubah angka jamnya — 09:00 tetap 09:00, kini dengan tambahan +07:00. Sebaliknya, tz_convert() memindahkan representasi ke zona lain sehingga angka jamnya berubah, tetapi momen waktu yang dimaksud tetap sama. File berikut memakai deret transaksi pagi hari dan menyandingkan tiga zona:
import pandas as pd
stempel = pd.date_range("2026-01-05 09:00", periods=3, freq="D")
transaksi = pd.Series([120, 135, 128], index=stempel, name="penjualan")
jakarta = transaksi.tz_localize("Asia/Jakarta")
print("Naive di-localize ke Asia/Jakarta:")
print(jakarta)
print("\nDikonversi ke UTC:")
print(jakarta.tz_convert("UTC"))
print("\nDikonversi ke Asia/Tokyo:")
print(jakarta.tz_convert("Asia/Tokyo"))
print("\ntz_localize(None) menghapus zona waktu:")
print(jakarta.tz_localize(None))
print("\nWaktu yang tidak ada saat DST musim semi:")
try:
pd.Timestamp("2026-03-08 02:30").tz_localize("America/New_York")
except ValueError as exc:
print(exc)
print("\nWaktu ambigu saat jam DST diputar mundur:")
try:
pd.Timestamp("2026-11-01 01:30").tz_localize("America/New_York")
except ValueError as exc:
print(exc)
aman = pd.Timestamp("2026-03-08 02:30").tz_localize(
"America/New_York", nonexistent="shift_forward"
)
print("\nnonexistent='shift_forward' menggeser ke waktu sah:")
print(aman)Output yang Dihasilkan:
Naive di-localize ke Asia/Jakarta:
2026-01-05 09:00:00+07:00 120
2026-01-06 09:00:00+07:00 135
2026-01-07 09:00:00+07:00 128
Name: penjualan, dtype: int64
Dikonversi ke UTC:
2026-01-05 02:00:00+00:00 120
2026-01-06 02:00:00+00:00 135
2026-01-07 02:00:00+00:00 128
Name: penjualan, dtype: int64
Dikonversi ke Asia/Tokyo:
2026-01-05 11:00:00+09:00 120
2026-01-06 11:00:00+09:00 135
2026-01-07 11:00:00+09:00 128
Name: penjualan, dtype: int64
tz_localize(None) menghapus zona waktu:
2026-01-05 09:00:00 120
2026-01-06 09:00:00 135
2026-01-07 09:00:00 128
Name: penjualan, dtype: int64
Waktu yang tidak ada saat DST musim semi:
2026-03-08 02:30:00 is a nonexistent time due to daylight savings time. Try using the 'nonexistent' argument.
Waktu ambigu saat jam DST diputar mundur:
Cannot infer dst time from 2026-11-01 01:30:00, try using the 'ambiguous' argument
nonexistent='shift_forward' menggeser ke waktu sah:
2026-03-08 03:00:00-04:00Baris 09:00:00+07:00 setelah tz_localize("Asia/Jakarta") menjadi 02:00:00+00:00 setelah tz_convert("UTC") dan 11:00:00+09:00 setelah tz_convert("Asia/Tokyo"); ketiganya adalah momen yang sama di tiga zona. tz_localize(None) kemudian menghapus zona dan mengembalikan index ke bentuk naive. Wajib diingat urutannya: tz_localize untuk naive menjadi aware, tz_convert untuk aware ke aware; memanggil tz_convert pada index naive memunculkan error karena belum ada zonanya.
DST: Waktu Nonexistent dan Ambiguous #
Indonesia tidak menerapkan DST, tetapi data global sering berasal dari wilayah yang menerapkannya, seperti America/New_York. Skrip di atas memperlihatkan dua kegagalannya: 2026-03-08 02:30 di New York tidak pernah ada karena jam dipindah maju ke 03:00 saat musim semi, dan 2026-11-01 01:30 muncul dua kali saat jam diputar mundur di awal November. Tanpa penanganan, keduanya memunculkan ValueError dengan saran argumen yang jelas. Solusinya terlihat pada baris terakhir skrip: nonexistent="shift_forward" menggeser waktu tak sah ke 03:00-04:00, sedangkan untuk waktu ambigu tersedia argumen ambiguous berupa list boolean atau string "NaT" pada Series.tz_localize().
Dokumentasi pandas.Period
dan pandas.period_range
merinci konversi antarfrekuensi periode, sementara pandas.Series.tz_localize
memuat daftar lengkap opsi nonexistent dan ambiguous. Dengan Period dan zona waktu, perangkat waktu kita lengkap. Chapter berikutnya menyibak satu persoalan praktis yang belum kita sentuh: tanggal yang hilang dari deret waktu, dan cara mengisinya dengan asfreq serta interpolasi.