22

私のコンピューターは、 ではないen-USカルチャで構成されています。

ネイティブの Win32GetDateFormat関数を使用すると、正しくフォーマットされた日付が取得されます。

  • 22//11//2011 4::42::53P̰̃M]

正解です; また、Windows がそれをレンダリングする方法でもあります。

  • タスクバー

    ここに画像の説明を入力

  • 地域と言語の設定

    ここに画像の説明を入力

  • ウィンドウズ・エクスプローラ

    ここに画像の説明を入力

  • 見通し

    ここに画像の説明を入力

現在のロケールを使用して.NETで日付を文字列に変換しようとすると、次のようになります。

DateTime.Now.ToString();
DateTime.Now.ToString(CultureInfo.CurrentCulture);

私は間違った日付を取得します:

  • 22////11////2011 4::::42::::53P̰̃M]

.NET のこのバグは、バグのある .NET コードを使用する Windows のどこでも明らかです。

  • Windows イベント ビューアー:

    ここに画像の説明を入力

  • タスク スケジューラ:

    ここに画像の説明を入力

  • SQL Server 管理スタジオ:

    ここに画像の説明を入力

.NET にバグがないようにするにはどうすればよいですか?

現在のカルチャを使用して (正しく) 日付と時刻を文字列に変換するにはどうすればよいですか?

: ユーザーは Windows を任意のロケール設定に設定できます。現在、私のプログラムは 有効な設定を適切に処理しません。ユーザーに「そんなことはしないでください」と言うのはかなり意地悪です。

同様の例が Delphi から来ています。Delphi では、日付区切り記号が複数の文字であってはならないことを前提としています。Windows が、日付区切り文字に複数の文字を使用するロケールで構成されている場合:

  • sk-SK (スロバキア - スロバキア) :.

日付は次のようにフォーマットする必要があります。

22. 11. 2011

コード ライブラリは、1 文字を超える日付区切り記号を受け入れることができず、次のようにフォールバックします。

22/11/2011

過去には、そのような極端なケースを気にしないようにと提案する人もいたかもしれません。そのような提案は私にとって重要ではありません。

タイトルを変更して私の質問の意味を変えようとする人との小競り合いは避けたいと思います。しかし問題は、アプリケーションのバグを見つけるために特別に設計された疑似ロケールに限定されません。

ボーナスおしゃべり

以下は、世界中の日付形式のユニークなリストです。

  • 11.11.25
  • 2011.11.25
  • 2011/11/25
  • 2011.11.25
  • 2011.11.25.
  • 2011/11/25
  • 2011-11-25
      1. 2011年
  • 11.11.25
  • 2011.11.25
  • 2011 年 11 月 25 日。
  • 2011.11.25。
  • 2011/11/25
  • 2011年11月25日
  • 2011/11/25
  • 2554/11/25
  • 25-11-11
  • 2011 年 11 月 25 日
  • 29/12/32

特に興味深いのは、グレゴリオ暦を使用しない最後の例です。

  • アラビア語 (サウジアラビア) ar-SA: 29/12/32 02:03:07 ã
  • ディベヒ (モルディブ) dv-MV: 29/12/32 14:03:07
  • Dari/Pashto (アフガニスタン) prf-AF / ps-AF: 29/12/32 2:03:07 غ.و

これらは、心配する必要のない特殊なケースですが。


2011 年 12 月 14 日更新:

バグの別のデモンストレーションは、Datetime.Parse解析できないことDateTime.ToStringです:

String s = DateTime.Today.ToString("d");   //returns "14////12////2011"
DateTime d = DateTime.Parse(s);            //expects "dd//MM//yyyy"

.Parse例外をスローします。


更新 02//8、2012 09::56'12:

日付区切り文字の使用は、正しくないだけでなく、非推奨です。MSDN から:

LOCALE_SDATE

Windows Vista 以降:この定数は非推奨です。LOCALE_SSHORTDATE代わりに使用してください。カスタム ロケールには、1 つの統一された区切り文字がない場合があります。たとえば、「2006 年 12 月 31 日」などの形式が有効です。

LOCALE_STIME

