lubridate

Parsing tanggal dan komputasi waktu dengan lubridate.

Daftar isi

Tanggal jarang tersimpan dalam bentuk yang siap dihitung. Berkas ekspor menuliskannya dengan urutan komponen yang berbeda-beda, seperti 2026-06-01 atau 15/08/2026, dan pengurangan dua teks tanggal tidak menghasilkan selisih yang bermakna. Paket lubridate mengurai teks semacam itu menjadi data tanggal sungguhan, lalu menyediakan alat untuk komputasi waktunya. Topik ini membahas pemilihan parser, pengambilan komponen tanggal, perhitungan selisih, penambahan durasi, serta pengurutan catatan berdasarkan tanggal tetap.

Parser tanggal sesuai urutan komponen#

lubridate termasuk inti tidyverse, sehingga ikut termuat saat tidyverse dipanggil. Parser-nya, yaitu fungsi yang mengurai teks tanggal, dinamai menurut urutan komponen yang diharapkan: ymd() untuk tahun-bulan-hari, dmy() untuk hari-bulan-tahun, dan mdy() untuk bulan-hari-tahun. Pemilihan parser menentukan cara teks dibaca, sehingga urutan komponennya harus mengikuti sumber datanya. Ketiganya mengembalikan objek Date bawaan R, yaitu tipe tanggal yang menyimpan jumlah hari sejak 1 Januari 1970.

r
library(tidyverse)

awal <- ymd("2026-06-01")
akhir <- dmy("15/08/2026")
awal
akhir
class(awal)
text Keluaran
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.2.1     ✔ readr     2.2.0
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ ggplot2   4.0.3     ✔ tibble    3.3.1
✔ lubridate 1.9.5     ✔ tidyr     1.3.2
✔ purrr     1.2.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
[1] "2026-06-01"
[1] "2026-08-15"
[1] "Date"

Pemisah di antara komponen bersifat fleksibel, sehingga tanda hubung dan garis miring dibaca sama. Yang menentukan hanyalah urutan komponennya. Teks 14/07/2025 hanya benar apabila diurai dengan dmy(), sedangkan teks 06-01-2026 diurai dengan mdy() agar terbaca sebagai 1 Juni 2026. Apabila urutan teks tidak cocok dengan parser yang dipilih, hasilnya berupa NA disertai peringatan, sehingga keliru memilih parser menghilangkan data tanpa pernah muncul sebagai error. Parser lain seperti ydm(), myd(), dan dym() tersedia untuk urutan komponen yang lebih jarang, sehingga seluruh kombinasi urutan tercakup. Objek Date dari ketiga parser tersebut dapat dibandingkan dan diurutkan langsung karena penyimpanan internalnya berupa bilangan.

r
ymd("2026-06-01")
dmy("14/07/2025")
mdy("06-01-2026")
text Keluaran
[1] "2026-06-01"
[1] "2025-07-14"
[1] "2026-06-01"

Waktu dengan ymd_hms()#

Tanggal yang menyertakan jam, menit, dan detik diurai dengan ymd_hms(). Urutan komponennya menyertakan bagian waktu, dan hasilnya bertipe POSIXct, yaitu penyimpanan tanggal-waktu R yang menghitung jumlah detik sejak 1 Januari 1970. Tanpa argumen tz, parser waktu mengembalikan hasil dalam zona UTC.

r
jadwal <- ymd_hms("2026-06-01 09:30:00")
jadwal
class(jadwal)
as.numeric(ymd("2026-06-01"))
as.numeric(jadwal)
text Keluaran
[1] "2026-06-01 09:30:00 UTC"
[1] "POSIXct" "POSIXt" 
[1] 20605
[1] 1780306200

