C:\Windows\System32\locale.nls
0x03F8F0あたりに元号の定義があるみたいなので追加。
月 | 日 | 年 | ? | ? | 年(文字列) | \0 | 年号 | \0 | |||||||||||||||
01 | 00 | 01 | 00 | E4 | 07 | 0C | 00 | 32 | 00 | 30 | 00 | 32 | 00 | 30 | 00 | 00 | 00 | 7D | 69 | CD | 73 | 00 | 00 |
1 | 1 | 2020 | ? | ? | 2 | 0 | 2 | 0 | \0 | 楽 | 珍 | \0 |
ちなみに平成はこのような感じになっている。
月 | 日 | 年 | ? | ? | 年(文字列) | \0 | 年号 | \0 | |||||||||||||||
01 | 00 | 08 | 00 | C5 | 07 | 0C | 00 | 31 | 00 | 39 | 00 | 38 | 00 | 39 | 00 | 00 | 00 | 73 | 5E | 10 | 62 | 00 | 00 |
1 | 8 | 1989 | ? | ? | 1 | 9 | 8 | 9 | \0 | 平 | 成 | \0 |
ほかにアドレス長かジャンプテーブルの箇所があるはずだが見つけられなかった。でも一応動くみたい。
以下のレジストリに元号を追加する。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras "1868 01 01"="明治_明_Meiji_M" "1912 07 30"="大正_大_Taisho_T" "1926 12 25"="昭和_昭_Showa_S" "1989 01 08"="平成_平_Heisei_H"
https://msdn.microsoft.com/ja-jp/library/windows/desktop/ee923790(v=vs.85).aspx
OSに依存(XPの場合はlocale.nls、7の場合は未確認)。
Windows7/.Net3.5の場合は、上記Windows7のレジストリに元号を追加しても反映されない。Windows7のlocale.nlsの変更が必要かも?(未確認)。
しかし、OS側が対応できない場合は、リフレクションを使い力技でJapaneseCalendar, DateTimeFormatInfoのインスタンスを書き換えることで元号追加可能。JapaneseCalendarのヘルパークラスのインスタンスのm_EraInfo、m_eras、DateTimeFormatInfoのキャッシュm_eraNames、m_abbrevEraNames、m_abbrevEnglishEraNamesを書き換える。.Net4〜では未確認。
private void UpdateJapaneseEra (DateTimeFormatInfo dateTimeFormatInfo, object[,] NewEraInfoParameter) { if (System.Environment.Version.Major >= 4) {return;} var len = NewEraInfoParameter.GetLength(0); string[] new_EraNames = new string[len]; string[] new_AbbrevEraNames = new string[len]; string[] new_AbbrevEnglishEraNames = new string[len]; for (int i = len-1; i >= 0; i--) { new_EraNames[i] = (string)NewEraInfoParameter.GetValue(i, 0); new_AbbrevEraNames[i] = (string)NewEraInfoParameter.GetValue(i, 1); new_AbbrevEnglishEraNames[i] = (string)NewEraInfoParameter.GetValue(i, 2); } Type dateTimeFormatInfoType = dateTimeFormatInfo.GetType(); FieldInfo eraNamesFieldInfo = dateTimeFormatInfoType.GetField("m_eraNames", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); eraNamesFieldInfo.SetValue(dateTimeFormatInfo, new_EraNames); FieldInfo abbrevEraNamesFieldInfo = dateTimeFormatInfoType.GetField("m_abbrevEraNames", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); abbrevEraNamesFieldInfo.SetValue(dateTimeFormatInfo, new_AbbrevEraNames); FieldInfo abbrevEnglishEraNamesFieldInfo = dateTimeFormatInfoType.GetField("m_abbrevEnglishEraNames", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); abbrevEnglishEraNamesFieldInfo.SetValue(dateTimeFormatInfo, new_AbbrevEnglishEraNames); }
private void UpdateJapaneseEra (JapaneseCalendar japaneseCalendar, object[,] NewEraInfoParameter) { if (System.Environment.Version.Major >= 4) {return;} Type japaneseCalendarType = japaneseCalendar.GetType(); FieldInfo helperFieldInfo = japaneseCalendarType.GetField("helper", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); object helper = helperFieldInfo.GetValue(japaneseCalendar); Type helperType = helper.GetType(); FieldInfo eraInfoFieldInfo = helperType.GetField("m_EraInfo", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); Array eraInfo = (Array)eraInfoFieldInfo.GetValue(helper); Type eraInfoType = eraInfo.GetValue(0).GetType(); int len = NewEraInfoParameter.GetLength(0); Array new_EraInfo = Array.CreateInstance(eraInfoType, len); int[] new_eras = new int[len]; // Build parameters int n = 0; int minYear = 1; int maxYear = 9999; for (int i = NewEraInfoParameter.GetLength(0)-1; i >= 0; i--) { int Year = (int)NewEraInfoParameter.GetValue(i, 3); int Month = (int)NewEraInfoParameter.GetValue(i, 4); int Day = (int)NewEraInfoParameter.GetValue(i, 5); if (n == 0) { maxYear = 9999 - Year - 1; } else { maxYear = (int)NewEraInfoParameter.GetValue(i+1, 3) - Year + 1; } new_EraInfo.SetValue(eraInfoType.Assembly.CreateInstance(eraInfoType.FullName ,false ,BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static ,null ,new object[] { i + 1 , (new DateTime(Year, Month, Day)).Ticks , Year - 1 , minYear , maxYear } ,null ,null) , n); new_eras[i] = len - i; n++; } eraInfoFieldInfo.SetValue(helper, new_EraInfo); /* FieldInfo eraInfoFieldInfo2 = japaneseCalendarType.GetField("m_EraInfo", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); eraInfoFieldInfo2.SetValue(japaneseCalendar, new_EraInfo); */ FieldInfo erasFieldInfo = helperType.GetField("m_eras", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); erasFieldInfo.SetValue(helper, new_eras); }
この2つでJapaneseCalendarとDateTimeFormatInfoに元号を追加する。簡単に処理するメソッドを作ると、こんな感じでしょうか。
private CultureInfo GetJapaneseCultureInfo () { var eras = new object[,] { {"明治", "明", "M", 1868, 01, 01}, {"大正", "大", "T", 1912, 07, 30}, {"昭和", "昭", "S", 1926, 12, 25}, {"平成", "平", "H", 1989, 01, 09}, {"楽珍", "楽", "R", 2020, 01, 01}}; var curinfo = new CultureInfo("ja-JP"); var cal = new JapaneseCalendar(); UpdateJapaneseEra(cal, eras); curinfo.DateTimeFormat.Calendar = cal; //DateTimeFormat.Calenderにカレンダーをセットした後にコールすること。 UpdateJapaneseEra(curinfo.DateTimeFormat, eras); return curinfo; }
使ってみると、
var culinfo = GetJapaneseCultureInfo(); System.Diagnostics.Debug.Print(new DateTime(2019, 12, 31).ToString(culinfo)); System.Diagnostics.Debug.Print(new DateTime(2020, 01, 01).ToString(culinfo));
出力
平成 31/12/31 0:00:00 楽珍 1/1/1 0:00:00
OSに依存(レジストリの設定)。
2003の場合、C:\Program Files\Common Files\Microsoft Shared\OFFICE11\MSO.dllに定義されているようだが、ソースコードもないので根本的には追加できない。※追加はできないが、定義されている場所と内容は見たらすぐわかるので、明治の位置に大正、大正の位置に昭和、昭和の位置に平成、平成の位置に新元号、というように明治を落として一個ずつずらせば新元号を表示することができる(明治は表示できなくなる)。
そもそも元号表示できません。
lib/tcl8.5/msgs/ja.msg を修正する。
::msgcat::mcset ja LOCALE_ERAS
の値に新元号を追加する。この値はunicodeでエスケープされているが、次のようなリストである。
{-9223372036854775808 西暦 0} {-3061011600 明治 1867} {-1812186000 大正 1911} {-1357635600 昭和 1925} { 600220800 平成 1988}
リストの最初の数値は1970年1月1日からのepoch timeで、clock scanで取得することができる。2つ目は元号名で、3つ目は西暦からの差分である。例えば2020年1月1日より"楽珍"に改元する場合、{1577804400 楽珍 2019}をunicodeエスケープして、ja.msgのmsgcat::mcset ja LOCALE_ERASに追加すればよい。
::msgcat::mcset ja LOCALE_ERAS "\u007b-9223372036854775808 \u897f\u66a6 0\u007d \u007b-3061011600 \u660e\u6cbb 1867\u007d \u007b-1812186000 \u5927\u6b63 1911\u007d \u007b-1357635600 \u662d\u548c 1925\u007d \u007b600220800 \u5e73\u6210 1988\u007d \u007b1577804400 \u697d\u73cd 2019\u007d"
使ってみると、こんな感じ。
tclsh86 % clock format [clock seconds] -format %Ex -locale ja 平成28年07月14日 % clock format [clock scan 2019-12-31] -format %Ex -locale ja 平成31年12月31日 % clock format [clock scan 2020-01-01] -format %Ex -locale ja 楽珍01年01月01日