Tanggal tersimpan di MongoDB sebagai BSON Date — jumlah milidetik sejak 1 Januari 1970, tanpa nama bulan dan tanpa zona waktu. Laporan yang dibaca manusia butuh kebalikannya: “terbit tahun berapa?”, “penjualan per bulan berapa?”, “tampilkan sebagai 2026-12-04”. Operator tanggal di aggregation mengerjakan uraian, format, dan aritmetika tersebut tanpa membawa data ke aplikasi. Chapter ini menguraikan tanggal terbit buku dan mengelompokkan transaksi toko menjadi laporan bulanan.

Peta materi Chapter ini:

  • Operator pengurai: $year, $month, $dayOfMonth, $dayOfWeek, $hour.
  • Konversi dua arah: $dateToString (dengan timezone) dan $dateFromString.
  • Aritmetika tanggal: $dateTrunc, $dateAdd, $dateDiff, lalu pola klasik group per bulan.

Operator Pengurai Tanggal #

Semua operator berikut adalah expression yang menerima tanggal dan mengembalikan angka:

OperatorHasil
$year / $month / $dayOfMonthtahun, bulan (1–12), tanggal (1–31)
$dayOfWeekhari dalam minggu, 1 = Minggu sampai 7 = Sabtu
$hour / $minute / $secondpotongan waktu dalam UTC (atau timezone yang diberikan)

Siapkan data uji — katalog buku plus riwayat transaksi toko:

