- 技術 💻
和暦⇔西暦変換|Java・JS・Python・SQL・Excel、元年と改元日
和暦と西暦の変換は「年だけ」で計算すると、改元のあった年に間違えます。 2019年は4月30日までが平成31年、5月1日からが令和元年で、1989年は1月7日までが昭和64年、1月8日からが平成元年です。正しく変換するには、元号ごとの「改元日」と比べる必要があります。
Java には和暦の暦(JapaneseChronology)があり、JavaScript は Intl.DateTimeFormat で和暦を表示できます。Python・SQL・Excel は元号の一覧表を持って計算します。この記事では、どの言語でも同じ結果になるコードを、改元日の前後で動かした結果つきで載せています。
確認環境:OpenJDK 21.0.10、Node.js 22.22.2(ICU 78.2)、Python 3.11.15、PostgreSQL 16.15、MySQL 8.0.46、LibreOffice Calc 24.2(確認日 2026年10月10日。2026年10月10日は令和8年です)。
早見表:西暦と和暦の対応
年の引き算(改元日より後の日付で使う)
| 元号 | 改元日(この日から) | 西暦から和暦 | 和暦から西暦 | 略字 |
|---|---|---|---|---|
| 令和 | 2019年5月1日 | 西暦 − 2018 | 令和 + 2018 | R |
| 平成 | 1989年1月8日 | 西暦 − 1988 | 平成 + 1988 | H |
| 昭和 | 1926年12月25日 | 西暦 − 1925 | 昭和 + 1925 | S |
| 大正 | 1912年7月30日 | 西暦 − 1911 | 大正 + 1911 | T |
| 明治 | 1868年10月23日 | 西暦 − 1867 | 明治 + 1867 | M |
改元をまたぐ年(ここだけは日付で判断する)
| 西暦 | 改元日の前日まで | 改元日から |
|---|---|---|
| 2019年 | 平成31年(1月1日〜4月30日) | 令和元年(5月1日〜) |
| 1989年 | 昭和64年(1月1日〜1月7日) | 平成元年(1月8日〜) |
| 1926年 | 大正15年(1月1日〜12月24日) | 昭和元年(12月25日〜) |
| 1912年 | 明治45年(1月1日〜7月29日) | 大正元年(7月30日〜) |
よく使う年
| 西暦 | 和暦 | 西暦 | 和暦 |
|---|---|---|---|
| 2026年 | 令和8年 | 2000年 | 平成12年 |
| 2025年 | 令和7年 | 1995年 | 平成7年 |
| 2024年 | 令和6年 | 1990年 | 平成2年 |
| 2020年 | 令和2年 | 1985年 | 昭和60年 |
| 2010年 | 平成22年 | 1975年 | 昭和50年 |
| 2005年 | 平成17年 | 1965年 | 昭和40年 |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「和暦⇔西暦変換|Java・JS・Python・SQL・Excel、元年と改元日」
よくある間違い:年だけで元号を決める
// 年だけで判定する書き方(2019年1月1日〜4月30日を令和にしてしまう)
const era = year >= 2019 ? '令和' : year >= 1989 ? '平成' : '昭和';
この書き方では、2019年4月30日が「令和元年」、1989年1月7日が「平成元年」になります。どちらも実際には存在しない日付です。元号は年ではなく、改元日と比べて決めます。
もう1つの間違いは、和暦の入力を検証しないことです。「平成32年1月1日」(平成は31年4月30日まで)や「令和元年4月30日」(令和は5月1日から)は存在しない日付ですが、単純な足し算では「2020年1月1日」「2019年4月30日」に変換できてしまいます。以下のコードはどちらもエラーとして扱います。
Java:JapaneseChronology を使う
Java 8 以降の java.time には和暦の暦 JapaneseChronology があり、令和にも対応しています。DateTimeFormatter に暦を指定すると、西暦の LocalDate を和暦で表示でき、和暦の文字列を LocalDate に戻せます。
import java.time.LocalDate;
import java.time.chrono.JapaneseChronology;
import java.time.chrono.JapaneseDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField;
import java.util.Locale;
import java.util.Map;
public class Wareki {
// 「令和7年10月10日」形式(元年は「1年」)
static final DateTimeFormatter BASIC =
DateTimeFormatter.ofPattern("GGGGy年M月d日", Locale.JAPAN)
.withChronology(JapaneseChronology.INSTANCE);
// 1年を「元年」と表示・解析する形式
static final DateTimeFormatter GANNEN = new DateTimeFormatterBuilder()
.appendText(ChronoField.ERA, java.time.format.TextStyle.FULL)
.appendText(ChronoField.YEAR_OF_ERA, Map.of(1L, "元")) // 1 → 元
.appendLiteral('年')
.appendValue(ChronoField.MONTH_OF_YEAR).appendLiteral('月')
.appendValue(ChronoField.DAY_OF_MONTH).appendLiteral('日')
.toFormatter(Locale.JAPAN)
.withChronology(JapaneseChronology.INSTANCE)
.withResolverStyle(ResolverStyle.STRICT);
// 和暦の文字列を解析する形式(存在しない日付はエラーにする)
static final DateTimeFormatter PARSER =
DateTimeFormatter.ofPattern("GGGGy年M月d日", Locale.JAPAN)
.withChronology(JapaneseChronology.INSTANCE)
.withResolverStyle(ResolverStyle.STRICT);
static LocalDate parseWareki(String text) {
String s = text.replace("元年", "1年"); // 元年を1年として読む
return LocalDate.from(PARSER.parse(s));
}
// 「R8.10.10」形式(元号の略字+年.月.日)
static final DateTimeFormatter SHORT =
DateTimeFormatter.ofPattern("GGGGGy.M.d", Locale.JAPAN)
.withChronology(JapaneseChronology.INSTANCE);
public static void main(String[] args) {
String[] days = {"1926-12-24", "1926-12-25", "1989-01-07", "1989-01-08",
"2019-04-30", "2019-05-01", "2026-10-10"};
System.out.println("西暦 | GGGGy年M月d日 | 元年表記 | 略記");
for (String d : days) {
LocalDate date = LocalDate.parse(d);
System.out.printf("%s | %s | %s | %s%n", d,
BASIC.format(date), GANNEN.format(date), SHORT.format(date));
}
// 和暦 → 西暦
for (String s : new String[]{"令和元年5月1日", "平成31年4月30日", "昭和64年1月7日", "令和8年10月10日"}) {
System.out.println(s + " → " + parseWareki(s));
}
// 存在しない和暦
for (String s : new String[]{"平成32年1月1日", "令和元年4月30日", "令和8年2月30日"}) {
try {
System.out.println(s + " → " + parseWareki(s));
} catch (Exception e) {
System.out.println(s + " → 例外: " + e.getMessage());
}
}
System.out.println("JapaneseDate = " + JapaneseDate.from(LocalDate.of(2026, 10, 10)));
}
}
$ java -Dstdout.encoding=UTF-8 Wareki.java
西暦 | GGGGy年M月d日 | 元年表記 | 略記
1926-12-24 | 大正15年12月24日 | 大正15年12月24日 | T15.12.24
1926-12-25 | 昭和1年12月25日 | 昭和元年12月25日 | S1.12.25
1989-01-07 | 昭和64年1月7日 | 昭和64年1月7日 | S64.1.7
1989-01-08 | 平成1年1月8日 | 平成元年1月8日 | H1.1.8
2019-04-30 | 平成31年4月30日 | 平成31年4月30日 | H31.4.30
2019-05-01 | 令和1年5月1日 | 令和元年5月1日 | R1.5.1
2026-10-10 | 令和8年10月10日 | 令和8年10月10日 | R8.10.10
令和元年5月1日 → 2019-05-01
平成31年4月30日 → 2019-04-30
昭和64年1月7日 → 1989-01-07
令和8年10月10日 → 2026-10-10
平成32年1月1日 → 例外: Text '平成32年1月1日' could not be parsed: year, month, and day not valid for Era
令和元年4月30日 → 例外: Text '令和1年4月30日' could not be parsed: year, month, and day not valid for Era
令和8年2月30日 → 例外: Text '令和8年2月30日' could not be parsed: year, month, and day not valid for Era
JapaneseDate = Japanese Reiwa 8-10-10
パターン文字の意味
| パターン | 意味 | 2019-05-01 の結果 |
|---|---|---|
GGGG |
元号(正式名) | 令和 |
G |
元号(短い形) | 令和 |
GGGGG |
元号(略字) | R |
y |
元号の中の年(1始まり) | 1 |
M/d |
月/日 | 5/1 |
GGGGy年だけでは「元年」にならず「1年」と表示されます。 元年にしたい場合は、DateTimeFormatterBuilder.appendText(ChronoField.YEAR_OF_ERA, Map.of(1L, "元"))で1年の表示を「元」に置き換えます- 読み取りは「元年」を「1年」に置き換えてから解析すると、どちらの表記でも受け付けられます
ResolverStyle.STRICTを付けると、元号の期間外や存在しない日がすべて例外になります。付けない既定(SMART)で試すと、次のように通ってしまう入力がありました
SMART(既定)の結果
平成32年1月1日 → 例外 Text '平成32年1月1日' could not be parsed: Invalid YearOfEra for Era: Heisei 32
令和1年4月30日 → 2019-04-30 ← 令和になる前の日付なのに通る
令和8年2月30日 → 2026-02-28 ← 月末に丸められる
- 標準出力に日本語を出すときは
-Dstdout.encoding=UTF-8を付けます(付けないと環境によっては?に化けます) JapaneseChronologyが扱えるのは明治6年(1873年)以降です。それより前の日本は旧暦(太陰太陽暦)だったため、西暦との日付の対応が単純な引き算になりません
JavaScript:Intl.DateTimeFormat で表示、読み取りは自作
ブラウザと Node.js では、ロケールに ja-JP-u-ca-japanese(日本の暦)を指定すると和暦で表示できます。
const d = new Date('2019-05-01T00:00:00+09:00');
new Intl.DateTimeFormat('ja-JP-u-ca-japanese', { dateStyle: 'long', timeZone: 'Asia/Tokyo' }).format(d);
// '令和元年5月1日'
d.toLocaleDateString('ja-JP-u-ca-japanese', { era: 'short', timeZone: 'Asia/Tokyo' });
// '令和1/5/1'
new Intl.DateTimeFormat('ja-JP-u-ca-japanese', { era: 'narrow', year: 'numeric', timeZone: 'Asia/Tokyo' }).format(d);
// 'R元年'
Node.js 22(ICU 78.2)では dateStyle: 'long' で「元年」まで表示されました。ただし formatToParts で年の部品を取ると '1' が返るため、部品を組み立てて表示する場合は自分で「元」に置き換えます。表示の細部は ICU(国際化ライブラリ)の版で変わるので、画面に出す形式は自分の実行環境で一度確かめておきます。
timeZone: 'Asia/Tokyo' は必ず付けます。 付けないと、サーバーやブラウザの時差によって日付が1日ずれ、改元日の前後で元号が変わってしまうことがあります。
和暦の文字列を西暦に戻す関数は JavaScript に用意されていないので、元号の一覧表を使って作ります。
// 和暦⇔西暦の変換(改元日まで見る)
const ERAS = [ // 新しい順。start は改元日
{ name: '令和', short: 'R', start: '2019-05-01' },
{ name: '平成', short: 'H', start: '1989-01-08' },
{ name: '昭和', short: 'S', start: '1926-12-25' },
{ name: '大正', short: 'T', start: '1912-07-30' },
{ name: '明治', short: 'M', start: '1868-10-23' },
];
// 西暦 → 和暦(Intl を使う)
function toWareki(date, { gannen = true } = {}) {
const fmt = new Intl.DateTimeFormat('ja-JP-u-ca-japanese', {
era: 'long', year: 'numeric', month: 'numeric', day: 'numeric', timeZone: 'Asia/Tokyo',
});
const parts = Object.fromEntries(fmt.formatToParts(date).map((p) => [p.type, p.value]));
const year = gannen && parts.year === '1' ? '元' : parts.year;
return `${parts.era}${year}年${parts.month}月${parts.day}日`;
}
// 和暦 → 西暦(「令和元年5月1日」「R1.5.1」「平成31年4月30日」などを受け付ける)
function fromWareki(text) {
const s = text
.replace(/[0-9]/g, (c) => String.fromCharCode(c.charCodeAt(0) - 0xfee0))
.replace(/元/, '1')
.trim();
const m = s.match(/^(令和|平成|昭和|大正|明治|[RHSTMrhstm])\s*(\d{1,2})[年.\/-](\d{1,2})[月.\/-](\d{1,2})日?$/);
if (!m) throw new Error(`和暦として読めません: ${text}`);
const era = ERAS.find((e) => e.name === m[1] || e.short === m[1].toUpperCase());
const [y, mo, d] = [Number(m[2]), Number(m[3]), Number(m[4])];
const startYear = Number(era.start.slice(0, 4));
const date = new Date(Date.UTC(startYear + y - 1, mo - 1, d));
const iso = date.toISOString().slice(0, 10);
// 2月30日のような日付と、元号の期間外(平成32年など)をはじく
const next = ERAS[ERAS.indexOf(era) - 1];
if (date.getUTCMonth() !== mo - 1 || date.getUTCDate() !== d
|| iso < era.start || (next && iso >= next.start)) {
throw new Error(`存在しない日付です: ${text}`);
}
return iso;
}
module.exports = { toWareki, fromWareki, ERAS };
if (require.main === module) {
for (const d of ['1926-12-24', '1926-12-25', '1989-01-07', '1989-01-08', '2019-04-30', '2019-05-01', '2026-10-10']) {
console.log(d, '→', toWareki(new Date(d + 'T00:00:00+09:00')));
}
for (const s of ['令和元年5月1日', 'R1.5.1', '平成31年4月30日', 'S64/1/7', 'h1-1-8', '令和8年10月10日',
'平成32年1月1日', '令和元年4月30日', '令和8年2月30日']) {
try { console.log(s, '→', fromWareki(s)); } catch (e) { console.log(s, '→', e.message); }
}
}
$ node wareki.js
1926-12-24 → 大正15年12月24日
1926-12-25 → 昭和元年12月25日
1989-01-07 → 昭和64年1月7日
1989-01-08 → 平成元年1月8日
2019-04-30 → 平成31年4月30日
2019-05-01 → 令和元年5月1日
2026-10-10 → 令和8年10月10日
令和元年5月1日 → 2019-05-01
R1.5.1 → 2019-05-01
平成31年4月30日 → 2019-04-30
S64/1/7 → 1989-01-07
h1-1-8 → 1989-01-08
令和8年10月10日 → 2026-10-10
平成32年1月1日 → 存在しない日付です: 平成32年1月1日
令和元年4月30日 → 存在しない日付です: 令和元年4月30日
令和8年2月30日 → 存在しない日付です: 令和8年2月30日
fromWareki は「令和元年5月1日」「R1.5.1」「平成31年4月30日」(全角数字)「S64/1/7」「h1-1-8」を同じように読み取り、元号の期間外と存在しない日付をエラーにします。
Python:元号の一覧表で変換する
Python の標準ライブラリには和暦の暦が無いので、元号の一覧表を持って計算します。
"""和暦⇔西暦の変換(標準ライブラリのみ。改元日まで見る)"""
import re
from datetime import date
ERAS = [ # 新しい順。(元号, 略字, 改元日)
('令和', 'R', date(2019, 5, 1)),
('平成', 'H', date(1989, 1, 8)),
('昭和', 'S', date(1926, 12, 25)),
('大正', 'T', date(1912, 7, 30)),
('明治', 'M', date(1868, 10, 23)),
]
def to_wareki(d: date, gannen: bool = True) -> str:
for name, _, start in ERAS:
if d >= start:
y = d.year - start.year + 1
ys = '元' if (gannen and y == 1) else str(y)
return f'{name}{ys}年{d.month}月{d.day}日'
raise ValueError(f'明治より前の日付です: {d}')
_PAT = re.compile(r'^(令和|平成|昭和|大正|明治|[RHSTM])\s*(\d{1,2})[年./-](\d{1,2})[月./-](\d{1,2})日?$', re.I)
def from_wareki(text: str) -> date:
s = text.translate({i: i - 0xFEE0 for i in range(0xFF10, 0xFF1A)}).replace('元', '1').strip()
m = _PAT.match(s)
if not m:
raise ValueError(f'和暦として読めません: {text}')
i = next(i for i, e in enumerate(ERAS) if m[1] in (e[0], e[1], e[1].lower()))
name, _, start = ERAS[i]
d = date(start.year + int(m[2]) - 1, int(m[3]), int(m[4])) # 2月30日は ValueError
if d < start or (i > 0 and d >= ERAS[i - 1][2]):
raise ValueError(f'{name}の期間外です: {text}')
return d
if __name__ == '__main__':
for s in ['1926-12-24', '1926-12-25', '1989-01-07', '1989-01-08', '2019-04-30', '2019-05-01', '2026-10-10']:
print(s, '→', to_wareki(date.fromisoformat(s)))
for s in ['令和元年5月1日', 'R1.5.1', '平成31年4月30日', 'S64/1/7', '令和8年10月10日',
'平成32年1月1日', '令和元年4月30日', '令和8年2月30日']:
try:
print(s, '→', from_wareki(s))
except ValueError as e:
print(s, '→', e)
$ python3 wareki.py
1926-12-24 → 大正15年12月24日
1926-12-25 → 昭和元年12月25日
1989-01-07 → 昭和64年1月7日
1989-01-08 → 平成元年1月8日
2019-04-30 → 平成31年4月30日
2019-05-01 → 令和元年5月1日
2026-10-10 → 令和8年10月10日
令和元年5月1日 → 2019-05-01
R1.5.1 → 2019-05-01
平成31年4月30日 → 2019-04-30
S64/1/7 → 1989-01-07
令和8年10月10日 → 2026-10-10
平成32年1月1日 → 平成の期間外です: 平成32年1月1日
令和元年4月30日 → 令和の期間外です: 令和元年4月30日
令和8年2月30日 → day is out of range for month
存在しない日(2月30日)は date() が ValueError を出すので、元号の期間外と同じく except ValueError で受け止められます。
SQL:元号テーブルを作って結合する
SQL には(Oracle を除いて)和暦の関数が無いので、元号の一覧をテーブルにして、日付が「改元日以上・次の改元日未満」に入る行と結合します。
CREATE TABLE japanese_era (
name VARCHAR(4) PRIMARY KEY,
short_name CHAR(1) NOT NULL,
start_date DATE NOT NULL,
end_date DATE -- 次の元号の改元日(現在の元号は NULL)
);
INSERT INTO japanese_era VALUES
('令和', 'R', '2019-05-01', NULL),
('平成', 'H', '1989-01-08', '2019-05-01'),
('昭和', 'S', '1926-12-25', '1989-01-08'),
('大正', 'T', '1912-07-30', '1926-12-25'),
('明治', 'M', '1868-10-23', '1912-07-30');
西暦→和暦(PostgreSQL)
SELECT d.dt,
e.name || CASE WHEN EXTRACT(YEAR FROM d.dt) - EXTRACT(YEAR FROM e.start_date) + 1 = 1
THEN '元' ELSE (EXTRACT(YEAR FROM d.dt) - EXTRACT(YEAR FROM e.start_date) + 1)::text END
|| '年' || EXTRACT(MONTH FROM d.dt) || '月' || EXTRACT(DAY FROM d.dt) || '日' AS wareki
FROM (VALUES (DATE '1989-01-07'), (DATE '1989-01-08'), (DATE '2019-04-30'),
(DATE '2019-05-01'), (DATE '2026-10-10')) d(dt)
JOIN japanese_era e
ON d.dt >= e.start_date AND (e.end_date IS NULL OR d.dt < e.end_date)
ORDER BY d.dt;
dt | wareki
------------+-----------------
1989-01-07 | 昭和64年1月7日
1989-01-08 | 平成元年1月8日
2019-04-30 | 平成31年4月30日
2019-05-01 | 令和元年5月1日
2026-10-10 | 令和8年10月10日
西暦→和暦(MySQL)
SELECT d.dt,
CONCAT(e.name,
IF(YEAR(d.dt) - YEAR(e.start_date) + 1 = 1, '元', YEAR(d.dt) - YEAR(e.start_date) + 1),
'年', MONTH(d.dt), '月', DAY(d.dt), '日') AS wareki
FROM (SELECT DATE '1989-01-07' AS dt UNION ALL SELECT '1989-01-08' UNION ALL SELECT '2019-04-30'
UNION ALL SELECT '2019-05-01' UNION ALL SELECT '2026-10-10') d
JOIN japanese_era e
ON d.dt >= e.start_date AND (e.end_date IS NULL OR d.dt < e.end_date)
ORDER BY d.dt;
MySQL 8.0.46 でも同じ5行が返りました。
和暦→西暦(PostgreSQL)
SELECT w.era, w.y, w.m, w.d,
make_date(EXTRACT(YEAR FROM e.start_date)::int + w.y - 1, w.m, w.d) AS seireki
FROM (VALUES ('令和', 1, 5, 1), ('平成', 31, 4, 30), ('昭和', 64, 1, 7)) w(era, y, m, d)
JOIN japanese_era e ON e.name = w.era;
era | y | m | d | seireki
------+----+---+----+------------
令和 | 1 | 5 | 1 | 2019-05-01
平成 | 31 | 4 | 30 | 2019-04-30
昭和 | 64 | 1 | 7 | 1989-01-07
変換結果が start_date 以上・end_date 未満かを WHERE に足すと、期間外の和暦をはじけます。
Oracle:NLS_CALENDAR を指定する
Oracle は TO_CHAR の第3引数で暦を和暦(Japanese Imperial)にできます。EE が元号、YY が元号の中の年です。
SELECT TO_CHAR(DATE '2019-05-01', 'EEYY"年"MM"月"DD"日"',
'NLS_CALENDAR=''Japanese Imperial''') AS wareki
FROM dual;
Excel:表示は書式、計算は元号表
和暦で表示する
| 方法 | 書き方 | 2019/5/1 の表示 |
|---|---|---|
書式(TEXT 関数) |
=TEXT(A2,"ggge年m月d日") |
令和1年5月1日 |
| 略字 | =TEXT(A2,"ge.m.d") |
R1.5.1 |
| 元年で表示(セルの書式) | Ctrl+1 →[日付]→[カレンダーの種類]を和暦にし、「元年を使用して1年目を表示」にチェック | 令和元年5月1日 |
g が元号、e が元号の中の年です。Microsoft のサポート記事では、元年の表示はセルの書式設定の[日付]で、ロケールを日本語、カレンダーの種類を和暦にして、元年を使う項目にチェックを入れる手順が案内されています。
元号・年・月・日の列から西暦を求める
元号表(G〜J列)を作り、VLOOKUP で開始年を引いて計算します。期間外は「期間外」と表示します。
| G 元号 | H 開始年 | I 改元日 | J 次の改元日 | |
|---|---|---|---|---|
| 2 | 令和 | 2019 | 2019/05/01 | 9999/12/31 |
| 3 | 平成 | 1989 | 1989/01/08 | 2019/05/01 |
| 4 | 昭和 | 1926 | 1926/12/25 | 1989/01/08 |
| 5 | 大正 | 1912 | 1912/07/30 | 1926/12/25 |
| 6 | 明治 | 1868 | 1868/10/23 | 1912/07/30 |
=IF(OR(DATE(VLOOKUP(A2,$G$2:$J$6,2,FALSE)+B2-1,C2,D2)<VLOOKUP(A2,$G$2:$J$6,3,FALSE),
DATE(VLOOKUP(A2,$G$2:$J$6,2,FALSE)+B2-1,C2,D2)>=VLOOKUP(A2,$G$2:$J$6,4,FALSE)),
"期間外",
DATE(VLOOKUP(A2,$G$2:$J$6,2,FALSE)+B2-1,C2,D2))
(実際には1行で入力します。A列に元号、B〜D列に年・月・日)
LibreOffice Calc 24.2 で計算した結果です。
| 元号 | 年 | 月 | 日 | 西暦 |
|---|---|---|---|---|
| 令和 | 1 | 5 | 1 | 2019/05/01 |
| 平成 | 31 | 4 | 30 | 2019/04/30 |
| 昭和 | 64 | 1 | 7 | 1989/01/07 |
| 令和 | 8 | 10 | 10 | 2026/10/10 |
| 平成 | 32 | 1 | 1 | 期間外 |
Excel 365 なら LET 関数で DATE(...) を1回だけ書く形にまとめられます。なお Excel の日付は1900年1月1日からなので、明治32年(1899年)以前の日付はセルの日付として扱えません。
入力フォームで和暦を受け付けるときの注意
| 項目 | おすすめ |
|---|---|
| 入力の形 | 「元号の選択欄+年・月・日の数値欄」にすると、表記ゆれ(R1、令和元、令1)を受けずに済む |
| 自由入力を受ける場合 | 全角数字・略字(R/H/S)・区切り(年月日、.、/、-)・「元」を正規化してから読む |
| 検証 | 元号の期間外(平成32年)と存在しない日(2月30日)をはじく |
| 保存 | DB には西暦の日付型で保存し、和暦は表示するときに変換する |
| 新しい元号への備え | 元号を一覧表やテーブルで持ち、コードに 2018 などの数字を直接書かない |
よくある質問
Q. 「令和1年」と「令和元年」、どちらで表示すべきですか。 A. 公的な書類や一般向けの画面では「元年」が使われることが多く、帳票やデータの受け渡しでは数字の「1年」に統一することもあります。読み取り側は両方を受け付け、表示は用途に合わせて選ぶのが扱いやすい形です。
Q. 2019年の書類は平成と令和のどちらですか。 A. 4月30日までは平成31年、5月1日からは令和元年です。年だけでなく月日で判断します。
Q. 新しい元号になったらコードを直す必要がありますか。 A. Java と JavaScript(Intl)は、実行環境(JDK や ICU)の更新で新しい元号に対応します。Python・SQL・Excel の元号表は、自分で1行追加します。どちらの場合も、元号を一覧で管理しておけば修正は1か所で済みます。
Q. 明治5年より前の日付は変換できますか。
A. 明治5年12月2日までの日本は旧暦(太陰太陽暦)で、翌日が明治6年1月1日(グレゴリオ暦)です。それより前の日付は、年の引き算では西暦の日付に正しく対応しません。Java の JapaneseChronology も明治6年以降を扱います。
まとめ
- 元号は年ではなく改元日で決める:平成31年4月30日の翌日が令和元年5月1日、昭和64年1月7日の翌日が平成元年1月8日
- 年の引き算:令和=西暦−2018、平成=西暦−1988、昭和=西暦−1925(改元日より後の日付で使う)
- Java は
JapaneseChronology+DateTimeFormatter。元年はappendTextで置き換え、読み取りはResolverStyle.STRICTで期間外をはじく - JavaScript は
Intl.DateTimeFormat('ja-JP-u-ca-japanese')で表示、timeZone: 'Asia/Tokyo'を必ず付ける。読み取りは元号表で自作 - Python・SQL・Excel は元号表(元号・略字・改元日・次の改元日)を持って計算する
- 保存は西暦の日付型、和暦は表示のときだけにすると、新しい元号にも1か所の修正で対応できる
参考・出典
確認日はいずれも 2026年10月10日です。
- Java SE 21 API「java.time.chrono.JapaneseChronology」:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/chrono/JapaneseChronology.html
- Java SE 21 API「java.time.chrono.JapaneseEra」:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/chrono/JapaneseEra.html
- Java SE 21 API「DateTimeFormatter」:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/format/DateTimeFormatter.html
- MDN「Intl.DateTimeFormat」:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat
- Microsoft サポート(Excel の元年の表示):https://support.microsoft.com/ja-JP/Excel/japan-era-changes-and-gannen
- Oracle Database Globalization Support Guide「Calendar Systems」:https://docs.oracle.com/en/database/oracle/oracle-database/19/nlspg/setting-up-globalization-support-environment.html
- PostgreSQL 16 文書「日付/時刻関数(make_date)」:https://www.postgresql.org/docs/16/functions-datetime.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「和暦⇔西暦変換|Java・JS・Python・SQL・Excel、元年と改元日」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。