【JavaScript】リアルタイム時計の作り方完全ガイド|setInterval・デジタル時計・カウントダウン・タイムゾーン対応まで解説

Webページにリアルタイムで更新される時計を実装したい、カウントダウンタイマーを作りたい――そんな場面で必ず登場するのが setIntervalDate オブジェクトの組み合わせです。

この記事では、1秒ごとに更新されるシンプルな時計の作り方から始まり、デジタル時計のCSSスタイリング、複数タイムゾーン対応、カウントダウンタイマー、タブが非アクティブなときに処理を止めるパフォーマンス最適化まで、実践的な実装パターンを体系的に解説します。

スポンサーリンク

基本実装:1秒ごとに時刻を更新する

まずは最もシンプルな構成を見てみましょう。setInterval で関数を1秒ごとに呼び出し、Date オブジェクトから現在時刻を取得してDOMに書き出します。

HTML
<div id="clock"></div>
基本の時計
function updateClock() {
  const now = new Date();
  document.getElementById('clock').textContent = now.toLocaleTimeString('ja-JP');
}

updateClock();            // 初回即時実行(setInterval は1秒後から始まるため)
setInterval(updateClock, 1000);
ポイント:setInterval は登録後1秒経ってから初めて呼ばれます。updateClock() を先に一度呼んでおかないと、表示が1秒間空白になります。

setInterval の詳しい使い方は【JavaScript】setIntervalの使い方完全解説も参照してください。

日付と時刻を両方表示するパターン

年月日・曜日・時刻を個別に取得して組み合わせることで、自由なフォーマットで表示できます。

HTML
<div id="date-display"></div>
<div id="time-display"></div>
日付・時刻を分離して表示
const DAYS = ['日', '月', '火', '水', '木', '金', '土'];

function updateDateTime() {
  const now = new Date();
  const year  = now.getFullYear();
  const month = String(now.getMonth() + 1).padStart(2, '0');
  const day   = String(now.getDate()).padStart(2, '0');
  const week  = DAYS[now.getDay()];
  const hours = String(now.getHours()).padStart(2, '0');
  const mins  = String(now.getMinutes()).padStart(2, '0');
  const secs  = String(now.getSeconds()).padStart(2, '0');

  document.getElementById('date-display').textContent =
    `${year}年${month}月${day}日(${week})`;
  document.getElementById('time-display').textContent =
    `${hours}:${mins}:${secs}`;
}

updateDateTime();
setInterval(updateDateTime, 1000);

Date オブジェクトの各メソッドの詳細は【JavaScript】日付を取得する方法完全ガイドをご覧ください。

Intl.DateTimeFormat でタイムゾーン・多言語対応

グローバル対応が必要なサービスでは Intl.DateTimeFormat を使うと、タイムゾーン指定や多言語フォーマットが簡単に実現できます。

タイムゾーン付き時計
// 複数都市の時計を同時表示するユーティリティ
const CITY_CLOCKS = [
  { label: '東京',     timeZone: 'Asia/Tokyo' },
  { label: 'ニューヨーク', timeZone: 'America/New_York' },
  { label: 'ロンドン',   timeZone: 'Europe/London' },
];

function formatWithTimezone(date, timeZone, locale = 'ja-JP') {
  return new Intl.DateTimeFormat(locale, {
    timeZone,
    year:   'numeric',
    month:  '2-digit',
    day:    '2-digit',
    hour:   '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false,
  }).format(date);
}

function updateCityClocks() {
  const now = new Date();
  CITY_CLOCKS.forEach(({ label, timeZone }) => {
    const el = document.getElementById(`clock-${timeZone.replace('/', '-')}`);
    if (el) el.textContent = `${label}: ${formatWithTimezone(now, timeZone)}`;
  });
}

updateCityClocks();
setInterval(updateCityClocks, 1000);

Intl APIの詳しい使い方は【JavaScript】Intl APIを使った日付・数値の国際化フォーマットも参考にしてください。

デジタル時計のCSS スタイリング

見た目にこだわったデジタル時計を実装してみましょう。Google Fonts の等幅フォント + CSSだけで本格的なクロックUIが作れます。

HTML(デジタル時計)
<div class="digital-clock">
  <span id="dc-date"></span>
  <span id="dc-time"></span>
</div>
CSS(デジタル時計スタイル)
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&display=swap');

.digital-clock {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  background: #0f172a;
  color: #38bdf8;
  font-family: 'Share Tech Mono', monospace;
  padding: 32px 48px;
  border-radius: 12px;
  box-shadow: 0 0 30px rgba(56, 189, 248, 0.3);
}

#dc-date {
  font-size: 1.2rem;
  opacity: 0.8;
}

#dc-time {
  font-size: 3rem;
  letter-spacing: 0.1em;
  text-shadow: 0 0 10px rgba(56, 189, 248, 0.8);
}
JavaScript(デジタル時計)
const DAYS = ['日', '月', '火', '水', '木', '金', '土'];

