- JavaScript
JavaScript日付フォーマット|yyyy-mm-dd変換6選とズレ対策
JavaScriptでDateをyyyy-mm-dd形式にするいちばん短い書き方は、date.toLocaleDateString("sv-SE") です。 外部ライブラリなしで 2026-10-02 が得られます。一方、よく見かける date.toISOString().slice(0, 10) は、日本時間の0時〜8時台に実行すると前日の日付になります。
この記事では、yyyy-mm-ddへの変換方法を6通り並べて比較し、日付が1日ずれる現象を TZ=Asia/Tokyo で再現して直します。あわせて、月が0始まりであること、new Date("2026-10-02") がUTCとして解釈されること、曜日・時刻つき・和暦のコピペ用コードまでを扱います。コードはNode.js 22.22.2(ICU 78.2)で実行し、出力をそのまま載せています。 TemporalはNode.js 22に搭載されていないため、公式ポリフィル @js-temporal/polyfill 0.5.1で実行しました。
日付フォーマットの早見表
入力はすべて const d = new Date(2026, 9, 2, 8, 5, 9)(日本時間の2026年10月2日 金曜日 8時5分9秒)です。
| 欲しい形式 | 書き方 | 出力 |
|---|---|---|
yyyy-mm-dd |
d.toLocaleDateString("sv-SE") |
2026-10-02 |
yyyy/mm/dd |
d.toLocaleDateString("ja-JP", { year: "numeric", month: "2-digit", day: "2-digit" }) |
2026/10/02 |
yyyy/m/d |
d.toLocaleDateString("ja-JP") |
2026/10/2 |
yyyymmdd |
d.toLocaleDateString("sv-SE").replaceAll("-", "") |
20261002 |
yyyy-mm-dd hh:mm:ss |
d.toLocaleString("sv-SE") |
2026-10-02 08:05:09 |
yyyy-mm-ddThh:mm:ss |
d.toLocaleString("sv-SE").replace(" ", "T") |
2026-10-02T08:05:09 |
yyyy年m月d日 |
d.toLocaleDateString("ja-JP", { dateStyle: "long" }) |
2026年10月2日 |
yyyy年m月d日(曜) |
d.toLocaleDateString("ja-JP", { year: "numeric", month: "long", day: "numeric", weekday: "short" }) |
2026年10月2日(金) |
| 曜日だけ | d.toLocaleDateString("ja-JP", { weekday: "short" }) |
金 |
hh:mm |
d.toLocaleTimeString("ja-JP", { hour: "2-digit", minute: "2-digit" }) |
08:05 |
| 和暦 | d.toLocaleDateString("ja-JP-u-ca-japanese", { era: "long", year: "numeric", month: "long", day: "numeric" }) |
令和8年10月2日 |
日本時間に固定して yyyy-mm-dd |
new Intl.DateTimeFormat("sv-SE", { timeZone: "Asia/Tokyo" }).format(d) |
2026-10-02 |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「JavaScript日付フォーマット|yyyy-mm-dd変換6選とズレ対策」
yyyy-mm-ddに変換する6通りの比較
比較表
| 方法 | コードの長さ | 実行環境のタイムゾーンへの依存 | 向いている場面 |
|---|---|---|---|
| 1. padStart | 長め | 依存する(ローカル時刻) | 仕組みが見える。区切りや並びを自由に変えたい |
2. toLocaleDateString("sv-SE") |
最短 | 依存する(ローカル時刻) | 手早くyyyy-mm-ddが欲しい |
3. formatToParts |
長め | 依存する(オプションで固定可) | 年・月・日を部品として取り出して組み立てたい |
4. Intl.DateTimeFormat + timeZone |
中 | 依存しない(指定した地域で固定) | サーバーなど、どこで動いても日本の日付にしたい |
5. toISOString(補正つき) |
中 | 補正すれば依存する(ローカル時刻) | 既存コードの toISOString().slice(0, 10) を直したい |
| 6. Temporal | 短い | 明示的に指定する | 新しいコードで、日付だけを型として扱いたい |
6通りのコードと実行結果
次のコードを format6.mjs として保存し、npm install @js-temporal/polyfill のあと TZ=Asia/Tokyo node format6.mjs で実行します。
// format6.mjs(Node.js 22.22.2 / @js-temporal/polyfill 0.5.1)
import { Temporal } from "@js-temporal/polyfill";
const d = new Date(2026, 9, 2, 8, 5, 9); // 月は0始まり。9 が10月
console.log(d.toString());
// 1. padStart で組み立てる
const p = (n) => String(n).padStart(2, "0");
console.log("1", `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`);
// 2. スウェーデン語ロケール(sv-SE)は yyyy-mm-dd 形式
console.log("2", d.toLocaleDateString("sv-SE"));
// 3. formatToParts で部品に分けて組み立てる
const parts = new Intl.DateTimeFormat("ja-JP", {
year: "numeric", month: "2-digit", day: "2-digit",
}).formatToParts(d);
const m = Object.fromEntries(parts.map((x) => [x.type, x.value]));
console.log("3", `${m.year}-${m.month}-${m.day}`);
// 4. タイムゾーンを指定して整形する
console.log("4", new Intl.DateTimeFormat("sv-SE", { timeZone: "Asia/Tokyo" }).format(d));
// 5. toISOString(そのまま)と(補正つき)
console.log("5a", d.toISOString().slice(0, 10));
const local = new Date(d.getTime() - d.getTimezoneOffset() * 60000);
console.log("5b", local.toISOString().slice(0, 10));
// 6. Temporal
const zdt = Temporal.Instant.fromEpochMilliseconds(d.getTime())
.toZonedDateTimeISO("Asia/Tokyo");
console.log("6", zdt.toPlainDate().toString());
実行結果(TZ=Asia/Tokyo):
Fri Oct 02 2026 08:05:09 GMT+0900 (Japan Standard Time)
1 2026-10-02
2 2026-10-02
3 2026-10-02
4 2026-10-02
5a 2026-10-01
5b 2026-10-02
6 2026-10-02
5a(toISOString().slice(0, 10) をそのまま使う方法)だけが 2026-10-01 と前日になっています。 これが日付ずれの正体で、次の章で詳しく見ます。
formatToParts が返す配列は次のとおりです。区切り文字は literal という種類で入っているので、必要な部品だけを取り出せます。
[{"type":"year","value":"2026"},{"type":"literal","value":"/"},
{"type":"month","value":"10"},{"type":"literal","value":"/"},
{"type":"day","value":"02"}]
各方法のポイント
1. padStart:getMonth() は0始まりなので + 1 が必要です。padStart(2, "0") は「2桁になるまで左を0で埋める」という意味で、String(2).padStart(2, "0") は "02" になります。
2. sv-SE:スウェーデンの日付表記がISO 8601と同じ yyyy-mm-dd であることを利用した書き方です。同じ形式になるロケールを実行して確かめました。
| ロケール | toLocaleDateString(ロケール) |
2桁指定つき |
|---|---|---|
sv-SE(スウェーデン) |
2026-10-02 |
2026-10-02 |
en-CA(カナダ英語) |
2026-10-02 |
2026-10-02 |
fr-CA(カナダのフランス語) |
2026-10-02 |
2026-10-02 |
ja-JP |
2026/10/2 |
2026/10/02 |
en-US |
10/2/2026 |
10/02/2026 |
en-GB |
02/10/2026 |
02/10/2026 |
de-DE |
2.10.2026 |
02.10.2026 |
ロケールごとの表記は、ブラウザやNode.jsに組み込まれたICUというデータに基づいています。sv-SE の形式は長く安定していますが、仕様で yyyy-mm-dd と固定されているわけではありません。データベースやAPIに渡す値のように形式が厳密に決まっている場面では、1(padStart)や3(formatToParts)のように自分で組み立てる方法が確実です。
4. timeZone指定:Intl.DateTimeFormat に timeZone を渡すと、実行環境のタイムゾーンに関係なく、指定した地域の日付で整形できます。同じ d をニューヨークの日付として整形すると 2026-10-01 になりました(日本の朝8時は、ニューヨークでは前日の夜です)。
6. Temporal:日付だけを表す Temporal.PlainDate の toString() が、そのまま yyyy-mm-dd を返します。MDNでは2026年10月2日時点で「Limited availability(主要ブラウザの一部で未対応)」と案内されているため、ブラウザ向けではポリフィルの利用か、対応状況の確認が前提になります。型の種類やDateからの移行は「JavaScript Temporal 使い方」の記事で扱っています。
toISOStringで日付が1日ずれる現象を再現して直す
再現:日本時間の0時〜8時台は前日になる
toISOString() は、常にUTC(協定世界時)で文字列を作ります。 日本時間はUTCより9時間進んでいるので、日本時間の0時〜8時台は、UTCではまだ前日です。
// shift.mjs(TZ=Asia/Tokyo node shift.mjs)
for (const h of [0, 8, 9, 23]) {
const x = new Date(2026, 9, 2, h, 0, 0); // 日本時間 10月2日 h時
console.log(String(h).padStart(2), "時",
"| ローカル", x.toLocaleDateString("sv-SE"),
"| ISO", x.toISOString(),
"→", x.toISOString().slice(0, 10));
}
実行結果(TZ=Asia/Tokyo):
0 時 | ローカル 2026-10-02 | ISO 2026-10-01T15:00:00.000Z → 2026-10-01
8 時 | ローカル 2026-10-02 | ISO 2026-10-01T23:00:00.000Z → 2026-10-01
9 時 | ローカル 2026-10-02 | ISO 2026-10-02T00:00:00.000Z → 2026-10-02
23 時 | ローカル 2026-10-02 | ISO 2026-10-02T14:00:00.000Z → 2026-10-02
| 日本時間 | toISOString().slice(0, 10) |
正しい日付との差 |
|---|---|---|
| 0:00〜8:59 | 2026-10-01 |
1日前になる |
| 9:00〜23:59 | 2026-10-02 |
一致する |
昼間に動かして確認すると正しく見えるため、朝の時間帯だけ日付が違うという形で表に出てきます。 「午前中だけ昨日の日付で登録される」「日付の初期値が前日になる」といった症状は、この組み合わせが原因であることがよくあります。
同じコードを TZ=UTC で実行すると、すべての時間帯で 2026-10-02 になりました。サーバーのタイムゾーンがUTCだと再現せず、利用者のブラウザ(日本時間)でだけ起こる、ということもあります。
直し方
| 直す前 | 直した後 | 考え方 |
|---|---|---|
d.toISOString().slice(0, 10) |
d.toLocaleDateString("sv-SE") |
ローカル時刻で整形する(最短) |
| 同上 | `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}` |
ローカル時刻の部品から組み立てる |
| 同上 | new Date(d.getTime() - d.getTimezoneOffset() * 60000).toISOString().slice(0, 10) |
時差ぶんずらしてからISO文字列にする |
| 同上 | new Intl.DateTimeFormat("sv-SE", { timeZone: "Asia/Tokyo" }).format(d) |
日本時間に固定する |
3番目の「補正つきtoISOString」の仕組みを補足します。getTimezoneOffset() は「UTCとの差を分で返す」メソッドで、日本時間では -540(マイナス9時間)です。
const d = new Date(2026, 9, 2, 8, 5, 9);
console.log(d.getTimezoneOffset()); // -540
const local = new Date(d.getTime() - d.getTimezoneOffset() * 60000);
console.log(local.toISOString().slice(0, 10)); // 2026-10-02
-540 × 60000 ミリ秒を「引く」ので、結果として9時間ぶん進めたDateができます。そのDateをUTCとして文字列にすると、日本時間の日付と一致します。TZ=America/New_York(オフセット 240)でも 2026-10-02 になることを確認しました。ただし、この local は「見かけの時刻を合わせるためにずらしたDate」で、本来の時刻とは別物です。文字列を作る用途だけに使い、ほかの計算には流用しないようにします。
どのタイムゾーンで日付を決めるのかを先に決める
日付のずれを考えるときは、「どこの日付が欲しいのか」を先に決めると整理しやすくなります。
| 欲しい日付 | 使うもの |
|---|---|
| 利用者の端末の日付(ブラウザで表示・入力に使う) | toLocaleDateString("sv-SE")、padStart |
| 常に日本の日付(サーバーがどこにあっても) | Intl.DateTimeFormat に timeZone: "Asia/Tokyo" |
| UTCの日付(ログやファイル名を世界共通にしたい) | toISOString().slice(0, 10) |
toISOString() 自体は正しく動いています。UTCの日付が欲しい場面ではそのまま使えて、ローカルの日付が欲しい場面では別の方法を選ぶ、という使い分けです。
Dateを作るときの2つの罠
フォーマットが正しくても、もとのDateがずれていれば結果もずれます。作る側の罠を2つ押さえておきます。
罠1:月は0始まり
new Date(年, 月, 日) の月は、0が1月、11が12月です。
console.log(new Date(2026, 10, 2).toLocaleDateString("sv-SE")); // 10月のつもり
console.log(new Date(2026, 9, 2).toLocaleDateString("sv-SE")); // 正しくは 9
console.log(new Date(2026, 9, 2).getMonth()); // 取り出しても 9
実行結果:
2026-11-02
2026-10-02
9
範囲を超えた値は、エラーにならずに繰り上がります。
| 書き方 | 結果 | 補足 |
|---|---|---|
new Date(2026, 12, 1) |
2027-01-01 |
12は「翌年の1月」 |
new Date(2026, 10, 0) |
2026-10-31 |
日に0を指定すると前月の末日 |
new Date(2026, 1, 30) |
2026-03-02 |
2月30日は3月2日に繰り上がる |
作るときは 月 - 1、取り出すときは getMonth() + 1 と対にして覚えます。「日に0で前月末」は、月末日を求める定番の書き方として使えます(10月の末日なら new Date(2026, 10, 0))。
罠2:new Date("2026-10-02") はUTCとして解釈される
文字列からDateを作るとき、日付だけの yyyy-mm-dd 形式はUTCの0時として解釈されます。 時刻を付けると、ローカル時刻として解釈されます。
for (const s of ["2026-10-02", "2026-10-02T00:00:00", "2026/10/02",
"2026-10-02T00:00:00Z", "2026-10-02T00:00:00+09:00"]) {
const x = new Date(s);
console.log(s.padEnd(26), x.toString().slice(0, 33), "| getHours", x.getHours());
}
実行結果(TZ=Asia/Tokyo):
2026-10-02 Fri Oct 02 2026 09:00:00 GMT+0900 | getHours 9
2026-10-02T00:00:00 Fri Oct 02 2026 00:00:00 GMT+0900 | getHours 0
2026/10/02 Fri Oct 02 2026 00:00:00 GMT+0900 | getHours 0
2026-10-02T00:00:00Z Fri Oct 02 2026 09:00:00 GMT+0900 | getHours 9
2026-10-02T00:00:00+09:00 Fri Oct 02 2026 00:00:00 GMT+0900 | getHours 0
| 文字列 | 解釈 | 日本時間での時刻 |
|---|---|---|
"2026-10-02"(日付だけ) |
UTCの0時 | 9:00 |
"2026-10-02T00:00:00"(時刻つき、オフセットなし) |
ローカルの0時 | 0:00 |
"2026-10-02T00:00:00Z" |
UTCの0時 | 9:00 |
"2026-10-02T00:00:00+09:00" |
日本時間の0時 | 0:00 |
"2026/10/02"(スラッシュ区切り) |
ローカルの0時(仕様外の形式) | 0:00 |
日本時間では9時になるだけで日付は変わりませんが、UTCより遅れている地域では前日になります。 TZ=America/New_York で実行した結果です。
new Date("2026-10-02").toString() → Thu Oct 01 2026 20:00:00 GMT-0400 (Eastern Daylight Time)
getDate() → 1
toLocaleDateString("sv-SE") → 2026-10-01
スラッシュ区切りの "2026/10/02" はECMAScriptの仕様で決められた形式ではなく、解釈は実行環境に任されています。Node.js 22ではローカルの0時になりましたが、環境に左右されない書き方にするなら、文字列を分解して数値で渡すのが確実です。
// "yyyy-mm-dd" の文字列を、ローカル時刻の0時のDateにする
function parseLocalDate(s) {
const [y, m, d] = s.split("-").map(Number);
return new Date(y, m - 1, d);
}
console.log(parseLocalDate("2026-10-02").toString());
Fri Oct 02 2026 00:00:00 GMT+0900 (Japan Standard Time)
<input type="date"> の値は yyyy-mm-dd 形式の文字列なので、そのまま new Date(value) に渡すとこの罠に当たります。上の parseLocalDate を通すか、文字列のまま扱うと、日付がずれません。
往復で起こる組み合わせ
2つの罠は、組み合わさるとわかりにくくなります。日本時間で new Date("2026-10-02") を作ると9時のDateになり、toISOString().slice(0, 10) は 2026-10-02 を返します(実行して確認)。UTC解釈で作ってUTCで文字列にしているので、往復が偶然そろうわけです。一方、new Date(2026, 9, 2)(ローカルの0時)を toISOString() に通すと 2026-10-01 になります。
| 作り方 | 文字列への変換 | 結果 |
|---|---|---|
new Date("2026-10-02")(UTC解釈) |
toISOString().slice(0, 10)(UTC) |
そろう |
new Date(2026, 9, 2)(ローカル) |
toLocaleDateString("sv-SE")(ローカル) |
そろう |
new Date(2026, 9, 2)(ローカル) |
toISOString().slice(0, 10)(UTC) |
日本時間では前日 |
new Date("2026-10-02")(UTC解釈) |
toLocaleDateString("sv-SE")(ローカル) |
UTCより遅れた地域では前日 |
「作るとき」と「文字列にするとき」で、ローカルかUTCかをそろえるのが、ずれを防ぐ基本です。
曜日・時刻つき・和暦のコピペ用コード
ここからは、よく使う形式のコードをまとめます。入力は早見表と同じ const d = new Date(2026, 9, 2, 8, 5, 9) で、出力はすべて TZ=Asia/Tokyo のNode.js 22.22.2で確認したものです。
曜日を付ける
// 数値で取り出して自分で変換(0が日曜)
console.log(d.getDay(), "日月火水木金土"[d.getDay()]); // 5 金
// Intl に任せる
console.log(d.toLocaleDateString("ja-JP", { weekday: "short" })); // 金
console.log(d.toLocaleDateString("ja-JP", { weekday: "long" })); // 金曜日
// 日付とまとめて
console.log(d.toLocaleDateString("ja-JP",
{ year: "numeric", month: "long", day: "numeric", weekday: "short" }));
// 2026年10月2日(金)
console.log(d.toLocaleDateString("ja-JP",
{ year: "numeric", month: "2-digit", day: "2-digit", weekday: "short" }));
// 2026/10/02(金)
getDay() は曜日(0〜6)、getDate() は日(1〜31)です。名前が似ているので、取り違えに気づけるよう表にしておきます。
| メソッド | 返すもの | 2026-10-02 08:05:09 での値 |
|---|---|---|
getFullYear() |
年 | 2026 |
getMonth() |
月(0始まり) | 9 |
getDate() |
日 | 2 |
getDay() |
曜日(0が日曜) | 5 |
getHours() / getMinutes() / getSeconds() |
時・分・秒 | 8 / 5 / 9 |
getTime() |
1970年1月1日(UTC)からのミリ秒 | ― |
getTimezoneOffset() |
UTCとの差(分) | -540 |
時刻を付ける
const p = (n) => String(n).padStart(2, "0");
// yyyy-mm-dd hh:mm:ss
console.log(`${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ` +
`${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`);
console.log(d.toLocaleString("sv-SE"));
// どちらも 2026-10-02 08:05:09
// yyyy/mm/dd hh:mm:ss
console.log(d.toLocaleString("ja-JP", {
year: "numeric", month: "2-digit", day: "2-digit",
hour: "2-digit", minute: "2-digit", second: "2-digit",
}));
// 2026/10/02 08:05:09
// オプションなしだと0埋めされない
console.log(d.toLocaleString("ja-JP")); // 2026/10/2 8:05:09
// 時刻だけ
console.log(d.toLocaleTimeString("ja-JP", { hour: "2-digit", minute: "2-digit" })); // 08:05
// dateStyle / timeStyle でおまかせ
console.log(d.toLocaleString("ja-JP", { dateStyle: "full", timeStyle: "short" }));
// 2026年10月2日金曜日 8:05
dateStyle |
出力(ja-JP) |
|---|---|
"full" |
2026年10月2日金曜日 |
"long" |
2026年10月2日 |
"medium" |
2026/10/02 |
"short" |
2026/10/02 |
dateStyle と timeStyle は、year や month などの個別指定と同時には使えません。「おまかせで整える」か「項目ごとに指定する」かのどちらかを選びます。オプションの全体像は「JavaScript Intlの使い方」の記事にまとめています。
和暦で表示する
ロケールに -u-ca-japanese を付けると、和暦になります。
const wareki = "ja-JP-u-ca-japanese";
console.log(d.toLocaleDateString(wareki));
// R8/10/2
console.log(d.toLocaleDateString(wareki,
{ era: "long", year: "numeric", month: "long", day: "numeric" }));
// 令和8年10月2日
console.log(d.toLocaleDateString(wareki,
{ era: "long", year: "numeric", month: "long", day: "numeric", weekday: "long" }));
// 令和8年10月2日金曜日
console.log(d.toLocaleDateString(wareki,
{ era: "narrow", year: "2-digit", month: "2-digit", day: "2-digit" }));
// R08/10/02
console.log(d.toLocaleDateString(wareki, { era: "short", year: "numeric" }));
// 令和8年
改元の前後も実行して確かめました。
| 日付 | 出力 |
|---|---|
new Date(2019, 3, 30) |
平成31年4月30日 |
new Date(2019, 4, 1) |
令和元年5月1日 |
1年目は「元年」と表示されます。 同じ指定は { calendar: "japanese" } オプションでも書けます(toLocaleDateString("ja-JP", { calendar: "japanese", dateStyle: "long" }) で 令和元年5月1日)。
era の値 |
表示 |
|---|---|
"long" |
令和 |
"short" |
令和 |
"narrow" |
R |
書式文字列で指定できる関数
「yyyy/MM/dd HH:mm のような書式文字列で指定したい」という場合の、短い関数です。
function formatDate(date, fmt = "yyyy-MM-dd") {
const pad = (n, len = 2) => String(n).padStart(len, "0");
const map = {
yyyy: date.getFullYear(),
MM: pad(date.getMonth() + 1),
dd: pad(date.getDate()),
HH: pad(date.getHours()),
mm: pad(date.getMinutes()),
ss: pad(date.getSeconds()),
SSS: pad(date.getMilliseconds(), 3),
E: "日月火水木金土"[date.getDay()],
};
return fmt.replace(/yyyy|MM|dd|HH|mm|ss|SSS|E/g, (key) => map[key]);
}
console.log(formatDate(d)); // 2026-10-02
console.log(formatDate(d, "yyyy/MM/dd HH:mm:ss")); // 2026/10/02 08:05:09
console.log(formatDate(d, "yyyy年MM月dd日(E)")); // 2026年10月02日(金)
console.log(formatDate(d, "yyyyMMdd_HHmmss")); // 20261002_080509
| 記号 | 意味 | 例 |
|---|---|---|
yyyy |
年(4桁) | 2026 |
MM |
月(2桁) | 10 |
dd |
日(2桁) | 02 |
HH |
時(24時間、2桁) | 08 |
mm |
分(2桁) | 05 |
ss |
秒(2桁) | 09 |
SSS |
ミリ秒(3桁) | 000 |
E |
曜日(1文字) | 金 |
大文字の MM が月、小文字の mm が分です。この区別は多くの日付ライブラリと共通なので、あとでライブラリに置き換えるときも書式をそのまま使いやすくなります。
無効な日付を渡したときの動き
フォーマットの前に、Dateが有効かどうかを確かめておくと、原因を追いやすくなります。
const bad = new Date("abc");
console.log(bad.toLocaleDateString("sv-SE")); // Invalid Date
console.log(String(bad.getFullYear())); // NaN
try {
bad.toISOString();
} catch (e) {
console.log(e.name + ":", e.message); // RangeError: Invalid time value
}
try {
new Intl.DateTimeFormat("sv-SE").format(bad);
} catch (e) {
console.log(e.name + ":", e.message); // RangeError: Invalid time value
}
| 呼び出し | 無効なDateのとき |
|---|---|
toLocaleDateString() |
文字列 "Invalid Date" を返す |
getFullYear() など |
NaN を返す |
toISOString() |
RangeError: Invalid time value |
Intl.DateTimeFormat().format() |
RangeError: Invalid time value |
padStartで組み立てる方法では NaN-NaN-NaN という文字列ができてしまいます。整形の前に Number.isNaN(date.getTime()) で判定すると、画面やデータに Invalid Date や NaN が入り込むのを防げます。
function toYmd(date) {
if (!(date instanceof Date) || Number.isNaN(date.getTime())) return "";
return date.toLocaleDateString("sv-SE");
}
どの方法を選ぶかの判断表
| 状況 | 選ぶ方法 |
|---|---|
ブラウザで、利用者の端末の日付を yyyy-mm-dd にしたい |
toLocaleDateString("sv-SE") |
| 形式が厳密に決まった値(APIやデータベースに渡す)を作りたい | padStart、または formatDate 関数 |
| サーバーで、どこに置いても日本の日付にしたい | Intl.DateTimeFormat("sv-SE", { timeZone: "Asia/Tokyo" }) |
| 年・月・日を別々に取り出して並べ替えたい | formatToParts |
| UTCの日付でそろえたい(ログのファイル名など) | toISOString().slice(0, 10) |
| 画面に「2026年10月2日(金)」のように表示したい | toLocaleDateString("ja-JP", { ... }) |
| 和暦で表示したい | "ja-JP-u-ca-japanese" |
| 日付だけ・時刻だけを型として扱いたい | Temporal(対応状況を確認、またはポリフィル) |
| 日付の加減算やタイムゾーン変換が多い | Temporal、または日付ライブラリ |
つまずきやすいポイントのチェックリスト
| 症状 | 原因 | 直し方 |
|---|---|---|
| 朝だけ日付が前日になる | toISOString() はUTC |
toLocaleDateString("sv-SE") などローカルの方法に変える |
| 月が1つずれる | getMonth() は0始まり |
取り出すとき + 1、作るとき - 1 |
2026-10-2 のように0埋めされない |
オプションなしの整形 | padStart(2, "0") または month: "2-digit" |
| 日と曜日を取り違える | getDate() と getDay() |
日は getDate()、曜日は getDay() |
new Date("2026-10-02") が9時になる |
日付だけの文字列はUTC解釈 | 分解して new Date(y, m - 1, d) |
| 環境によって結果が違う | 実行環境のタイムゾーンが違う | timeZone を明示する |
Invalid Date や NaN が表示される |
無効なDateを整形した | Number.isNaN(date.getTime()) で先に判定 |
dateStyle でエラーになる |
year などの個別指定と併用した |
どちらか一方にする |
よくある質問
Q. yyyy-mm-ddにする一番簡単な方法はどれですか?
A. date.toLocaleDateString("sv-SE") です。スウェーデン語ロケールの日付表記が yyyy-mm-dd であることを利用しています。形式が厳密に決まった値を作る場面では、getFullYear()・getMonth() + 1・getDate() と padStart(2, "0") で組み立てる方法が、表記データの変更に左右されず確実です。
Q. toISOString().slice(0, 10) で日付がずれるのはなぜですか?
A. toISOString() が常にUTCで文字列を作るためです。日本時間はUTCより9時間進んでいるので、0時〜8時台はUTCではまだ前日です。今回の実行でも、日本時間10月2日の0時と8時は 2026-10-01、9時以降は 2026-10-02 になりました。ローカルの日付が欲しいときは toLocaleDateString("sv-SE") などに置き換えます。
Q. サーバーとブラウザで日付が違うのはなぜですか?
A. 実行環境のタイムゾーンが違うためです。サーバーはUTC、ブラウザは日本時間、という組み合わせがよくあります。どこで動いても同じ結果にしたいときは、Intl.DateTimeFormat に timeZone: "Asia/Tokyo" のようにタイムゾーンを明示します。
Q. 日付ライブラリは使ったほうがよいですか? A. 表示用の整形だけなら、この記事の標準機能で足ります。日付の加減算、期間の計算、複数のタイムゾーンの変換が多い場合は、Temporal(ポリフィルを含む)や日付ライブラリを使うと、コードが短く読みやすくなります。
まとめ
- yyyy-mm-ddの最短は
toLocaleDateString("sv-SE")。 形式を厳密に固定したいならpadStartで組み立てます。 toISOString()は常にUTC。 日本時間の0時〜8時台は前日の日付になります。- どこの日付が欲しいかを先に決める。 端末の日付、日本の日付、UTCの日付で使う方法が変わります。
- 月は0始まり。 作るとき
- 1、取り出すとき+ 1です。 new Date("2026-10-02")はUTCの0時。 ローカルの0時にしたいなら、分解して数値で渡します。- 曜日・和暦は
toLocaleDateStringのオプションで。 和暦は"ja-JP-u-ca-japanese"を指定します。 - 整形の前に有効な日付か確かめる。
Number.isNaN(date.getTime())で判定できます。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「JavaScript日付フォーマット|yyyy-mm-dd変換6選とズレ対策」
株式会社RJCでは、JavaScriptの日付フォーマットやタイムゾーンの扱い方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。