Delphi での新元号対応

# はじめに
新元号 **"令和"** が発表されましたね！

**See also:**

 - [改元に伴う企業等の情報システム改修等への対応 (経済産業省)](https://www.meti.go.jp/policy/it_policy/kaigen/kaigen_taiou.html)

# Delphi での新元号対応

Windows 限定の話をすると、基本的には Windows Update でレジストリキーが降ってくれば終わりです。

**追記: 2019/04/26**
Windows 10 (1809) 以外は 4/26 のアップデートで元号レジストリに令和が追加されます。

 - [日本の元号変更に関する Windows の更新プログラムについて - KB4469068 (Microsoft)](https://support.microsoft.com/ja-jp/help/4469068/summary-of-new-japanese-era-updates-kb4469068)


## エンバカさんの記事

事前にエンバカデロからも新元号対応の記事が出ていました。

 - [元号が改正された場合の西暦と和暦の相互変換について [JAPAN]](https://community.idera.com/developer-tools/b/blog/posts/japan?fbclid=IwAR1UoqDlB04G9H7-WRACVatzwgAzz4F3kfvHtNYSu-xvOS7VYB39k9JC1AY)
 - [Delphi／C++Builder 10.2.3 Tokyoの新元号対応について](https://community.idera.com/developer-tools/b/blog/posts/delphi-c-builder-10-2-3-tokyo?fbclid=IwAR1Hw1i_CgRLJ2GYVXvvzWS_mNEG0wq0kE43P9uRRLPKLgcJiLgC7eEDxuI)

## レジストリファイル

こんなもんですよね。

```ini:SetNewEra.reg
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras]
"2019 05 01"="令和_令_Reiwa_R"
```

...ただ。

> Note that this only impacts machines running Windows 7 and later or .NET Framework 4 and later. 

 - [Era Handling for the Japanese Calendar (Microsoft)](https://docs.microsoft.com/ja-jp/windows/desktop/Intl/era-handling-for-the-japanese-calendar)

XP とか Vista だと自前処理なんですかね？
![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/91e8b66b-4237-22da-00ae-7da142045ded.png)
XP / Vista いずれにも [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars] 以下がありません。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/f1bdd18a-f905-2609-ccd3-4ba2b45fd8ca.png)

**追記 2019/04/11**

Windows Update により、Win32 API の一部は 2019/05/01 を過ぎるまでは上記レジストリを見なくなる事があるようです。Delphi だと DateTimeToString() や FormatDateTime() が影響を受けます。

| OS | KB |
|:--:|:--:|
| Windows 10 | [KB4493509](https://support.microsoft.com/ja-jp/help/4493509/windows-10-update-kb4493509) |
| Windows 8.1 | [KB4493446](https://support.microsoft.com/ja-jp/help/4493446/windows-8-1-update-kb4493446) |
| Windows 7 | [KB4493472](https://support.microsoft.com/ja-jp/help/4493472/windows-7-update-kb4493472) |

上記アップデータの適用にはご注意ください。

**追記: 2019/04/26**
修正モジュールが公開されました。

オプション扱いなので自動更新はされませんが、Windows 10 は Windows Update の画面で`[更新プログラムのチェック]`ボタンを押してしまうと更新されてしまいますので、ご注意ください。

| Windows | モジュール |モジュール<br>(SO) | 
|:-:|:-:|:-:|
| Windows 10 (1809) | 未提供<br>(後日提供) |
| Windows 10 (1803) | [KB4493437](https://support.microsoft.com/ja-jp/help/4493437/windows-10-update-kb4493437) | |
| Windows 10 (1709) | [KB4493440](https://support.microsoft.com/ja-jp/help/4493440/windows-10-update-kb4493440) | |
| Windows 10 (1703) | [KB4493436](https://support.microsoft.com/ja-jp/help/4493436/windows-10-update-kb4493436) | |
| Windows 10 (1607)<br>Windows Server 2016 | [KB4493473](https://support.microsoft.com/ja-jp/help/4493473/windows-10-update-kb4493473) | |
| Windows 10 (RTM) | [KB4498375](https://support.microsoft.com/ja-jp/help/4498375/windows-10-update-kb4498375) | [](https://support.microsoft.com/ja-jp/help/4498375/windows-10-update-kb4498375) |
| Windows 8.1<br>Windows Server 2012 R2 | [KB4493443](https://support.microsoft.com/ja-jp/help/4493443/windows-8-1-update-kb4493443) | [KB4496878](https://support.microsoft.com/ja-jp/help/4496878/windows-8-1-update-kb4496878) |
| Windows Server 2012 | [KB4493462](https://support.microsoft.com/ja-jp/help/4493462/windows-server-2012-update-kb4493462) | [KB4496877](https://support.microsoft.com/ja-jp/help/4496877/windows-server-2012-update-kb4496877) |
| Windows 7 SP1<br>Windows Server 2008 R2 SP1 | [KB4493453](https://support.microsoft.com/ja-jp/help/4493453/windows-7-update-kb4493453) | [KB4496880](https://support.microsoft.com/ja-jp/help/4496880/windows-7-update-kb4496880)  |
| Windows Server 2008 SP2 | [KB4493460](https://support.microsoft.com/ja-jp/help/4493460/windows-server-2008-update-kb4493460) | [KB4496879](https://support.microsoft.com/ja-jp/help/4496879/windows-server-2008-update-kb4496879)|

これらの修正モジュールをインストールすると元号レジストリに `令和` が追加されます。**但し、これらの修正モジュールをインストールしても、Win32 API の一部は 2019/05/01 になるまで元号レジストリを参照しません。**

**See also:**

 - [新元号への対応について (Microsoft)](https://www.microsoft.com/ja-jp/mscorp/newera/default.aspx)
 - [Summary of new Japanese era Windows updates - KB4469068 (Microsoft)](https://support.microsoft.com/en-us/help/4469068/summary-of-new-japanese-era-updates-kb4469068)
 - [アプリケーションの新元号対応 (Microsoft)](https://docs.microsoft.com/ja-jp/windows/uwp/design/globalizing/japanese-era-change)
 - [日本の元号の変更について - KB4469068 (Microsoft)](https://support.microsoft.com/ja-jp/help/4469068/summary-of-new-japanese-era-updates-kb4469068)

## VarToDateTime()

VarToDateTime() は上記レジストリを見ていないようなので注意が必要です。

 - [System.Variants.VarToDateTime (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.Variants.VarToDateTime)
 - [C++ Builder / TDateTime > from/to[新しい元号]変換処理 > .NET Framework関連の処理でVarToDateTime()が正常動作しないので自前変換した (Qiita)](https://qiita.com/7of9/items/d6dadd3364362a2ea55d)
 - [らいなタン (スワンマン) さんのツイート](https://twitter.com/lynatan/status/1112939193787793408)

※ VarToDateTime() は OleAut32.dll の [VariantChangeTypeEx()](https://docs.microsoft.com/en-us/windows/desktop/api/oleauto/nf-oleauto-variantchangetypeex) を利用しています。

**追記 2019/04/12**
Windows Update により、VarToDateTime() がレジストリを参照するようになります。こちらは 2019/05/01 を過ぎていなくても変換されるようです。

### VarToDateTime() に渡す文字列を細工する

VarToDateTime() は `平成` には対応しているのですから、日付文字列中に `令和` が出現したら `平成` に置換し、年も +30 して置換する関数を作ればよさそうです。

| 元号 | Year | 年 | Month | 月 | Day | 日 |
|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
| 令和  | 02  | 年  | 12 | 月  | 22  | 日  |

 ↓ ↓ ↓

| 元号 | Year | 年 | Month | 月 | Day | 日 |
|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
| 平成<br>(置換)  | 32<br>(+30)  | 年  | 12 | 月  | 22  | 日  |


...という事で、関数 **CheckEraDateString()** を作ってみました。Delphi 7 等の古い ANSI 版 Delphi でも動作します。

```pascal
  uses
    ..., System.SysUtils;

  function CheckEraDateString(EraDateString: string): string;
  const
    FIRST_YEAR = '元年';
    ErasH: array [0..3] of string = ('平成', '平', 'Heisei', 'H');
    ErasR: array [0..3] of string = ('令和', '令', 'Reiwa', 'R');
  var
    i, EraIdx, EraStrIdx, EraYearStart, Year: Integer;
    P: PChar;
  begin
    result := EraDateString;
    EraIdx := -1;
    for i := Low(ErasR) to High(ErasR) do
      begin
        EraStrIdx := Pos(ErasR[i], EraDateString);
        if EraStrIdx > 0 then
          begin
            EraIdx := i;
            Break;
          end;
      end;
    if EraIdx = -1 then
      Exit;
    if Pos(FIRST_YEAR, Result) > 0 then
      Result := StringReplace(Result, FIRST_YEAR, '1年', []);
    Delete(result, EraStrIdx, Length(ErasR[EraIdx]));
    Insert(ErasH[EraIdx], result, EraStrIdx);
    EraStrIdx := EraStrIdx + Length(ErasH[EraIdx]) - 1;
    EraYearStart := -1;
    Year := 0;
    P := PChar(result);
    Inc(P, EraStrIdx);
    while P^ <> #$00 do
      begin
        Inc(EraStrIdx);
        {$IFDEF UNICODE}
        if not CharInSet(P^, ['0'..'9']) then
        {$ELSE}
        if not (P^ in ['0'..'9']) then
        {$ENDIF}
          begin
            if EraYearStart < 0 then
              begin
                Inc(P);
                Continue;
              end;
            Break;
          end;
        if EraYearStart < 0 then
          EraYearStart := EraStrIdx;
        Year := Year * 10 + (Ord(P^) - Ord('0'));
        Inc(P);
      end;
    Delete(result, EraYearStart, EraStrIdx - EraYearStart);
    Insert(IntToStr(Year + 30), result, EraYearStart);
  end; { CheckEraDateString }
```

使い方は

```pascal
  Dt := VarToDateTime('令和02年12月22日');
　↓
  Dt := VarToDateTime(CheckEraDateString('令和02年12月22日'));
```

VarToDateTime() の実パラメータに CheckEraDateString() を噛ますだけです。

```
令和 02 年 12 月 22 日
令和 02 / 12 / 22
令和 02 - 12 - 22
令和02年12月22日
令和02/12/22
令和02-12-22
令和 2 年 12 月 22 日
令和 2 / 12 / 22
令和 2 - 12 - 22
令和2年12月22日
令和2/12/22
令和2-12-22
令 02 年 12 月 22 日
令 02 / 12 / 22
令 02 - 12 - 22
令02年12月22日
令02/12/22
令02-12-22
令 2 年 12 月 22 日
令 2 / 12 / 22
令 2 - 12 - 22
令2年12月22日
令2/12/22
令2-12-22
R 02 年 12 月 22 日
R 02 / 12 / 22
R 02 - 12 - 22
R02年12月22日
R02/12/22
R02-12-22
R 2 年 12 月 22 日
R 2 / 12 / 22
R 2 - 12 - 22
R2年12月22日
R2/12/22
R2-12-22
```

上記のような文字列を変換できます。`令和` 対応の oleaut32.dll や locale.nls が降ってきたら、

```pascal
  uses
    ..., System.SysUtils;

  function CheckEraDateString(EraDateString: string): string;
  begin
    result := EraDateString;
  end; { CheckEraDateString }
```

って書き換えればいいかと思います。

### VarToDateTime() に渡す文字列を細工する (正規表現版)

正規表現を使った CheckEraDateString() 関数です。

```pascal
uses
  System.SysUtils, System.StrUtils, RegularExpressions;


  function CheckEraDateString(EraDateString: string): string;
  const
    EXP = '(?<ERA>[^\d\s]*)\s*(?<YEAR>\d{1,4})\s*(/|-|年)\s*(?<MONTH>\d{1,2})\s*(/|-|月)\s*(?<DAY>\d{1,2})\s*日*';
    ErasR: array [0..3] of string = ('令和', '令', 'Reiwa', 'R');
  begin
    result := EraDateString;
    with TRegEx.Match(result, EXP) do
      begin
        if not Success then
          Exit;
        if AnsiIndexText(Groups.Item['ERA'].Value, ErasR) < 0 then
          Exit;
        CheckEraDateString := Format('平成%d年%d月%d日',
                                     [Groups.Item['YEAR' ].Value.ToInteger + 30,
                                      Groups.Item['MONTH'].Value.ToInteger,
                                      Groups.Item['DAY'  ].Value.ToInteger])
      end;
  end; { CheckEraDateString }
```

短くていいのですが、検証が大変そうです (w

**See also:**

 - [System.RegularExpressions (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.RegularExpressions)
 - [SkRegExp (GitHub)](https://github.com/shukomiya/skregexp)
 - [正規表現の活用 (主に Delphi 2009 以降) (DEKO のアヤシいお部屋)](https://ht-deko.com/tech064.html#0011_W)


## FormatDateTime()

表示だけの問題だとは思いますが、

```pascal
 s := FormatDateTime('ee/mm/dd', Dt);
```

みたいな処理があると、

```pascal
'31/04/30' // <- 平成
'01/05/01' // <- 令和
'10/05/01' // <- 平成 
```

そのうち判断付かなくなると思いますので早めの対応を行った方がいいと思います。**ソートできませんしね**。

また、レジストリに頼らず元号を自前変換するアプリケーションでは FormatDateTime() の `gg` や `ee` を使ってはいけません。

**「’令和’ee/mm/dd にして年を -30 すりゃいいのでは？」**と思われたかもしれませんが、日付を誤魔化す事になりますのでダメです...理由を例で示します。

```pascal
program EraTest;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

var
  s: string;
begin
  // レジストリが対応しない場合、(平成) 31/05/01 と表示される
  s := FormatDateTime('ee/mm/dd', EncodeDate(2019, 05, 01));
  Writeln(s);

  // 年から 30 引いて、見かけ上 (令和) 01/05/01 にする
  s := FormatDateTime('ee/mm/dd', EncodeDate(2019 - 30, 05, 01));
  Writeln(s);

  Readln;
end.
```

実行結果は次の通りです。正しいように見えますね。

```
31/05/01 // (平成) 31/05/01
01/05/01 // (令和) 01/05/01 
```

そこに颯爽と現れるのが**うるう年**です。

```pascal
  // レジストリが対応しない場合、(平成) 32/02/29 と表示される
  s := FormatDateTime('ee/mm/dd', EncodeDate(2020, 02, 29));
  Writeln(s);

  // 年から 30 引いて、見かけ上 (令和) 02/02/29 にする...？
  s := FormatDateTime('ee/mm/dd', EncodeDate(2020 - 30, 02, 29));
  Writeln(s);
```

令和2年2月29日 (平成32年2月29日) は存在しても、平成2年2月29日は存在しないのです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/a091a4c4-3d21-47dd-9953-e4831a2a30a2.png)
VarToDateTime() の細工はあくまで同じ日を指しているので問題は発生しませんが、上記の処理は日付が異なる (30 年スライドさせている) ためにうるう年で問題が発生します。

※ FormatDateTime() や DateTimeToString() は Kernel32.dll の [GetDateFormat()](https://docs.microsoft.com/windows/desktop/api/datetimeapi/nf-datetimeapi-getdateformatw) を利用しています。

**See also:**

 - [System.SysUtils.DateTimeToString (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.SysUtils.DateTimeToString)
 - [System.SysUtils.FormatDateTime (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.SysUtils.FormatDateTime)

## StrToDate() / TFormatSettings
StrToDate() で TFormatSettings を指定すると和暦文字列 -> TDateTime 変換ができますが、例えば `令和01/05/01` のような日付文字列の変換に失敗すると、01 年が 2001 年と解釈され、2001/05/01 を返すので注意が必要です。

TFormatSettings は内部で EnumCalendarInfo() を呼んでおり、これは元号レジストリの影響を受けます。未来の日付の和暦であっても大丈夫です。

[エンバカさんの記事](https://community.idera.com/developer-tools/b/blog/posts/japan) にあるように、和暦->西暦変換を行う場合には　`年` `月` `日` を　`/` で置換する必要があるのですが、それさえ行えば `元年` も変換します。元年レジストリには影響されません。

```pascal
var
  JPNEraFormat: TFormatSettings;
  strDate: String;
  timestamp: TDateTime;
begin
  strDate := '令和元年5月1日';
  JPNEraFormat := TFormatSettings.Create('ja-JP');
  JPNEraFormat.ShortDateFormat := 'ggee/m/d';

  strDate := StringReplace(strDate, '年', '/', []);
  strDate := StringReplace(strDate, '月', '/', []);
  strDate := StringReplace(strDate, '日', '', []);

  timestamp := StrToDate(strDate,JPNEraFormat);
  Writeln(strDate + '->' + FormatDateTime('yyyy/mm/dd', timestamp));
```

また、XE5 以降であれば TFormatSettings を使って元号とその開始年を列挙する事ができます。

```pascal:EnumEras.pas
program EnumEras;
{$APPTYPE CONSOLE}

uses
  SysUtils;

var
  JpFormat: TFormatSettings;
  EraInfo: TFormatSettings.TEraInfo;
begin
  JpFormat := TFormatSettings.Create('ja-JP');
  for EraInfo in JpFormat.EraInfo do
    Writeln(Format('%s: %d', [EraInfo.EraName, EraInfo.EraOffset]));
end.
```

実行結果は次の通りです (新元号レジストリ適用済)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/a58696fc-dcc1-9d9a-34dc-182b00da03ed.png)


**See also:**

 - [EnumCalendarInfo() (Microsoft)](https://docs.microsoft.com/ja-jp/windows/desktop/api/winnls/nf-winnls-enumcalendarinfow)
 - [System.SysUtils.StrToDate() (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.SysUtils.StrToDate)
 - [System.SysUtils.TFormatSettings (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.SysUtils.TFormatSettings)

## Delphi で元号のレジストリキーを読む

こんな感じですかね。

```pascal:EraTest.pas
program EraTest;

{$APPTYPE CONSOLE}

uses
  System.Classes, System.StrUtils, System.SysUtils, System.Types,
  System.Win.Registry, Winapi.Windows;

type
  TEraRec =
    record
      EraName: string;        // 長い元号 (日本語)
      EraShortName: string;   // 短い元号 (日本語)
      EraEnName: string;      // 長い元号 (英語)
      EraEnShortName: string; // 短い元号 (英語)
      StartDate: TDate;       // 元号開始日付
    end;

var
  reg: TRegistry;
  Value: string;
  ValueNames: TStringList;
  Era: TEraRec;
  Eras: array of TEraRec;
  i: Integer;
  StrArr: TStringDynArray;

begin
  reg := TRegistry.Create;
  ValueNames := TstringList.Create;
  try
    reg.RootKey := HKEY_LOCAL_MACHINE;
    if reg.OpenKeyReadOnly('SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras') then
      begin
        reg.GetValueNames(ValueNames);
        ValueNames.Sort;
        SetLength(Eras, ValueNames.Count);
        for i := 0 to ValueNames.Count - 1 do
          begin
            Value := Reg.ReadString(ValueNames[i]);
            with Eras[i] do
              begin
                StrArr := SplitString(Value, '_');
                EraName        := StrArr[0];
                EraShortName   := StrArr[1];
                EraEnName      := StrArr[2];
                EraEnShortName := StrArr[3];
                StrArr := SplitString(ValueNames[i], ' ');
                StartDate := EncodeDate(StrArr[0].ToInteger,  // 年
                                        StrArr[1].ToInteger,  // 月
                                        StrArr[2].ToInteger); // 日
              end;
          end;
        reg.CloseKey;
      end;
  finally;
    ValueNames.Free;
    reg.Free;
  end;

  // 出力
  for Era in Eras do
    begin
      Write(Era.EraName);
      Write(': ');
      Writeln(FormatDateTime('YYYY/MM/DD', Era.StartDate));
    end;
  Readln;
end.
```

実行結果は次の通りです (新元号レジストリ適用済)。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/2c41b5d8-3728-b428-d9de-25024ffd0e1d.png)

## 元年

Windows 10 October 2018 Update (1809) 以前は新しい元号の最初の年は `1年` となりますが、Window 10 Insider Preview を適用していると `元年` がデフォルトになるようです。

※ Windows 10 の次のリリースででどちらがデフォルトになるのかは不明です。

元年の設定は `[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese]` の **InitialEraYear** で切り替えられます。

```ini:InitialEraYear_1年ベース.reg
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese]
"InitialEraYear"="1年"
```

```ini:InitialEraYear_元年ベース.reg
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese]
"InitialEraYear"="元年"
```

但し、**この設定は Delphi の FormatDateTime() や DateTimeToString() に影響を及ぼしません。**しかしながら FormatDateTime() や DateTimeToString() 内で使われている [GetDateFormat() API](https://docs.microsoft.com/ja-jp/windows/desktop/api/datetimeapi/nf-datetimeapi-getdateformatw) 自体は元年レジストリを参照しますので注意が必要です。

```pascal:FirstEraYearTest.pas
program FirstEraYearTest;
uses
  SysUtils, Windows;

var
  st: TSystemTime;
  Buffer: array [0..255] of Char;
  FormatStr: string;
begin
  DateTimeToSystemTime(EncodeDate(2019, 05, 01), st);
  FormatStr := 'ggyy''年''MM''月''d''日''';
  GetDateFormat(GetThreadLocale, DATE_USE_ALT_CALENDAR, @st, PChar(FormatStr), Buffer, Length(Buffer));

  Writeln(StrPas(Buffer));
end.
```

実行結果は次の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/3c6efdec-c2d1-a694-2c64-968672bbd33e.png)
何故同じ GetDateFormat() を使っておきながら FormatDateTime() や DateTimeToString() が元年レジストリの影響を受けないかというと、内部で元号と和暦年を別々に処理しているからです。

```
"ggy'年'M'月'd'日'"            -> 元号レジストリの影響を受ける
"gg" + "y" + "'年'M'月'd'日'"  -> 元号レジストリの影響を受けない
```

`日付書式文字列内に '年' が含まれる` という条件を満たさないため元年表示されないという訳です。

**See also:**

 - [新元号対応へのマイクロソフトの取り組み (Microsoft)](https://www.microsoft.com/ja-jp/mscorp/newera/default.aspx)
 - [Windows 用の日本の新元号対応更新プログラムについて - KB4469068 (Microsoft)](https://support.microsoft.com/ja-jp/help/4469068/summary-of-new-japanese-era-updates-kb4469068)
 - [日本マイクロソフト、新元号対応に関連する情報を集約したWebページを公開 (窓の杜)](https://forest.watch.impress.co.jp/docs/news/1173020.html)
 - [.NET Framework 用の日本の新元号対応更新プログラムの概要 (Microsoft)](https://support.microsoft.com/ja-jp/help/4477957/new-japanese-era-updates-for-net-framework)
 - [Gannen vs Ichinen (blogs.msdn.microsoft.com)](https://blogs.msdn.microsoft.com/shawnste/2018/11/12/gannen-vs-ichinen/)
 - [【令和】Microsoft の元号対応が迷走している件 (Qiita)](https://qiita.com/ht_deko/items/249db60e1c99ba440969)

## 合字

`㍾`、`㍽`、`㍼`、`㍻` の令和版は Unicode のコードポイントで **U+32FF** です。フォントが対応しないと場所の確保だけになっちゃいますけれど。

合字は Shift_JIS (CP932 含む) ではサポートされません。

> Microsoft Windows コード ページ 932 (MS932)、すなわちシフト JIS エンコーディングは、新元号の合字をサポートしません。Unicode の日本の新元号の合字 (漢字 1 文字) の文字をマルチ バイト文字に変換中に文字が正しく表示されないことがあります。MS932 エンコーディングでその逆を行う変換の場合も同様です。

ANSI 版 Delphi で元号の表示に合字を使っていたら厄介なことになります。このご時世に外字ファイルを作るのはちょっとヤですねぇ。

**2019/04/26 追記:**
4/26 リリースの修正モジュールにより、日本語フォントに合字の令和が追加されます。Windows 10 (1809) には修正モジュールが提供されていません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/4dddc162-d700-ecc1-79c0-2e072d05eca6.png)

**2019/05/02 追記:**
5/2 リリースの Windows 10 (1809) 用修正モジュールにより、日本語フォントに合字の令和が追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/f1327eea-8f15-02ac-1923-0e4c1aea3c10.png)
**See also:**

 - [Windows 用の日本の新元号対応更新プログラムについて - KB4469068 (Microsoft)](https://support.microsoft.com/ja-jp/help/4469068/summary-of-new-japanese-era-updates-kb4469068)
 - [平成の次、新元号のUnicodeコードポイントは「U+32FF」(マイナビニュース)](https://news.mynavi.jp/article/20180908-690029/)


# おわりに

1998 年に最初に作った和暦入力コンポーネントを 2019 年になって更新する事になろうとは思いませんでした (w
![image.png](https://qiita-image-store.s3.amazonaws.com/0/21785/28927f5f-596c-f4c6-b26f-3fdba817f4f6.png)

 - [和暦入力コンポーネント (DEKO のアヤシいお部屋)](https://ht-deko.com/junkbox.html#GENED)

## 代替ルーチン
ver 0.90 にて、和暦操作ユニット EraUtils.pas を分離しました。次のルーチンの代替ルーチンが含まれます。

 - DateTimeToString() -> DateTimeToString_Era()
 - FormatDateTime() -> FormatDateTime_Era() 
 - VarToDateTime() -> VarToDateTime_Era()

ver 1.00 にて DateTimeToString_Era() / FormatDateTime_Era() に元年対応パラメータを追加しました。`IsFirstYearAsNumber` を False に設定すると元年表示になります。

```pascal
// 令和n年 -> 平成n+30年 変換
function CheckEraDateString(EraDateString: string): string;

// 日付から元号インデックスを得る
function DateToEraIndex(ADate: TDateTime): Integer;

// TDateTime から和暦へデコード
procedure DecodeEraDate(const DateTime: TDateTime; var EraIdx, Year, Month, Day: Word);

// 和暦から TDateTime へエンコード
function EncodeEraDate(EraIdx, Year, Month, Day: Word): TDateTime;

// DateTimeToString() 代替
procedure DateTimeToString_Era(var Result: string; const Format: string; DateTime: TDateTime; const IsFirstYearAsNumber: Boolean = True); overload;
procedure DateTimeToString_Era(var Result: string; const Format: string; DateTime: TDateTime; const FormatSettings: TFormatSettings; const IsFirstYearAsNumber: Boolean = True); overload;

// FormatDateTime() 代替
function FormatDateTime_Era(const Format: string; DateTime: TDateTime; const IsFirstYearAsNumber: Boolean = True): string; overload;
function FormatDateTime_Era(const Format: string; DateTime: TDateTime; const FormatSettings: TFormatSettings; const IsFirstYearAsNumber: Boolean = True): string; overload;

// VarToDateTime() 代替
function VarToDateTime_Era(const V: Variant): TDateTime;

```

 - Delphi 7 / 2007 / XE / 10.3 Rio で動作確認済です。
 - このコンポーネントは初出が古いので、(互換性のため) できる限り古い書き方で書いていますが、Delphi 7 よりも前の環境での動作は保証しません。
 - 和暦入力コンポーネントをインストールせず、EraUtils.pas 単独で使う事もできます。