JavaScript の日付を「日本時間の YYYY-MM-DD」にしたいときの書き方と落とし穴
フロントエンドで「カレンダーで選んだ日付を YYYY-MM-DD 形式の文字列にしたい」という場面、よくありますよね。
友人のコードを見ていたら、こんな書き方をしていました。
const isoDate = selectedDate.toLocaleDateString("ja-JP", {
year: "numeric",
month: "2-digit",
day: "2-digit",
}).replace(/\//g, "-");
-
toLocaleDateString("ja-JP", ...)で2025/11/21 -
/を-に置き換えて2025-11-21
という流れです。
これでももちろん動きますが、ChatGPT に「もっといい書き方ない?」と聞いたところ、こんなコードを提案されました。
const isoDate = new Date(
selectedDate.getTime() - selectedDate.getTimezoneOffset() * 60 * 1000,
)
.toISOString()
.slice(0, 10);
ここで湧いた疑問がこれです。
この書き方だと
"ja-JP"も"Asia/Tokyo"も書いてないのに、なんで日本時間になるの?
この疑問をきっかけに、UTC と getTimezoneOffset()、そして「JavaScript がどうやって自分のタイムゾーンを判断しているか」を整理してみます。
そもそも UTC とは?
UTC(Coordinated Universal Time / 協定世界時) は、世界中で使われる基準となる時間です。
各国のローカルタイムは、UTC を基準にして「+何時間」「−何時間」という形で決まっています。
- 日本(JST) = UTC + 9時間
- アメリカ(ニューヨーク) = UTC −5時間
昔は GMT(グリニッジ標準時)が使われていましたが、現在の正式な基準は UTC です。
GMT とほぼ同じですが、UTC は原子時計をベースにした、より精密な定義になっています。
getTimezoneOffset() とは何か?
getTimezoneOffset() は JavaScript の Date オブジェクトに用意されているメソッドで、
「その日時のローカルタイムゾーンと UTC の“差”を、分単位で返す」
というものです。
例:
const d = new Date();
const offset = d.getTimezoneOffset();
console.log(offset);
日本(JST, UTC+9)の環境で実行すると、だいたいこうなります。
-540
- 540分 = 9時間
- マイナスなのは「UTC − ローカル時間」の差として定義されているから
(UTC よりローカルが進んでいるとマイナスになる)
「日本」とはどこにも書いてないのに、なぜ日本時間になるのか?
ポイント:JavaScript は OS のタイムゾーン設定を見ている
getTimezoneOffset() 自体には「日本」という文字列は一切出てきません。
代わりに、JavaScript(ブラウザや Node.js)は実行環境の OS のタイムゾーン設定を見ています。
-
macOS / Windows の「日付と時刻」の設定で
タイムゾーンが「大阪 / 札幌 / 東京」になっている -
その状態で
new Date()やgetTimezoneOffset()を呼ぶ -
JavaScript は
「この環境のローカルタイムゾーンは JST (UTC+9) なんだな」
と解釈する -
その結果として
getTimezoneOffset()が-540を返す
つまり:
どこかで「日本です」とコードに書いているわけではなく、
「OS が日本時間になっているから、結果として日本時間として扱われている」
だけです。
同じコードをアメリカの PC で動かすと別の値(例:300 = 5時間分)が返ってきます。
何が嬉しいのか?
このトリックはざっくり言うと、
「ローカルで選んだ日付を、日付としてズレないようにしたまま
YYYY-MM-DDを取り出す」
ためのテクニックです。
-
toISOString()は UTC で文字列を作るので、そのまま使うと日付がズレる場合がある - そこで事前にローカルタイムの分だけ時間を補正してから
toISOString()している
日本の PC で使うと、結果的に日本時間ベースの日付になりますが、
それは OS が日本時間だからそうなっているだけで、
このコード自体が「日本固定」なわけではありません。
Discussion
それなら getTime() 外してもよさそうですね Date 型のプリミティブは getTime() の数値なので
なるほど!
ありがとうございます