【JavaScript】日時の計算まとめ|タイムスタンプ・UTC 変換・タイムゾーン・date-fns / Day.js・Temporal API まで解説

JavaScript の日時計算は Date オブジェクトが基本ですが、タイムスタンプの扱い、UTC とローカル時刻の変換、タイムゾーン対応など、深く理解しておくべきポイントが多くあります。この記事では Date の加算・減算・差分といった基本操作は関連記事に譲り、タイムスタンプ・UTC・タイムゾーン・ライブラリ活用・Temporal API など一歩進んだ内容を体系的にまとめます。

この記事でわかること
・タイムスタンプ(UNIX エポック)の取得と Date への変換
・UTC とローカル時刻の違い・getUTC 系メソッド
・Intl.DateTimeFormat によるタイムゾーン変換
・date-fns と Day.js の比較と導入方法
・Temporal API の概要と基本操作
・日時計算でよくあるバグの回避方法
スポンサーリンク

タイムスタンプ(UNIX エポック)の取得と変換

タイムスタンプは 1970 年 1 月 1 日 00:00:00 UTC(UNIX エポック)からの経過ミリ秒です。JavaScript のタイムスタンプはミリ秒単位ですが、他の言語や API では秒単位の場合があるため注意が必要です。

タイムスタンプの取得
// 方法1: Date.now()(最も高速)
const ts1 = Date.now();
console.log(ts1); // 1775145600000(ミリ秒)

// 方法2: getTime()
const ts2 = new Date().getTime();

// 方法3: 単項 + 演算子
const ts3 = +new Date();
タイムスタンプから Date に変換
// ミリ秒タイムスタンプ → Date
const date1 = new Date(1775145600000);
console.log(date1.toISOString()); // "2026-03-31T00:00:00.000Z"

// 秒タイムスタンプ(UNIX time)→ Date
const unixTime = 1775145600;
const date2 = new Date(unixTime * 1000); // 1000倍してミリ秒に
console.log(date2.toISOString()); // "2026-03-31T00:00:00.000Z"
環境 タイムスタンプの単位 変換
JavaScript ミリ秒 そのまま new Date(ms)
UNIX / Python / PHP new Date(sec * 1000)
MySQL UNIX_TIMESTAMP new Date(sec * 1000)
API から受け取るタイムスタンプが秒単位かミリ秒単位かを必ず確認してください。桁数で判断できます(秒: 10 桁、ミリ秒: 13 桁)。

UTC とローカル時刻の違い

new Date() で生成される Date オブジェクトは内部的にUTCで値を保持しますが、getHours() などの get 系メソッドはローカルタイムゾーンの値を返します。

JavaScript
const date = new Date("2026-03-31T00:00:00Z"); // UTCの0時

// ローカル(日本: UTC+9)
console.log(date.getHours());     // 9(日本時間の9時)
console.log(date.getDate());      // 31

// UTC
console.log(date.getUTCHours());  // 0(UTCの0時)
console.log(date.getUTCDate());   // 31

get 系と getUTC 系の対応表

ローカル UTC 取得する値
getFullYear() getUTCFullYear()
getMonth() getUTCMonth() 月(0〜11)
getDate() getUTCDate()
getHours() getUTCHours()
getMinutes() getUTCMinutes()
getDay() getUTCDay() 曜日(0〜6)

タイムゾーンオフセットの取得

JavaScript
const offset = new Date().getTimezoneOffset();
console.log(offset); // -540(日本は UTC+9 → -9*60 = -540分)

// 時間単位に変換
console.log(-offset / 60); // 9
getTimezoneOffset()UTC からローカルへの差分を分単位で返すため、日本(UTC+9)では -540(負の値)になります。符号に注意してください。

Intl.DateTimeFormat でタイムゾーンを変換する

Date オブジェクトにはタイムゾーンを変更するメソッドがありません。別のタイムゾーンで表示するには Intl.DateTimeFormattoLocaleStringtimeZone オプションを使います。

JavaScript
const date = new Date();

// 日本時間で表示
console.log(date.toLocaleString("ja-JP", {
  timeZone: "Asia/Tokyo"
}));
// "2026/3/31 9:00:00"

// ニューヨーク時間で表示
console.log(date.toLocaleString("ja-JP", {
  timeZone: "America/New_York"
}));
// "2026/3/30 20:00:00"(EDT: UTC-4)

// ロンドン時間で表示
console.log(date.toLocaleString("ja-JP", {
  timeZone: "Europe/London"
}));
// "2026/3/31 1:00:00"(BST: UTC+1)
タイムゾーン名も表示する
const options = {
  timeZone: "America/New_York",
  year: "numeric", month: "2-digit", day: "2-digit",
  hour: "2-digit", minute: "2-digit",
  timeZoneName: "short"
};

console.log(new Date().toLocaleString("en-US", options));
// "03/30/2026, 08:00 PM EDT"

date-fns と Day.js の比較

標準の Date だけでは複雑な日時計算が冗長になります。モダンな日時ライブラリとして date-fnsDay.js が広く使われています。

比較項目 date-fns Day.js
設計思想 関数型(個別インポート) Moment.js 互換のチェーン型
バンドルサイズ 使う関数だけ(Tree-shaking 対応) コア約 2KB + プラグイン
ミュータブル イミュータブル イミュータブル
TypeScript 型定義内蔵 型定義内蔵
タイムゾーン date-fns-tz で対応 dayjs/plugin/utc + timezone
学習コスト 関数名を覚える必要あり Moment.js 経験者は低い
date-fns の例
import { addDays, format, differenceInDays } from "date-fns";

const today = new Date();