Windows Vista 以降:この定数は非推奨です。LOCALE_STIMEFORMAT代わりに使用してください。カスタム ロケールには、1 つの統一された区切り文字がない場合があります。たとえば、「03:56'23」などの形式が有効です。

4

2 に答える 2

7

この特定のバグは、 のようなパターンでエスケープされない一部の特殊文字の変換によるものShortDatePatternです。

ShortDatePattern = "d//MM//yyyy";

/DateTimeFormatパターンでは「日付区切り記号を挿入する」ことを意味しますが、ここでは、文字列がシステムから構造にコピーされるときに、(少なくとも私のシステムでは) 展開が既に行われています。悲しいことに、エスケープがありません(明らかに、特殊文字を区切り文字として使用していない言語では表示されず、英語ではそれ自体に置き換えられるため表示されません)

DateTimeFormat唯一の解決策は、インスタンスのすべてのパターンでセパレーターをエスケープすることです。

var c = new System.Globalization.CultureInfo("qps-ploc", true);
c.DateTimeFormat.ShortDatePattern =
        c.DateTimeFormat.ShortDatePattern.Replace("/", "'/'");
c.DateTimeFormat.LongTimePattern =
        c.DateTimeFormat.LongTimePattern.Replace(":", "':'");
Console.WriteLine(DateTime.Now.ToString(c));

3 つの一般的なケースすべての完全なコード サンプルを次に示します。

日付から文字列へ

/// <summary>Convert a date to the short date string in the current locale (e.g. 30//11//2011)</summary>
/// <param name="value">A DateTime to be converted to a short date string</param>
/// <returns>A string containing the localized version of the date</returns>
public static String DateToStr(DateTime value)
{
    String format = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;

    //The bug in .NET is that it assumes "/" in a date pattern means "the date separator"
    //What .NET doesn't realize is that the locale strings returned by Windows are the Windows format strings. 
    //The bug is exposed in locale's that use two slashes as for their date separator:
    //  dd//MM//yyyy
    // Which .NET misinterprets to give:
    //  30////11////2011
    // when really it should be taken literally to be:
    //  dd'//'MM'//'yyyy
    //which is what this fix does
    format = format.Replace("/", "'/'"); 

    return value.ToString(format);
}

弦を張る時間

/// <summary>
/// Convert a time to string using the short time format in the current locale(e.g. 7::21 AM)
/// </summary>
/// <param name="value">A DateTime who's time portion will be converted to a localized string</param>
/// <returns>A string containing the localized version of the time</returns>
public static String TimeToStr(DateTime value)
{
    String format = CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern;

    //The bug in .NET is that it assumes ":" in a time pattern means "the time separator"
    //What .NET doesn't realize is that the locale strings returned by Windows are the Windows format strings. 
    //The bug is exposed in locale's that use two colons as their time separator:
    //  h::mm::ss tt
    // Which .NET misinterprets to give:
    //  11::::39::::17 AM
    // when really it should be taken literally to be:
    //  h'::'mm'::'ss tt
    //which is what this fix does
    format = format.Replace(":", "':'"); 

    return value.ToString(format);
}

日時を文字列に

/// <summary>
/// Convert a datetime to a string in the current locale (e.g. 30//11//2001 7::21 AM) 
/// </summary>
/// <param name="datetime">A DateTime to be converted to a general string in the current locale</param>
/// <returns>A string containing the localized version of the datetime</returns>
public static String DateTimeToStr(DateTime datetime)
{
    return DateToStr(datetime)+" "+TimeToStr(datetime);
}
于 2011-11-30T13:59:01.813 に答える
2

最善の策は、MS でバグを記録し、これらのエッジ ケースを検出して処理する拡張メソッドを作成することです。

このようなもの(私の頭の上から):

public static class DateTimeFix
{
    public static string FixedToString(this DateTime value)
    {
        if (IsEdgeCase())
            return FixEdgeCase(value);
        else
            return value.ToString();
    }

    // Edge case logic below
}

次に、次を使用します。

DateTime.Now.FixedToString()

あなたのコードで。

于 2011-11-30T12:20:43.457 に答える