Lesstep
Japanese dates · Intl.DateTimeFormat · Checked October 29, 2026

Dates that come out wrong for Japanese users

Three things go wrong with dates in a Japanese app: the order (Japanese dates start with the year, so 10/29/2026 reads as an American date), the era that some Japanese documents use, and a date that is saved one day early, because Japan is nine hours ahead of UTC. The Intl API in every browser and in Node.js handles the first two, and the third takes one rule: keep a date as a YYYY-MM-DD string.

Year, month, day

Pass the date to Intl.DateTimeFormat with the user's language instead of building it from pieces. In Node.js 24 and in Chromium 152:

const d = new Date(2026, 9, 29);
const fmt = (o) =>
  new Intl.DateTimeFormat("ja-JP", o);

fmt({ dateStyle: "medium" }).format(d);
// 2026/10/29
fmt({ dateStyle: "long" }).format(d);
// 2026年10月29日
fmt({ dateStyle: "full" }).format(d);
// 2026年10月29日木曜日
fmt({ month: "long", day: "numeric",
  weekday: "short" }).format(d);
// 10月29日(木)

The last one puts the weekday in parentheses, as Day.js's Japanese llll format does too. The same date with "en-US" is 10/29/2026, so a format string such as "{month}/{day}/{year}" in your translation file keeps the American order in Japanese.

Date libraries have the same formats in their Japanese locale. From their locale files: date-fns's ja gives y/MM/dd for P and y年M月d日 for PPP; Day.js's ja gives YYYY/MM/DD for L and YYYY年M月D日 for LL. Use those tokens, not a fixed pattern like MM/dd/yyyy.

The era: 令和

Japan also counts years by the emperor's era. 2026 is 令和8年, Reiwa 8. Intl has the calendar built in, with the ca extension in the language tag (or the calendar option):

const era = new Intl.DateTimeFormat(
  "ja-JP-u-ca-japanese",
  { dateStyle: "long" },
);
era.format(d); // 令和8年10月29日

Two things to know before you use it:

Show the era where your Japanese users expect it, and keep the default elsewhere: "ja-JP" alone uses the Western year.

The day that comes out one early

A date picker that gives you a Date gives you local midnight. In Japan, midnight on October 29 is 15:00 on October 28 in UTC, and toISOString() always answers in UTC (MDN):

new Date(2026, 9, 29).toISOString();
// "2026-10-28T15:00:00.000Z"

Code that keeps the first ten characters of that, or sends the Date as JSON and lets the server take the date part, saves October 28 for every user in Japan. You don't see it in a time zone behind UTC, such as anywhere in the Americas, where local midnight is still the same date in UTC. Parsing has the opposite rule: in new Date("2026-10-29"), "date-only forms are interpreted as a UTC time and date-time forms are interpreted as a local time" (MDN).

The fix is to keep a date that has no time as a string. <input type="date"> already gives you one: its value "is always formatted yyyy-mm-dd" (MDN). Store and send that string, not a Date. When you do have a Date from the user's own clock, build the string from its local parts:

function toDateString(d) {
  const m = String(d.getMonth() + 1);
  const day = String(d.getDate());
  return [d.getFullYear(),
    m.padStart(2, "0"),
    day.padStart(2, "0")].join("-");
}

Servers usually run in UTC, so from midnight to 9 a.m. in Japan, "today" on the server is still yesterday. Give Intl the time zone when you write a date for someone in Japan:

new Intl.DateTimeFormat("ja-JP", {
  dateStyle: "long",
  timeZone: "Asia/Tokyo",
}).format(new Date());

At 8:30 a.m. in Tokyo on October 29, that gives 2026年10月29日; with timeZone: "UTC" it gives 2026年10月28日. In a browser, leave timeZone out: the default is the user's own.

Date inputs and calendars

<input type="date"> is displayed "based on the locale of the user's browser", not by your page: in Chromium 152 with Japanese as the browser language, it showed 2026/10/29, and 年/月/日 when empty, under both lang="en" and lang="ja". You can't see what a Japanese user sees by switching your page to Japanese.

If people type dates into a text field, expect 2026/10/29, 10月29日 and full-width digits. A usability audit of one Japanese app (report) found dates in full-width digits and in the 月日 form rejected, and the input method turning / into ・. Normalize the text first (how), then parse.

A calendar of your own should start the week on Sunday for Japan. In the Unicode CLDR data, new Intl.Locale("ja-JP").getWeekInfo().firstDay is 7 (Sunday) in Node.js 24 and Chromium 152; for "en-GB" it is 1 (Monday).

The rest of a Japanese check

A date one day off is easy to miss until a booking or a deadline lands on the wrong day. The same pass catches Enter while typing Japanese (the fix), yen charged 100 times over (the fix), kanji drawn in their Chinese shapes (the fix), headings that break in the middle of a word (the fix), postal codes rejected when typed in Japanese (the fix), address forms in the American order (the fix), phone numbers sent to +81 with the 0 still in (the fix), PDFs with the Japanese missing (the fix), search that finds nothing in Japanese (the fix), previews cut down to “...” in Japanese (the fix), lists sorted in the wrong order (the fix), email subjects that arrive garbled (the fix), CSV files that open garbled in Excel (the fix), Japanese titles that give an empty URL slug (the fix), file names garbled on upload and download (the fix), dates in the Japanese era that come out wrong (the fix), full-width spaces that get past your checks (the fix), public holidays your app gets wrong (the fix), and vertical text that comes out sideways or upside down (the fix).

Lesstep translates web apps into Japanese and delivers them as one pull request. In Complete (US$249), we move every date to Intl with the user's language, check that a date picked in Japan reaches your server as the same day, and add the era where your users need it, in the same pull request. The first step is a free sample.

Node.js is a trademark of the OpenJS Foundation. This page is not affiliated with or endorsed by it, or by the date-fns or Day.js projects.

More: Enter sends the message while typing Japanese · Stripe charges 100 times too much in yen · Kanji in their Chinese shapes · Japanese headings that break in the middle of a word · Forms that reject numbers typed in Japanese · Address forms in the Japanese order · Japanese phone numbers in forms · Japanese text missing from your PDFs · Search that finds nothing in Japanese · Length limits that break in Japanese · Japanese lists sorted in the wrong order · Japanese email subjects that arrive garbled · Japanese CSV files that open garbled in Excel · Japanese titles that give an empty URL slug · Japanese file names garbled on upload and download · Japanese era dates that come out wrong · Full-width spaces that get past your checks · Japanese public holidays your app gets wrong · Japanese vertical text that comes out sideways or upside down · Japanese localization checklist · Japanese localization for your web app