function updateDigitalClock() {
  const now = new Date();
  const pad = (n) => String(n).padStart(2, '0');
  const dateStr = `${now.getFullYear()}/${pad(now.getMonth()+1)}/${pad(now.getDate())}(${DAYS[now.getDay()]})`;
  const timeStr = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
  document.getElementById('dc-date').textContent = dateStr;
  document.getElementById('dc-time').textContent = timeStr;
}

updateDigitalClock();
setInterval(updateDigitalClock, 1000);

clearInterval で時計を停止・再開する

スタート/ストップボタンを付けた制御可能な時計の実装例です。setInterval の戻り値(タイマーID)を保持しておくことで、いつでも停止できます。

HTML
<div id="clock-display"></div>
<button id="btn-toggle">停止</button>
停止・再開対応の時計
let timerId = null;

function tick() {
  document.getElementById('clock-display').textContent =
    new Date().toLocaleTimeString('ja-JP');
}

function startClock() {
  if (timerId !== null) return; // 多重起動を防止
  tick();
  timerId = setInterval(tick, 1000);
  document.getElementById('btn-toggle').textContent = '停止';
}

function stopClock() {
  clearInterval(timerId);
  timerId = null;
  document.getElementById('btn-toggle').textContent = 'スタート';
}

document.getElementById('btn-toggle').addEventListener('click', () => {
  timerId !== null ? stopClock() : startClock();
});

startClock(); // 初期起動

カウントダウンタイマーの実装

期日まであと何日・何時間・何分・何秒かを表示するカウントダウンタイマーです。サイトのリリース告知やキャンペーン終了時刻の表示に活用できます。

HTML
<div id="countdown">
  <span id="cd-days"></span>日
  <span id="cd-hours"></span>時間
  <span id="cd-mins"></span>分
  <span id="cd-secs"></span>秒
</div>
カウントダウンタイマー
// 目標日時を設定(例: 2026年12月31日 23:59:59 JST)
const TARGET = new Date('2026-12-31T23:59:59+09:00');

function updateCountdown() {
  const now = new Date();
  const diff = TARGET - now; // ミリ秒差

  if (diff <= 0) {
    clearInterval(countdownTimer);
    document.getElementById('countdown').textContent = '終了しました';
    return;
  }

  const totalSecs = Math.floor(diff / 1000);
  const days  = Math.floor(totalSecs / 86400);
  const hours = Math.floor((totalSecs % 86400) / 3600);
  const mins  = Math.floor((totalSecs % 3600) / 60);
  const secs  = totalSecs % 60;

  const pad = (n) => String(n).padStart(2, '0');
  document.getElementById('cd-days').textContent  = days;
  document.getElementById('cd-hours').textContent = pad(hours);
  document.getElementById('cd-mins').textContent  = pad(mins);
  document.getElementById('cd-secs').textContent  = pad(secs);
}

updateCountdown();
const countdownTimer = setInterval(updateCountdown, 1000);
タイムゾーン指定のコツ:ターゲット日時は +09:00 のようにオフセットを明示するのが安全です。new Date("2026-12-31T23:59:59") のようにオフセットなしで書くと、実行環境のタイムゾーンによって解釈が変わる場合があります。

requestAnimationFrame と setInterval の使い分け

時計の実装には setInterval が一般的ですが、アニメーションを伴う場合は requestAnimationFrame(rAF)も選択肢になります。それぞれの特徴を比較します。

setInterval requestAnimationFrame
更新タイミング 指定間隔(例: 1000ms) ブラウザの描画フレーム(通常60fps)
用途 1秒単位の時計、カウントダウン 滑らかなアニメーション付き時計
タブ非表示時 間隔が不正確になる場合あり 自動的に一時停止(省電力)
停止方法 clearInterval cancelAnimationFrame

秒単位の時計なら setInterval で十分ですが、アナログ時計の針をなめらかに動かしたい場合などは requestAnimationFrame が適しています。

requestAnimationFrame を使った時計
let rafId;

function tickRaf() {
  document.getElementById('clock').textContent =
    new Date().toLocaleTimeString('ja-JP');
  rafId = requestAnimationFrame(tickRaf);
}

rafId = requestAnimationFrame(tickRaf);

// 停止するとき
// cancelAnimationFrame(rafId);

パフォーマンス最適化:タブが非アクティブなときに停止する

時計が常に動き続けると、バックグラウンドタブでも無駄にCPUを使います。visibilitychange イベントを使えば、タブが非表示のときに自動停止・復帰させることができます。

タブ非表示時に停止する実装
let clockTimer = null;

function tick() {
  document.getElementById('clock').textContent =
    new Date().toLocaleTimeString('ja-JP');
}

function startClock() {
  tick();
  clockTimer = setInterval(tick, 1000);
}

function stopClock() {
  clearInterval(clockTimer);
  clockTimer = null;
}

// タブの表示状態が変わったら制御
document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    stopClock(); // タブが非表示になったら停止
  } else {
    startClock(); // タブが表示されたら即座に再開
  }
});

startClock();
補足:requestAnimationFrame はタブが非表示になると自動的に一時停止するため、この処理は不要です。setInterval を使う場合のみ有効です。

アクセシビリティ対応(aria-live)

