JavaScriptの時間フォーマット&変換:よくある落とし穴
JavaScriptでタイムスタンプ、ISO文字列、タイムゾーン、DST、解析ルールを正しく扱い、時間の表示・変換での落とし穴を避ける方法を学ぶ。

JavaScriptで時間がだいたいどう壊れるか
JavaScriptの時間に関するバグは、しばしば「時計が完全に狂っている」ようには見えません。代わりに小さなズレとして現れます:自分のノートパソコンでは正しい日付が、同僚のマシンではずれている、あるいはAPIレスポンスは問題なさそうに見えるが別のタイムゾーンで表示するとおかしくなる、季節変化の周辺で「1つずれている」ように見えるレポート、などです。
よくある症状
通常、次のいずれか(または複数)が見られます:
- 1時間ずれる:特にサマータイム(DST)付近、または値が意図せずローカル時間とUTCの間で変換されているとき。
- 1日ずれる:日付のみの値(例:"2025-12-23")がタイムゾーンによって前日/翌日として表示される。
- タイムゾーンが違う:時刻自体は正しく見えるが、期待したオフセット(例:
+02:00)と異なる。 - フォーマットの不一致:"Chromeでは動くがSafariでは違う"、サーバーとブラウザで文字列の解析結果が異なる、など。
なぜ起きるのか:"時間"は複数の意味を持つ
大きな問題は、**時間(time)**という語が異なる概念を指しうることです:
- 瞬間(instant):世界中で特定の一瞬(例:"2025-12-23T10:00:00Z")。ログ、イベント、API保存には通常これが欲しい。
- カレンダー日(calendar date):タイムゾーンを含まないカレンダー上の日(例:誕生日、請求日)。瞬間として扱うと日付を越えてズレる可能性がある。
- 壁時計時間(wall-clock time):"ベルリンで午前9:00" のようにタイムゾーンや夏時間のルールに依存する時間。
JavaScriptの組み込みである Date はこれらをカバーしようとしますが、主に瞬間(instant)を表現しつつ、表示ではローカル表示に寄せるため、意図しない変換が起きやすくなります。
この記事の焦点
このガイドは実用的な内容に絞っています:ブラウザとサーバー間で予測可能な変換を行う方法、ISO 8601 のようなより安全な形式の選び方、そしてよくある落とし穴(秒とミリ秒の混同、UTCとローカル、解析差)を見抜く方法です。目的は理論ではなく、"なぜズレたのか"という驚きを減らすことです。
時間のデータ型:タイムスタンプ、Date、文字列
JavaScriptの時間バグは、見た目は似ていても置き換えられない表現を混ぜるところから始まることが多いです。
よく見る3つの表現
1) エポックミリ秒(数値)
1735689600000 のような単なる数値は通常「1970-01-01T00:00:00Z からのミリ秒」を意味します。これはタイムゾーンやフォーマットを持たない瞬間です。
2) Dateオブジェクト(瞬間のラッパー)
Date はタイムスタンプと同じ種類の瞬間を保持します。混乱を招く点は、Date を出力するときに、環境のローカルルールでフォーマットされることが多い点です(明示しない限り)。
3) 整形済み文字列(人間向け表示)
"2025-01-01", "01/01/2025 10:00", "2025-01-01T00:00:00Z" のような文字列は一義的ではありません。ISO 8601 の Z 付きは明確ですが、他はロケールに依存したりタイムゾーン情報がなかったりします。
“瞬間” と “人間向け表示” の違い
- 瞬間(instant):"世界的にこの正確な瞬間"(エポックミリ秒やUTCでのISO文字列として保存するのがベスト)。
- 人間向け表示(display):"ユーザーが見るべき時間"(ロケールとタイムゾーンに依存)。
同じ瞬間はタイムゾーンによって異なって表示されます:
const instant = new Date("2025-01-01T00:00:00Z");
instant.toLocaleString("en-US", { timeZone: "UTC" });
// "1/1/2025, 12:00:00 AM"
instant.toLocaleString("en-US", { timeZone: "America/Los_Angeles" });
// "12/31/2024, 4:00:00 PM" (前日)
“1つの"真実の源"(source of truth)を選ぶ
アプリやAPI全体で一貫した内部表現(一般的にはエポックミリ秒かUTCのISO 8601)を選び、それに従ってください。Date や整形文字列への変換は、入力解析とUI表示の境界でのみ行いましょう。
タイムスタンプ:秒とミリ秒(簡単に混同する)
“タイムスタンプ”とは通常 1970-01-01 00:00:00 UTC からの経過時間(エポック) を指します。問題はシステムごとに単位が違うことです。
JavaScript の Date は ミリ秒 を使うため、APIやDB、ログでよく使われる 秒 と混同しがちです。
覚え方
- Unixタイムスタンプ(秒):
1704067200 - JavaScriptタイムスタンプ(ミリ秒):
1704067200000
同じ瞬間で、ミリ秒の方は末尾に3桁多いです。
安全な変換(秒 ↔ ミリ秒)
単位が明確に分かるよう、明示的な乗除を使いましょう:
// seconds -> Date
const seconds = 1704067200;
const d1 = new Date(seconds * 1000);
// milliseconds -> Date
const ms = 1704067200000;
const d2 = new Date(ms);
// Date -> seconds
const secondsOut = Math.floor(d2.getTime() / 1000);
// Date -> milliseconds
const msOut = d2.getTime();
古典的なバグ:秒をそのまま Date() に渡す
一見正しそうですが、ts が秒単位のときは間違いです:
const ts = 1704067200; // seconds
const d = new Date(ts); // WRONG: treated as milliseconds
結果は 1970年 に近い日付になります。なぜなら 1,704,067,200 ミリ秒はエポックから約19日後にしかならないからです。
クイックな検証とデバッグチェック
単位が不明なときは簡単なガードを入れます:
function asDateFromUnknownEpoch(x) {
// crude heuristic: seconds are ~1e9-1e10, milliseconds are ~1e12-1e13
if (x < 1e11) return new Date(x * 1000); // assume seconds
return new Date(x); // assume milliseconds
}
const input = Number(valueFromApi);
console.log({ input, digits: String(Math.trunc(input)).length });
console.log('as ISO:', asDateFromUnknownEpoch(input).toISOString());
“桁数”が約10なら秒、約13ならミリ秒の可能性が高いです。デバッグ時に toISOString() を出すと単位ミスを即座に見つけられます。
ローカル時間とUTC:なぜ表示がズレるのか
Date は内部的には1つの瞬間を保存しますが、異なるタイムゾーンで表示できるため混乱を招きます。
内部的には Date は「Unixエポックからのミリ秒」を保持しており、その数はUTCでの一瞬を表します。表示時に ローカル時間(マシン設定)と UTC のどちらでフォーマットするかで“シフト”が発生します。
ローカル用ゲッターとUTC用ゲッター
多くの Date API はローカル版とUTC版の両方を持っています。同じ瞬間に対して異なる値を返します:
const d = new Date('2025-01-01T00:30:00Z');
d.getHours(); // ローカルタイムの時間
d.getUTCHours(); // UTC の時間
d.toString(); // ローカル時間の文字列
d.toISOString(); // UTC(常に末尾に Z が付く)
例えばマシンがニューヨーク(UTC-5)にあると、このUTC時刻はローカルでは前日の "19:30" と表示されます。サーバーがUTCに設定されていると "00:30" と表示されます。同じ瞬間でも表示が異なるのです。
ログが“おかしい”理由
ログは往々にして Date#toString() を使ったり、Date を暗黙に文字列化して出力しますが、これは環境のローカルタイムゾーンを使います。つまり同じコードでもローカル設定次第で異なるタイムスタンプが出ます。
実用的な方針
時間は UTC(例:エポックミリ秒や toISOString())で保存・送信し、表示時にユーザーのロケールに変換しましょう:
- API には
toISOString()やエポックミリ秒を使う - UI には
Intl.DateTimeFormat等でユーザーのタイムゾーンに合わせてフォーマットする
素早いプロトタイピングでも、API契約に早めにこれを組み込むと良いです。フィールド名を createdAtMs や createdAtIso のように明示すると、サーバー(Go + PostgreSQL)とクライアント(React)で意味がぶれません。
ISO 8601 文字列:APIに最も安全な形式
ブラウザ、サーバー、データベース間で日付/時刻をやり取りするなら、ISO 8601 文字列がデフォルトとして最も安全です。明示的で広くサポートされ、重要な点としてタイムゾーン情報を持ちます。
明示的なUTCまたはオフセットを使う
2つの良い交換形式:
- UTC 時刻(ローカルゾーンを気にしない場合に推奨):
2025-03-04T12:30:00Z - オフセット付きのローカル時刻(壁時計時間が重要な場合に推奨):
2025-03-04T12:30:00+02:00
"Z" は何か?
Z は Zulu 時刻、すなわち UTC を示します。したがって 2025-03-04T12:30:00Z は "UTCで12:30" を表します。
+02:00 のようなオフセットが重要な場合は?
オフセットはイベントがローカル文脈に紐づくときに重要です(予約、店舗の営業時間など)。2025-03-04T12:30:00+02:00 は UTCより2時間進んだ瞬間を指し、2025-03-04T12:30:00Z とは異なる瞬間です。
あいまいな日付文字列を避ける
03/04/2025 のような文字列はトラップです:3月4日か4月3日か?環境や利用者で解釈が違います。2025-03-04(ISO日付)や完全なISO日時を使いましょう。
安全なラウンドトリップ(string → Date → string)
const iso = "2025-03-04T12:30:00Z";
const d = new Date(iso);
const back = d.toISOString();
console.log(iso); // 2025-03-04T12:30:00Z
console.log(back); // 2025-03-04T12:30:00.000Z
このラウンドトリップはAPIで望ましい振る舞いです:一貫して予測可能でタイムゾーンにも明示的です。
解析の落とし穴:Date.parse とブラウザ差
Date.parse() は便利に感じますが、ISO 8601 でない文字列はエンジンごとのヒューリスティックに頼るため結果が異なることがあります。つまり同じ入力が実行環境によって異なる結果(あるいは解析失敗)を起こす可能性があります。
なぜ Date.parse() が変わるのか
JavaScriptはISO 8601スタイルの文字列についてのみ安定した解析を標準化しています(それでもタイムゾーンの扱いなど細部は重要)。"03/04/2025", "March 4, 2025", "2025-3-4" のような“フレンドリー”な形式は:
- ロケールの仮定に基づいて月/日や日/月を解釈する
- タイムゾーンが欠けている場合にローカル時間として扱うエンジンと拒否するエンジンがある
- わずかに不正な文字列を "かなり近い" と解釈するエンジンもある…そしてある時点でそうでなくなる
予測できない形の文字列は予測できない結果を生みます。
驚きのケース:YYYY-MM-DD
よくあるトラップは "YYYY-MM-DD"(例:"2025-01-15")です。多くの開発者はこれが ローカルの深夜 と解釈されることを期待しますが、実際には環境によっては UTCの深夜 と解釈されることがあります。
この違いは重要です:UTCの深夜をローカル時間に変換すると、ネガティブなタイムゾーン(アメリカ等)では 前日 になることがあり、「日付が1日ずれる」バグの原因になります。
解析チェックリスト:ユーザー入力 vs サーバー入力
-
サーバー/API入力について:
- タイムゾーンを明示した完全なISO 8601を優先(例:
2025-01-15T13:45:00Zまたは2025-01-15T13:45:00+02:00)。 - 日付のみの値は 瞬間 として扱わず、誕生日や期限日であれば単なるデータ(
"YYYY-MM-DD")として保持し、タイムゾーンを定義しない限りDateに変換しない。
- タイムゾーンを明示した完全なISO 8601を優先(例:
-
ユーザー入力について:
03/04/2025のような曖昧な形式は、UIで意味を固定しない限り受け入れない。- コントロールされた入力(デートピッカー等)を使い、既知の形式を出力させる。
- フリーテキストを解析する必要がある場合は、受け入れる形式を前もって定義して強制する。
ヒューリスティックではなく明示的ルールを使う
Date.parse() に任せる代わりに次のいずれかを選んでください:
- サーバーはISO 8601のみ受け入れ、他は拒否する。
- 既知の形式を手動で解析する(文字列を分割して
new Date(year, monthIndex, day)をローカル日付用に使うなど)。 - 精確な瞬間にはタイムスタンプ(エポックミリ秒)を使う。表示は後で行う。
時間データがクリティカルな場合、"自分の環境では解析される" は十分ではありません—解析ルールを明確にし、一貫させましょう。
Intl.DateTimeFormatで人間向けにフォーマットする
ユーザーに期待される形で日付/時刻を表示するには、JavaScript の Intl.DateTimeFormat が最良のツールです。ロケールのルール(順序、区切り、月名)を使い、month + '/' + day のような脆い手法を避けられます。
手作業より優れている理由
手作業のフォーマットはしばしば米国スタイルに偏り、ゼロ埋めを忘れたり24/12時間表記の混乱を招いたりします。Intl.DateTimeFormat は表示するタイムゾーンを明示できるため、データをUTCで保存してUIでローカル表示するような設計で重要です。
実際に使うオプション
「きれいに表示するだけ」なら dateStyle と timeStyle が簡潔です:
const d = new Date('2025-01-05T16:30:00Z');
// ユーザーのロケール + ユーザーのローカルタイムゾーン
console.log(new Intl.DateTimeFormat(undefined, {
dateStyle: 'medium',
timeStyle: 'short'
}).format(d));
// 特定のタイムゾーンを強制(イベント時刻に良い)
console.log(new Intl.DateTimeFormat('en-GB', {
dateStyle: 'full',
timeStyle: 'short',
timeZone: 'UTC'
}).format(d));
時間の表記(24/12時間)を明確にしたい場合は hour12 を使います:
console.log(new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: '2-digit',
hour12: true
}).format(d));
実用的なUIパターン
UI内の「タイプ」ごとに1つのフォーマッタ関数を決め(メッセージ時刻、ログ、イベント開始など)、timeZone の判断を意図的に行う:
- 「自分にとっていつ起きたか」はローカル時間で表示する。
- 監査ログやサーバーイベント、チーム間の調整は固定ゾーン(多くはUTC)で表示する。
こうすることで壊れやすい独自フォーマットの集合を維持することなく、一貫したロケール対応の出力が得られます。
夏時間(DST):隠れた1時間ずれのバグ
夏時間(DST)はタイムゾーンが特定の日付でUTCオフセットを(通常は1時間)変更することです。厄介なのは、DSTは単にオフセットを変えるだけでなく、一部のローカル時間が“存在しない”/“重複する”ようにする点です。
存在しない/重複する壁時計時間
時計が 進められる(spring forward) 場合、ある範囲のローカル時間は存在しなくなります。多くの地域で01:59から03:00に飛ぶので、02:30 のような時刻は"存在しない" ことがあります。
時計が 戻される(fall back) 場合、ある範囲のローカル時間が2回起きます。例えば 01:30 が切り替え前と後の2回発生し、同じ壁時計時刻が異なる2つの瞬間を指すことがあります。
「24時間足す」と「翌日同じ時刻」は違う
DST境界付近では次は同じとは限りません:
- 24時間足す:厳密に24 * 60 * 60秒後。
- 翌日同じローカル時刻:そのタイムゾーンで「明日の9:00」に合わせる。
DSTが始まると「明日の9:00」は23時間後かもしれません。DSTが終わると25時間後かもしれません。
// Scenario: schedule “same local time tomorrow”
const d = new Date(2025, 2, 8, 9, 0); // Mar 8, 9:00 local
const plus24h = new Date(d.getTime() + 24 * 60 * 60 * 1000);
const nextDaySameLocal = new Date(d);
nextDaySameLocal.setDate(d.getDate() + 1);
// Around DST, plus24h and nextDaySameLocal can differ by 1 hour.
setHours が驚く理由
date.setHours(2, 30, 0, 0) のような操作は "spring forward" の日には正規化され(多くの場合03:30になる)02:30 が存在しないため期待と異なる結果になります。
より安全なアプローチ
- 経過時間(期間)の算術は UTCで行う:エポックミリ秒やUTCメソッドを使う。
- ローカルでのスケジューリング の場合は意図を明確にする:「翌日9:00」を意図するなら
setDateのようなカレンダー操作を使い、ミリ秒を単純に足さない。 - API交換には オフセット付きのISO 8601 または
Zを使い、瞬間を曖昧にしない。
期間(duration)と日付(date):タイマーにDateを使わない
一般的なバグ原因は、Date をカレンダー時刻でないもの(期間)を表すのに使うことです。
タイムスタンプ は「いつ起きたか?」に答え、期間 は「どれくらい長いか?」に答えます(例:「3分12秒」)。これらは別概念で混ぜると奇妙な計算やタイムゾーン/DSTの影響を受けた予期せぬ表示になります。
なぜ Date は期間に向かないのか
Date は常にエポックに対する時点を表します。例えば「90秒」を Date で表すと、実際には「1970-01-01 に90秒を足したもの」を保持しており、フォーマットすると 01:01:30 のようになったり、1時間ズレたり、意図しない日付が出てきたりします。
期間には生の数値を使いましょう:
- 期間は 秒 か ミリ秒(どちらか一つを決める)で保持する。
- 演算は数値で行う。
- 表示するときにだけ文字列に変換する。
秒を HH:mm:ss に変換する例
カウントダウンやメディア長に使えるシンプルなフォーマッタ:
function formatHMS(totalSeconds) {
const s = Math.max(0, Math.floor(totalSeconds));
const hh = String(Math.floor(s / 3600)).padStart(2, "0");
const mm = String(Math.floor((s % 3600) / 60)).padStart(2, "0");
const ss = String(s % 60).padStart(2, "0");
return `${hh}:${mm}:${ss}`;
}
formatHMS(75); // "00:01:15" (countdown timer)
formatHMS(5423); // "01:30:23" (media duration)
分から来る場合は先に掛け算(minutes * 60)して値を数値のまま保持し、レンダリング時にのみ変換してください。
比較、ソート、範囲処理での失敗を避ける
時間を比較するときは、フォーマット済みの文字列ではなく数値を比べるのが最も安全です。Date オブジェクトは内部的にエポックミリ秒の数値ラッパーなので、比較は最終的に「数値 vs 数値」にしたいです。
安全な比較(タイムスタンプを使う)
getTime()(または Date.valueOf())を使って確実に比較しましょう:
const a = new Date('2025-01-10T12:00:00Z');
const b = new Date('2025-01-10T12:00:01Z');
if (a.getTime() < b.getTime()) {
// a is earlier
}
// 省略形:
if (+a < +b) {
// unary + calls valueOf()
}
ロケールに依存する "1/10/2025, 12:00 PM" のようなフォーマット済み文字列を比較するのは避けてください。例外は、すべて同じフォーマットかつ同じタイムゾーン(例:すべて ...Z)のISO 8601文字列で、これは辞書順でソート可能です。
ソートと範囲フィルタリング
タイムでソートするならエポックミリ秒でソートすれば簡単です:
items.sort((x, y) => new Date(x.createdAt).getTime() - new Date(y.createdAt).getTime());
範囲内フィルタリングも同様:
const start = new Date('2025-01-01T00:00:00Z').getTime();
const end = new Date('2025-02-01T00:00:00Z').getTime();
const inRange = items.filter(i => {
const t = new Date(i.createdAt).getTime();
return t >= start && t < end;
});
“日の始まり/終わり”(ローカル vs UTC)
“日の始まり” はローカル時間かUTCかで変わります:
// ローカルの始まり/終わり
const d = new Date(2025, 0, 10); // ローカルの1月10日
const localStart = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
const localEnd = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59, 999);
// UTC の始まり/終わり
const utcStart = new Date(Date.UTC(2025, 0, 10, 0, 0, 0, 0));
const utcEnd = new Date(Date.UTC(2025, 0, 10, 23, 59, 59, 999));
早い段階でどちらを意味するかを決め、比較や範囲ロジック全体で一貫させてください。
デバッグチェックリスト:時間変換バグの診断方法
時間のバグはランダムに見えますが、まず 何を持っているか(タイムスタンプ?文字列?Date?)と どこでズレが入ったか(解析、タイムゾーン変換、フォーマット)を特定すれば原因が見えてきます。
1) 同じ瞬間の“3つの見え方”を取得する
まず同じ値を3通りの見え方でログに出しましょう。これで単位の問題かローカル/UTCの問題か解析の問題かがすぐわかります。
console.log('raw input:', input);
const d = new Date(input);
console.log('toISOString (UTC):', d.toISOString());
console.log('toString (local):', d.toString());
console.log('timezone offset (min):', d.getTimezoneOffset());
見るべき点:
toISOString()が明らかにおかしい(例:1970年や遠い未来)なら 秒とミリ秒の混同 を疑う。toISOString()は正しいがtoString()がズレているなら ローカルタイムゾーン表示 の問題。getTimezoneOffset()が日付によって変わるなら 夏時間をまたいでいる。
2) 実行環境を確認する:タイムゾーンとロケール
“自分の環境では動く”という報告は、環境デフォルトが異なるだけのことが多いです。
- ブラウザ:OSのタイムゾーン設定とブラウザ言語を確認し、次をログに出す:
console.log(Intl.DateTimeFormat().resolvedOptions());
- Node.js / サーバー:プロセスのタイムゾーンを確認:
console.log('TZ:', process.env.TZ);
console.log(Intl.DateTimeFormat().resolvedOptions().timeZone);
サーバーがUTC、ローカルが別ゾーンだと、明示的に timeZone を指定しない限りフォーマット出力は異なります。
3) 時間が壊れやすい箇所にテストを追加する
DSTの境界や「端っこ」時間周りにユニットテストを作りましょう:
- 対象地域のDST切替前後の1時間前・後
- 月末/年末、
23:30→00:30の変わり目 - サポートする複数タイムゾーン
反復が早いなら、これらのテストをスキャフォールディングに組み込み、デプロイ前に回帰を捕まえられるようにします。
出荷前チェックリスト
- 入力は明確な契約になっている:エポックミリ秒 か オフセット付きのISO 8601。
"2025-03-02 10:00"のような曖昧な文字列はない。- フォーマットは常に
localeと(必要なら)timeZoneを指定する。 - テストは対象地域の DST 境界をカバーする。
信頼できる時間処理のための推奨パターン
JavaScriptで信頼できる時間処理をするには、"真実の源(source of truth)" を選び、保存から表示まで一貫させることがほとんどです。
シンプルなベストプラクティス
保存と計算はUTCで行い、ユーザー向けのローカル時間は表示の詳細として扱います。
システム間の送受信はオフセット付きのISO 8601文字列(できれば Z)を使いましょう。数値のエポックを使う場合は単位を文書化し一貫させてください(JSではミリ秒が一般的)。
ユーザー向けのフォーマットは Intl.DateTimeFormat(または toLocaleString)で行い、決定的な出力が必要な場合は timeZone を明示してください(例:常に UTC を表示、または特定のビジネスゾーンを使う)。
DB vs API vs UI に関する意思決定ガイド
- データベース:瞬間はUTCとして保存(エポックミリ秒かUTC日付型)。壁時計時間(例:店舗の開店09:00)のようにローカル時刻を本当に保存するドメイン以外はローカル日時を避ける。
- API 境界:
Z付きのISO 8601 を推奨(例:2025-12-23T10:15:00Z)。エポックを使うならcreatedAtMsのようにフィールド名で単位を明示する。 - UI:保存したUTCをユーザーのロケール向けにフォーマットする。スケジューリングUIではタイムゾーンを明示的にラベル付けし、変換は明示的に行う。
ライブラリを使う価値がある場合
リカリングイベント、複雑なタイムゾーンルール、DSTに安全な算術("翌日同じローカル時間" など)、不揃いな入力の大量解析が必要なら専用の日時ライブラリの検討に値します。利点はAPIが明確になり、エッジケースのバグが減ることです。
もっと詳しく知りたい場合は /blog を参照してください。ツールやサポートの評価をするなら /pricing をご覧ください。
よくある質問
JavaScript のタイムスタンプにはどの形式を使うべきですか?
エポックミリ秒、または UTC の ISO 8601 文字列を内部形式として使います。人に値を表示するときだけローカル形式に変換してください。
Unix タイムスタンプが 1970 年の日付になるのはなぜですか?
JavaScript の Date はミリ秒を想定しています。秒単位の Unix タイムスタンプは、new Date() に渡す前に 1000 を掛けてください。
同じ日付なのにコンピューターによって時刻が異なるのはなぜですか?
Date は1つの時点を保存しますが、JavaScript は多くの場合、コンピューターのローカルタイムゾーンで表示します。toISOString() と toString() を比較し、データが変わったのか、表示だけが変わったのかを確認してください。
日付が1日ずれるのを防ぐにはどうすればよいですか?
誕生日や請求日などの日付のみの値は、YYYY-MM-DD として保持します。意図したタイムゾーンも定義しない限り、Date に変換しないでください。
API で最も安全な日付形式は何ですか?
UTC を表す Z を含む完全な ISO 8601 値、たとえば 2025-03-04T12:30:00Z を送るか、明示的なオフセットを含めてください。03/04/2025 のような文字列は、人やブラウザーによって異なる解釈をされるため避けてください。
ユーザー向けの日付はどのように整形すべきですか?
Intl.DateTimeFormat を使い、画面で固定のタイムゾーンが必要な場合は timeZone を指定してください。手作業で文字列を組み立てなくても、ユーザーのロケールに合わせて日付を整形できます。
ユーザーが入力した日付に Date.parse を使うべきですか?
非公式な文字列や曖昧な文字列には依存しないでください。API からは既知の ISO 形式を受け取るか、自分で定義したルールに従って管理されたユーザー入力を解析してください。
カウントダウンや継続時間に Date を使うのを避けるべきなのはなぜですか?
Date はカレンダー上の時点を表す一方、期間は単なる時間の長さです。期間は秒またはミリ秒として保存し、表示時にその数値を整形してください。
夏時間は JavaScript の日付にどのような影響を与えますか?
経過時間の計算にはエポックミリ秒を使ってください。名前付きタイムゾーンで「明日の午前9時」のような予定を扱う場合は、カレンダーに基づく操作を使い、DST の切り替わり日をテストしてください。
JavaScript の時刻に関するバグを最も速くデバッグする方法は何ですか?
生の入力、toISOString()、toString()、getTimezoneOffset() をログに出力してください。ISO 値が誤っている場合は解析や単位の問題であることが多く、ローカル文字列だけが異なる場合は通常、タイムゾーン表示の違いです。