.NET Date to string дает неверные строки в псевдокультурах Vista - PullRequest
22 голосов
/ 23 ноября 2011

Мой компьютер настроен на культуру , а не en-US.

При использовании встроенной функции Win32 GetDateFormat я получаю правильно отформатированные даты:

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

Это правильно; а также как Windows отображает это:

  • панель задач

    enter image description here

  • Регион и языковые настройки

    enter image description here

  • Проводник Windows

    enter image description here

  • Прогноз

    enter image description here

Когда я пытаюсь преобразовать дату в строку в .NET, используя мою текущую локаль, например ::

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

у меня неверная дата:

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

Эта ошибка в .NET очевидна везде в Windows, где используется ошибка .NET код:

  • Средство просмотра событий Windows:

    enter image description here

  • Планировщик заданий:

    enter image description here

  • SQL Server Management Studio:

    enter image description here

Как сделать так, чтобы .NET не глючил?

Как преобразовать даты и время в строки, используя текущую культуру (правильно)?

Примечание : пользователю разрешено установить их Windows для любых региональных настроек они хотят. Как и сейчас, моя программа не справится действительные настройки правильно. Говорить пользователю «Не делай этого» довольно подло.

Аналогичный пример взят из Delphi, который предполагает, что дата разделитель никогда не может содержать более одного символа. Когда Windows настроен с языком, который использует несколько символов для даты разделитель, например ::1100*

  • sk-SK (Словакия - Словакия): .

где даты должны быть отформатированы как:

22. 11. 2011

библиотека кода не может принять разделитель даты длиннее одного символ и возвращается к:

22/11/2011

В прошлом некоторые могли бы предложить вам не беспокоиться о таком крае случаи . Такие предложения не имеют никакого значения для меня.

Я избегу ссоры с кем-то, кто хочет изменить смысл моего вопроса, изменив название. Но вопрос не ограничивается псевдо-локалями, а именно предназначен для поиска ошибок в приложениях.

Бонусная болтовня

Вот уникальный список форматов дат со всего мира:

  • 11.11.25
  • 11.25.2011
  • 11/25/2011
  • 2011.11.25
  • 2011.11.25.
  • 2011/11/25
  • 2011-11-25
      1. 2011
  • 25.11.11
  • 25.11.2011
  • 25.11.2011 г.
  • 25.11.2011.
  • 25 // 11 // 2011
  • 25/11 2011
  • 25/11/2011
  • 25/11/2554
  • 25-11-11
  • 25.11.2011
  • 29/12/32

Особый интерес представляет последний пример, в котором не использует григорианский календарь :

  • Арабский (Саудовская Аравия) ar-SA: 29.12.32 02:03:07 م
  • Дивехи (Мальдивские Острова) dv-MV: 29/12/32 14: 03: 07
  • Дари / Пушту (Афганистан) prf-AF / ps-AF: 29/12/32 2:03:07 غ.و

Хотя это крайние случаи, о которых вам никогда не придется беспокоиться.


Обновление 14 // 12 // 2011:

Еще одна демонстрация ошибки: 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.У пользовательской локали может не быть единого, одинакового символа разделителя.Например, допустим формат «12/31, 2006».

LOCALE_STIME

Windows Vista и более поздние версии: Эта константаосуждается.Используйте LOCALE_STIMEFORMAT вместо этого.У пользовательской локали может не быть единого, одинакового символа разделителя.Например, допустим формат «03: 56'23».

Ответы [ 2 ]

7 голосов
/ 30 ноября 2011

Эта конкретная ошибка связана с преобразованием некоторых специальных символов, которые не экранированы в шаблонах, таких как 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));

Вот полные примеры кода для всех трех распространенных случаев

Дата в строку

/// <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);
}
2 голосов
/ 30 ноября 2011

Лучше всего зарегистрировать ошибку с 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()

в вашем коде.

...