Подтвердить что ты не робот

.NET Date to string дает недопустимые строки в Vista Pseudo-culture

Мой компьютер настроен с культурой not 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

  • Outlook

    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:

    enter image description here

Как сделать .NET не ошибкой?

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

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

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

  • 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

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

  • Арабский (Саудовская Аравия) 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".

4b9b3361

Ответ 1

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

Datetime для строки

/// <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

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

в вашем коде.