◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SONNET 4.5 — Claude API での提供終了は 11/30、残り32日。API 連携を足したアプリは移行先の確認を●SDK 58 ISSUE — Android ウィジェットが定期更新されない、Stripe が静的リンクになるなど初期報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SONNET 4.5 — Claude API での提供終了は 11/30、残り32日。API 連携を足したアプリは移行先の確認を●SDK 58 ISSUE — Android ウィジェットが定期更新されない、Stripe が静的リンクになるなど初期報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/Rork入門
◈ Rork入門/2026-10-29初級

家計簿アプリの最初の指示で、あとから効く3つの一行。金額は整数の円、月はローカル日付、入力は先に整える

家計簿のような記録アプリを AI に作らせる最初の指示に、金額の持ち方・月の境目・入力欄の整え方を一行ずつ足すだけで、月末の集計ずれを防げます。Node 22 で動かして確かめた挙動と、そのまま使える関数を載せています。

Rork610初心者24家計簿アプリ最初の指示日付集計

月初の午前0時半に1,200円の支出を入れたのに、画面の「10月の合計」が増えている——記録系のアプリで、いちばん最初に気づきにくい不具合は、この種類のものだと思います。

家計簿のように「入力して、保存して、月ごとに足す」アプリは、Rork のようなツールに最初の指示を出すとたいてい動くかたちで出てきます。ただ、動くことと、月末に数字が合うことは別の話です。最初の指示に一行ずつ足しておくと後から効く点は3つあり、金額・月の境目・入力欄の順に、手を動かして確かめました。

個人開発の合間に試した小さな検証ですが、先にお伝えしておきたいのは、ここで載せるコードを私自身が確かめたのは Node 22 の上までだということです。Rork が実際に生成するコードはその時々で違いますので、「生成されたコードがこの3点を守っているか」を見る物差しとして使っていただければと思います。

1. 金額は「円の整数」で持つ

小数で金額を持つと、足し算のたびに小さなずれが混ざります。手元で確かめると、0.1 + 0.2 + 0.3 は 0.6 にならず 0.6000000000000001 になりました。1980.1・320.2・99.9 の3つを足すと 2400.2 ちょうどに見えますが、これは偶然にそう見えているだけで、件数が増えれば表示のどこかで端数が顔を出します。

円で記録するなら、そもそも小数は要りません。最初の指示に次の一行を足します。

金額は円の整数(number)で保存し、小数は使わない。表示のときだけ桁区切りを付ける。

外貨を扱う場合は、最小単位(セント)の整数で持つのが同じ考え方です。

2. 月の境目は「ローカル日付」から作る

月ごとの合計を出すとき、生成されたコードがよく使うのが toISOString().slice(0, 7) です。これは UTC の時刻から月を切り出します。日本の端末で 11月1日 00:30 の支出を入れると、次のようになりました。

const spent = new Date("2026-11-01T00:30:00+09:00");
spent.toISOString().slice(0, 7); // "2026-10"  ← UTC の月
spent.getMonth() + 1;            // 11         ← 端末のローカルの月(TZ=Asia/Tokyo)

日本時間の午前9時までの支出は、UTC ではまだ前日です。だから毎月1日の朝9時前の記録だけが、前の月に入ってしまいます。月末に「1件だけ合わない」不具合が出る場合、ほぼこの形です。

最初の指示にはこう書いておきます。

月ごとの集計キーは、端末のローカル日付(getFullYear / getMonth)から作る。toISOString は集計に使わない。

3. 入力欄の文字列は、保存の前に一度だけ整える

金額の入力欄に打たれる文字は、数字だけとは限りません。JavaScript の Number と parseInt に通すと、次のように割れます。

入力NumberparseInt
1,200NaN1
1200(全角)NaNNaN
1200円NaN1200
(空欄)0NaN
12.512.512

「1,200」が parseInt では 1 になる点は、見落とすと厄介です。エラーにならないまま、1円として保存されてしまいます。空欄が Number で 0 になるのも同じで、0円の支出が静かに積み上がります。

3つをまとめた関数と、動かして確かめたテスト

次の関数は、3点を一か所に集めたものです。

// ledger.mjs
// 金額は「円の整数」で持つ。入力文字列はここで一度だけ整える。
export function parseYen(input) {
  const half = String(input)
    .replace(/[0-9]/g, (c) => String.fromCharCode(c.charCodeAt(0) - 0xfee0))
    .replace(/[,、,\s円¥¥]/g, "");
  if (!/^\d{1,9}$/.test(half)) return null; // 空・小数・文字混じりは受け付けない
  return Number(half);
}
 
