JSONYAMify

Home / Blog / Format Tanggal JSON

Format Tanggal di JSON: ISO 8601, Unix Timestamp, dan Timezone

Oleh Andi Putra Ogie · Update: Juli 2026 · 8 menit baca

Bug terkait tanggal adalah salah satu sumber masalah paling sering di aplikasi yang berkomunikasi lewat JSON — mulai dari tanggal yang "mundur satu hari" karena timezone, sampai error parsing karena format tanggal berbeda antara backend dan frontend. Masalahnya berakar dari satu fakta sederhana: JSON tidak punya tipe data date bawaan. Spesifikasi JSON cuma mengenal string, number, boolean, null, object, array — titik. Semua representasi tanggal di JSON sebenarnya cuma string atau number biasa yang "disepakati" formatnya oleh aplikasi.

Kenapa JSON Tidak Punya Tipe Date?

JSON dirancang sederhana dan minimal secara sengaja oleh Douglas Crockford. Menambahkan tipe date akan membuka masalah baru: format apa yang dipakai? Timezone gimana? Kalender apa (Gregorian saja, atau harus mendukung kalender lain)? Daripada membuka kotak pandora ini, JSON membiarkan aplikasi memilih representasi sendiri, biasanya lewat string dengan format yang disepakati. Keputusan desain minimalis ini sebenarnya konsisten dengan filosofi JSON secara keseluruhan — spesifikasinya sengaja dibuat sekecil mungkin supaya mudah diimplementasikan ulang di hampir semua bahasa pemrograman tanpa ambiguitas.

Opsi 1: ISO 8601 (Paling Direkomendasikan)

ISO 8601 adalah standar internasional untuk representasi tanggal dan waktu, dan menjadi konvensi de facto paling umum dipakai di JSON API modern:

{
  "createdAt": "2026-07-15T09:30:00Z",
  "birthDate": "1997-03-22"
}

Beberapa komponen penting dalam format ini:

{
  "eventTime": "2026-07-15T16:30:00+07:00"
}

Kelebihan: universal, bisa langsung di-parse oleh hampir semua bahasa pemrograman modern (new Date() di JavaScript, datetime.fromisoformat() di Python), human-readable, dan urutan alfabetis string-nya sama dengan urutan kronologis (berguna untuk sorting sederhana tanpa parsing).

Opsi 2: Unix Timestamp

Unix timestamp merepresentasikan waktu sebagai jumlah detik (atau milidetik) sejak 1 Januari 1970 00:00:00 UTC:

{
  "createdAt": 1784094600
}

Kelebihan: ringkas (number, bukan string panjang), tidak ambigu soal timezone karena selalu relatif ke UTC, mudah dipakai untuk kalkulasi matematis (selisih waktu tinggal dikurangi).
Kekurangan: tidak human-readable sama sekali — kamu tidak bisa langsung tahu ini tanggal berapa hanya dengan melihat angkanya, harus dikonversi dulu. Juga rawan ambiguitas detik vs milidetik — beberapa sistem (seperti JavaScript Date.now()) pakai milidetik, sementara yang lain (seperti Unix time standar) pakai detik, dan salah asumsi bisa bikin tanggal meleset ribuan tahun.

// JavaScript pakai milidetik
Date.now()  // 1784094600000

// Python time.time() pakai detik (float)
time.time()  // 1784094600.123

Opsi 3: Format Kustom (Sebaiknya Dihindari)

Beberapa sistem lama masih memakai format kustom seperti "15/07/2026" atau "July 15, 2026". Ini sebaiknya dihindari di API baru karena ambigu (DD/MM vs MM/DD) dan sulit di-parse otomatis tanpa tahu locale/format spesifiknya terlebih dahulu. Kalau kamu terpaksa bekerja dengan sistem yang memakai format ini, selalu dokumentasikan formatnya secara eksplisit dan jangan biarkan client menebak.

Masalah Timezone yang Sering Muncul

Salah satu bug paling umum: menyimpan/mengirim tanggal tanpa informasi timezone, sehingga ambigu jam berapa sebenarnya di lokasi berbeda:

