はじめに
私は Delphi 使いです。なので、新元号対応は Win32 API に依存していることになります。
Delphi の FormatDateTime() / DateTimeToString() は元号レジストリを参照するので、レジストリに新しい元号が追加されれば新元号対応できる...はずでした。
- Era Handling for the Japanese Calendar (Microsoft)
- 元号が改正された場合の西暦と和暦の相互変換について [JAPAN] (Embarcadero)
- Delphi/C++Builder 10.2.3 Tokyoの新元号対応について (Embarcadero)
事の始まり
4/10 の井之上@エンバカデロさんのツイートが発端でした。
Windows 10 1809 向けに今日付け(2019/04/10) でリリースされたアップデートのうち、KB4493509 がインストールされていると、元号の処理がおかしくなる気がする。
— 井之上@エンバカデロ (@kaz_inoue) 2019年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.
ところが...
ぎゃあああああああぁぁぁぁぁぁぁぁ! pic.twitter.com/TWVpHkx8Ad
— DEKO (@ht_deko) 2019年4月10日
4/10 のアップデートを適用すると元号レジストリを追加していても新元号が表示されなくなってしまいました。
システム日付を 2019/05/01 以降に変更すれば新元号が使えるようになります。つまり、2019/05/01 になるまで新元号が使えない事になります。
これはバグなんですかね?仕様なんですかね?
いずれにせよ一度リリースされてしまった以上、何らかの対策をするしかありません。
Windows は?
Windows 自体は未来の日付の和暦を扱えます。Windows 7 SP1 以降で新元号に対応します。
元号のレジストリキーは [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras]
です。ここに 令和
の値を書き込んでやれば新元号が使えるようになります。
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 を最新の状態にしてもこのレジストリエントリは追加されません。
追記: 2019/04/26
Windows 10 (1809) 以外に修正モジュールが公開されました。
Windows 8.1 以前用の修正モジュールはオプション扱いなので自動更新されませんが、Windows 10 は Windows Update の画面で[更新プログラムのチェック]
ボタンを押すと更新されてしまいますので、ご注意ください。
Windows | モジュール | モジュール (SO) |
---|---|---|
Windows 10 (1809) | KB4495667 | |
Windows 10 (1803) | KB4493437 | |
Windows 10 (1709) | KB4493440 | |
Windows 10 (1703) | KB4493436 | |
Windows 10 (1607) Windows Server 2016 |
KB4493473 | |
Windows 10 (RTM) | KB4498375 | |
Windows 8.1 Windows Server 2012 R2 |
KB4493443 | KB4496878 |
Windows Server 2012 | KB4493462 | KB4496877 |
Windows 7 SP1 Windows Server 2008 R2 SP1 |
KB4493453 | KB4496880 |
Windows Server 2008 SP2 | KB4493460 | KB4496879 |
これらの修正モジュールをインストールすると元号レジストリに 令和
が追加されます。元年レジストリは更新されません。
日本語フォントに U+32FF の 令和
が追加されます。
追記: 2019/05/02
Windows 10 (1809) 用の修正モジュール KB4501835 が公開されました...が、Windows Update の [更新プログラムのチェック]
で出てくるようになるまで時間が掛かるかもしれません。WSUS で管理されていない PC は "Microsoft Update カタログ" から修正モジュールをダウンロードしてインストールする必要があります。
追記: 2019/05/04
Windows 10 (1809) 用の修正モジュール KB4495667 が公開されました。Windows Update の [更新プログラムのチェック]
でインストール可能です。KB4501835 をインストールしていてもインストールされます。
- Windows 用の日本の新元号対応更新プログラムについて - KB4469068 (Microsoft)
- Summary of new Japanese era Windows updates - KB4469068 (Microsoft)
- Microsoft、新元号“令和”対応パッチを「Windows 10 バージョン 1809」にも提供 (窓の杜)
- 「MS P ゴシック」「MS UI ゴシック」を使ったExcelシートのレイアウトが狂う問題 (窓の杜)
Win32 API は?
先述の通り、Windows Update により一部の Win32 API が 2019/05/01 になるまで元号レジストリを見なくなります。Delphi だと FormatDateTime() や DateTimeToString() が Win32 API を利用しています。
Microsoft の Visual C++ ももちろん影響を受けます。
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 を利用していると思われます。
- oleauto.h ヘッダ (Microsoft)
- 【令和】新元号対応に関するMicrosoft Win32等の時限措置に関する検証について (Qiita)
- VB6、およびOffice VBAの元号対応の状況について (Qiita)
.NET は?
.NET (.NET Framework / .NET Core) は未来の日付の和暦を扱えます。サポートされているのは .NET Framework 3.5 以降です。
.NET Framework 3.5 は元号がハードコードされていましたが、Windows 7 以降であれば Windows Update を適用する事により、元号レジストリを参照するようになります。
VB6 の互換関数は元年問題が出そうですが...
※ VB6.Format() 関数は元年レジストリで元年 / 1年表記が切り替わります。
Office は?
Office は元号レジストリを参照し、未来の日付の和暦を扱えます。サポートされているのは Office 2010 以降です。
...でもちょっと待ってくださいよ。少なくとも 4/11 の時点ではこうアナウンスしてましたよね?
Windows 上で実行されている Office 製品は 2019 年 5 月 1 日に新元号が開始されるまで、新元号を表示しませんのでご注意ください。
どうして今は変わってるのです?
元号開始時に、お客様に可能な限り最適なエクスペリエンスをご体験いただけるよう、 5 月 1 日に新元号が開始される前に、一部の Office 製品で新元号が表示されるようになります。
※ VBA に関しては基本的に OLE 準拠だと思われます。
元年
元年の件でも Microsoft は文言の修正を行っています。
元年表記をデフォルトに変更 (1 年表記にも変更可能)
元年表記も選択できるよう変更
Windows
元年のレジストリは Windows 10 Insider Preview で元年デフォルト、それ以外の Windows 10 では 1年デフォルトになっているようです。つまり、次のリリースが 2019/05 と言われている新しいビルドでも元年のレジストリは元年デフォルトになると思われます。
元年のレジストリは [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese]
にある InitialEraYear
です。
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese]
"InitialEraYear"="元年"
...ただ、Windows での元年の扱いは 2019/04/10 時点ではバラバラのようです。
※「新元号への対応について」 の文言変更を信じるならば、新しいビルドでも 1809 同様、1年デフォルトになる事もあり得ると思います。
追記: 2019/06/06
5/22 に Windows 10 May 2019 Update (1903) が一般公開されました。
紆余曲折ありアップデートが降ってこない機種もあったようですが、6 月に入ってから順次アップデートが降ってくるようになったようです。
この 1903 へアップデートを行うと元年レジストリが元年に設定されます。
Win32 API
GetDateFormat() や GetDateFormatEx() は元年レジストリを参照するようです。
#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;
}
年
のクォーテーション関係なしに元年表示するようです。また、2019/05/01 を待たずとも元年レジストリが元年
になっていれば平成等は元年表示されます。テストは平成元年でやればよさそうですね。
元年レジストリを参照しない設定で元年表示対応させたければ力業となります。1年
とか 01年
を 元年
に置換すれば OK です。
釈迦に説法だとは思いますが、日付書式文字列をパースしてから置換する必要があります。単純置換してはいけません。
OLE
OLE は元年レジストリを参照するようです。
.NET
.NET は Windows の元年レジストリを参照しません。
.NETFramework 4.52 以前は以下のレジストリを参照します。Switch.System.Globalization.FormatJapaneseFirstYearAsANumber
の値が 1 だと1年
表記になります。
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\AppContext]
"Switch.System.Globalization.FormatJapaneseFirstYearAsANumber"=dword:00000001
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\AppContext]
"Switch.System.Globalization.FormatJapaneseFirstYearAsANumber"=dword:00000001
.NET Framework 4.6 以降や .NET Core での元年対応 (1年表記) 方法はまた異なりますので、このレジストリは見なかった事にした方が混乱しなくていいかと思います。
また、.NET Framework は、KB4489192 がインストールされると元年の扱いが変わってしまうようです。
書式パターンに "年" の文字を囲む半角の引用符が含まれているかいないかに関係なく、1 年目の日付の書式が変更されている日本の元号の元年の文字を出力できます。
2019/04/14 時点では、まだ Windows Update に降ってきていませんが...KB4489192 がいきなりリリースされたら怖いですね。
なお、KB4489192 は Windows 10 (1809) 用のモジュールであり、他のバージョンでは対応モジュールが異なります。
Windows 10 (1809) |
Windows 10 (1803) |
Windows 8.1 | Windows 7 SP1 |
|
---|---|---|---|---|
.NET Franework 3.5 |
KB4489192 | KB4489894 | KB4488663 | KB4488662 |
.NET Franework 4.5.2 |
KB4488667 | KB4488669 | ||
.NET Franework 4.6 以降 |
KB4489192 | KB4489894 | KB4488665 | KB4488666 |
※「新元号への対応について」 の文言変更を信じるならば、これらのモジュールが Windows Update でリリースされない事もあり得ると思います。
追記: 2019/04/30
@CodeOne さんの記事を読みました。
Windows 10 Version 1803 に今回のパッチをあてたところ、シングルクォーテーションなしの書式 "y年" で「元年」と出力される変更も適用されていることが確認できました。
ぇ?
慌てて確認。検証コードは次の通り (対象フレームワーク: 4.5.2) で、これを EXE にし、4/26 の修正モジュールを適用した Windows 7 / 8.1 / 10 (1803) で実行してみました。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
var CultureInfo = new System.Globalization.CultureInfo("ja-JP", false);
CultureInfo.DateTimeFormat.Calendar = new System.Globalization.JapaneseCalendar();
Console.WriteLine(System.DateTime.Now.ToString("yyyy/MM/dd"));
DateTime dt = new DateTime(2019, 5, 1);
Console.WriteLine(dt.ToString("ggy'年'M'月'd'日'", CultureInfo));
Console.WriteLine(dt.ToString("ggy年M月d日", CultureInfo));
Console.ReadKey();
}
}
}
まずは Windows 7 (SP)。シングルクォーテーションで括られていなければ 1年
表記でした。
次に Windows 8.1。こちらもシングルクォーテーションで括られていなければ 1年
表記でした。
そして Windows 10 (1803)。こちらはシングルクォーテーションに関係なく 元年
表記でした。
どういう事なの?.NET の挙動は .NET の修正モジュールで変更になるんじゃなかったの?
追記: 2019/05/02
Windows 10 (1809) 用の新元号対応モジュール KB4501835 の場合、シングルクォーテーションで括られていなければ 1年
表記でした。Windows 10 (1803) 用の新元号対応モジュール KB4493437 と挙動が違う...
またややこしい事に。
Windows 10 1809 |
Windows 10 1803 |
Windows 8.1 | Windows 7 | |
---|---|---|---|---|
新元号対応 モジュール未適用 |
1年 | 1年 | 1年 | 1年 |
新元号対応 モジュール適用 |
1年 | 元年 | 1年 | 1年 |
追記: 2019/05/04
Windows 10 (1809) 用の新元号対応モジュール KB4495667 も、シングルクォーテーションで括られていなければ 1年
表記でした。
追記: 2019/06/06
どの段階何のモジュールか不明ですが (2019/05 のマンスリーロールアップ?)、Windows 10 (1809) や Windows 8.1, Windows 7 もシングルクォーテーションに関係なく 元年
表記されるようになりました。
Windows 10 1903 |
Windows 10 1809 |
Windows 10 1803 |
Windows 8.1 | Windows 7 |
---|---|---|---|---|
元年 | 元年 | 元年 | 元年 | 元年 |
- 2019 年 4 月 3 日 — KB4489192 Windows 10 Version 1809 および Windows Server 2019 用の .NET Framework 3.5 および 4.7.2 の累積的な更新プログラム (Microsoft)
- Working with calendars (Microsoft)
- [.NET][改元] 「元年」表記に変わる日付書式が今になって拡大!(フレームワーク別の対策が必要)――マイクロソフト様、重大な変更をしれっとリリースしないで (Qiita)
Office
Excel や Access は元年レジストリを参照しないようです。Excel や Access は常に 1年
表示です。
勝手に 01
-> 元
となる事はないのでトラブルは少ないと思いますが、元年表示対応は力業となります。
[種類] テキスト ボックスに以下の文字列をコピーして貼り付けます。
[<=43585][$-ja-JP]ggge"年"m"月"d"日";[>=43831]ggge"年"m"月"d"日";ggg"元年"m"月"d"日"
Microsoft の Excel での元年対応例は令和のみの対応なのでこのやり方は評価しません。もう少しマトモな手段を提示すべきです。
Access は VBA を使うでしょうから、テーブルでの和暦表示以外はどうとでもなると思います。
Word や Power Point の日付挿入 (日付と時刻) は元年レジストリを参照するようです。
Excel と Access が 1年
表記な理由ですが、
互換性の観点から 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/5/1, 5/2, 5/4
2019/05/01 を迎えましたので、Win32 API でもすべての API が元号レジストリを参照するようになりました。Windows Update を最新にした状態での時系列は次のようになります。
Win32 API | OLE | .NET | Office | |
---|---|---|---|---|
2019/04/09 以前 | 参照 | 参照しない | 参照 | 参照 |
2019/04/10 以降 (マンスリーロールアップ) |
時限参照 | 参照 | 参照 | 参照 |
2019/04/26 以降 (元号レジストリ追加)1 |
時限参照 | 参照 | 参照 | 参照 |
2019/05/01 以降 (改元) |
参照 | 参照 | 参照 | 参照 |
2019/05/02 以降 (元号レジストリ追加)2 |
参照 | 参照 | 参照 | 参照 |
2019/05/04 以降 (元号レジストリ追加)3 |
参照 | 参照 | 参照 | 参照 |
新元号を事前に使う事の是非
経済産業省の資料にはこうあります。
(3)元号法第1項に基づく政令の公布後の取扱い
元号法(昭和 54 年法律第 43 号)第1項に基づく政令の公布日から施行日前までの間において、各府省が作成し公にする文書に元号を用いて改元日以降の年を表示する場合は、「平成」を用いるものとする(注)。
しかしながら、これは官公庁の公文書の話であり、続けて次のような注意書きがあります。
(注)改元日以降に国民からの申請等又は各府省の通知等に用いられる様式の変更、改元に伴う情報システムの改修等、国民に混乱や不便を生じさせない観点から必要な場合、公布等を除き、「令和」を用いて準備のための手続を行うことができる。
つまり、官公庁であれ民間であれ新元号を用いた事前準備は OK なのです。
改元後の初年を元年と表記する意味
元年表記に法的な根拠はありません。あくまで慣習です。経済産業省の資料にはこうあります。
(2)予算
国の予算における会計年度の名称については、原則、改元日以降は、当年度全体を通じて「令和元年度」とし、これに伴い、当年度予算の名称は、各府省が改元日以降に作成する文書においては「令和元年度予算」と表示するものとする。
これは国の会計年度の話であり「改元後の和暦の初年に元年を使わなければならない」という根拠ではありません。
元年
の理屈で言えば、年の最初の日である 1 月 1 日は 元日
であるため、慣習と言うのなら "gggy'年'M'月'd'日'" に 2020/1/1 が渡されたら 令和 2 年元日 と表示しなくてはなりません。
書式指定が yyyy
であっても yyy
であっても yy
であっても y
であっても 元
って表示されるというのはおかしな話で、やはりコンピュータの処理としては間違っている気がします。元年を使用するにしても画面表示や帳票出力に留めるべきで、やりとりするデータや入力項目で元年を使うべきではないでしょう。
Win32API の GetDateFormat() や .NET の ToString() は表示用だけでなくデータ生成にも使われているのですから、API やフレームワークの挙動を変更するのは悪手だと思います。Microsoft もその危険性を理解しているからこそ、Excel や Access では元年表記しないようにしているのだと思います。
...とはいえ**「日付書式文字列に 年
(という文字) を含めなければ必ず 1 年表記になる」**のですから、解っていれば対処は難しくありません。
おまけ
2019/05/01 時点での対応表です。
注意
Windows 10 (1803) は、4/26 の令和対応モジュール KB4493437 または、.NET の修正モジュール KB4489894 (1809 の KB4489192 に相当) が適用されていると、日付書式文字列内の 年
をシングルクォーテーションで括っていなくても 元年
表記となります。
1803 よりも前の Windows 10 では未確認です。