連載
» 2018年10月31日 05時00分 公開

.NET TIPS:西暦と和暦を変換するには?

.NET Frameworkではどの言語のバージョンにも、日本固有の暦である和暦を扱う機能が含まれている。C#およびVBで西暦と和暦を変換する方法を解説する。

[川俣晶, 山本康彦,共著]
「.NET TIPS」のインデックス

連載「.NET TIPS」

本稿は2003/06/06に初版公開された記事を改訂し 、Visual Studio 2017でコードの動作検証、図版の追加、仕様変更について追記、全般的な構成の変更などを行ったものです。


 本稿では、西暦に基づくDateTime構造体と和暦の文字列とを相互に変換する方法を解説する。なお、2018年以降、元号の扱いに関して.NET Frameworkに重大な仕様変更があるので、改訂の機会に追記した。

POINT 西暦と和暦を変換する方法

西暦と和暦を変換する方法まとめ 西暦と和暦を変換する方法まとめ


 特定のトピックをすぐに知りたいという方は以下のリンクを活用してほしい。

 また、元号を略称(「平」や「H」など)で表示する方法については、.NET TIPS「日付の年号を略称で表示するには?[C#、VB]」をご覧いただきたい。新元号への対応を確実に行うために独自の年号テーブルを持つ方法についても、「日付の年号を表示するには?[独自テーブル参照編]」で紹介している。

和暦についての情報を得る

 和暦に関する情報は、JapaneseCalendarクラス(System.Globalization名前空間)が提供してくれる。このクラスはCalendarクラス(System.Globalization名前空間)を継承したもので、同じように世界中のさまざまな暦が、このクラスを継承したクラスとして存在している。

 以下に、このJapaneseCalendarクラスを用いて、日付時刻を保持するDateTime構造体に含まれる年情報を、年号と年数で取得するサンプルプログラムを示す。Visual Studio 2017でVBプロジェクトを新規作成して試す場合には、Mainプロシージャ内のコードをコピー&ペーストしてほしい(以下、同様)

// wareki1.cs

using System;
using System.Globalization;

class WarekiSample1 {
  static void Main() {

    DateTime 日付 = new DateTime(2003, 7, 1, 12, 34, 56);
    JapaneseCalendar カレンダー = new JapaneseCalendar();
    Console.WriteLine(カレンダー.GetEra(日付));
    // 出力:4

    string [] 元号名 = { "明治", "大正", "昭和", "平成" };
    Console.WriteLine(元号名[カレンダー.GetEra(日付) - 1]);
    // 出力:平成

    // あるいは、元号名の配列を持たずに、以下のようにしてもよい
    // CultureInfo カルチャ = new CultureInfo("ja-JP", true);
    // カルチャ.DateTimeFormat.Calendar = カレンダー;
    // string 日付の元号
    //   = カルチャ.DateTimeFormat.GetEraName(カレンダー.GetEra(日付));
    // Console.WriteLine(日付の元号);
    // 出力:平成

    Console.WriteLine(カレンダー.GetYear(日付));
    // 出力:15
  }
}

// コンパイル方法:csc wareki1.cs

' wareki1.vb

Imports System
Imports System.Globalization

Module WarekiSample1
  Sub Main()
    Dim 日付 As DateTime = New DateTime(2003,7,1,12,34,56)
    Dim カレンダAs JapaneseCalendar = New JapaneseCalendar()

    Console.WriteLine(カレンダー.GetEra(日付))
    ' 出力:4

    Dim 元号名 As String() = {"明治", "大正", "昭和", "平成"}
    Console.WriteLine(元号名(カレンダー.GetEra(日付) - 1))
    ' 出力:平成

    ' あるいは、元号名の配列を持たずに、以下のようにしてもよい
    ' Dim カルチャ As CultureInfo = New CultureInfo("ja-JP", True)
    ' カルチャ.DateTimeFormat.Calendar = カレンダー
    ' Dim 日付の元号 As String _
    '   = カルチャ.DateTimeFormat.GetEraName(カレンダー.GetEra(日付))
    ' Console.WriteLine(日付の元号)
    ' 出力:平成

    Console.WriteLine(カレンダー.GetYear(日付))
    ' 出力:15

  End Sub
End Module

' コンパイル方法:vbc wareki1.vb

西暦で示された日付から年号と年数を取得するサンプルプログラム
C#版サンプルプログラムのダウンロード
VB版サンプルプログラムのダウンロード

 このサンプルプログラムでは、和暦を扱うということで変数名などにあえて日本語を使ってみた。

 さて、ここでポイントになるのは、もちろんJapaneseCalendarクラスである。このインスタンスを作成し、幾つかのメソッドを呼び出している。GetEraメソッドは、指定された日付時刻が示す元号を得る。結果は整数で得られ、1が明治、2が大正、3が昭和、4が平成となる。GetYearメソッドは、その元号内での年数を整数で返す。この2つを使えば、「2003年」が「平成15年」であることを突き止められるわけである。

DateTime構造体を和暦の文字列に変換する

 ただ単にDateTime構造体の値を和暦の文字列に変換するだけなら、もっと手軽な方法がある。以下はそれを示した例である。

// wareki2.cs

using System;
using System.Globalization;

class WarekiSample2 {
  static void Main() {

    CultureInfo culture = new CultureInfo("ja-JP", true);
    culture.DateTimeFormat.Calendar = new JapaneseCalendar();

    DateTime target = new DateTime(2003, 7, 1);
    string result = target.ToString("ggyy年M月d日", culture);
    Console.WriteLine(result);
    // 出力:平成15年7月1日
  }
}

// コンパイル方法:csc wareki2.cs

' wareki2.vb

Imports System
Imports System.Globalization

Module WarekiSample2
  Sub Main()
    Dim culture As CultureInfo = New CultureInfo("ja-JP", True)
    culture.DateTimeFormat.Calendar = New JapaneseCalendar()

    Dim target As DateTime = New DateTime(2003, 7, 1)
    Dim result As String = target.ToString("ggyy年M月d日", culture)
    Console.WriteLine(result)
    ' 出力:平成15年7月1日

  End Sub
End Module

' コンパイル方法:vbc wareki2.vb

西暦の日付を和暦の文字列に変換するサンプルプログラム
C#版サンプルプログラムのダウンロード
VB版サンプルプログラムのダウンロード

 ポイントは、CultureInfoクラス(System.Globalization名前空間)だ。これは各地域の文化固有の情報を扱うクラスである。コンストラクタのパラメーターに"ja-JP"を付けてインスタンスを作成すると、「日本語の日本」についてのインスタンスが得られる。

 しかし、標準では、「日本語の日本」も西暦(グレゴリオ暦)が選択されているので、これを和暦(JapaneseCalendarクラスで示される)に置き換える必要がある。それに該当するのは、culture.DateTimeFormat.CalendarにJapaneseCalendarクラスのインスタンスを代入している行である。これにより、このCultureInfoクラスのインスタンスは、和暦という暦を持つカルチャーを示すオブジェクトとなった。

 後はこれを用いて、DateTime型の値から文字列への変換をToStringメソッドを用いて行えばよい。ToStringメソッドの2つ目のパラメーターにCultureInfoクラスのインスタンスが指定されている点に注意をしていただきたい。なお、書式指定内の「gg」は元号名を示す。もちろん「yy」は年、「M」は月、「d」は日である。

和暦の文字列をDateTime構造体に変換する

 逆の変換も同様の手法で実現できる。和暦の文字列をDateTime構造体の値に変換するには、ToStringメソッドではなく、ParseExactメソッドを用いればよい。以下はそれを記述した例である。

// wareki3.cs

using System;
using System.Globalization;

class WarekiSample3 {
  static void Main() {

    CultureInfo culture = new CultureInfo("ja-JP", true);
    culture.DateTimeFormat.Calendar = new JapaneseCalendar();

    string target = "平成15年7月1日";
    DateTime result
      = DateTime.ParseExact(target, "ggyy年M月d日", culture);
    Console.WriteLine(result.ToLongDateString());
    // 出力:2003年7月1日
  }
}

// コンパイル方法:csc wareki3.cs

' wareki3.vb

Imports System
Imports System.Globalization

Module WarekiSample3
  Sub Main()
    Dim culture As CultureInfo = New CultureInfo("ja-JP", True)
    culture.DateTimeFormat.Calendar = New JapaneseCalendar()

    Dim target As String = "平成15年7月1日"
    Dim result As DateTime = DateTime.ParseExact(target, "ggyy年M月d日", culture)
    Console.WriteLine(result.ToLongDateString())
    ' 出力:2003年7月1日

  End Sub
End Module

' コンパイル方法:vbc wareki3.vb

和暦の文字列をDateTime構造体に変換するサンプルプログラム
C#版サンプルプログラムのダウンロード
VB版サンプルプログラムのダウンロード

 このサンプルプログラムのポイントは、ParseExactメソッドの第3パラメーターに、和暦を持つ日本語の日本を示すCultureInfoオブジェクトを指定している点である。書式指定文字などは、ToStringメソッドで使用したものと同じである。

例外を出さずにDateTime構造体へ変換する

 上記のParseExactメソッドを使ったコードは、文字列が日付として正しくないときには例外が発生してしまう。.NET Framework 2.0以降で使えるTryParseExactメソッドならば、例外は発生しない(次のコード)。

using System;
using System.Globalization;

class Program
{
  static void Main(string[] args)
  {
    CultureInfo culture = new CultureInfo("ja-JP", true);
    culture.DateTimeFormat.Calendar = new JapaneseCalendar();

    string target = "平成30年10月23日";
    if (DateTime.TryParseExact(target, "ggyy年M月d日", culture,
                  DateTimeStyles.AssumeLocal, out DateTime result))
    {
      Console.WriteLine(result.ToLongDateString());
      // 出力:2018年10月23日
    }
  }
}

Imports System.Globalization

Module Module1
  Sub Main()
    Dim culture As CultureInfo = New CultureInfo("ja-JP", True)
    culture.DateTimeFormat.Calendar = New JapaneseCalendar()

    Dim target As String = "平成30年10月23日"
    Dim result As DateTime
    If (DateTime.TryParseExact(target, "ggyy年M月d日", culture,
                  DateTimeStyles.AssumeLocal, result)) Then
      Console.WriteLine(result.ToLongDateString())
      ' 出力:2018年10月23日
    End If
  End Sub
End Module

TryParseExactメソッドは変換できたかどうかを返す(上:C#、下:VB)
TryParseExactメソッドは、変換に成功するとtrueを、失敗したときはfalseを返す。変換結果は引数の最後に与えたDateTime型の変数に格納される。
2番目の引数であるフォーマット指定の文字列は、ToStringメソッドで使用したものと同じである。
4番目の引数であるDateTimeStyles列挙体は、日付文字列の空白を無視するかどうかや、タイムゾーンの扱いをどうするかといったオプション指定である。ここでは、日付文字列からタイムゾーンを判定できないときはローカル時刻と見なすように指定している。たいていの場合は、DateTimeStyles.Noneでよい。
なお、このC#のコードでは、C# 7の新機能である「出力変数宣言」(引数を与える場所でその変数の宣言もする)を使っているので、このままコンパイルするにはVisual Studio 2017以降が必要である。C# 7より以前の環境では、ローカル変数resultの宣言をif文の前に置いてほしい。

元号の扱いについて重大な仕様変更

 2018年6月、Microsoftから元号の扱いについて.NET Frameworkの仕様を変更すると発表があった。次の2点である。

  • 元号の終了日を過ぎた日付文字列の扱い:
    例えば「平成32年1月1日」や「昭和65年1月1日」のような日付文字列は、これまでは正しい元号で書かれていない(=次の元号であるべき)ということでParseExactメソッド/TryParseExactメソッドでの変換に失敗していた。仕様変更後は、失敗せずに変換されるようになる。もしも、従来の仕様を利用して元号表記の誤りを検出しているようなコードがあったならば、この仕様変更によって正しく動作しなくなるので注意が必要だ。
  • 元号テーブルのソース(.NET Framework 3.5):
    .NET Framework 3.5までは元号テーブルを.NET Framework内に持っていた。これが、.NET Framework 4.0以降と同様に、レジストリから取得するように変更される。これによって、新元号が公表された際にWindowsアップデートで対応されるようになる。

 どちらの仕様変更も、適用時期がはっきりしていない(2018年10月時点)。前者の仕様変更は、Windows 10 1803以降に限っては2018年7月のWindowsアップデートで適用されたが、他のバージョンのWindowsへの適用は未定のままだ。

 また、前者の仕様変更は、従来通りの動作に戻す方法も提供されている。詳しくは、TechNetのブログ記事「.NET Framework の新元号対応予定について - Japan New Era Name Support Blog」をご覧いただきたい。

 前者の仕様変更によってどのような違いが生じるかを次のコードに示す。

using System;
using System.Globalization;

class Program
{
  static void Main(string[] args)
  {
    CultureInfo culture = new CultureInfo("ja-JP", true);
    culture.DateTimeFormat.Calendar = new JapaneseCalendar();

    string target = "明治150年10月23日";
    try
    {
      DateTime result
        = DateTime.ParseExact(target, "ggyy年M月d日", culture);
      Console.WriteLine(result.ToLongDateString());
      // 出力(Windows 10):2017年10月23日
    }
    catch (FormatException ex)
    {
      Console.WriteLine("{0}: {1}", ex.GetType().Name, ex.Message);
      // 出力(Windows 7):FormatException: 文字列で表される DateTime がカレンダー
      // System.Globalization.JapaneseCalendar でサポートされていません。
    }

    DateTime result2;
    if (DateTime.TryParseExact(target, "ggyy年M月d日", culture,
                  DateTimeStyles.AssumeLocal, out result2))
    {
      Console.WriteLine(result2.ToLongDateString());
      // 出力(Windows 10):2017年10月23日
    }
    else
    {
      Console.WriteLine("「{0}」はDateTimeに変換できません", target);
      // 出力(Windows 7):「明治150年10月23日」はDateTimeに変換できません
    }
  }
}

Imports System.Globalization

Module Module1
  Sub Main()
    Dim culture As CultureInfo = New CultureInfo("ja-JP", True)
    culture.DateTimeFormat.Calendar = New JapaneseCalendar()

    Dim target As String = "明治150年10月23日"

    Try
      Dim result As DateTime _
        = DateTime.ParseExact(target, "ggyy年M月d日", culture)
      Console.WriteLine(result.ToLongDateString())
      ' 出力(Windows 10):2017年10月23日
    Catch ex As FormatException
      Console.WriteLine("{0}: {1}", ex.GetType().Name, ex.Message)
      ' 出力(Windows 7):FormatException: 文字列で表される DateTime がカレンダー
      ' System.Globalization.JapaneseCalendar でサポートされていません。
    End Try

    Dim result2 As DateTime
    If (DateTime.TryParseExact(target, "ggyy年M月d日", culture,
                  DateTimeStyles.AssumeLocal, result2)) Then
      Console.WriteLine(result2.ToLongDateString())
      ' 出力(Windows 10):2017年10月23日
    Else
      Console.WriteLine("「{0}」はDateTimeに変換できません", target)
      ' 出力(Windows 7):「明治150年10月23日」はDateTimeに変換できません
    End If
  End Sub
End Module

元号終了日を超えた日付の扱いが変わる(上:C#、下:VB)
「明治150年」などといった表記は、これまでは受け付けられなかった。それが2018年からの仕様変更で、受け付けられるように変わる。
2018年10月時点では、Windows 10 1803(2018年7月のWindowsアップデートを適用したもの)以降だけに仕様変更が適用されており、Windows 7には未適用であるため、コメントに示したような実行結果の違いが出る。

カテゴリ:クラスライブラリ 処理対象:日付と時刻
使用ライブラリ:JapaneseCalendarクラス(System.Globalization名前空間)
使用ライブラリ:Calendarクラス(System.Globalization名前空間)
使用ライブラリ:CultureInfoクラス(System.Globalization名前空間)
関連TIPS:日付の年号を略称で表示するには?[C#、VB]
関連TIPS:日付の年号を表示するには?[独自テーブル参照編]


更新履歴

【2018/10/31】Visual Studio 2017でコードの動作検証、図版の追加、仕様変更について追記、全般的な構成の変更などを行いました。

【2003/06/06】初版公開。


「.NET TIPS」のインデックス

.NET TIPS

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。