Budi di Jakarta mengajukan rapat daring “besok pukul 10:30” kepada Siti di Tokyo. Tanpa zona waktu, angka 10:30 punya dua makna berbeda: Budi bersantai sarapan, Siti sudah mendekati makan siang. Object datetime yang kita buat di Chapter datetime dan timedelta belum bisa membedakan keduanya. Chapter ini memperkenalkan atribut tzinfo — penentu apakah sebuah datetime bersifat naive (ambigu) atau aware (menunjuk momen absolut), lengkap dengan cara mengonversi antar zona dan galat yang muncul bila keduanya dicampur.
Naive versus Aware #
| Aspek | Naive | Aware |
|---|---|---|
Atribut tzinfo | None | berisi aturan zona waktu |
| Makna | ambigu — zona tidak diketahui | satu momen absolut di garis waktu |
| Dibuat dengan | datetime(2025, 6, 15, 10, 30) | ditambah argumen tzinfo=... |
| Contoh kegagalan | “10:30 itu Jakarta atau Tokyo?” | tidak ambigu |
Atribut tzinfo adalah tempat interpreter Python menyimpan aturan zona. Ketiadaan aturan itulah yang membuat object naive:
from datetime import datetime, timezone
naive = datetime(2025, 6, 15, 10, 30)
aware = datetime(2025, 6, 15, 3, 30, tzinfo=timezone.utc)
print(naive.tzinfo)
print(aware.tzinfo)Output yang Dihasilkan:
None
UTCObject naive tidak membawa informasi zona sama sekali, sedangkan object aware menyatakan dirinya berada di zona UTC (Coordinated Universal Time) — acuan universal yang dipakai seluruh dunia. Memanggil datetime.now() tanpa argumen menghasilkan object naive yang mengikuti zona waktu sistem operasi, sehingga nilainya berbeda-beda antar mesin; kebiasaan yang lebih aman adalah memanggil datetime.now(tz=...) seperti yang akan kita praktikkan pada Chapter zoneinfo.
Offset Tetap dengan class timezone #
Class timezone membuat aturan zona sederhana berupa offset tetap dari UTC. Untuk Waktu Indonesia Barat yang tidak mengenal perubahan musim, offset +7 jam sudah mewakili dengan tepat:
from datetime import datetime, timezone, timedelta
wib = timezone(timedelta(hours=7))
rapat_jakarta = datetime(2025, 6, 15, 10, 30, tzinfo=wib)
print(rapat_jakarta.isoformat())
print(rapat_jakarta.utcoffset())Output yang Dihasilkan:
2025-06-15T10:30:00+07:00
7:00:00Mari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 3 (
wib = timezone(timedelta(hours=7))): membuat objecttimezonedengan offset +7 jam dari UTC; konstanta siap pakaitimezone.utcmewakili offset nol. - Baris 4 (
datetime(..., tzinfo=wib)): object aware dibuat dengan menempelkan aturan zonawiblewat argumentzinfo. - Baris 6 (
isoformat()): representasi ISO 8601 dari object aware selalu diakhiri offset, yaitu+07:00. - Baris 7 (
utcoffset()): mengembalikan selisih terhadap UTC sebagai objecttimedelta, dicetak7:00:00.
Momen yang Sama, Angka yang Berbeda #
Nilai terpenting dari object aware bukan tampilannya, melainkan momen absolut yang ditunjuknya. Dua object aware yang menunjuk momen sama dianggap setara meski angka jamnya berbeda:
from datetime import datetime, timezone, timedelta
wib = timezone(timedelta(hours=7))
jakarta = datetime(2025, 6, 15, 10, 30, tzinfo=wib)
utc = datetime(2025, 6, 15, 3, 30, tzinfo=timezone.utc)
print(jakarta == utc)
print(jakarta - utc)Output yang Dihasilkan:
True
0:00:00Saat membandingkan atau mengurangkan dua object aware, interpreter Python masing-masing mengonversinya ke UTC terlebih dahulu — 10:30+07:00 dan 03:30+00:00 adalah momen yang sama persis, terbukti dari hasil True dan selisih nol.
Konversi Zona dengan astimezone #
Method astimezone() mengonversi object aware ke zona lain tanpa mengubah momen absolutnya — hanya tampilan dindingnya yang berganti:
from datetime import datetime, timezone, timedelta
wib = timezone(timedelta(hours=7))
jst = timezone(timedelta(hours=9))
rapat = datetime(2025, 6, 15, 10, 30, tzinfo=wib)
rapat_tokyo = rapat.astimezone(jst)
print(rapat_tokyo.isoformat())Output yang Dihasilkan:
2025-06-15T12:30:00+09:00Rapat pukul 10:30 WIB adalah pukul 12:30 waktu Tokyo — Siti di Tokyo melihat angka yang berbeda untuk rapat yang sama. Alurnya dapat digambarkan begitu:
Diagram di atas memperlihatkan inti kerjanya: zona ditempelkan (tzinfo), momen absolut dipatok lewat UTC, lalu tampilan dinding dihitung ulang untuk zona tujuan. Aritmetika pada object aware juga tetap berada dalam zonanya sendiri:
from datetime import datetime, timezone, timedelta
wib = timezone(timedelta(hours=7))
takeoff = datetime(2025, 6, 15, 23, 30, tzinfo=wib)
mendarat = takeoff + timedelta(hours=2)
print(mendarat.isoformat())Output yang Dihasilkan:
2025-06-16T01:30:00+07:00Penerbangan lepas 23:30 WIB dan mendarat 2 jam kemudian jatuh pada 01:30 tanggal 16 Juni — interpreter Python menggulir tanggal secara otomatis karena offset +07:00 tetap dipertahankan.
Mencampur Naive dan Aware Memicu TypeError #
Interpreter Python menolak keras membandingkan object naive dengan aware. Simpan kode berikut sebagai bandingkan_naive.py, lalu jalankan dengan python3 bandingkan_naive.py:
from datetime import datetime, timezone
lokal = datetime(2025, 6, 15, 10, 30)
dunia = datetime(2025, 6, 15, 3, 30, tzinfo=timezone.utc)
print(lokal < dunia)Output yang Dihasilkan (dicetak interpreter Python ke stderr):
Traceback (most recent call last):
File "bandingkan_naive.py", line 6, in <module>
print(lokal < dunia)
^^^^^^^^^^^^^
TypeError: can't compare offset-naive and offset-aware datetimesPenolakan ini adalah fitur keselamatan, bukan gangguan. Membandingkan “10:30 zona tidak diketahui” dengan “03:30 UTC” tidak punya jawaban yang pasti, sehingga interpreter Python memilih berhenti daripada menebak. Solusinya adalah konsistensi: seluruh pipeline sebaiknya memakai object aware — dan cara paling praktis membuatnya adalah memakai nama zona resmi lewat module zoneinfo pada Chapter berikutnya.