WindowsXP

C:\Windows\System32\locale.nls
0x03F8F0あたりに元号の定義があるみたいなので追加。

??年(文字列)\0年号\0
01000100E4070C00320030003200300000007D69CD730000
112020??\0\0

ちなみに平成はこのような感じになっている。

??年(文字列)\0年号\0
01000800C5070C0031003900380039000000735E10620000
181989??\0\0

ほかにアドレス長かジャンプテーブルの箇所があるはずだが見つけられなかった。でも一応動くみたい。

Windows7〜

以下のレジストリに元号を追加する。

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

.Net Framework2〜3.5

OSに依存(XPの場合はlocale.nls、7の場合は未確認)。
Windows7/.Net3.5の場合は、上記Windows7のレジストリに元号を追加しても反映されない。Windows7のlocale.nlsの変更が必要かも?(未確認)。 しかし、OS側が対応できない場合は、リフレクションを使い力技でJapaneseCalendar, DateTimeFormatInfoのインスタンスを書き換えることで元号追加可能。JapaneseCalendarのヘルパークラスのインスタンスのm_EraInfom_eras、DateTimeFormatInfoのキャッシュm_eraNamesm_abbrevEraNamesm_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

.Net Framework4〜

OSに依存(レジストリの設定)。

Microsoft Office

2003の場合、C:\Program Files\Common Files\Microsoft Shared\OFFICE11\MSO.dllに定義されているようだが、ソースコードもないので修正できない。

Tcl8.4

そもそも元号表示できません。

Tcl8.5〜

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日

http://www.tcl-lang.org/cgi-bin/tct/tip/173.txt


|New|Edit|Freeze|Diff|Backup|Upload|Copy|Rename|
Last-modified: 2016-07-14 (Thu) 16:30:21 (378d)
HTML convert time: 0.025 sec.