Perbandingan as.numeric() memperlihatkan perbedaan satuan penyimpanannya. Untuk tanggal dan jam yang sama, Date menyimpan jumlah hari sejak 1 Januari 1970, sedangkan POSIXct menyimpan jumlah detiknya. Karena itu Date tepat untuk tanggal kalender tanpa jam, sedangkan POSIXct digunakan apabila jam turut dianalisis. Parser waktu lainnya mengikuti pola yang sama, yaitu ymd_hm() tanpa detik dan ymd_h() tanpa menit. Selisih dua POSIXct menghasilkan difftime dalam satuan detik, kebalikan dari selisih Date yang berupa hari, sehingga durasi dalam jam atau menit dihitung dengan membagi selisih detiknya. Konversi antara keduanya tetap mungkin dilakukan melalui as_datetime() dan as_date(), tetapi pertimbangan zona waktunya ikut terlibat sehingga hasilnya perlu diperiksa.

Mengambil komponen tanggal#

Komponen tanggal diambil dengan fungsi year(), month(), day(), dan wday(). Keempatnya mengembalikan bilangan, sehingga hasilnya langsung dapat diteruskan ke filter() atau dihitung statistiknya. Fungsi wday() mengembalikan nomor hari dalam seminggu, dengan Minggu sebagai hari pertama secara bawaan, sehingga Senin bernilai 2.

r
year(awal)
month(awal)
day(awal)
wday(awal)
text Keluaran
[1] 2026
[1] 6
[1] 1
[1] 2

Nomor hari itu cukup untuk penghitungan, tetapi laporan biasanya memerlukan nama harinya. Nama hari bawaan R bergantung pada pengaturan bahasa sistem, sehingga lebih andal apabila nama harinya didefinisikan sendiri sebagai vektor, lalu vektor itu diindeks dengan hasil wday(). Komponen berupa bilangan ini paling sering digunakan untuk mengelompokkan data, misalnya menghitung banyaknya catatan per bulan pada data yang lebih besar. Apabila komponen yang terurai perlu dirangkai kembali menjadi tanggal, lubridate menyediakan make_date() dan make_datetime(), meskipun kebutuhan tersebut lebih jarang daripada sekadar mengambil komponennya.

r
nama_hari <- c("Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu")
nama_hari[wday(akhir)]
text Keluaran
[1] "Sabtu"

Selisih antar tanggal#

Selisih dua tanggal Date diperoleh langsung melalui pengurangan, dan hasilnya bertipe difftime dengan satuan hari. Fungsi as.numeric() mengubahnya menjadi angka biasa agar dapat digunakan dalam perhitungan berikutnya. Tipe difftime dapat menampung satuan lain, tetapi hasil pengurangan Date selalu dilaporkan dalam hari.

r
selisih <- akhir - awal
selisih
class(selisih)
as.numeric(selisih)
text Keluaran
Time difference of 75 days
[1] "difftime"
[1] 75

lubridate juga menyediakan interval melalui operator %--%. Interval menyimpan kedua ujung tanggalnya dan menjadi masukan fungsi time_length(), yang menghitung panjangnya dalam satuan yang dipilih. Satu interval yang sama dapat dihitung ulang dalam satuan lain tanpa membentuk ulang intervalnya, sehingga perhitungan hari dan bulan cukup dilakukan sekali. Apabila kedua ujung intervalnya tertukar, time_length() mengembalikan nilai negatif, sehingga urutan ujungnya perlu dijaga.

r
masih <- awal %--% akhir
time_length(masih, "day")
time_length(masih, "month")
text Keluaran
[1] 75
[1] 2.451613

Hitungan bulan pada time_length() mengikuti kalender, sehingga rentang 1 Juni sampai 15 Agustus 2026 menghasilkan 2.451613, yaitu dua bulan penuh ditambah 14 dari 31 hari Agustus. Nilai semacam ini lebih tepat untuk durasi dalam bulan daripada pembagian tetap 30 hari per bulan.

Menambahkan durasi dan %m+%#

Durasi ditambahkan langsung ke tanggal. Fungsi days(), months(), dan years() menghasilkan durasi kalender yang dijumlahkan pada tanggalnya. Penjumlahan bulan memiliki jebakan bila tanggal asalnya berada di akhir bulan, karena 31 Januari ditambah satu bulan mengarah ke 31 Februari yang tidak ada.

