質問者
PCの日付設定が和暦になっていると、DateTime.ToStringを行うと区切り文字が「年」になってしまう

質問
-
お世話になります。
VisualStudio2017、C#で開発をしております。
問題が発生する環境の前提条件として以下の条件があります。
日付のデータ形式が以下のようになっている
- カレンダー:和暦
- 日付(短い形式):「平成○○年○月○日」
この条件の時に、プログラムのターゲットフレームワークを .NETFramework2.0~3.5 にすると、以下のコードで出力結果がおかしくなります。
static void Main(string[] args) { var culture = CultureInfo.CreateSpecificCulture("ja-JP"); culture.DateTimeFormat.Calendar = new GregorianCalendar(); DateTime date = new DateTime(2000, 1, 1); Console.WriteLine(date.ToString("yyyy/MM/dd", culture)); // 「2000年01年01」 と表示されてしまう Console.Read(); }
Win10 Pro(x64)、Win7(x86) 二つの環境で試してみましたが、どちらも同じ挙動を行います。
DateTimeFormat.DateSeparatorがなぜか「年」になってしまっているため、このような挙動になっています。
ちなみにターゲットフレームワークが.Net4.0以上の場合は「2000/01/01」と正常に表示されます。
不可解なのが、Calendarをセットする前に以下のようにDateSeparatorにアクセスをすると正常な表示になります。
static void Main(string[] args) { var culture = CultureInfo.CreateSpecificCulture("ja-JP"); var dummy = culture.DateTimeFormat.DateSeparator; //何の意味もないアクセスするためだけの処理 culture.DateTimeFormat.Calendar = new GregorianCalendar(); DateTime date = new DateTime(2000, 1, 1); Console.WriteLine(date.ToString("yyyy/MM/dd", culture)); // 「2000/01/01」 と正常に表示 Console.Read(); }
これは.NET3.5系のバグなのでしょうか?何か根本的な解決方法や情報がありましたらご教授いただければ幸いです。
ちなみにターゲットを.NET4以上にあげるのは現状難しいです・・・^^;
- 編集済み Kinenows 2019年8月29日 7:29
- カレンダー:和暦
すべての返信
-
そもそも DateTime 型に対する書式文字 "/" はロケール依存であり、必ずしもスラッシュの出力を意味しません。あくまでも DateTimeFormatInfo.DateSeparator プロパティの値を表示するためのものであるはずです。
同様に、書式文字 ":" もロケール依存であり、コロンではなく DateTimeFormatInfo.TimeSeparator を表すものです。
確実に「/」が出力されるようにしたいのであれば、下記のように書かなければなりません。
// Console.WriteLine(date.ToString("yyyy/MM/dd", culture)); Console.WriteLine(date.ToString("yyyy\\/MM\\/dd", culture)); Console.WriteLine(date.ToString(@"yyyy\/MM\/dd", culture));
-
var dummy = culture.DateTimeFormat.DateSeparator; //何の意味もないアクセスするためだけの処理
上記については、DateTimeFormat プロパティにアクセスするだけで回避されます。
var dummy = culture.DateTimeFormat;
しかし本質的には、書式指定を見直すべき案件であると思います。
また、.NET バージョンによって結果が異なっているのは、.NET Framework のバージョンによって、DateTimeFormat プロパティの初期値の構築手続きに差異があるためです。
※下記はイメージコードであり、実際のものとは多少異なります。
// System.Globalization.CultureInfo public virtual DateTimeFormatInfo DateTimeFormat { get { if (this.dateTimeInfo == null) { DateTimeFormatInfo dateTimeFormatInfo; #if CLR4 dateTimeFormatInfo = new DateTimeFormatInfo(this.m_cultureData, this.Calendar); #else dateTimeFormatInfo = new DateTimeFormatInfo(this.m_cultureTableRecord, CultureInfo.GetLangID(this.cultureID), this.Calendar); #endif dateTimeFormatInfo.m_isReadOnly = this.m_isReadOnly; Thread.MemoryBarrier(); this.dateTimeInfo = dateTimeFormatInfo; } return this.dateTimeInfo; } set { #if CLR4 if (value == null) { throw new ArgumentNullException("value", Environment.GetResourceString("ArgumentNull_Obj")); } this.VerifyWritable(); #else this.VerifyWritable(); if (value == null) { throw new ArgumentNullException("value", Environment.GetResourceString("ArgumentNull_Obj")); } #endif this.dateTimeInfo = value; } }
手元に資料が無いので確認できませんが、カルチャ周りについては、CLR1 から CLR2 の段階でも仕様変更があったはず…。
- 編集済み 魔界の仮面弁士MVP 2019年8月29日 8:36
-
とりあえず下記の場合「.NET Framework 2.0 ~ 3.5」+ 「culture1 パターン」においてのみ "年" で出力されることになるようです。
//var cultureX = CultureInfo.CreateSpecificCulture("ja-JP"); ////var dummy = culture.DateTimeFormat; //cultureX.DateTimeFormat.Calendar = new GregorianCalendar(); var culture0 = new CultureInfo("ja-JP", true) { DateTimeFormat = { Calendar = new GregorianCalendar(), DateSeparator = "/" } }; var culture1 = new CultureInfo("ja-JP", true) { DateTimeFormat = { Calendar = new GregorianCalendar() } }; var culture2 = new CultureInfo("ja-JP", false) { DateTimeFormat = { Calendar = new GregorianCalendar() } }; var culture3 = new CultureInfo("ja-JP", true) { DateTimeFormat = { Calendar = new JapaneseCalendar() } }; var culture4 = new CultureInfo("ja-JP", false) { DateTimeFormat = { Calendar = new JapaneseCalendar() } }; DateTime date = new DateTime(2000, 1, 1); foreach (var culture in new[] { culture0, culture1, culture2, culture3, culture4 }) { Console.WriteLine("---{0} : '{1}'", culture.DateTimeFormat.NativeCalendarName, culture.DateTimeFormat.DateSeparator); Console.WriteLine(date.ToString("yyyy/MM/dd", culture)); // Console.WriteLine(date.ToString("yyyy\\/MM\\/dd", culture)); // Console.WriteLine(date.ToString(@"yyyy\/MM\/dd", culture)); }
.Net4では正常な動作を行うため、何か情報はないかと助けを求めている次第です。
対策として挙げられるのは今のところ、以下の 4 つですね。
- DateSeparator に依存せぬよう、「/」書式を 「\/」書式に改める。
- DateSeparator が「/」となるような CultureInfo を利用する。
- コードは修正せず、.NET Framework バージョンのみ変更する。
- コントロールパネルの地域設定で和暦モードを指定しないようにする。
自分としては 1 を推奨しますが、次点で 2 かな…? 3 や 4 は問題の先送りでしか無いので、個人的には避けたいところです。