Edited at

[.NET][改元] 「元年」表記に変わる日付書式が今になって拡大!(フレームワーク別の対策が必要)――マイクロソフト様、重大な変更をしれっとリリースしないで


「元年」表記の既定動作が突然変わった!

新元号「令和」が発表されて間もない4月5日、一緒に仕事するエンジニアの方に教えていただき、愕然としました。

2019 年 4 月 3 日 — KB4489192 Windows 10 Version 1809 および Windows Server 2019 用の .NET Framework 3.5 および 4.7.2 の累積的な更新プログラム


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


改元まで残り1か月を切り、新元号の Windows への組み込みが差し迫った、このタイミングで?

本気でしょうか。

.NET Framework 用の日本の新元号対応更新プログラムの概要(2019/4/3付)


日本の元号の書式で元年の文字を出力するにはシングル クォーテーションを付ける必要があった問題を解決する新しい品質更新プログラムも含まれています。そのため、.NET Framework でカスタムの日付と時刻の書式に “y年” または “y’年'” のいずれを使用しても、年の数値 1 を元という書式で示すことができるようになりました。


どうやら本気のようです。


どうなるのか?

以下のようになります。

var japanseCalendarCulture = new CultureInfo("ja-JP") { DateTimeFormat = { Calendar = new JapaneseCalendar() } };

Assert.AreEqual("平成元年", new DateTime(1989, 2, 1).ToString("ggyy年", japanseCalendarCulture));
Assert.AreEqual("令和元年", new DateTime(2019, 5, 1).ToString("ggyy年", japanseCalendarCulture));


何が変わったのか?

これまでの改元対応では、「元年」表記されるのは "yy'年'" のように「年」をシングルクォーテーションで囲んだときだけでした。

"yy年" のようにシングルクォーテーションで囲まない場合、これまでは「01年」でしたが、今後は「元年」と表記されるようになります。

※ここでは書式 "yy" を例に説明を進めますので、書式 "y" の場合は「1年」に読み替えてください。


何が困るのか?


  • 「01年」が「元年」と表記されるように変わる。(これ自体は仕様として許容できることが多いでしょう)

  • 和暦文字列の年部分が数値であることを前提とした処理でエラーが発生する。

  • 和暦文字列の年部分が2文字であることを前提とした処理でエラーが発生する。

  • カルチャを指定しない DateTime.Parse, DateTime.TryParse メソッドが「元年」の和暦文字列を解釈できない。

※こうした文字列解析処理の是非はここでは問題にしません。現実にありうるなら対処が必要、という視点で書いています。


これまでの経緯

2018年8月、.NET Blog(英語)で .NET Framework で「元年」表記が既定になるとアナウンスされました。

2018年10月、対応版のパッチがリリースされ、実際に書式 "yy'年'" で和暦初年が「元年」に文字列化されることが確認できました。

一方で、「年」をシングルクォーテーションで囲まない "yy年" 書式による和暦初年文字列は、新元号検証用のレジストリ設定が公開された後も後述する FormatJapaneseFirstYearAsANumber 設定に関係なく「01年」でした。

※この間の実行結果は、『「元年」に対応した.NET Frameworkを試してみた - GrapeCity.devlog』や『西暦と和暦を変換するには?:.NET TIPS - @IT』にあるとおりです(いずれも 2019/4/14 時点)。

中途半端な仕様だなとは感じつつも、特に変更予定の告知もなく、公式に検証方法まで案内されているので、「この仕様で行くのだろう。シングルクォーテーションはつけていないので対応は不要だ」と判断した人は少なくないでしょう。

もちろん「最終的にはシングルクォーテーションなしにも対応するのでは?」という可能性は想定しえたわけですが、明確な予告のない状況で具体的な対応に踏み切ることはそうそうできません。


リリース状況

この KB4489192 のリリース事情がまた複雑です。

2018年3月20日(日本時間)、.NET Framework 2018年3月更新がリリースされました。

このパッチによって "y年" が「元年」に変換されるようになることが、リリース報告記事『.NET Framework March 2019 Update | .NET Blog(英語)』に記載されています。