js
use latihan_agregasi2
db.dropDatabase()
db.buku.insertMany([
  { judul: "Laut Bercerita", penulis: "Leila S. Chudori", harga: 98000, kategori: "fiksi", terjual: 120, tanggal_terbit: ISODate("2017-02-01T00:00:00Z"), tag: ["fiksi", "sastra", "penghargaan"], ulasan: [ { pengulas: "Budi", bintang: 5 }, { pengulas: "Siti", bintang: 4 } ] },
  { judul: "Bumi", penulis: "Tere Liye", harga: 89000, kategori: "fiksi", terjual: 250, tanggal_terbit: ISODate("2014-09-02T00:00:00Z"), tag: ["fiksi", "remaja", "fantasi"], ulasan: [ { pengulas: "Dewi", bintang: 5 }, { pengulas: "Budi", bintang: 4 }, { pengulas: "Siti", bintang: 5 } ] },
  { judul: "Filosofi Teras", penulis: "Henry Manampiring", harga: 89000, kategori: "nonfiksi", terjual: 180, tanggal_terbit: ISODate("2018-07-01T00:00:00Z"), tag: ["filsafat", "pengembangan diri"], ulasan: [ { pengulas: "Siti", bintang: 5 } ] },
  { judul: "Atomic Habits", penulis: "James Clear", harga: 115000, kategori: "nonfiksi", terjual: 310, tanggal_terbit: ISODate("2018-10-16T00:00:00Z"), tag: ["pengembangan diri", "terjemahan"], ulasan: [ { pengulas: "Budi", bintang: 5 }, { pengulas: "Dewi", bintang: 4 } ] },
  { judul: "Rumah Kaca", penulis: "Tere Liye", harga: 72000, kategori: "fiksi", terjual: 120, tanggal_terbit: ISODate("2016-06-15T00:00:00Z"), tag: ["fiksi", "remaja"], ulasan: [ { pengulas: "Dewi", bintang: 4 } ] },
  { judul: "Bumi Manusia", penulis: "Pramoedya A. Toer", harga: 105000, kategori: "sastra", terjual: 150, tanggal_terbit: ISODate("1980-05-25T00:00:00Z"), tag: ["sastra", "sejarah"], ulasan: [ { pengulas: "Budi", bintang: 5 }, { pengulas: "Siti", bintang: 5 }, { pengulas: "Dewi", bintang: 4 } ] },
  { judul: "Hujan Bulan Juni", penulis: "Sapardi Djoko Damono", harga: 65000, kategori: "puisi", terjual: 75, tanggal_terbit: ISODate("1994-06-10T00:00:00Z"), tag: ["puisi", "sastra"] },
  { judul: "Ensiklopedia Sains Populer", penulis: "Tim Sains", harga: 250000, kategori: "referensi", terjual: 40, tanggal_terbit: ISODate("2020-03-05T00:00:00Z"), tag: ["referensi", "sains"], ulasan: [ { pengulas: "Dewi", bintang: 4 } ] }
])
db.transaksi.insertMany([
  { tanggal: ISODate("2026-11-28T10:20:00Z"), buku_judul: "Bumi", jumlah: 2 },
  { tanggal: ISODate("2026-11-29T14:45:00Z"), buku_judul: "Atomic Habits", jumlah: 3 },
  { tanggal: ISODate("2026-11-30T16:10:00Z"), buku_judul: "Filosofi Teras", jumlah: 1 },
  { tanggal: ISODate("2026-12-01T09:15:00Z"), buku_judul: "Bumi", jumlah: 2 },
  { tanggal: ISODate("2026-12-01T11:30:00Z"), buku_judul: "Atomic Habits", jumlah: 1 },
  { tanggal: ISODate("2026-12-01T15:45:00Z"), buku_judul: "Bumi", jumlah: 3 },
  { tanggal: ISODate("2026-12-02T10:05:00Z"), buku_judul: "Laut Bercerita", jumlah: 1 },
  { tanggal: ISODate("2026-12-02T13:20:00Z"), buku_judul: "Filosofi Teras", jumlah: 4 },
  { tanggal: ISODate("2026-12-02T16:40:00Z"), buku_judul: "Bumi", jumlah: 2 },
  { tanggal: ISODate("2026-12-03T09:50:00Z"), buku_judul: "Atomic Habits", jumlah: 2 },
  { tanggal: ISODate("2026-12-03T14:10:00Z"), buku_judul: "Bumi Manusia", jumlah: 1 },
  { tanggal: ISODate("2026-12-03T17:25:00Z"), buku_judul: "Bumi", jumlah: 1 },
  { tanggal: ISODate("2026-12-04T10:30:00Z"), buku_judul: "Rumah Kaca", jumlah: 2 },
  { tanggal: ISODate("2026-12-04T13:55:00Z"), buku_judul: "Bumi", jumlah: 2 },
  { tanggal: ISODate("2026-12-04T18:05:00Z"), buku_judul: "Hujan Bulan Juni", jumlah: 3 }
])

Output yang Dihasilkan:

text
switched to db latihan_agregasi2
{ ok: 1, dropped: 'latihan_agregasi2' }
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId('6ad3a5ec10f2060a56545c1f'),
    '1': ObjectId('6ad3a5ec10f2060a56545c20'),
    '2': ObjectId('6ad3a5ec10f2060a56545c21'),
    '3': ObjectId('6ad3a5ec10f2060a56545c22'),
    '4': ObjectId('6ad3a5ec10f2060a56545c23'),
    '5': ObjectId('6ad3a5ec10f2060a56545c24'),
    '6': ObjectId('6ad3a5ec10f2060a56545c25'),
    '7': ObjectId('6ad3a5ec10f2060a56545c26')
  }
}
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId('6ad3a5ec10f2060a56545c27'),
    '1': ObjectId('6ad3a5ec10f2060a56545c28'),
    '2': ObjectId('6ad3a5ec10f2060a56545c29'),
    '3': ObjectId('6ad3a5ec10f2060a56545c2a'),
    '4': ObjectId('6ad3a5ec10f2060a56545c2b'),
    '5': ObjectId('6ad3a5ec10f2060a56545c2c'),
    '6': ObjectId('6ad3a5ec10f2060a56545c2d'),
    '7': ObjectId('6ad3a5ec10f2060a56545c2e'),
    '8': ObjectId('6ad3a5ec10f2060a56545c2f'),
    '9': ObjectId('6ad3a5ec10f2060a56545c30'),
    '10': ObjectId('6ad3a5ec10f2060a56545c31'),
    '11': ObjectId('6ad3a5ec10f2060a56545c32'),
    '12': ObjectId('6ad3a5ec10f2060a56545c33'),
    '13': ObjectId('6ad3a5ec10f2060a56545c34'),
    '14': ObjectId('6ad3a5ec10f2060a56545c35')
  }
}

