◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 32 days left. If your app calls it, check the migration target●SDK 58 ISSUE — Early reports include Android widgets not refreshing on a schedule and Stripe linking statically●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 32 days left. If your app calls it, check the migration target●SDK 58 ISSUE — Early reports include Android widgets not refreshing on a schedule and Stripe linking statically●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Getting Started
◈ Getting Started/2026-10-29Beginner

Three One-Line Additions to Your First Budget-App Prompt: Integer Yen, Local-Date Months, Normalized Input

Adding one line each about how money is stored, where a month begins, and how input is cleaned can prevent month-end totals from drifting in a household-budget app. Behavior checked on Node 22, with a small function you can reuse.

Rork610beginner22budget appfirst promptdatestotals

You log a 1,200 yen expense at 12:30 a.m. on the first of the month, and the screen's "October total" goes up instead of November's. In record-keeping apps, this is the kind of bug that tends to go unnoticed longest.

Apps that let you enter an amount, save it, and sum by month usually come out of a tool like Rork looking fine on the first try. Working and adding up correctly at month-end are different things, though. Here are three lines worth adding to the first prompt, checked in order: how money is stored, where a month begins, and how input is cleaned.

One caveat up front: I only verified the code below on Node 22. What Rork actually generates will vary, so please treat this as a yardstick for reading generated code, not as a drop-in.

1. Store money as integer yen

Decimals add tiny errors as you sum. In my run, 0.1 + 0.2 + 0.3 gave 0.6000000000000001, not 0.6. Summing 1980.1, 320.2 and 99.9 happens to look like exactly 2400.2, but that is luck; with more entries, stray digits show up somewhere.

For yen you never need decimals. Add this to the first prompt:

Store amounts as integer yen (number). No decimals. Format with thousands separators only when displaying.

For foreign currencies, the same idea means storing the smallest unit (cents) as an integer.

2. Build the month key from the local date

Generated code often groups by month with toISOString().slice(0, 7), which reads the month from UTC. On a device in Japan, an expense at 00:30 on November 1:

const spent = new Date("2026-11-01T00:30:00+09:00");
spent.toISOString().slice(0, 7); // "2026-10"  <- the UTC month
spent.getMonth() + 1;            // 11         <- the device's local month (TZ=Asia/Tokyo)

Before 9 a.m. Japan time it is still the previous day in UTC, so only entries logged early on the first of the month land in the previous month. If your totals are off by exactly one entry at month-end, this is the usual shape.

Build the monthly aggregation key from the device's local date (getFullYear / getMonth). Never use toISOString for aggregation.

3. Clean the input once, before saving

What people type into an amount field is not always digits. Passing it through Number and parseInt splits like this:

InputNumberparseInt
1,200NaN1
1200 (full-width)NaNNaN
1200円NaN1200
(empty)0NaN
12.512.512

The one to watch is "1,200" becoming 1 under parseInt: no error, just a 1 yen entry saved. An empty field becoming 0 under Number works the same way, quietly piling up 0 yen expenses.

One function for all three, and the test I ran

// ledger.mjs
// Money is integer yen. Input strings are normalized here, once.
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; // empty, decimal, or mixed text is rejected
  return Number(half);
}
 
// Month key comes from the local date (toISOString is UTC, so avoid it)
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 });

Everything passed with TZ=Asia/Tokyo node ledger.test.mjs.

The surprise: the same test fails under another time zone

Running it with TZ=UTC failed at the aggregation step: 2026-10 came out as 2180 and 2026-11 as 300. The 1,200 yen entry from 00:30 on November 1 (Japan time) is October 31, 15:30 on a UTC device.

The function isn't wrong; a month is defined by where the person lives, and that is the intended behavior. But it will confuse you when a test that passes on your laptop fails on an overseas server or in CI. Pinning TZ at the top of the test was the cheapest fix I found.

Three lines to paste into the first prompt

Store amounts as integer yen (number); no decimals.
Build monthly aggregation keys from the device's local date; never use toISOString for aggregation.
For amount input, strip full-width digits, commas and "円", check that only digits remain, and if not, don't save and return to the field.

Fixing these later means editing screens that were already generated, so putting them in the first prompt saves rounds. For adding requests in small steps, see writing prompts that add one feature at a time, and for screen-level instructions, UI generation prompt patterns.

For apps that handle numbers, decide how to hold them and where to cut them first. As an indie developer I'd draw that line the same way for any app. A good next step is to take whatever record-keeping app you have now, and check which month an entry made before 9 a.m. on the first landed in.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

◈ Getting Started2026-08-17
Write .gitignore Before You Run git init on an Exported Rork Project
Running git init on a freshly exported Rork project puts your signing keys and .env straight into history. I measured what gets committed when the ignore file comes first versus last, and which exclusion patterns actually work.
◈ Getting Started2026-10-29
When a list is empty and no error shows, open Rork's Logs panel before asking for a fix
A Rork preview shows an empty list but no red error. Before repeating the same fix request, have the app print its values to the Logs panel and split the cause into three places.
◈ Getting Started2026-10-28
Before You Change Plans, Check That Your Re-Cloned Code Can Be Rebuilt Without Rork
Exporting Rork code to GitHub doesn't guarantee you can rebuild it elsewhere. A Python script that checks lock files, app identifiers, images, import targets and env-var names in a fresh clone, plus an honest note on what was and wasn't tested.