// Ambigu — timezone mana?
{ "meetingTime": "2026-07-15T14:00:00" }

// Jelas — eksplisit UTC
{ "meetingTime": "2026-07-15T14:00:00Z" }

// Jelas — eksplisit offset WIB
{ "meetingTime": "2026-07-15T21:00:00+07:00" }

Praktik terbaik: selalu simpan dan kirim waktu dalam UTC di backend/API, lalu konversi ke timezone lokal user hanya di sisi presentasi/UI. Ini menghindari bug klasik seperti "meeting muncul di jam yang salah" ketika user dan server berada di timezone berbeda.

Tanggal Tanpa Waktu (Date-Only)

Untuk data yang memang cuma butuh tanggal tanpa jam spesifik (misalnya tanggal lahir), gunakan format tanggal saja tanpa komponen waktu:

{ "birthDate": "1997-03-22" }

Hindari menambahkan waktu palsu seperti "1997-03-22T00:00:00Z" untuk data yang secara konsep tidak punya waktu, karena ini bisa menimbulkan bug timezone — tanggal lahir yang disimpan sebagai midnight UTC bisa "mundur satu hari" kalau ditampilkan di timezone yang lebih lambat dari UTC.

Durasi dan Interval

Untuk merepresentasikan durasi (bukan titik waktu), ISO 8601 juga punya format khusus:

{ "sessionTimeout": "PT30M" }

PT30M berarti "period time 30 minutes". Format ini kurang umum dipakai dibanding representasi durasi dalam angka detik/menit biasa ("sessionTimeoutSeconds": 1800), tapi berguna kalau kamu butuh presisi kalender seperti "P1M" (1 bulan, yang panjangnya bisa bervariasi 28-31 hari). Untuk kebanyakan API internal yang tidak butuh presisi kalender kompleks, representasi angka biasa dalam detik atau milidetik jauh lebih mudah dipahami dan dikalkulasi ulang di sisi client dibanding parsing string durasi ISO 8601 yang formatnya kurang familiar bagi banyak developer.

Konsistensi Antar Bahasa Pemrograman

Salah satu keuntungan ISO 8601 adalah dukungan native di hampir semua bahasa modern, tapi cara parsing-nya tetap perlu diperhatikan detailnya. JavaScript new Date("2026-07-15T09:30:00Z") bekerja langsung tanpa library tambahan. Python butuh datetime.fromisoformat() (Python 3.11+ sudah mendukung suffix Z secara native, versi lebih lama perlu .replace("Z", "+00:00") dulu). Java punya Instant.parse() dari paket java.time. Meskipun formatnya sama, selalu test parsing di bahasa dan versi runtime spesifik yang kamu pakai, karena ada perbedaan detail dukungan edge case antar versi. Perbedaan ini sering jadi sumber bug tersembunyi ketika tim backend dan mobile memakai bahasa berbeda dan salah satunya ternyata lebih strict soal format yang diterima.

Rekomendasi Praktis

💡 Saat menyiapkan contoh response API dengan field tanggal, pastikan formatnya konsisten di seluruh dokumen. Gunakan JSONYAMify untuk memformat dan memeriksa JSON contoh kamu sebelum dimasukkan ke dokumentasi.
🔧 Rapikan Contoh JSON dengan Field Tanggal di JSONYAMify

Date Formats in JSON: ISO 8601, Unix Timestamps, and Timezones

By Andi Putra Ogie · Updated: July 2026 · 8 min read

Date-related bugs are one of the most common sources of trouble in applications that communicate over JSON — from dates showing "one day behind" due to timezone issues, to parsing errors caused by mismatched date formats between backend and frontend. The root cause is one simple fact: JSON has no built-in date data type. The JSON spec only recognizes strings, numbers, booleans, null, objects, arrays — that's it. Every date representation in JSON is really just a plain string or number whose format the application has "agreed on."

Why Doesn't JSON Have a Date Type?

JSON was deliberately designed to be simple and minimal by Douglas Crockford. Adding a date type would open up new problems: which format? What about timezones? Which calendar (Gregorian only, or does it need to support others)? Rather than open that Pandora's box, JSON lets applications choose their own representation, usually through a string in an agreed-upon format.