Uraikan tanggal_terbit milik Atomic Habits menjadi potongan-potongan angka:

js
db.buku.aggregate([
  { $match: { judul: "Atomic Habits" } },
  { $project: { _id: 0, judul: 1, tanggal_terbit: 1,
      tahun: { $year: "$tanggal_terbit" },
      bulan: { $month: "$tanggal_terbit" },
      tanggal: { $dayOfMonth: "$tanggal_terbit" },
      hariKe: { $dayOfWeek: "$tanggal_terbit" } } }
])

Output yang Dihasilkan:

text
[
  {
    judul: 'Atomic Habits',
    tanggal_terbit: ISODate('2018-10-16T00:00:00.000Z'),
    tahun: 2018,
    bulan: 10,
    tanggal: 16,
    hariKe: 3
  }
]

16 Oktober 2018 terurai menjadi tahun: 2018, bulan: 10, tanggal: 16, dan hariKe: 3 — Selasa, karena $dayOfWeek menghitung Minggu sebagai hari pertama. Operator waktu bekerja dengan cara yang sama; $hour pada transaksi jam 18:05 UTC mengembalikan 18.

$dateToString dan $dateFromString #

Uraian angka cocok untuk mesin; manusia ingin teks terformat. $dateToString menerima format dengan penentu seperti %Y (tahun 4 digit), %m (bulan), %d (tanggal), %H (jam), %M (menit). Opsi timezone menggeser tampilan ke zona waktu pilihan — penting karena BSON Date selalu disimpan UTC:

js
db.transaksi.aggregate([
  { $match: { buku_judul: "Hujan Bulan Juni" } },
  { $project: { _id: 0, buku_judul: 1, tanggal: 1,
      utc: { $dateToString: { format: "%Y-%m-%d %H:%M", date: "$tanggal" } },
      jakarta: { $dateToString: { format: "%Y-%m-%d %H:%M", date: "$tanggal", timezone: "Asia/Jakarta" } } } }
])

Output yang Dihasilkan:

text
[
  {
    tanggal: ISODate('2026-12-04T18:05:00.000Z'),
    buku_judul: 'Hujan Bulan Juni',
    utc: '2026-12-04 18:05',
    jakarta: '2026-12-05 01:05'
  }
]

Satu transaksi yang sama tampil 2026-12-04 18:05 dalam UTC dan 2026-12-05 01:05 dalam waktu Jakarta — tanggalnya bergeser karena pukul 18:05 UTC sudah 01:05 pagi hari berikutnya di Jakarta. Tanpa timezone, angka jam yang dibaca pengguna bisa terasa “salah satu hari”; sebutkan zona waktu secara eksplisit di setiap laporan yang ditampilkan manusia.

Arah sebaliknya, $dateFromString, mengubah teks menjadi BSON Date — berguna saat memuat data dari CSV atau form:

js
db.buku.aggregate([
  { $match: { judul: "Bumi" } },
  { $project: { _id: 0, judul: 1, tanggal_terbit: 1,
      edisiBaru: { $dateFromString: { dateString: "2024-09-02" } } } }
])

Output yang Dihasilkan:

text
[
  {
    judul: 'Bumi',
    tanggal_terbit: ISODate('2014-09-02T00:00:00.000Z'),
    edisiBaru: ISODate('2024-09-02T00:00:00.000Z')
  }
]

