C#で日付のフォーマットを変更していた際に、思い通りの挙動にならず
仕様について調べたのでそれの備忘録です
発生した問題
using System.Globalization;
using System.Threading;
// 2025/01/06
CultureInfo originalCulture = new CultureInfo("ja-JP");
originalCulture.DateTimeFormat.Calendar = new JapaneseCalendar();
originalCulture.DateTimeFormat.ShortDatePattern = "ggy年M月d日";
Thread.CurrentThread.CurrentCulture = originalCulture;
DateTime.Today.ToString();
// 令和7年1月6日 0:00:00
Thread.CurrentThread.CurrentCulture
で和暦で日付を表示させるようにしていましたが...
↓
using System.Globalization;
using System.Threading;
// 2025/01/06
CultureInfo originalCulture = new CultureInfo("ja-JP");
originalCulture.DateTimeFormat.Calendar = new JapaneseCalendar();
originalCulture.DateTimeFormat.ShortDatePattern = "ggy年M月d日";
Thread.CurrentThread.CurrentCulture = originalCulture;
// 非同期で実行
Task.Run(() => {
DateTime.Today.ToString();
});
// 2025/01/06 0:00:00
非同期処理だと日付のフォーマットがデフォルトのままでした
問題の原因
Thread.CurrentThread.CurrentCulture
は設定したスレッドでのみ有効で、非同期処理は別のスレッドで実行されるため日付のフォーマットがデフォルトになっていました。
問題の解決策
解決策 (2025/01/08追記)
1.非同期処理の中でDateTimeFormatを設定
using System.Globalization;
using System.Threading;
// 2025/01/06
CultureInfo originalCulture = new CultureInfo("ja-JP");
originalCulture.DateTimeFormat.Calendar = new JapaneseCalendar();
originalCulture.DateTimeFormat.ShortDatePattern = "ggy年M月d日";
// 非同期で実行
Task.Run(() => {
// 非同期処理の中でDateTimeFormatを設定
Thread.CurrentThread.CurrentCulture = originalCulture;
DateTime.Today.ToString();
});
// 令和7年1月6日 0:00:00
- 良い点:どこで日付のフォーマットが変更されたか見やすい
- 悪い点:非同期処理をするたび(スレッドが変わるたび)に設定しないといけない
2.CultureInfo.DefaultThreadCurrentCultureで全体のフォーマットを設定
CultureInfo originalCulture = new CultureInfo("ja-JP");
originalCulture.DateTimeFormat.Calendar = new JapaneseCalendar();
originalCulture.DateTimeFormat.ShortDatePattern = "ggy年M月d日";
CultureInfo.DefaultThreadCurrentCulture = originalCulture;
// 非同期で実行
Task.Run(() => {
DateTime.Today.ToString();
});
// 令和7年1月6日 0:00:00
- 良い点:設定されていない全ての非同期処理のフォーマットが切り替わる
- 悪い点:全てのフォーマットが切り替わるので、個別でフォーマットを変えたいときに不便
2025/01/08追記
コメントでアドバイスをいただいたので、追記させていただきます(ありがとうございます!)
1.2の合わせ技やString
の書式指定で個別のFormatを綺麗に設定できそうです
日付フォーマットの適用順
CultureInfo.DefaultThreadCurrentCulture
↓
Thread.CurrentThread.CurrentCulture
↓
String.Format
やToString
上に行くほど範囲が広くなり、下に行くほど優先度が高くなります
using System.Globalization;
using System.Threading;
// 2025/01/06
CultureInfo originalCulture = new CultureInfo("ja-JP");
originalCulture.DateTimeFormat.Calendar = new JapaneseCalendar();
originalCulture.DateTimeFormat.ShortDatePattern = "ggy年M月d日";
// 全体のデフォルトFormatを設定
CultureInfo.DefaultThreadCurrentCulture = originalCulture;
// 非同期で実行
Task.Run(() => {
DateTime.Today.ToString();
DateTime.Today.ToString();
DateTime.Today.ToString();
});
// 令和7年1月6日 0:00:00
// 令和7年1月6日 0:00:00
// 令和7年1月6日 0:00:00
// 非同期で実行(非同期処理内全部に設定 和暦→西暦)
Task.Run(() => {
// 西暦のカレンダーでフォーマットを設定
CultureInfo asyncCulture = new CultureInfo("ja-JP");
asyncCulture.DateTimeFormat.Calendar = new GregorianCalendar();
asyncCulture.DateTimeFormat.ShortDatePattern = "yyyy年MM月dd日";
Thread.CurrentThread.CurrentCulture = asyncCulture;
DateTime.Today.ToString();
DateTime.Today.ToString();
DateTime.Today.ToString();
});
// 2025/01/06 0:00:00
// 2025/01/06 0:00:00
// 2025/01/06 0:00:00
// 非同期で実行(個別に設定 和暦→西暦)
Task.Run(() => {
// 西暦のカレンダーを設定
CultureInfo gregorianCulture = new CultureInfo("ja-JP");
gregorianCulture.DateTimeFormat.Calendar = new GregorianCalendar();
// デフォルト
DateTime.Today.ToString();
// ToStringのカスタム日時書式指定子で変換
DateTime.Today.ToString("yyyy年M月d日", gregorianCulture);
// String.Formatで変換
string.Format(gregorianCulture, "{0:yyyy年MM月dd日}", DateTime.Today);
});
// 令和7年1月6日 0:00:00
// 2025年1月6日
// 2025年01月06日
実行環境 (2025/01/10追記)
.NET Framework 4.5
.NET Framework
のバージョンによって非同期処理時のCurrentCultureの挙動が異なるようです
↓こちらの記事で、詳しく解説されています。
おわりに
今回の問題は、同期処理を非同期処理に変更する時(逆もしかり)などに、よく起こりそうなので、そういった修正をする際には日付のフォーマットに注意しておこうと思いました。
参考にさせていただいた記事、サイト