はじめに
開発中、データベースから取得したデータ(transactions)と今日の日付(date)を比較する処理で、意図せず一致してしまう現象に遭遇しました。
const dayStr = date.toISOString().split("T")[0];
const txs = transactions.filter(
(tx) => tx.date.slice(0, 10) === dayStr
);
例えば以下のケースで一致してしまいます。
- transactions の date:2025-08-12
- date:2025-08-13
今回は、この原因と解決策を共有します。
原因
結論から言うと、Date オブジェクトの内部表現とtoISOString() の仕様が原因です。
JavaScript の Date オブジェクトの内部表現
JavaScript の Date は内部的に UTC(協定世界時) で保持されます。
ただし、toString() などで出力するときにはローカルタイムに変換されます。
const now = new Date('2025-08-12T00:00:00');
console.log(now.toString());
// 例: "Tue Aug 12 2025 00:00:00 GMT+0900 (日本標準時)"
どのタイムゾーンで作っても、内部的には同じ UTC のタイムスタンプが保持されています。
toISOString() は常に UTC
toISOString() は常に UTC で返します。
例えば、日本時間(UTC+9)で 2025-08-12T00:00:00 を作成しても…
const d = new Date('2025-08-12T00:00:00');
console.log(d.toISOString());
// "2025-08-11T15:00:00.000Z"
Z は UTC を意味します。つまり、日本時間の真夜中は UTC だと前日の午後になります。
今回の事例
今回のコードでは、date に日本時間で "2025-08-13" が入っていました。
const dayStr = date.toISOString().split("T")[0];
しかし date.toISOString() は "2025-08-12T15:00:00.000Z" を返すため、dayStr は "2025-08-12" になります。
一方で、DB から取得した tx.date は "2025-08-12"(日本時間基準)。
その結果、実際には日付が異なるのに、一致してしまう現象が発生しました。
解決策
ローカル日付を扱いたい場合は、getFullYear() / getMonth() / getDate() を使って日付文字列を組み立てるのが安全です。
const dayStr = [
date.getFullYear(),
String(date.getMonth() + 1).padStart(2, "0"),
String(date.getDate()).padStart(2, "0"),
].join("-");
const txs = transactions.filter(
(tx) => tx.date.slice(0, 10) === dayStr
);
これなら、タイムゾーンに依存せずローカル日付で比較できます。
まとめ
- Date は内部的には UTC で保持される
- toISOString() は常に UTC で返す
- ローカル日付を使いたいときは getFullYear() / getMonth() / getDate() を使う
この違いを意識すれば、カレンダーや日付表示での 「1日ずれ」問題 を防げる