Microsoft
Delphi
Win32API
新元号

【令和】Microsoft の元号対応が迷走している件


はじめに

私は Delphi 使いです。なので、新元号対応は Win32 API に依存していることになります。

Delphi の FormatDateTime() / DateTimeToString() は元号レジストリをいじれば新元号に対応できる...はずでした。


事の始まり

4/10 の井之上@エンバカデロさんのツイートが発端でした。


試してみる

4/10 よりも前かつ元号レジストリで令和を追加している場合、次のコードは 令和01年05月01日 を表示していました。

program EraTest;

{$APPTYPE CONSOLE}

uses
System.SysUtils;

var
s: string;
begin
DateTimeToString(s, 'ggee年mm月dd日', Encodedate(2019, 05, 01));
Writeln(s);
Readln;
end.

ところが...

システム日付を 2019/05/01 以降に変更すれば新元号が使えるようになります。つまり、2019/05/01 になるまで新元号が使えない事になります。

これはバグなんですかね?仕様なんですかね?

いずれにせよ一度リリースされてしまった以上、何らかの対策をするしかありません。


Windows は?

Windows 自体は未来の日付の和暦を扱えます。Windows 7 SP1 以降で新元号に対応します。

image.png

元号のレジストリキーは [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras] です。ここに 令和 の値を書き込んでやれば新元号が使えるようになります。


SerNewEra.reg

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras]
"2019 05 01"="令和_令_Reiwa_R"


現時点 (2019/4/14) では、Windows Update を最新の状態にしてもこのレジストリエントリは追加されません。


Win32 API は?

先述の通り、Windows Update により一部の Win32 API が 2019/05/01 になるまで元号レジストリを見なくなります。Delphi だと FormatDateTime() や DateTimeToString() が Win32 API を利用しています。

image.png

Microsoft の Visual C++ ももちろん影響を受けます。

image.png

image.png


OLE は?

あえて分けますが、Win32 API と OLE (Automation) の新元号対応状況は次のようになっています。

4/10 のアップデート前:

Win32 API
OLE

レジストリ
参照
参照しない

未来の和暦

×

4/10 のアップデート後:

Win32 API
OLE

レジストリ
参照
参照

未来の和暦
×

OLE は 4/10 のアップデート前まで元号レジストリを見ていなかったので 令和に対応できていませんでした。アップデート後は未来の日付の和暦を扱えるようになります。

Delphi だと VarToDateTime() が OLE を利用しています。Visual Basic 6.0 の和暦対応ルーチンの一部も OLE を利用していると思われます。


.NET は?

.NET は未来の日付の和暦を扱えます。サポートされているのは .NET Framework 3.5 以降です。

image.png

image.png

VB6 の互換関数は元年問題が出そうですが...

image.png

image.png

※ VB6.Format() 関数は元年レジストリで元年 / 1年表記が切り替わります。


Office は?

Office は未来の日付の和暦を扱えます。サポートされているのは Office 2010 以降です。

image.png

...でもちょっと待ってくださいよ。少なくとも 4/11 の時点ではこうアナウンスしてましたよね?

ちょっと前:

image.png


Windows 上で実行されている Office 製品は 2019 年 5 月 1 日に新元号が開始されるまで、新元号を表示しませんのでご注意ください。


どうして今は変わってるのです?

現状:

image.png


元号開始時に、お客様に可能な限り最適なエクスペリエンスをご体験いただけるよう、 5 月 1 日に新元号が開始される前に、一部の Office 製品で新元号が表示されるようになります。


※ VBA に関しては基本的に OLE 準拠だと思われます。


元年

元年の件でも Microsoft は文言の修正を行っています。

ちょっと前:

image.png


元年表記をデフォルトに変更 (1 年表記にも変更可能)


現状:

image.png


元年表記も選択できるよう変更



Windows

元年のレジストリは Windows 10 Insider Preview で元年デフォルト、それ以外の Windows 10 では 1年デフォルトになっているようです。つまり、次のリリースが 2019/05 と言われている新しいビルドでも元年のレジストリは元年デフォルトになると思われます。

元年のレジストリは [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese] にある InitialEraYear です。


FirstYear.reg

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese]
"InitialEraYear"="元年"


...ただ、Windows での元年の扱いは現時点ではバラバラのようです。

image.png

image.png

「新元号への対応について」 の文言変更を信じるならば、新しいビルドでも 1809 同様、1年デフォルトになる事もあり得ると思います。