Option 1: ISO 8601 (Most Recommended)

ISO 8601 is the international standard for date and time representation, and has become the de facto convention most widely used in modern JSON APIs:

{
  "createdAt": "2026-07-15T09:30:00Z",
  "birthDate": "1997-03-22"
}

A few important components of this format:

{
  "eventTime": "2026-07-15T16:30:00+07:00"
}

Pros: universal, parseable directly by nearly every modern programming language (new Date() in JavaScript, datetime.fromisoformat() in Python), human-readable, and its string alphabetical order matches chronological order (handy for simple sorting without parsing).

Option 2: Unix Timestamp

A Unix timestamp represents time as the number of seconds (or milliseconds) since January 1, 1970 00:00:00 UTC:

{
  "createdAt": 1784094600
}

Pros: compact (a number, not a long string), unambiguous about timezone since it's always relative to UTC, easy to use for math (computing a time difference is just subtraction).
Cons: not human-readable at all — you can't tell what date it is just by looking at the number, it has to be converted first. Also prone to seconds vs milliseconds ambiguity — some systems (like JavaScript's Date.now()) use milliseconds, while others (like standard Unix time) use seconds, and a wrong assumption can throw the date off by thousands of years.

// JavaScript uses milliseconds
Date.now()  // 1784094600000

// Python's time.time() uses seconds (float)
time.time()  // 1784094600.123

Option 3: Custom Formats (Best Avoided)

Some legacy systems still use custom formats like "15/07/2026" or "July 15, 2026". These should be avoided in new APIs since they're ambiguous (DD/MM vs MM/DD) and hard to parse automatically without knowing the specific locale/format in advance. If you're forced to work with a system using such a format, always document it explicitly and never leave the client guessing.

The Timezone Problem That Keeps Coming Up

One of the most common bugs: storing or sending a date without timezone information, making it ambiguous what time it actually is in different locations:

// Ambiguous — which timezone?
{ "meetingTime": "2026-07-15T14:00:00" }

// Clear — explicit UTC
{ "meetingTime": "2026-07-15T14:00:00Z" }

// Clear — explicit offset for Jakarta
{ "meetingTime": "2026-07-15T21:00:00+07:00" }

Best practice: always store and send times in UTC on the backend/API, and only convert to the user's local timezone on the presentation/UI side. This avoids the classic bug of "the meeting shows up at the wrong time" when the user and server are in different timezones.

Date-Only Values

For data that genuinely only needs a date without a specific time (e.g. a birth date), use a date-only format with no time component:

{ "birthDate": "1997-03-22" }

Avoid tacking on a fake time like "1997-03-22T00:00:00Z" for data that conceptually has no time, since this can introduce a timezone bug — a birth date stored as UTC midnight can "shift back a day" when displayed in a timezone behind UTC.

Durations and Intervals

To represent a duration (not a point in time), ISO 8601 also has a dedicated format:

{ "sessionTimeout": "PT30M" }

PT30M means "period time 30 minutes." This format is less commonly used than representing a duration as plain seconds/minutes ("sessionTimeoutSeconds": 1800), but it's useful when you need calendar precision like "P1M" (1 month, whose length can vary from 28-31 days).

Consistency Across Programming Languages

One advantage of ISO 8601 is native support in nearly every modern language, but the parsing details still matter. JavaScript's new Date("2026-07-15T09:30:00Z") works out of the box with no extra library. Python needs datetime.fromisoformat() (Python 3.11+ natively supports the Z suffix; older versions need .replace("Z", "+00:00") first). Java has Instant.parse() from the java.time package. Even though the format is the same, always test parsing in the specific language and runtime version you're using, since edge-case support details can differ between versions.

Practical Recommendation

💡 When preparing sample API responses with date fields, make sure the format stays consistent throughout the document. Use JSONYAMify to format and inspect your sample JSON before adding it to documentation.
🔧 Clean Up Sample JSON With Date Fields on JSONYAMify