スクリーンリーダーを使うユーザーに対して、時計の更新を適切に伝えるには aria-live 属性を設定します。ただし、毎秒読み上げられると非常にうるさくなるため、時刻表示は aria-live="off"(または属性なし)にするのが一般的です。

アクセシブルな時計のマークアップ
<!-- 時計は視覚的な補助のため、スクリーンリーダーからは除外 -->
<div id="clock" aria-hidden="true"></div>

<!-- カウントダウン終了など重要な通知のみ読み上げる場合 -->
<div id="countdown-result" aria-live="polite" aria-atomic="true"></div>

完成形サンプル(コピペOK)

これまでの内容をまとめた、日付・時刻・曜日を表示するシンプルな完成サンプルです。

完成形 HTML
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>リアルタイム時計</title>
  <style>
    #clock-widget {
      font-family: monospace;
      text-align: center;
      padding: 24px;
    }
    #clock-date { font-size: 1.2rem; color: #555; }
    #clock-time { font-size: 3rem; font-weight: bold; color: #0ea5e9; }
  </style>
</head>
<body>
  <div id="clock-widget" aria-hidden="true">
    <div id="clock-date"></div>
    <div id="clock-time"></div>
  </div>
  <script>
    const DAYS = ["日","月","火","水","木","金","土"];
    function updateClock() {
      const now = new Date();
      const pad = (n) => String(n).padStart(2, "0");
      document.getElementById("clock-date").textContent =
        `${now.getFullYear()}年${pad(now.getMonth()+1)}月${pad(now.getDate())}日(${DAYS[now.getDay()]})`;
      document.getElementById("clock-time").textContent =
        `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
    }
    updateClock();
    setInterval(updateClock, 1000);
  </script>
</body>
</html>

よくある問題とデバッグ

最初の1秒間だけ空白になる

setInterval は最初の呼び出しを1秒後に行います。setInterval の前に関数を一度直接呼び出すことで解決します。

解決策
// NG: 1秒間だけ空白になる
setInterval(updateClock, 1000);

// OK: 即時実行してから定期実行
updateClock();
setInterval(updateClock, 1000);

秒が飛んだり重複したりする

複数箇所で setInterval を呼んでいると、タイマーが重複して動いてしまいます。setInterval の戻り値を変数に保存し、再実行前に clearInterval してリセットするのが安全です。

多重起動の防止
let timerId = null;

function startClock() {
  clearInterval(timerId); // 既存タイマーを必ずクリア
  timerId = setInterval(updateClock, 1000);
}

setInterval の時刻がズレていく

setInterval(fn, 1000) は正確に1000msごとに実行されるわけではありません。タブの負荷や処理時間によって数ms〜数十ms単位でズレが蓄積します。長時間動かすアプリでズレが問題になる場合は、実行ごとに Date から正確な時刻を読み取るため実用上は問題ありません。時計の表示は常に new Date() から取得するので、ズレは表示には影響しません。

よくある質問

QsetInterval と setTimeout の違いは?
AsetInterval は指定間隔で繰り返し実行します。setTimeout は1回だけ実行します。時計のように繰り返す場合は setInterval、または setTimeout を再帰的に呼ぶ方法もあります。詳しくはsetIntervalの使い方完全解説をご覧ください。
Q別のタイムゾーンの時刻を表示するには?
AIntl.DateTimeFormattimeZone オプションを指定します。例えば timeZone: "America/New_York" とするとニューヨーク時刻が表示されます。タイムゾーン識別子は IANA タイムゾーンデータベースの名称を使います。
Q12時間表示(午前/午後)にするには?
AtoLocaleTimeString("ja-JP", { hour12: true }) で「午前9:05:30」のような12時間表示になります。Intl.DateTimeFormat を使う場合は hour12: true を指定してください。
Qアナログ時計の針はどう動かせますか?
ACSSの transform: rotate() で針の角度を計算して回転させます。秒針なら now.getSeconds() * 6 度、分針なら (now.getMinutes() * 6) + (now.getSeconds() * 0.1) 度が基本計算式です。なめらかな動きには requestAnimationFrame を使うのがおすすめです。
Q時計をページから削除するときにタイマーを止めるには?
ASPA環境でコンポーネントをアンマウントする際は必ず clearInterval(timerId) を呼んでください。タイマーが残ったままだとメモリリークの原因になります。React の場合は useEffect のクリーンアップ関数、Vue 3 の場合は onUnmounted で停止します。

まとめ

JavaScriptでリアルタイム時計を実装する核心は setInterval で1秒ごとに new Date() を呼んでDOMを更新する」ことです。これを押さえれば、あとはフォーマットや見た目・機能を拡張していくだけです。

  • setInterval の前に関数を一度呼んで初回空白を防ぐ
  • clearInterval でタイマーIDを管理して多重起動を防ぐ
  • タイムゾーン対応には Intl.DateTimeFormattimeZone オプション
  • カウントダウンは目標日時との差分(ミリ秒)を計算して分解する
  • visibilitychange イベントでバックグラウンドタブ時のCPU消費を節約

日付まわりのより深い操作(加算・減算・差分計算)については【JavaScript】日時の計算まとめも合わせてご覧ください。