Win32 API

GetDateFormat() や GetDateFormatEx() は元年レジストリを参照するようです。


FirstEraYearTest.cpp

#include <iostream>

#include <windows.h>
#include <datetimeapi.h>
#include <winnls.h>

int main()
{
WCHAR Buffer[256];
SYSTEMTIME st;
std::wcout.imbue(std::locale("JPN_JPN"));

GetLocalTime(&st);

GetDateFormatW(GetThreadLocale(), NULL, &st, L"yyyy/MM/dd", Buffer, _countof(Buffer));
std::wcout << Buffer << std::endl;

st.wYear = 2019;
st.wMonth = 5;
st.wDay = 1;

GetDateFormatW(GetThreadLocale(), DATE_USE_ALT_CALENDAR, &st, L"ggy年M月d日", Buffer, _countof(Buffer));
std::wcout << Buffer << std::endl;
GetDateFormatW(GetThreadLocale(), DATE_USE_ALT_CALENDAR, &st, L"ggy'年'M月d日", Buffer, _countof(Buffer));
std::wcout << Buffer << std::endl;

GetDateFormatEx(LOCALE_NAME_USER_DEFAULT, DATE_USE_ALT_CALENDAR, &st, L"ggy年M月d日", Buffer, _countof(Buffer), NULL);
std::wcout << Buffer << std::endl;
GetDateFormatEx(LOCALE_NAME_USER_DEFAULT, DATE_USE_ALT_CALENDAR, &st, L"ggy'年'M月d日", Buffer, _countof(Buffer), NULL);
std::wcout << Buffer << std::endl;
}


image.png

のクォーテーション関係なしに元年表示するようです。また、2019/05/01 を待たずとも元年レジストリが元年になっていれば平成等は元年表示されます。テストは平成元年でやればよさそうですね。

image.png

元年レジストリを参照しない設定で元年表示対応させたければ力業となります。1年 とか 01年元年 に置換すれば OK です。

釈迦に説法だとは思いますが、日付書式文字列をパースしてから置換する必要があります。単純置換してはいけません。


OLE

OLE は元年レジストリを参照するようです。


.NET Framework

.NET Framework は、KB4489192 がインストールされると元年の扱いが変わってしまうようです。


書式パターンに "年" の文字を囲む半角の引用符が含まれているかいないかに関係なく、1 年目の日付の書式が変更されている日本の元号の元年の文字を出力できます。


現時点 (2019/04/14) では、まだ Windows Update に降ってきていませんが...KB4489192 がいきなりリリースされたら怖いですね。

なお、KB4489192 は Windows 10 (1809) 用のモジュールであり、他のバージョンでは対応モジュールが異なります

Windows 10
(1809)
Windows 8.1
Windows 7
SP1

.NET Franework
3.5
KB4489192
KB4488663
KB4488662

.NET Franework
4.5.2

KB4488667
KB4488669

.NET Franework
4.6 以降
KB4489192
KB4488665
KB4488666

「新元号への対応について」 の文言変更を信じるならば、これらのモジュールが Windows Update でリリースされない事もあり得ると思います。


Office

Excel や Access は元年レジストリを参照しないようです。Excel や Access は常に 1年 表示です。勝手に 01 -> となる事はないのでトラブルは少ないと思いますが、元年表示対応は力業となります。

image.png

image.png

Word や Power Point の日付挿入 (日付と時刻) は元年レジストリを参照するようです。

image.png

image.png

Excel と Access が 1年 表記な理由ですが、

image.png


互換性の観点から Excel, Access などにおいて 2019 年 4 月時点では「1年」を使用している。


へ、へぇ~...。

※ VBA に関しては基本的に OLE 準拠だと思われます。


おわりに

つまり現時点 (2019/04/14) ではこういう事になります。

Win32 API
OLE
.NET
Office

元号レジストリ
参照
参照
参照
参照

未来の和暦
×


元年レジストリ
参照
参照
参照しない
Excel や Access
は参照しない

Win32 API で、未来の和暦 (新元号) を使うのなら、2019/05/01 までは次のアップデータを適用しないようにするしかありません。

OS
KB

Windows 10
KB4493509

Windows 8.1
KB4493446

Windows 7
KB4493472

または Win32 API を使わずに独自実装するかです。

2019/05/01 を迎えれば殆どの問題は解決すると思われますが、検証するにも日本では GW 真っ只中なんですよね...。


おまけ

現時点 (2019/04/19) での対応表です。

image.png