Kalender Nasional Masehi dengan API Hari Libur - Episode 2

Kalender Nasional Masehi dengan API Hari Libur - Episode 2

Table of contents



Kalender Nasional Masehi dengan API Hari Libur: Dari Kode Sunyi Jadi Fitur Hidup - Ada sesuatu yang menarik dari potongan kode yang sempat tersimpan begitu lama di folder proyek diam, tapi punya potensi besar. Beberapa waktu lalu, kita sudah membahas bagaimana membuat kalender dinamis berbasis PHP dengan navigasi bulan dan tahun yang interaktif. Kini, di versi keduanya, kode itu hidup kembali. Lebih pintar, lebih terhubung, dan bisa bicara langsung dengan dunia luar lewat API Hari Libur Nasional.

Sama seperti proyek sebelumnya, script ini juga lahir dari sebuah kebutuhan nyata tapi tak pernah dipakai oleh klien. Ironis? Mungkin.

Namun, seperti banyak karya digital yang “terlalu dini untuk zamannya”, ia akhirnya menemukan panggungnya di sini — sebagai contoh nyata bagaimana reuse kode bisa membuka peluang baru.



Integrasi API Hari Libur: Kalender yang Selalu Update Otomatis

Di versi terbaru ini, kalender tidak lagi bergantung pada daftar statis hari libur yang diketik manual. Sebaliknya, ia menarik data langsung dari API publik — https://api-harilibur.vercel.app — yang menyajikan informasi hari libur nasional dan daerah di Indonesia dalam format JSON.

Cukup dengan baris sederhana ini:

$apiUrl = "https://api-harilibur.vercel.app/api?month={$month}&year={$year}";
$response = @file_get_contents($apiUrl);

Kalender akan otomatis mengunduh data libur untuk bulan dan tahun yang sedang ditampilkan.

Lalu dengan logika berikut:

if ($holiday['is_national']) {
    $classes[] = 'holiday-national';
} else {
    $content = "<div class='holiday-local'>{$day}</div>";
}

Script mampu membedakan libur nasional dan libur lokal, lengkap dengan tooltip interaktif yang muncul saat pengguna mengarahkan kursor pada tanggal tertentu. Hasilnya? Sebuah kalender modern yang tidak hanya cantik, tapi juga selalu akurat tanpa perlu pembaruan manual.

Kalender Masehi Nasional dengan Hari Libur

Antara Estetika dan Fungsionalitas

Desainnya tetap mempertahankan prinsip dari versi pertama — minimalis, rapi, dan ringan, dengan tipografi halus serta navigasi intuitif. Namun kini, setiap tanggal libur ditandai dengan dua gaya visual yang berbeda:

  • Merah terang untuk libur nasional.
  • Lingkaran tepi merah untuk libur lokal atau keagamaan.

Dan yang paling keren? Saat kursor diarahkan ke tanggal libur, tooltip kecil muncul dengan nama perayaannya — membuat tampilan terasa hidup dan informatif tanpa kesan berlebihan.

Baca Juga: Cara Membuat Numerology Love Calculator dengan PHP

Belajar dari Kode yang Tidak Terpakai

Kalau kamu perhatikan baik-baik, ada sesuatu yang filosofis di sini.

Kadang kode yang tidak digunakan bukan berarti gagal.

Mungkin ia hanya menunggu waktu untuk berkembang menjadi sesuatu yang lebih matang.

Dari sekadar project snippet yang terabaikan, kini berubah jadi alat yang bisa bermanfaat untuk siapa pun yang ingin menampilkan kalender nasional interaktif di website mereka.

Kode Lengkap:

<?php

$year  = isset($_GET['y']) ? (int)$_GET['y'] : date('Y');
$month = isset($_GET['m']) ? (int)$_GET['m'] : date('n');

$bulanIndo = [
  1=>"Januari",2=>"Februari",3=>"Maret",4=>"April",5=>"Mei",6=>"Juni",
  7=>"Juli",8=>"Agustus",9=>"September",10=>"Oktober",11=>"November",12=>"Desember"
];