r
ymd("2026-01-31") + months(1)
ymd("2026-01-31") %m+% months(1)
ymd("2026-06-01") + days(14)
text Keluaran
[1] NA
[1] "2026-02-28"
[1] "2026-06-15"

Pada keluaran pertama, penjumlahan biasa menghasilkan NA tanpa pesan error. Operator %m+% menangani kasus tersebut dengan menjaga hasil tetap berada dalam bulan yang valid, sehingga 31 Januari 2026 diluncurkan ke 28 Februari 2026. Operator tersebut memiliki pasangan %m-% yang mengurangi bulan dengan aturan peluncuran yang sama, sedangkan durasi lain seperti weeks() dan years() mengikuti pola days() dan months(). Penjumlahan hari seperti days(14) tidak menghadapi masalah ini karena tanggal berikutnya selalu valid.

Contoh: durasi pelatihan dan urutan catatan#

Contoh berikut menggabungkan parser, selisih, dan pengurutan pada data kecil. Empat peserta pelatihan dicatat dengan tanggal mulai dan selesai yang tetap, lalu durasinya dihitung dan catatannya diurutkan berdasarkan tanggal mulai. Fungsi ymd() pada contoh ini menerima vektor teks sekaligus, sehingga seluruh kolom terurai dalam satu panggilan tanpa perulangan.

r
pelatihan <- tibble(
  nama = c("Ali", "Budi", "Citra", "Dewi"),
  mulai = ymd(c("2026-06-01", "2026-06-08", "2026-06-03", "2026-06-15")),
  selesai = ymd(c("2026-06-12", "2026-06-19", "2026-06-21", "2026-06-26"))
)
pelatihan <- pelatihan %>%
  mutate(durasi_hari = as.numeric(selesai - mulai)) %>%
  arrange(mulai)
pelatihan
text Keluaran
# A tibble: 4 × 4
  nama  mulai      selesai    durasi_hari
  <chr> <date>     <date>           <dbl>
1 Ali   2026-06-01 2026-06-12          11
2 Citra 2026-06-03 2026-06-21          18
3 Budi  2026-06-08 2026-06-19          11
4 Dewi  2026-06-15 2026-06-26          11

Kolom mulai dan selesai bertipe <date>, sehingga arrange() mengurutkannya menurut kalender. Kolom durasi_hari lahir dari pengurangan dua kolom Date yang diubah dengan as.numeric(), sehingga bertipe numerik dan dapat langsung dihitung rata-ratanya dengan summarize(). Baris dengan durasi terpanjang dicari dengan slice_max(durasi_hari) apabila data contohnya lebih besar. Pengurutan sebagai teks hanya tepat untuk format tahun-bulan-hari seperti contoh ini dan justru keliru untuk format lain, karena 02/09/2026 akan mendahului 15/08/2026 secara alfabetis meskipun tanggalnya lebih belakang. Apabila ada tanggal yang gagal diurai karena formatnya berbeda, kolomnya akan memuat NA dan justru menunjukkan baris yang perlu diperiksa.

Ringkasan#

  • Parser ymd(), dmy(), dan mdy() dipilih menurut urutan komponen teksnya, dan semuanya mengembalikan objek Date.
  • ymd_hms() mengurai tanggal-waktu menjadi POSIXct, yang bawaannya berada dalam zona UTC bila tz tidak ditetapkan.
  • year(), month(), day(), dan wday() mengembalikan komponen berupa bilangan, dan nama hari paling andal dibangun dari vektor sendiri yang diindeks dengan wday().
  • Pengurangan dua Date menghasilkan difftime berhari, sedangkan %--% membentuk interval yang panjangnya dihitung dengan time_length().
  • Penjumlahan bulan yang jatuh pada tanggal tidak valid menghasilkan NA, dan %m+% meluncurkannya ke akhir bulan yang valid.

Referensi#