A user reported that their mindfulness minutes showed "0" in the mobile app despite completing meditation sessions on the web. The culprit was one of JavaScript's most common traps.

The Bug

const dateString = new Date().toISOString().split('T')[0];

Looks innocent -- get today's date as YYYY-MM-DD. But toISOString() converts to UTC before formatting.

A user in CST (UTC-6) at 6:00 PM on December 15th:

new Date().toISOString();              // "2025-12-16T00:00:00.000Z"
new Date().toISOString().split('T')[0]; // "2025-12-16" -- WRONG

Their local date is December 15th, but the code returns December 16th because UTC is 6 hours ahead.

The Impact

The mobile app queried the API for the wrong date. User completes meditation at 6 PM on Dec 15, web saves it to Dec 15, mobile queries for Dec 16, backend returns zero. The bug only hits users in timezones behind UTC, and only during certain hours -- making it hard to reproduce.

The Fix

function formatLocalDate(date) {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}

I audited the entire codebase and found 6 places using the broken pattern:

File Impact
mindfulness.ts Wrong day queried
healthkit.ts (3 places) Data attributed to wrong day
MeditationScreen.tsx Sessions saved to wrong day
widgetDataService.ts Widget shows wrong date

All replaced with a shared formatLocalDate() utility.

When toISOString IS Right

It's not always wrong -- it's correct for timestamps (exact moments in time), server communication expecting UTC, and logging. It's wrong whenever you're extracting just the date portion to mean "today."

Takeaway

toISOString() always converts to UTC. That's by design. If you're anywhere behind UTC and it's past midnight in London, .split('T')[0] returns tomorrow. Search your codebase for .toISOString().split('T') -- you might be surprised.