Skip to content

/commands/javascript/format-date-yyyy-mm-dd

javascript format date as YYYY-MM-DD

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · v24.20.0updated
tool
JavaScript
versions
ES5+ (toISOString), Intl.DateTimeFormat: all current runtimes
os
any

For a YYYY-MM-DD string in UTC, use date.toISOString().slice(0, 10). For the local date (what the user's clock shows), build it from the parts: `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`, or let Intl.DateTimeFormat with the en-CA locale or formatToParts do it for a chosen time zone. The two can differ by a day around midnight.

UTC, local and a specific time zone

The example sets process.env.TZ at the top of the script (Node.js honours it at run time) so the output is the same on every machine; in real code the local time zone comes from the system.

bash
cat > date.mjs <<'EOF'
process.env.TZ = "Europe/Rome"; // pin the local zone for this example
const d = new Date("2026-03-01T23:30:00Z"); // 23:30 UTC on 1 March

console.log("UTC (toISOString):", d.toISOString().slice(0, 10));

const pad = (n) => String(n).padStart(2, "0");
const local = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
console.log("local parts:     ", local, `(TZ=${process.env.TZ})`);

const tokyo = new Intl.DateTimeFormat("en-CA", { timeZone: "Asia/Tokyo" }).format(d);
console.log("en-CA in Tokyo:  ", tokyo);

const parts = Object.fromEntries(
  new Intl.DateTimeFormat("en-US", { timeZone: "America/New_York", year: "numeric", month: "2-digit", day: "2-digit" })
    .formatToParts(d)
    .map((p) => [p.type, p.value]),
);
console.log("parts in New York:", `${parts.year}-${parts.month}-${parts.day}`);

try {
  new Date("31/12/2026").toISOString();
} catch (e) {
  console.log(`${e.name}: ${e.message}`);
}
EOF
node date.mjs

Output

UTC (toISOString): 2026-03-01
local parts:      2026-03-02 (TZ=Europe/Rome)
en-CA in Tokyo:   2026-03-02
parts in New York: 2026-03-01
RangeError: Invalid time value

At 23:30 UTC it is already 2 March in Rome and Tokyo, but still 1 March in UTC and New York. That is the classic "off by one day" bug: toISOString() is always UTC.

Options

CodeResultTime zone
d.toISOString().slice(0, 10)2026-03-01Always UTC
getFullYear / getMonth() + 1 / getDate with padStartLocal YYYY-MM-DDThe runtime time zone (TZ or system setting)
new Intl.DateTimeFormat("en-CA").format(d)YYYY-MM-DDLocal, or the timeZone option
Intl.DateTimeFormat(...).formatToParts(d)Separate year, month, dayAny IANA zone; does not depend on a locale pattern
d.toLocaleDateString("sv-SE")YYYY-MM-DDLocal, or the timeZone option
getUTCFullYear / getUTCMonth / getUTCDateUTC partsAlways UTC, like toISOString

Pitfalls

  • Parsing a date-only string. new Date("2026-03-01") is read as midnight UTC, while new Date("2026-03-01T00:00") is midnight local time. Formatting the first with local getters west of UTC prints the previous day.
  • Locale patterns can change. en-CA and sv-SE happen to format as YYYY-MM-DD; formatToParts does not depend on any locale's pattern and is the safer choice for machine-readable output.
  • Invalid dates. toISOString() throws RangeError: Invalid time value on an invalid Date; the local getters return NaN, which prints as NaN-NaN-NaN.
  • Missing values. Calling .toISOString() on undefined throws Cannot read properties of undefined.

Confirm it worked

Test with a time close to midnight, as above, and in at least two time zones (TZ=America/New_York node app.js on Linux and macOS). If both print the date you expect, the formatting handles time zones correctly.

FAQ

Why is toISOString() one day off?

toISOString always returns the date in UTC. Shortly after midnight in a time zone east of UTC, or late in the evening west of it, the UTC date is a different day from the local one. Format the local parts with getFullYear, getMonth and getDate instead.

Why is getMonth() one less than the month?

getMonth() is zero-based: January is 0 and December is 11. Add 1 before padding it to two digits.

What throws RangeError: Invalid time value?

toISOString() on an invalid Date, for example new Date("31/12/2026"), which the parser does not understand. Check with Number.isNaN(date.getTime()) before formatting.

Sources