※上記@IT記事の共著者の一人である山本康彦氏が、3月20日にTwitterでこの記事を取り上げて話題にしていました。

しかし、そこに記載のない Windows 10 Version 1809 / Server 2019 向けの更新(KB4489192)は4月3日(日本時間)までずれ込みました(4/14現在もHTMLの title は「2019 年 3 月 20 日 — KB4489192」のままです)。

.NET Framework 用の日本の新元号対応更新プログラムの概要』にOSごとのKB一覧がありますが、


Windows 8.1 以前のバージョンのサポートされる Windows 用に .NET Framework のセキュリティおよび品質ロールアップを 2019 年 3 月にリリースしませんでした。


とありますので、実際には Windows 8.1 以前(Windows 7, 8.1, Server 2008/R2, 2012/R2)向けのリリースも4月1日までずれ込んだ可能性があります。

Windows 10 1809 / Server 2019 向けの KB4489192 は、Microsoft Update カタログ からダウンロードして Windows 10 Version 1809(October 2018 Update)環境にインストールできますが、4月14日時点でまだ Windows Update には上がってこないようです。

※『山市良のえぬなんとかわーるど: 本日の Windows Update - 2019 年 4 月 2 日(4 月に流れた 3 月 D、RS5 のみ)』にこの辺りの経緯が書いてあります。

※Windows 8.1 以前のOS向け .NET Framework 2018年3月更新の個々の Windows Update 配信状況は把握できておりません。

リリース済みですので、いつ Windows Update で配信されてもおかしくない状況ではあります。

逡巡しているかのような上記状況から、撤回/延期される可能性もゼロとは言い切れませんが。

⇒ [2019/04/14 追記]、[2019/04/29 追記]、[2019/15/15 追記] に続報があります。


マイクロソフト社の言い分(推測)

「品質更新」ですから、当初よりシングルクォーテーションなしにも対応すべきだった、ということなのでしょう。

カスタム数値書式指定文字列 | Microsoft Docs』には


リテラル文字列全体を引用符のアポストロフィで囲みます。


とありますので、囲んでいない方が悪い ⇒ 改善リリースもあらかじめ通知しなくてもいいだろう、という判断なのかもしれません。

※あくまで推測であり、違っているかもしれませんが、それなら不備と認識した時点で、余裕を持った事前のアナウンスが欲しかったです。


そうはいかない現実

現実には著名なサイトの技術記事でも和暦書式として "ggyy年" が普通に使われています。

ということは、現場レベルでもシングルクォーテーションなしの書式で和暦文字列化しているケースはかなり多いと考えられます。

問題が起こらないようにするためには、調査、実装、テスト、ユーザー様への通知が必要となります。

新元号「令和」の Windows 組み込みは間近に迫っています。

それまでに何とか直せば大丈夫、ということではありません。

生年月日など、平成元年のデータは現にシステムで扱っていることもあります。

急に「01年」が「元年」に変わっては困るのです。


どうすればいいのか?

"yy'年'" のようにシングルクォーテーションで囲っていた場合は、すでに既定で「元年」になっていますので、対応済みでしょう。

問題は "yy年" のように指定していた場合です。

KB4489192 が適用された後も「01年」表記を続けるためには対応が必要となります。

※システム内で「01年」表記に戻すことができたとしても、連携するシステムから「元年」のデータを受け取る可能性がありますので、協調して対応を進めましょう。


.NET Core の場合

プロジェクト直下にコンテンツとして以下のような runtimeconfig.template.json を配置してビルドすると、{AppName}.runtimeconfig.json に設定が追記され、既定の「元年」表記から「01年」表記に変わります。


runtimeconfig.template.json

{

"configProperties": {
"Switch.System.Globalization.FormatJapaneseFirstYearAsANumber": true
}
}

  ↓ (ビルド出力)


{AppName}.runtimeconfig.json(例)

{

"runtimeOptions": {
"tfm": "netcoreapp2.2",
"framework": {
"name": "Microsoft.AspNetCore.App",
"version": "2.2.0"
},
"configProperties": {
"Switch.System.Globalization.FormatJapaneseFirstYearAsANumber": true,
"System.GC.Server": true
}
}
}