// 7日後
const nextWeek = addDays(today, 7);

// フォーマット
console.log(format(nextWeek, "yyyy/MM/dd")); // "2026/04/07"

// 日数差分
console.log(differenceInDays(nextWeek, today)); // 7
Day.js の例
import dayjs from "dayjs";

const today = dayjs();

// 7日後
const nextWeek = today.add(7, "day");

// フォーマット
console.log(nextWeek.format("YYYY/MM/DD")); // "2026/04/07"

// 日数差分
console.log(nextWeek.diff(today, "day")); // 7
新規プロジェクトでは date-fns(Tree-shaking でバンドルサイズ最小化)が推奨です。Moment.js からの移行なら API が似ている Day.js が移行コスト低めです。Moment.js 自体は公式にメンテナンスモードとなっており、新規採用は非推奨です。

Temporal API(次世代の日時 API)

Temporal は Date の設計上の問題を根本から解決する新しい日時 API です。2026 年現在 TC39 Stage 3 で、ブラウザの実装が進行中です。

Date の問題点と Temporal の解決策

Date の問題 Temporal の解決
ミュータブル(setDate で元が変わる) イミュータブル(常に新しいオブジェクトを返す)
月が 0 始まり(1月 = 0) 1 始まり(1月 = 1)
タイムゾーン操作が弱い Temporal.ZonedDateTime で完全対応
文字列パースが不安定 厳密なフォーマット解析
「日付のみ」「時刻のみ」が表現できない PlainDate / PlainTime で分離
Temporal の基本例(将来のコード)
// 現在の日時(タイムゾーン付き)
const now = Temporal.Now.zonedDateTimeISO("Asia/Tokyo");

// 日付のみ
const date = Temporal.PlainDate.from("2026-03-31");
console.log(date.month); // 3(1始まり!)

// 7日後(イミュータブル)
const nextWeek = date.add({ days: 7 });
console.log(nextWeek.toString()); // "2026-04-07"

// 差分
const diff = date.until(nextWeek);
console.log(diff.days); // 7
Temporal は 2026 年 3 月現在、主要ブラウザでの完全サポートはまだありません。本番で使用するにはポリフィル(@js-temporal/polyfill)が必要です。

日時計算でよくあるバグとその回避

バグ 原因 回避方法
月がずれる getMonth() が 0 始まり 表示時に +1、設定時に -1
日付文字列が UTC で解釈される "2026-03-31" は UTC として解釈される(ブラウザ依存) new Date(2026, 2, 31) で直接指定
1月31日の 1 月後が 3 月になる setMonth の自動繰り上がり 月末補正を入れる
夏時間で日数計算がずれる DST 切り替え日をまたぐと 1 日が 23/25 時間 UTC ベースで計算するか Math.round を使用
元の Date が変わってしまう Date はミュータブル new Date(original) でコピーしてから操作
Date のコピーを忘れるバグ
// NG: 元の date が変わってしまう
const date = new Date("2026-03-31");
date.setDate(date.getDate() + 7);
console.log(date); // 2026-04-07(元の3/31が消えた!)

// OK: コピーしてから操作
const original = new Date("2026-03-31");
const future = new Date(original);
future.setDate(future.getDate() + 7);
console.log(original); // 2026-03-31(元は変わらない)
console.log(future);   // 2026-04-07

関連記事

よくある質問

Qタイムスタンプが秒単位かミリ秒単位かをどう判断しますか?
A桁数で判断できます。10 桁なら秒(UNIX time)、13 桁ならミリ秒(JavaScript)。秒単位の場合は new Date(timestamp * 1000) でミリ秒に変換してから Date に渡してください。
Qdate-fns と Day.js のどちらを選ぶべきですか?
Aバンドルサイズを最小化したいなら date-fns(Tree-shaking 対応)。Moment.js からの移行なら API が似ている Day.js。どちらもイミュータブルで TypeScript 対応です。Moment.js は新規採用非推奨です。
QTemporal API はもう使えますか?
A2026 年 3 月現在、TC39 Stage 3 でブラウザの実装が進行中です。本番で使うにはポリフィル(@js-temporal/polyfill)が必要です。月が 1 始まり、イミュータブル、タイムゾーン完全対応など Date の問題を解決する設計になっています。
Qnew Date(“2026-03-31”) は UTC とローカルどちらで解釈されますか?
AISO 8601 形式の日付のみ"2026-03-31")は仕様上 UTC として解釈されますが、ブラウザによって挙動が異なる場合があります。確実にローカル時刻で作成するには new Date(2026, 2, 31) のようにコンストラクタの引数で直接指定してください。
QDate がミュータブルで困る場合の対策は?
Anew Date(original) でコピーしてから操作すれば元の値を保持できます。根本的な解決には date-fns(常に新しい Date を返す)か、将来の Temporal API(イミュータブル設計)を使います。

まとめ

JavaScript の日時計算を体系的に整理しました。

  • タイムスタンプ: JavaScript はミリ秒、UNIX / PHP は秒(1000 倍で変換)
  • UTC とローカル: get 系はローカル、getUTC 系は UTC を返す
  • タイムゾーン変換: toLocaleStringtimeZone オプション
  • ライブラリ: date-fns(関数型)か Day.js(チェーン型)を選択
  • Temporal API: 月 1 始まり・イミュータブル・TZ 完全対応の次世代 API
  • バグ回避: 月の 0 始まり・Date のコピー忘れ・文字列パースの UTC 解釈に注意

日付の加算・減算・差分計算の詳細は「日付を操作する方法」「日時の差分を計算する方法」で解説しています。合わせて読むことで日時処理の全体像を把握できます。