.NET Frameworkが提供する和暦を扱う機能を用いて、西暦と和暦を変換する方法を解説する。また新元号対応および「元年」表記対応についても取り上げる。
本稿は2003/06/06に初版公開、2018/10/31に改訂した記事を再改訂し、仕様変更について追記を行ったものです。
本稿では、西暦に基づくDateTime構造体と和暦の文字列とを相互に変換する方法を解説する。なお、2018年以降、元号の扱いに関して.NET Frameworkに重大な仕様変更があるので、改訂の機会に追記した。
特定のトピックをすぐに知りたいという方は以下のリンクを活用してほしい。
また、元号を略称(「平」や「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
このサンプルプログラムでは、和暦を扱うということで変数名などにあえて日本語を使ってみた。
さて、ここでポイントになるのは、もちろんJapaneseCalendarクラスである。このインスタンスを作成し、幾つかのメソッドを呼び出している。GetEraメソッドは、指定された日付時刻が示す元号を得る。結果は整数で得られ、1が明治、2が大正、3が昭和、4が平成となる。GetYearメソッドは、その元号内での年数を整数で返す。この2つを使えば、「2003年」が「平成15年」であることを突き止められるわけである。
ただ単に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
ポイントは、CultureInfoクラス(System.Globalization名前空間)だ。これは各地域の文化固有の情報を扱うクラスである。コンストラクタのパラメーターに"ja-JP"を付けてインスタンスを作成すると、「日本語の日本」についてのインスタンスが得られる。
しかし、標準では、「日本語の日本」も西暦(グレゴリオ暦)が選択されているので、これを和暦(JapaneseCalendarクラスで示される)に置き換える必要がある。それに該当するのは、culture.DateTimeFormat.CalendarにJapaneseCalendarクラスのインスタンスを代入している行である。これにより、このCultureInfoクラスのインスタンスは、和暦という暦を持つカルチャーを示すオブジェクトとなった。
後はこれを用いて、DateTime型の値から文字列への変換をToStringメソッドを用いて行えばよい。ToStringメソッドの2つ目のパラメーターにCultureInfoクラスのインスタンスが指定されている点に注意をしていただきたい。なお、書式指定内の「gg」は元号名を示す。もちろん「yy」は年、「M」は月、「d」は日である。
逆の変換も同様の手法で実現できる。和暦の文字列を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
このサンプルプログラムのポイントは、ParseExactメソッドの第3パラメーターに、和暦を持つ日本語の日本を示すCultureInfoオブジェクトを指定している点である。書式指定文字などは、ToStringメソッドで使用したものと同じである。
上記の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
2018年6月、Microsoftから元号の扱いについて.NET Frameworkの仕様を変更すると発表があった。次の2点である(2018/12/19追記:3点目の仕様変更も追加された。後述する)。
・ 元号の終了日を過ぎた日付文字列の扱い:
例えば「平成32年1月1日」や「昭和65年1月1日」のような日付文字列は、これまでは正しい元号で書かれていない(=次の元号であるべき)ということでParseExactメソッド/TryParseExactメソッドでの変換に失敗していた。仕様変更後は、失敗せずに変換されるようになる。もしも、従来の仕様を利用して元号表記の誤りを検出しているようなコードがあったならば、この仕様変更によって正しく動作しなくなるので注意が必要だ。
・ 元号テーブルのソース(.NET Framework 3.5):
.NET Framework 3.5までは元号テーブルを.NET Framework内に持っていた。これが、.NET Framework 4.0以降と同様に、レジストリから取得するように変更される。これによって、新元号が公表された際にWindowsアップデートで対応されるようになる。
これらの仕様変更は、Windowsのバージョンと.NET Frameworkのバージョンと仕様変更の種類ごとに、個別にアップデートが提供される。Windows 7/8.xには、サポートが継続している.NET Framework用のアップデートが2018年11月に提供された。Windows 10の一部には2018年12月までに提供された。どのアップデートを提供したかという情報は公開されたが、未提供のアップデートの提供予定は公表されていない(2018年12月時点、詳細後述)。
また、前者の仕様変更は、従来通りの動作に戻す方法も提供されている。詳しくは、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());
// 出力(アップデート済):2017年10月23日
}
catch (FormatException ex)
{
Console.WriteLine("{0}: {1}", ex.GetType().Name, ex.Message);
// 出力(未アップデート):FormatException: 文字列で表される DateTime がカレンダー
// System.Globalization.JapaneseCalendar でサポートされていません。
}
DateTime result2;
if (DateTime.TryParseExact(target, "ggyy年M月d日", culture,
DateTimeStyles.AssumeLocal, out result2))
{
Console.WriteLine(result2.ToLongDateString());
// 出力(アップデート済):2017年10月23日
}
else
{
Console.WriteLine("「{0}」はDateTimeに変換できません", target);
// 出力(未アップデート):「明治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())
' 出力(アップデート済):2017年10月23日
Catch ex As FormatException
Console.WriteLine("{0}: {1}", ex.GetType().Name, ex.Message)
' 出力(未アップデート):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())
' 出力(アップデート済):2017年10月23日
Else
Console.WriteLine("「{0}」はDateTimeに変換できません", target)
' 出力(未アップデート):「明治150年10月23日」はDateTimeに変換できません
End If
End Sub
End Module
(2018/12/19追記)
2018年11月にサポート情報KB4477957「.NET Framework 用の日本の新元号対応更新プログラムの概要」が公開された。上で述べた仕様変更の他に、重要な情報が2つ追加されている。
追加された仕様変更である「元年」表記とは、和暦表記では最初の年を「元年」と呼ぶ風習に合わせたものだ。例えば、これまで「平成1年」と出力されていたものが、書式指定文字列によっては「平成元年」と出力されるように変わる(次のコード)。
using System;
using System.Globalization;
class Program
{
static void Main(string[] args)
{
CultureInfo culture = new CultureInfo("ja-JP", true);
culture.DateTimeFormat.Calendar = new JapaneseCalendar();
var dat = new DateTime(1989, 1, 8);
Console.WriteLine(dat.ToString("ggy'年'M月d日", culture));
Console.WriteLine(dat.ToString("ggy年M月d日", culture));
Console.WriteLine(dat.ToString("gy/M/d", culture));
// 出力(アップデート済):
// 平成元年1月8日
// 平成1年1月8日
// 平成1/1/8
// 出力(未アップデート):
// 平成1年1月8日
// 平成1年1月8日
// 平成1/1/8
}
}
Imports System.Globalization
Module Module1
Sub Main()
Dim culture As CultureInfo = New CultureInfo("ja-JP", True)
culture.DateTimeFormat.Calendar = New JapaneseCalendar()
Dim dat = New DateTime(1989, 1, 8)
Console.WriteLine(dat.ToString("ggy'年'M月d日", culture))
Console.WriteLine(dat.ToString("ggy年M月d日", culture))
Console.WriteLine(dat.ToString("gy/M/d", culture))
' 出力(アップデート済):
' 平成元年1月8日
' 平成1年1月8日
' 平成1/1/8
' 出力(未アップデート):
' 平成1年1月8日
' 平成1年1月8日
' 平成1/1/8
End Sub
End Module
和暦の文字列をDateTime構造体の値に変換するときも、「元年」が解釈されるように変わる(次のコード)。
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 target1 = "平成1年1月8日"; // 「1年」
DateTime result;
if (DateTime.TryParseExact(target1, "ggy年M月d日", culture,
DateTimeStyles.AssumeLocal, out result))
{
Console.WriteLine(result.ToString("yyyy/MM/dd"));
// 出力:1989/01/08
}
string target2 = "平成元年1月8日"; // 「元年」
if (DateTime.TryParseExact(target2, "ggy年M月d日", culture,
DateTimeStyles.AssumeLocal, out result))
{
Console.WriteLine(result.ToString("yyyy/MM/dd"));
// 出力(アップデート済):1989/01/08
}
else
{
Console.WriteLine("「{0}」はDateTimeに変換できません", target2);
// 出力(未アップデート):「平成元年1月8日」はDateTimeに変換できません
}
}
}
Imports System.Globalization
Module Module1
Sub Main()
Dim culture As CultureInfo = New CultureInfo("ja-JP", True)
culture.DateTimeFormat.Calendar = New JapaneseCalendar()
Dim target1 As String = "平成1年1月8日" ' 「1年」
Dim result As DateTime
If (DateTime.TryParseExact(target1, "ggy年M月d日", culture,
DateTimeStyles.AssumeLocal, result)) Then
Console.WriteLine(result.ToString("yyyy/MM/dd"))
' 出力:1989/01/08
End If
Dim target2 As String = "平成元年1月8日" ' 「元年」
If (DateTime.TryParseExact(target2, "ggy年M月d日", culture,
DateTimeStyles.AssumeLocal, result)) Then
Console.WriteLine(result.ToString("yyyy/MM/dd"))
' 出力(アップデート済):1989/01/08
Else
Console.WriteLine("「{0}」はDateTimeに変換できません", target2)
' 出力(未アップデート):「平成元年1月8日」はDateTimeに変換できません
End If
End Sub
End Module
この「元年」表記の仕様変更も、従来通りの動作に戻す方法が提供されている。詳しくは、Developer Tools Blogsの「Handling a new era in the Japanese calendar in .NET」をご覧いただきたい。
カテゴリ:クラスライブラリ 処理対象:日付と時刻
使用ライブラリ:JapaneseCalendarクラス(System.Globalization名前空間)
使用ライブラリ:Calendarクラス(System.Globalization名前空間)
使用ライブラリ:CultureInfoクラス(System.Globalization名前空間)
関連TIPS:日付の年号を略称で表示するには?[C#、VB]
関連TIPS:日付の年号を表示するには?[独自テーブル参照編]
【2018/12/19】仕様変更について追記を行いました。
【2018/10/31】Visual Studio 2017でコードの動作検証、図版の追加、仕様変更について追記、全般的な構成の変更などを行いました。
【2003/06/06】初版公開。
Copyright© Digital Advantage Corp. All Rights Reserved.