// 月のキーは端末のローカル日付から作る(toISOString は UTC なので使わない)
export function monthKey(date) {
  const m = String(date.getMonth() + 1).padStart(2, "0");
  return `${date.getFullYear()}-${m}`;
}
 
export function totalsByMonth(entries) {
  const totals = {};
  for (const e of entries) {
    const key = monthKey(new Date(e.spentAt));
    totals[key] = (totals[key] ?? 0) + e.amountYen;
  }
  return totals;
}

テストは次のとおりです。

// ledger.test.mjs
import assert from "node:assert/strict";
import { parseYen, totalsByMonth } from "./ledger.mjs";
 
assert.equal(parseYen("1,200"), 1200);
assert.equal(parseYen("1200"), 1200);
assert.equal(parseYen("1200円"), 1200);
assert.equal(parseYen(" ¥1,200 "), 1200);
assert.equal(parseYen(""), null);
assert.equal(parseYen("12.5"), null);
 
const entries = [
  { spentAt: "2026-10-31T23:50:00+09:00", amountYen: 980 },
  { spentAt: "2026-11-01T00:30:00+09:00", amountYen: 1200 },
  { spentAt: "2026-11-15T12:00:00+09:00", amountYen: 300 },
];
assert.deepEqual(totalsByMonth(entries), { "2026-10": 980, "2026-11": 1500 });

TZ=Asia/Tokyo node ledger.test.mjs では、すべて通りました。

想定外だった点:同じテストが、タイムゾーンしだいで落ちる

同じテストを TZ=UTC で動かすと、集計のところで落ちました。結果は 2026-10 が 2180、2026-11 が 300 です。11月1日 00:30(日本時間)の1,200円が、UTC の端末では 10月31日 15:30 として数えられるためです。

これは関数の間違いではなく、「月はその人の端末の暮らしている時間で決まる」という仕様どおりの動きです。ただ、テストを海外のサーバーや CI で動かすと、手元で通っていたものが落ちて戸惑います。テストの先頭で TZ を固定しておくのが、私の手元でいちばん手間のかからない対処でした。

最初の指示に、そのまま貼れる三行

金額は円の整数(number)で保存し、小数は使わない。
月ごとの集計キーは端末のローカル日付から作り、toISOString は集計に使わない。
金額の入力は、全角数字・カンマ・「円」を取り除いてから数字だけか確かめ、ダメなら保存せずに入力欄へ戻す。

あとから直す指示は、一度生成された画面に手を入れることになります。最初から三行入れておくほうが、直しの回数が減ります。指示を小さく足していく進め方は 機能を一つずつ足すプロンプトの書き方 に、画面まわりの指示は UI 生成のプロンプトのパターン にまとめています。

付け加えるなら、同じ「月のずれ」は支出だけでなく、個人開発のアプリで広告収益や課金の売上を月別に並べる場面でも起こります。集計の前に、月のキーをどの時刻から作っているかを一度見ておくと安心です。

数字を扱うアプリは、持ち方と区切り方を最初に決めておきます。 個人開発で自分のアプリを作るときも、この線引きは変わりません。まずは今お手元の記録アプリで、毎月1日の朝9時前に入れた記録が、どの月に入っているかを一件だけ確かめてみていただければと思います。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

◈ Rork 入門2026-08-17
Rork のコードをエクスポートしたら、git init より先に .gitignore を用意します
Rork からエクスポートしたプロジェクトをそのまま git init すると、鍵や .env まで履歴に載ります。除外設定を先に置いた場合と後から足した場合の差を実際に測り、効く書き方と効かない書き方を確かめました。
◈ Rork 入門2026-03-21
Rork でアプリがビルドできないとき、最初に切り分ける 8 つの症状
Rork でアプリがビルドできない、白い画面のまま進まない、ホットリロードが効かない。そんな症状を「どの層の問題か」から切り分ける FAQ です。依存関係の競合、Expo モジュール未解決、シミュレーター、生成フリーズ、Fix Now の使いどころまで 8 問で整理します。
◈ Rork 入門2026-10-29
一覧が空なのにエラーが出ないとき、直してと頼む前に Rork の Logs パネルを開きます
Rork のプレビューで一覧が空なのに赤いエラーが出ないとき、同じ修正依頼を重ねる前に Logs パネルへ値を出させて原因を三つに分ける手順を、依頼文の型とあわせてお伝えします。