.NET 4.6 以降で動作する場合(Windows Forms/WPF)

構成ファイル(App.config ⇒ {AppName}.exe.config)に以下のような AppContextSwitchOverrides 要素を追記すると、既定の「元年」表記が無効となり、「01年」表記になります。


App.config

<?xml version="1.0" encoding="utf-8"?>

<configuration>
<runtime>
  :
<AppContextSwitchOverrides value="Switch.System.Globalization.FormatJapaneseFirstYearAsANumber=true" />
  :

.NET 4 系はインプレースアップグレードですので、ターゲットフレームワークが .NET 4.6 未満でも、.NET 4.6 以降がインストールされた環境ではこの設定が有効となります。

ターゲットフレームワークが .NET 4.6 以上の場合、AppContext.SetSwitch(String, Boolean) メソッドで指定することも可能です。

AppContext.SetSwitch("Switch.System.Globalization.FormatJapaneseFirstYearAsANumber", true);

ただし、この設定にはキャッシュされる仕組みがあり(System.AppContextSwitches ソースコード参照)、一度 String.Format や DateTime.ToString による日付文字列化を行うと、その後に SetSwitch しても効かずに「元年」出力されたりします。設定のタイミングが遅れないよう、記述箇所には注意しましょう。


.NET 4.6 以降で動作する場合(ASP.NET)

Web.config に以下のような設定を追記すると、既定の「元年」表記が無効となり、「01年」表記になります。


Web.config

<?xml version="1.0" encoding="utf-8"?>

<configuration>
:
<appSettings>
<add key="AppContext.SetSwitch:Switch.System.Globalization.FormatJapaneseFirstYearAsANumber" value="true" />
:


.NET 3.5 や .NET 4.5.2 までのランタイムで動作する場合

※Windows 10 の場合、.NET Framework 4.6 以上がインストールされていますので、.NET 4 系のアプリケーションは(ターゲットフレームワークが 4.5.2 以下だとしても)該当しません。

Handling a new era in the Japanese calendar in .NET | .NET Blog』や『カレンダーの使用 | Microsoft Docs』で紹介されているように、レジストリ登録することで「01年」表記に戻すことが可能です。

Key     HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\AppContext

Name Switch.System.Globalization.FormatJapaneseFirstYearAsANumber
Type REG_SZ
Value 1

ただし、アプリケーション単位でなく、マシン全体の設定になってしまいます。

試してみたところ、.NET 4.6 以降で動作するアプリケーションには影響しないようです。

WOW64(64ビット Windows で x86 ターゲット)の場合は Key が変わります。

Key     HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\.NETFramework\AppContext

Name Switch.System.Globalization.FormatJapaneseFirstYearAsANumber
Type REG_SZ
Value 1

WOW64 については、上記 .NET BlogMicrosoft Docs にも記載がありません。

きちんと案内しなくて大丈夫なのだろうか。


別解(というより時間的余裕があるなら王道)

FormatJapaneseFirstYearAsANumber=true は設定せずに、「元年」表記でも問題ないようにコードを修正します。

「修正なしで問題ないだろう」と思っても、それを保証するためには、ソースコード全体の調査とある程度の動作検証が必要でしょう。

影響するようなコードを書いた覚えがなくても、ほかのプログラマが書いているかもしれません。

コード内に問題が見つからなくても、使用しているサードパーティーコンポーネントで問題が発生するかもしれません1

システム単体では問題がなくても、連携するシステムに「元年」を送ってエラーになることがあるかもしれません。

"yy" の直後に "'月'" や "月" がつかなければ「元年」表記にはならないので、どうしても「01」表記にしたい箇所については、年部分とその後ろを別々に文字列化して結合する、といった対応が考えられます。

なお、実行環境を完全に制御できる場合でない限り、 和暦初年が必ず「元年」表記になることを前提とする、逆の罠にはまらないように気をつけましょう。

そのコードは、「元年」表記が既定で有効になる前の環境で動く可能性があります。

※修正箇所については上記「何が困るのか?」をご参照ください。


VB6(Visual Basic 6.0)/VBA(Visual Basic for Applications) は?