Teks "2024-09-02" berubah menjadi ISODate('2024-09-02T00:00:00.000Z') — tepat sepuluh tahun setelah terbitan pertama. Format bawaan mengikuti %Y-%m-%dT%H:%M:%S ala ISO 8601; format lain tinggal diberikan lewat format.

Aritmetika Tanggal: $dateTrunc, $dateAdd, $dateDiff #

Tiga operator ini hadir sejak MongoDB 5.0 dan menggantikan trik milidetik lama. $dateAdd menambah satuan waktu, $dateDiff menghitung selisih, $dateTrunc memotong ke awal satuan:

js
db.buku.aggregate([
  { $match: { judul: "Bumi" } },
  { $project: { _id: 0, judul: 1, tanggal_terbit: 1,
      seratusHariSetelahTerbit: { $dateAdd: { startDate: "$tanggal_terbit", unit: "day", amount: 100 } },
      umurHari: { $dateDiff: { startDate: "$tanggal_terbit", endDate: ISODate("2026-12-05T00:00:00Z"), unit: "day" } },
      awalBulanTerbit: { $dateTrunc: { date: "$tanggal_terbit", unit: "month" } } } }
])

Output yang Dihasilkan:

text
[
  {
    judul: 'Bumi',
    tanggal_terbit: ISODate('2014-09-02T00:00:00.000Z'),
    seratusHariSetelahTerbit: ISODate('2014-12-11T00:00:00.000Z'),
    umurHari: Long('4477'),
    awalBulanTerbit: ISODate('2014-09-01T00:00:00.000Z')
  }
]

Bacaannya: 2 September 2014 ditambah 100 hari jatuh pada 11 Desember 2014; hingga 5 Desember 2026 buku tersebut berumur 4.477 hari; dan tanggal terbitnya dipotong ke awal bulan menjadi 1 September 2014. $dateSubtract adalah kebalikan $dateAdd, sedangkan unit menerima year, quarter, month, week, day, hingga millisecond.

Pola Klasik: Group per Bulan #

Inilah resep yang paling sering dipakai di laporan — kelompokkan transaksi per bulan dengan $dateToString sebagai kunci grup:

js
db.transaksi.aggregate([
  { $group: {
      _id: { $dateToString: { format: "%Y-%m", date: "$tanggal" } },
      totalJumlah: { $sum: "$jumlah" },
      jumlahTransaksi: { $sum: 1 }
  } },
  { $sort: { _id: 1 } }
])

Output yang Dihasilkan:

text
[
  { _id: '2026-11', totalJumlah: 6, jumlahTransaksi: 3 },
  { _id: '2026-12', totalJumlah: 24, jumlahTransaksi: 12 }
]

Tiga transaksi November (6 buku) dan dua belas transaksi Desember (24 buku) terkelompok rapi dengan label 2026-11 dan 2026-12. Alternatif kunci grup adalah { $dateTrunc: { date: "$tanggal", unit: "month" } } yang menghasilkan ISODate('2026-11-01') — pilih $dateToString bila label langsung ditampilkan, $dateTrunc bila hasilnya akan diolah tahap lanjut.

Rangkuman #

  • $year, $month, $dayOfMonth, $dayOfWeek, $hour mengurai BSON Date menjadi angka; $dayOfWeek menghitung 1 = Minggu.
  • $dateToString memformat tanggal ke teks dan mendukung timezone; $dateFromString mengubah teks menjadi tanggal.
  • $dateAdd, $dateSubtract, $dateDiff, dan $dateTrunc (MongoDB 5.0+) menggantikan hitungan milidetik manual.
  • Group per bulan: pakai $dateToString format %Y-%m sebagai _id grup, atau $dateTrunc bila hasilnya diolah lebih lanjut.

Tanggal terurai, angka terhitung. Berikutnya kita masuk ke struktur data yang paling sering diolah setelah angka: array — $filter, $map, $reduce, dan kawan-kawannya di Chapter Operator Array.