朝7時に日記を書いたのに、アプリの画面には「昨日の記録」と出てしまい、連続日数のカウントが 12 から 1 に戻ってしまう——日記や習慣の記録アプリを AI に作らせた方から、こうした相談をいただくことがあります。
普段は動いているのに、ときどきだけ間違えるのです。この種の不具合は、画面を何度見直しても見つかりません。私自身、最初は画面側の表示を疑って、遠回りをしました。原因はたいてい、画面ではなく「日付をどう文字にしているか」の一行にあります。
まず、起きている現象を確かめます
日本のスマートフォンで起きるのは、午前0時から午前9時までの記録です。この時間帯だけ、アプリが「昨日」と判断してしまいます。9時を過ぎると何事もなかったように直るため、夜に確かめると再現しません。
原因の一行は、AI が書きがちな次の形です。
// 日付キー("2026-11-07" のような文字)を作る関数。よく生成される形
const dateKey = (d) => d.toISOString().slice(0, 10);toISOString() は、端末の時刻ではなく世界標準時(UTC)に直した文字を返します。日本は UTC より 9 時間進んでいるので、日本時間の午前 8 時 59 分は、UTC ではまだ前の日の 23 時 59 分です。
Node 22 で、同じ瞬間を3通りに直してみました
端末の時刻設定を日本時間にして、下のスクリプトを動かしました。
// dk.mjs — 同じ瞬間を日付キーに直す 3 つの書き方
const pad = (n) => String(n).padStart(2, "0");
const keyUtc = (d) => d.toISOString().slice(0, 10);
const keyLocal = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
const keyIntl = (d, tz) => new Intl.DateTimeFormat("sv-SE", { timeZone: tz }).format(d);
const samples = ["2026-11-07T00:20:00+09:00", "2026-11-07T08:59:00+09:00",
"2026-11-07T09:00:00+09:00", "2026-11-07T23:50:00+09:00"];
for (const s of samples) {
const d = new Date(s);
console.log(s.slice(11, 16), "utc:", keyUtc(d), "local:", keyLocal(d), "intl:", keyIntl(d, "Asia/Tokyo"));
}TZ=Asia/Tokyo node dk.mjs の出力は次のとおりでした。
00:20 utc: 2026-11-06 local: 2026-11-07 intl: 2026-11-07
08:59 utc: 2026-11-06 local: 2026-11-07 intl: 2026-11-07
09:00 utc: 2026-11-07 local: 2026-11-07 intl: 2026-11-07
23:50 utc: 2026-11-07 local: 2026-11-07 intl: 2026-11-07
午前 0 時 20 分と 8 時 59 分で、utc の列だけが 11 月 6 日になっています。9 時ちょうどで 11 月 7 日に切り替わる、という挙動が、先ほどの相談とそのまま重なります。
連続日数が途切れるのは、「昨日」の求め方も同じ形だからです
日付キーだけ直しても、連続日数がまだずれることがあります。「昨日のキー」を作る行が、次のようになっている場合です。
// 昨日のキー。UTC のキーから 24 時間を引いている
const bad = (d) => keyUtc(new Date(d.getTime() - 86400000));
// 端末の暦で一日戻す書き方
const yesterdayKey = (d) => { const y = new Date(d); y.setDate(y.getDate() - 1); return keyLocal(y); };11 月 7 日の午前 0 時 20 分で試すと、yesterdayKey は 2026-11-06 を返しました。bad は 2026-11-05 を返しています。UTC のキーがすでに一日ずれているところへ、さらに 24 時間を引くので、二日分戻ってしまうのです。「今日の記録はあるのに、昨日が空」と判断され、連続日数が 1 に戻ります。
時刻を 24 時間で引く書き方は、夏時間のある地域でも一日がずれることがあります。setDate のように暦の上で一日戻す書き方なら、その心配は減ります。
直す方針は、日付キーの作り方を一か所に決めることです
手を入れる場所は多くありません。
- 日付キーを作る関数を、アプリの中で一つだけにします。
- その関数は、端末の暦(
getFullYearなどの組み合わせ)か、Intl.DateTimeFormatに時間帯を明示して作ります。 toISOString().slice(0, 10)は、日付キーには使いません。サーバーへ時刻そのものを送るときの形式として使うものです。
端末の暦と Intl のどちらを選ぶかは、「利用者が海外に出たら、日付の切り替わりはどこの時刻を基準にしたいか」で決めます。旅行先でも自分の生活の暦で数えたいなら端末の暦、日本時間で固定したいなら Asia/Tokyo を明示します。
日付の文字は、一か所の関数で、時間帯を決めてから作ります。 この線引きを、私は記録系アプリの最初の指示に入れるようにしています。
AI にそのまま渡せる指示文です
すでに作ったアプリを直すときは、次のように頼むと、探す範囲が絞れます。
日付キー(YYYY-MM-DD の文字)を作っている箇所をすべて探して、
共通の関数 toDateKey に一本化してください。
toDateKey は端末の暦(ローカルの年月日)で作り、toISOString().slice(0, 10) は使わないでください。
「昨日」「今日」の判定も、この関数と setDate(getDate() - 1) だけで行ってください。
直したあと、午前0時20分・午前8時59分・午前9時ちょうど・午後11時50分の4つの時刻で
連続日数が途切れないことを、テストコードで確かめてください。
最後の一文は省かないでください。画面を目で見るだけでは、午前 9 時までの挙動を確かめにくいと、いま思えば当たり前のことに、私は後から気づきました。
確かめたこと、確かめていないこと
ここまでの出力は、Node 22.23 で、時間帯を日本時間と世界標準時に切り替えて動かした結果です。世界標準時のまま動かすと、local の列も utc と同じ 11 月 6 日になりました。つまり端末の暦を使う書き方は、端末の設定に素直に従います。
一方で、実機の iPhone や Android で、Rork が出した画面と組み合わせて動かした確認は、この記事ではしていません。アプリの中で日付を扱う場所が複数ある場合は、上の指示文で一本化したあとに、ご自身の端末で朝の時間帯に一度だけ試していただければと思います。
同じ「記録の境目」の話は、家計簿アプリの最初の指示に足す三行でも月の境目として触れています。指示を小さく足していく進め方は機能を一つずつ足すプロンプトの書き方にまとめました。
個人開発で日替わりの表示や記録を扱っていると、日付の切り替わりは避けて通れない箇所なのだと思います。まずは手元のアプリで toISOString を検索して、日付キーに使っている行がないかを探すところから始めていただければと思います。