$prevMonth = $month - 1; $prevYear = $year;
$nextMonth = $month + 1; $nextYear = $year;
if ($prevMonth < 1) { $prevMonth = 12; $prevYear--; }
if ($nextMonth > 12) { $nextMonth = 1; $nextYear++; }

$apiUrl = "https://api-harilibur.vercel.app/api?month={$month}&year={$year}";
$holidays = [];

$response = @file_get_contents($apiUrl);
if ($response !== false) {
    $data = json_decode($response, true);
    if (is_array($data)) {
        foreach ($data as $h) {
            if (!empty($h['holiday_date']) && !empty($h['holiday_name'])) {
                $holidays[$h['holiday_date']] = [
                    'name' => $h['holiday_name'],
                    'is_national' => $h['is_national_holiday']
                ];
            }
        }
    }
}

$daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
$firstDay = date('w', strtotime("$year-$month-1"));
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Kalender <?= $bulanIndo[$month].' '.$year ?></title>
<style>
body{
  font-family: "Segoe UI", Arial, sans-serif;
  background:#fff;
  color:#000;
  text-align:center;
  padding:24px;
}
.header{
  display:flex;
  justify-content:center;
  align-items:center;
  gap:12px;
  margin-bottom:14px;
  flex-wrap:wrap;
}
h2{
  text-transform:capitalize;
  color:#1a4cff;
  margin:0;
  font-size:22px;
}
h2 span{ color:#000; }
.nav-btn{
  background:#f2f2f2;
  border:none;
  border-radius:6px;
  padding:6px 12px;
  font-size:15px;
  cursor:pointer;
  transition:0.2s;
}
.nav-btn:hover{ background:#e0e0e0; }
select{
  padding:5px 8px;
  font-size:15px;
  border-radius:6px;
  border:1px solid #ccc;
}
table.kalender{
  width:100%;
  border-collapse:collapse;
  margin:auto;
  max-width:700px;
}
.kalender th{
  text-transform:lowercase;
  padding:8px;
  font-weight:600;
  font-size:15px;
}
.kalender td{
  width:14.28%;
  height:48px;
  text-align:center;
  vertical-align:middle;
  font-size:16px;
  position:relative;
}
.minggu{ color:#e50000; }
.sabtu{ color:#444; }
.today{
  background:#eaf3ff;
  border-radius:6px;
  font-weight:bold;
}
.holiday-national{
  color:#e50000;
  font-weight:bold;
  cursor:pointer;
}
.holiday-local{
  border:1px solid #e50000;
  border-radius:50%;
  display:inline-block;
  width:30px;
  height:30px;
  line-height:30px;
  font-weight:600;
  cursor:pointer;
}
.tooltip{
  visibility:hidden;
  opacity:0;
  transition:opacity 0.2s ease, transform 0.2s ease;
  background:#1a1a1a;
  color:#fff;
  font-size:12px;
  padding:6px 8px;
  border-radius:6px;
  position:absolute;
  bottom:100%;
  left:50%;
  transform:translateX(-50%) translateY(-6px);
  white-space:nowrap;
  z-index:10;
}
td:hover .tooltip{
  visibility:visible;
  opacity:1;
  transform:translateX(-50%) translateY(-10px);
}
.list-libur{
  margin:20px auto 0;
  border-top:1px solid #ddd;
  max-width:700px;
  text-align:left;
  padding-top:10px;
  font-size:15px;
  line-height:1.6;
}
.list-libur span{
  color:#e50000;
  font-weight:bold;
  display:inline-block;
  width:22px;
  text-align:right;
}
.info{
  margin-top:8px;
  font-size:13px;
  color:#555;
}
</style>
</head>
<body>

<div class="header">
  <form method="get" style="display:flex; align-items:center; gap:8px;">
    <a class="nav-btn" href="?y=<?= $prevYear ?>&m=<?= $prevMonth ?>">«</a>
    <h2><?= strtolower($bulanIndo[$month]) ?> <span><?= $year ?></span></h2>
    <a class="nav-btn" href="?y=<?= $nextYear ?>&m=<?= $nextMonth ?>">»</a>
    <select name="m" onchange="this.form.submit()">
      <?php foreach ($bulanIndo as $i=>$b): ?>
        <option value="<?= $i ?>" <?= $i==$month?'selected':'' ?>><?= $b ?></option>
      <?php endforeach; ?>
    </select>
    <select name="y" onchange="this.form.submit()">
      <?php for($yy=date('Y')-5; $yy<=date('Y')+5; $yy++): ?>
        <option value="<?= $yy ?>" <?= $yy==$year?'selected':'' ?>><?= $yy ?></option>
      <?php endfor; ?>
    </select>
  </form>
</div>

<table class="kalender">
  <tr>
    <th class="minggu">minggu</th>
    <th>senin</th>
    <th>selasa</th>
    <th>rabu</th>
    <th>kamis</th>
    <th>jumat</th>
    <th class="sabtu">sabtu</th>
  </tr>
  <tr>
<?php
$day = 1;
$cell = 0;

for ($i = 0; $i < $firstDay; $i++) { echo "<td></td>"; $cell++; }

while ($day <= $daysInMonth) {
    $dateStr = sprintf('%04d-%02d-%02d', $year, $month, $day);
    $isToday = ($dateStr == date('Y-m-d'));
    $classes = [];
    $content = $day;
    $tooltip = '';

    if ($cell % 7 == 0) $classes[] = 'minggu';
    if ($cell % 7 == 6) $classes[] = 'sabtu';
    if ($isToday) $classes[] = 'today';

    if (isset($holidays[$dateStr])) {
        $holiday = $holidays[$dateStr];
        $tooltip = htmlspecialchars($holiday['name']);
        if ($holiday['is_national']) {
            $classes[] = 'holiday-national';
        } else {
            $content = "<div class='holiday-local'>{$day}</div>";
        }
    }

    $classAttr = $classes ? ' class="'.implode(' ', $classes).'"' : '';
    echo "<td$classAttr>$content";
    if ($tooltip) echo "<div class='tooltip'>$tooltip</div>";
    echo "</td>";

    $day++; $cell++;
    if ($cell % 7 == 0 && $day <= $daysInMonth) echo "</tr><tr>";
}

while ($cell % 7 != 0) { echo "<td></td>"; $cell++; }
?>
  </tr>
</table>

<div class="list-libur">
  <?php if (count($holidays)): ?>
    <?php foreach ($holidays as $tgl => $info): 
      $parts = explode('-', $tgl);
      if ($parts[0] == $year && (int)$parts[1] == $month): ?>
        <div>
          <span><?= (int)$parts[2] ?></span>
          <?= htmlspecialchars($info['name']) ?>
          <?= $info['is_national'] ? '' : ' <em>(lokal)</em>' ?>
        </div>
      <?php endif; endforeach; ?>
  <?php else: ?>
    <div>Tidak ada libur bulan ini.</div>
  <?php endif; ?>
</div>

<div class="info">
  <em>Data libur nasional & daerah dari <a href="https://api-harilibur.vercel.app" target="_blank">api-harilibur.vercel.app</a></em>
</div>

</body>
</html>

Penutup: Dari Eksperimen ke Solusi Nyata

Integrasi API seperti ini bukan sekadar soal teknis, tapi juga soal menyederhanakan hidup developer dan pengguna. Bayangkan punya website yang selalu update hari liburnya secara otomatis tanpa perlu sentuhan setiap bulan itu nilai profesional yang nyata.

Kalau kamu juga punya ide web atau sistem dinamis yang ingin diwujudkan mulai dari kalender pintar, sistem booking, hingga dashboard bisnis — aku bisa bantu bangun dari konsep hingga jadi. Kunjungi Jasa Pembuatan Website Profesional di Bali dan ubah ide digitalmu jadi kenyataan yang fungsional dan elegan.

[DEMO]



Artikel Terkait