BugForge
All lessons
Node.js · 07/19 7 min

Dates, timestamps and money in JS

Zero-based months, UTC vs local, seconds vs milliseconds, and why money is integer cents.

Timestamps: know the unit

Source Unit
Date.now(), date.getTime() milliseconds
Unix time, JWT exp/iat, most webhook headers seconds
const ageSec = Math.floor(Date.now() / 1000) - Number(req.headers["x-timestamp"]);
if (Math.abs(ageSec) > 300) return res.status(400).end();  // 5-minute tolerance

Mixing them is off by a factor of 1000: everything looks "expired" or nothing ever does.

Months are zero-based

new Date(2024, 0, 31);        // 31 January
d.getMonth() + 1;             // human month number
d.getDay();                   // day of WEEK (0 = Sunday), not day of month → getDate()

UTC vs local

new Date("2024-03-01");          // date-only string → parsed as UTC midnight
new Date("2024-03-01T00:00");    // date-time without offset → LOCAL midnight
d.toISOString();                 // always UTC, ends in Z
d.getUTCMonth(); d.getMonth();   // UTC vs local; reports should pick one and stick to it

Grouping sales by day with getDate() on a server in another timezone shifts rows into the wrong bucket. Use the getUTC* family for server-side reports.

Date ranges

An "end date" of 2024-03-31 stored as midnight excludes everything after 00:00 that day. Use a half-open range:

{ createdAt: { $gte: start, $lt: dayAfter(end) } }

Money

0.1 + 0.2;                    // 0.30000000000000004
(1.005).toFixed(2);           // "1.00"  (1.005 is really 1.00499999…)
(19.99).toFixed(2) + 1;       // "19.991"  toFixed returns a STRING
  • Store and compute in integer cents; format only at the edge: (cents / 100).toFixed(2).
  • Apply discounts and tax with one explicit rounding step: Math.round(cents * rate).
  • Sum line items as price * qty in cents, then round once, not per step.
Next in Node.js
Reading request input safely