Format a Date as YYYY-MM-DD in JavaScript

To format a date as YYYY-MM-DD for the user's day, read the calendar fields in the intended zone (or use Temporal.PlainDate.toString()) — toISOString().slice(0,10) gives the UTC day, which can be off by one. Part of UTC vs Local Time.

Why this scenario is tricky

new Date().toISOString().slice(0, 10) is the popular one-liner, but it returns the UTC calendar day. For a user a few hours west of UTC late in the evening, that is tomorrow's date; a few hours east early in the morning, yesterday's. The fix is to format from the fields in the zone you actually mean.

toISOString gives the UTC daySlicing the ISO string ignores the user's zonetoISOString gives the UTC daytoISOString().slice(0,10)UTC day — off by onePlainDate in the user's zonethe day they seeFormat from zoned fields, not from the UTC serialization.

Minimal working solution

import { Temporal } from '@js-temporal/polyfill';
// Today's civil date in the user's zone, already YYYY-MM-DD.
const ymd = Temporal.Now.plainDateISO('America/New_York').toString();

Zoned civil datePlainDate prints YYYY-MM-DD directlyZoned civil dateuser zoneplainDateISOtoString()

Full production version

// From an existing instant, project into a zone first.
import { Temporal } from '@js-temporal/polyfill';
function toYMD(instant: Temporal.Instant, zone: string): string {
  return instant.toZonedDateTimeISO(zone).toPlainDate().toString();
}

Instant to YMDProject to a zone, then take the dateInstant to YMDInstanttoZonedDateTimeISOtoPlainDatetoString

Verification snippet

Format a Date as YYYY-MM-DD in JavaScript — assertionsKey cases assert correctlyAssertions that prove the edge caselate-evening US userlocal day, not UTCPlainDate.toString()'2024-03-15'padded months'03' not '3'no time/zonecivil only

Common pitfalls

Format as YYYY-MM-DD pitfallsCommon mistakes and their fixesWrongRighttoISOString().slice(0,10)UTC dayPlainDate in the right zoneuser's daybuild from getMonth()+1 unpadded'2024-3-5'toString pads to 2 digitsvalid YMD

Frequently Asked Questions

Why is my YYYY-MM-DD date off by one day?

You formatted with toISOString(), which returns the UTC calendar day. For users not in UTC that can be the previous or next day. Format from the date fields in the user's zone, for example Temporal.Now.plainDateISO(zone).toString().

What is the simplest correct way to get YYYY-MM-DD?

If you want today in a zone, Temporal.Now.plainDateISO(zone).toString() already returns YYYY-MM-DD with zero-padding. From an existing instant, project it into the zone with toZonedDateTimeISO(zone) and call toPlainDate().toString().