Skip to the content

Hikidashi引き出し

Era dates (和暦) and kanji numerals both ways, the dictionary forms of a conjugated word, a reading shared out over its kanji, words pulled from pasted text, and how hard a sentence is to read.

Hikidashi (引き出し) is a drawer: each tool is pulled out when it is wanted. About the name

Contents

How-to

Read era years on a form

Accept a year the way people write it on a Japanese form, in either calendar, and store the Western year.

A form asks for a year of birth. Some people type 1985, some 昭和60年, some "Showa 60", some 60 with the era picked from a list. This recipe takes any of them and keeps one number, the Western year, with the era year beside it for showing back.

Take whatever was typed

Try the text as an era year first, then as a plain number. Each step answers null when it does not apply, so the order is the whole of the logic.

import { eraYearOf, formatEraYearJapanese, parseEraYear } from "@johnmorrisdotca/hikidashi/wareki";
import { parseJapaneseNumber } from "@johnmorrisdotca/hikidashi/numerals";

function readYear(typed: string): { western: number; shown: string } | null {
  const era = parseEraYear(typed);
  if (era !== null) return { western: era.westernYear, shown: formatEraYearJapanese(era) };
  const western = parseJapaneseNumber(typed);
  if (western === null || western < 1868 || western > 2100) return null;
  const asEra = eraYearOf(western);
  return { western, shown: asEra === null ? String(western) : formatEraYearJapanese(asEra) };
}

console.log(readYear("昭和60年"));    // { western: 1985, shown: "昭和60年" }
console.log(readYear("Showa 60"));    // the same
console.log(readYear("1985"));     // the same, from full-width digits
console.log(readYear("昭和65年"));    // null: there was no such year
{ western: 1985, shown: '昭和60年' }
{ western: 1985, shown: '昭和60年' }
{ western: 1985, shown: '昭和60年' }
null

The bounds on the plain number (1868 to 2100) are the form's own rule, not the package's: Hikidashi reads any whole number.

An era picked from a list

When the era is a <select> and the year a box, find the era in JAPANESE_ERAS and ask westernYearFor. It answers null for a year the era never reached, which is the error to show beside the box.

import { JAPANESE_ERAS, westernYearFor } from "@johnmorrisdotca/hikidashi/wareki";

const options = JAPANESE_ERAS.map((era) => ({ value: era.romaji, label: `${era.kanji} (${era.romaji})` }));
console.log(options.map((option) => option.label).join(", "));

const picked = JAPANESE_ERAS.find((era) => era.romaji === "Heisei")!;
console.log(westernYearFor(picked, 31), westernYearFor(picked, 32));    // 2019 null
明治 (Meiji), 大正 (Taisho), 昭和 (Showa), 平成 (Heisei), 令和 (Reiwa)
2019 null

A date, not a year

Two eras share every changeover year, so a year alone cannot say which one a day of 1989 belongs to. When the form has a whole date, use eraOnDate:

import { eraOnDate, formatEraYearJapanese } from "@johnmorrisdotca/hikidashi/wareki";

for (const day of ["1989-01-07", "1989-01-08", "2019-04-30", "2019-05-01"]) {
  const found = eraOnDate(day);
  console.log(day, found === null ? "not a day" : formatEraYearJapanese(found, { gannen: true }));
}
1989-01-07 昭和64年
1989-01-08 平成元年
2019-04-30 平成31年
2019-05-01 令和元年

A Date works too, and is read as the day it shows in its own time zone, the one the person picked. A date before 23 October 1868 is null: the package's eras start with 明治.

Show it back

Write the era year the way a Japanese form does, with 元年 for a first year, and the Western year beside it:

import { eraYearsOf, formatEraYearJapanese, formatEraYearRomaji } from "@johnmorrisdotca/hikidashi/wareki";

const both = eraYearsOf(2019);
console.log(both.map((one) => `${formatEraYearJapanese(one, { gannen: true })} (${formatEraYearRomaji(one)})`).join(" / "), "= 2019");
令和元年 (Reiwa 1) / 平成31年 (Heisei 31) = 2019

See also: eraYearsOf for the years with two answers, and why a year can belong to two eras.

View sourceEdit on GitHubMade from the source of @johnmorrisdotca/hikidashi 1.1.0.