2019年4月セキュリティ更新(KB4493509)を適用した Windows 10 Version 1809 環境での VB6/VBA 実行結果は以下のようになりました。

※新元号のレジストリ設定 "令和_令_Reiwa_R" を手動で登録しています。

※「←」の右は2019年4月セキュリティ更新をあてる以前の確認結果です。

IsDate, CDate が新元号レジストリ設定を解釈できるように、Format は「元年」でなく「01年」を返すように変わりました。

@tfukumori さんの記事『VB6、およびOffice VBAの元号対応の状況について』に環境別の詳細な検証結果があります。

ただし、Windows 10 の次期大型アップデート「19H1」の Insider Preview 版では、Format 関数の依存する元年/1年のレジストリ設定 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese] InitialEraYear が "元年" になっています(KB4469068「元年対 1年」参考)。

万全を期すならどちらを返しても問題ないように対応しておくのが安心と言えるでしょう。

⇒ 正式版の Windows 10 Version 1903 にアップデートしたところ、既定が "元年" に変わっていました([2019/05/28 追記])。

?VBA.IsDate("平成01年02月01日")

True

?VBA.IsDate("平成元年02月01日")
True

?VBA.IsDate("令和01年05月01日")
True(← False

?VBA.IsDate("令和元年05月01日")
True(← False

?VBA.CDate("平成01年02月01日")
1989/02/01

?VBA.CDate("平成元年02月01日")
1989/02/01

?VBA.CDate("令和01年05月01日")
2019/05/01(← 実行時エラー '13': 型が一致しません。)

?VBA.CDate("令和元年05月01日")
2019/05/01(← 実行時エラー '13': 型が一致しません。)

?VBA.Format$("1989/02/01", "gggee年")
(平成元年 ←)平成01年(← 平成元年)

?VBA.Format$("2019/05/01", "gggee年")
(令和元年 ←)令和01年(← 令和元年)

VB6 も Windows 10 でランタイムはサポートされているわけですから、IsDate, CDate, Format 等の動作について、対応方法まで明示する義務はないとしても、最終的にどう変わるのか、あるいは変わらないのか、わかりやすい形の公式情報が欲しいところです。


Windows Update で KB4489192 が配信されたら大騒ぎにならないか?

一度リリースして後に撤回された新元号「??」仮表記みたいに(あるいはそれ以上に)。

「大丈夫、皆対応しているよ」と、杞憂であればいいのですが。


マイクロソフト様

改元が間近に迫り、いろいろたいへんだとは推察しますが、特にシステムの改修が必要となるような変更については、対応に時間が必要ですので、修正内容とリリース/Windows Update 配信時期について、あらかじめ余裕を持った情報公開をお願いできればと思います。

2019 年 5 月の新元号への変更に関する更新


2019 年 4 月 1 日に新元号の名称が発表されました。弊社のエンジニアリング チームは、段階的に実稼働環境用の更新プログラムをリリースしていきます。完了するまでに数か月程度かかることがあります。


改元までに間に合わない機能や制限事項があれば、わかりやすい形でのご提示を望みます。

(届かないか…と思いつつ、回り回って伝わればと期待を込めて)


続報


[2019/04/12 追記]

Windows 10ミニTips(367) 「令和」前にチェック! 和暦を設定する方法 | マイナビニュース


先日筆者が日本マイクロソフトのセミナーに参加した際に知ったのだが、Windows 10でもハードコードが各所に残っているという。通常はHKEY_LOCAL_MACHINE¥SYSTEM¥CurrentControlSet¥Control¥Nls¥Calendars¥Japanese¥Erasキーに並ぶ値から元号データを取得するものの、日本語MS-IMEやVBScriptエンジンはハードコードのため、OSが新元号に対応しても平成31年という表記になってしまうそうだ。

日本マイクロソフトは現在改善に取り組んでいる。だが、「令和」が適用される修正プログラムの公開は2019年4月末になりそうだ。



[2019/04/13 追記]

新元号への対応について - Microsoft mscorp


4 月 5 日時点で主要な機能更新を終え、お客様、パートナー様にご確認いただける段階になっております。

4 月 1 日の新元号発表を受け、残りの作業である合字のデザイン、フォントの更新、レジストリ (設定データベース)、および IME の対応を進め、4 月中のご提供ができるよう準備を進めております。



[2019/04/14 追記]

@ht_deko さんの記事『【令和】Microsoft の元号対応が迷走している件』で気づきましたが、『新元号への対応について - Microsoft mscorp』の記載がここにきて変更されたようです。


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

  ↓

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


危惧する声を考慮したのか。

さらなる変更の予兆でしょうか。

.NET Framework 用の日本の新元号対応更新プログラムの概要』の記載は変わっていないようですが。


[2019/04/29 追記]

4月26日に配信開始された「令和」対応パッチ(プレビュー版)を Windows 10 Version 1803 環境にあてると、シングルクォーテーションなしの書式 "y年" で「元年」と出力される変更も適用されることが確認されました。

Windows 8.1 や Windows 7 向けのパッチではこの変更は適用されないようです。

「令和」対応パッチ(プレビュー版)には別の問題も含まれているので、こちらをご参照ください。

【警鐘】[改元][Windows][.NET] 「令和」対応パッチで画面が横に伸びる、文字が見切れる ― Windows Update 手動更新はちょっと待った方がいい


[2019/05/06 追記]

Windows 10 Version 1809, Server 2019 向けの「令和」対応パッチ(プレビュー版)が、5月2日に KB4501835、5月3日にはその他の累積的な修正を含む KB4495667 としてリリースされました。

これらのパッチでは、Version 1803 向けと異なり、シングルクォーテーションなしの書式 "y年" で「元年」と出力される変更は確認されませんでした。


[2019/05/15 追記]

2019年5月15日(日本時間)、5月の Windows 月例更新プログラムで「令和」対応パッチの正式版(プレビュー版の不具合を修正)がリリースされ、Windows Update 自動更新の対象となりました。

同日に配信された .NET Framework 累積更新プログラム KB4499405(.NET Framework 3.5/4.7.2 向け:KB4495590)が適用されると、シングルクォーテーションなしの書式 "y年" で「元年」と出力されるようになることが確認されました(確認環境:Windows 10 Version 1809)。

撤回されることはなく、この仕様で確定したと考えてよさそうです。

いよいよ自動更新の対象になりましたので、まだの場合は対応を急ぎましょう。


[2019/05/28 追記]

2019年5月22日(日本時間)に一般公開された「Windows 10 May 2019 Update」(開発コード 19H1/バージョン 1903)を適用すると、[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese] InitialEraYear が "1年" から "元年" に変わり、VB6 で Format 関数の戻り値に反映されることが確認されました。

.NET Framework の書式パターンを指定した文字列化や Excel の「和暦」セル書式には影響しません。

なお、現状では既知の不具合が多数ありますので、手動でパッチをあてる際は注意しましょう。

「Windows 10 May 2019 Update」には既知の不具合が12件 ~手動更新には十分注意 - 窓の杜

「May 2019 Update」に新たな既知の不具合が2件 ~手動更新には十分注意 - 窓の杜

⇒ 5月30日(日本時間)、修正パッチがリリースされました。


参考

過去にはこうした記事もありましたね。

マイクロソフトさん、お願いですから.NET Frameworkの動作を何の説明もなく変更するのはやめてください。- 改元対応で思うこと

元号1文字表記の問題について、収束までの経緯。

新元号(平成の次の元号)対応におけるMicrosoftのセミナー「新元号とマイクロソフト製品における対応」を受けてきました


Microsoft製品の新元号対応が難航していてスケジュールが遅れるかも。。。






  1. 一連の改元騒動について「和暦なんて使うから」という書き込みも散見されるので、あえて参考に挙げておきます。特殊な例とは思いますが、西暦を使っていれば安心とは限りません。

    [FlexGrid for WinForms] Windows 10 April 2018 Updateが適用された環境において、2019/5/1以降の日付に対してColumn.Formatで指定した書式が適用されない - ComponentOne for WinForms - ナレッジベースの詳細|Developer Tools - グレープシティ株式会社