概要

帳票や公文書まわりの開発では、いまだに和暦の要件が現役です。厄介なのは変換そのものではなく、境界と表記です。2019年は4月30日までが平成31年、5月1日からが令和元年で、年単位の変換テーブルでは正しく扱えません。さらに「令和1年」と「令和元年」のどちらで出力するかは帳票ごとに要件が分かれ、リリース後に指摘されがちなポイントでもあります。Java には JapaneseDate と JapaneseChronology が標準で入っており、こうした境界処理を含めて外部ライブラリなしで実装できます。この記事では、西暦と和暦の相互変換の基本形から、元号境界日の扱い、「元年」表記への対応、帳票向けフォーマットの組み立てまでを順に確認します。

使いどころ

自治体・官公庁向け帳票の日付欄を「令和7年3月15日」形式で出力する

紙の申込書から転記された和暦の生年月日を、西暦の LocalDate に変換して DB へ保存する

改元をまたぐ契約期間(平成30年〜令和2年)の年数計算を、境界日を考慮して正しく行う

コード例

和暦と西暦を相互変換するユーティリティ
import java.time.LocalDate;
import java.time.chrono.JapaneseChronology;
import java.time.chrono.JapaneseDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class WarekiConverter {

    // withChronology を忘れると ISO 暦でパースされ、
    // fromWareki が DateTimeParseException になる
    private static final DateTimeFormatter WAREKI_FMT =
        DateTimeFormatter.ofPattern("GGGGy年M月d日", Locale.JAPAN)
            .withChronology(JapaneseChronology.INSTANCE);

    public static String toWareki(LocalDate date) {
        var jpDate = JapaneseDate.from(date);
        return WAREKI_FMT.format(jpDate);
    }

    public static LocalDate fromWareki(String wareki) {
        var jpDate = JapaneseDate.from(
            WAREKI_FMT.parse(wareki));
        return LocalDate.from(jpDate);
    }

    public static void main(String[] args) {
        var today = LocalDate.of(2025, 3, 15);
        System.out.println(today + " → " + toWareki(today));
        // → 2025-03-15 → 令和7年3月15日

        var parsed = fromWareki("令和7年3月15日");
        System.out.println("令和7年3月15日 → " + parsed);
        // → 2025-03-15

        // 元号境界: 2019/4/30 と 2019/5/1 で元号が切り替わる
        System.out.println(toWareki(LocalDate.of(2019, 4, 30)));
        // → 平成31年4月30日
        System.out.println(toWareki(LocalDate.of(2019, 5, 1)));
        // → 令和1年5月1日
        //   (GGGGy パターンでは「元年」ではなく「1年」になる点に注意)
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

令和に完全対応。switch 式で元号略称の分岐が簡潔に書ける。record との組み合わせで変換結果を型安全に返せる。

Java 17
// Java 17: switch 式で元号略称を分岐
int base = switch (era) {
    case 'R' -> REIWA_BASE;
    case 'H' -> HEISEI_BASE;
    case 'S' -> SHOWA_BASE;
    case 'T' -> TAISHO_BASE;
    default  -> throw new IllegalArgumentException(
        "不明な元号略称: " + era);
};
return base + japYear;

Library Comparison

標準 API(JapaneseDate + DateTimeFormatter)和暦変換だけが必要で依存を増やしたくないとき。JDK のアップデートで新元号にも自動対応する。明治6年以前は扱えない。フォーマットパターンの指定にはロケールの知識が必要。
ICU4Jより高度な国際化や暦体系が必要な場合。JAR サイズが大きく(約11MB)、和暦変換だけなら標準 API で十分。

注意点

JapaneseDate は明治6年(1873年)以降しか扱えない。

元号の境界日を正しく扱うため、JapaneseChronology.INSTANCE を使うこと。

DateTimeFormatter の和暦パターンはロケール依存のため、Locale.JAPAN を明示する。

現場では「令和1年」と「令和元年」の表記揺れが帳票要件で問題になることがある。「元」表記が必要なら DateTimeFormatterBuilder でカスタムフォーマッタを構築すること。

FAQ

2019年5月1日より前の日付を変換するとどうなりますか?

JapaneseDate が境界日を判定するため、2019年4月30日は平成31年、5月1日は令和元年と正しく変換されます。自前の年単位テーブル変換では境界日でずれます。

「令和1年」ではなく「令和元年」と表示できますか?

標準のフォーマッタでは「令和1年」と出力されます。「元年」表記が帳票要件にあるなら、DateTimeFormatterBuilder で1年目だけ「元」を割り当てるカスタムフォーマッタを組みます。

次の改元があったとき、コードの修正は必要ですか?

元号データは JDK 側が持っているため、原則は JDK のアップデートだけで追従できます。ただし元号略称の変換など自前実装した部分は修正が必要です。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます