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

Ошибка в DateTime.ToString( "T" ) и DateTime.ToString( "G" )?

Microsoft указывает : как The time separator, но, похоже, есть как минимум два разделителя времени: один между часами и минутами и другой один между минутами и секундами.

Настройки формата длительного времени на панели управления http://uploads.wellisolutions.de/stackoverflow/ControlPanelLongTimeFormat.png

Формат длинного времени, отображаемый часами Windows http://uploads.wellisolutions.de/stackoverflow/ClockCustomFormat.png

Есть ли способ получить конкретные разделители времени? Мне нужен один между часами и минутами, а другой - между минутами и секундами.

Я также не хочу создавать строку DateTime другими способами, например. используя стандартную строку T или G, но оба они не работают

mydate.ToString("T");
// Output: 20-29-46
// Expected output: 20-29:46 (as shown by Windows clock)

mydate.ToString("G");
// Output: 09/03-2014 20-29-46
// Expected output: 09/03-2014 20-29:46
4b9b3361

Ответ 1

Получение разделителей

Как заявил Jeppe Stig Nielson (возможно, для него выше), нет хорошего способа получить второй таймер или разделитель даты, потому что в строке формата, например

HH-mm/HH:mm-HH/mm

их может быть несколько, даже с той же семантикой (например, между часами и минутами).

Отчет об ошибке Microsoft

Я зарегистрировался в Microsoft Connect и зарегистрировал ошибку как DateTime.ToString( "T" ) и DateTime.ToString( "G" ), Если у вас есть учетная запись Microsoft Connect, вы можете проголосовать, можете ли вы воспроизвести ошибку.

SSCCE для воспроизведения ошибки

using System;
using System.Globalization;

namespace DateTimeToString
{
    class Program
    {
        // Tested on
        // Microsoft Windows 7 Enterprise x64 Version 6.1.7601 Service Pack 1 Build 7601
        // I should have all official updates installed at the time of writing (2014-03-11)
        //
        // Microsoft Visual Studio Premium 2012 Version 11.0.61030.00 Update 4
        // Microsoft .NET Framework Version 4.5.50938
        //
        // Type: Console application x86
        // Target framework: .NET 4 Client Profile
        static void Main()
        {
            if (DateTimeFormatInfo.CurrentInfo.LongTimePattern != "HH-mm:ss" ||
                DateTimeFormatInfo.CurrentInfo.ShortDatePattern != "dd.MM/yyyy")
            {
                Console.WriteLine("Configure long time format to MM-mm:ss to reproduce the time bug.");
                Console.WriteLine("Configure short date format to dd.MM/yyyy to reproduce the date bug.");
                Console.WriteLine("Control Panel/Region and Language/Additional settings");
                return;
            }

            var dateTime = DateTime.Now;
            Console.WriteLine("Expected: " + dateTime.ToString("HH'-'mm':'ss"));
            Console.WriteLine("Actual  : " + dateTime.ToString("T"));
            Console.WriteLine();

            Console.WriteLine("Expected: " + dateTime.ToString("dd'.'MM'/'yyyy HH'-'mm':'ss"));
            Console.WriteLine("Actual  : " + dateTime.ToString("G"));
            Console.WriteLine();

            Console.WriteLine("Expected: " + dateTime.ToString("HH'-'mm':'ss"));
            Console.WriteLine("Actual  : " + dateTime.ToLongTimeString());
            Console.WriteLine();

            Console.WriteLine("Expected: " + dateTime.ToString("dd'.'MM'/'yyyy"));
            Console.WriteLine("Actual  : " + dateTime.ToShortDateString());
            Console.ReadLine();
        }
    }
}

Обход

В качестве обходного пути мы можем использовать собственные методы GetTimeFormat и GetDateFormat.

static class Program
{
    static void Main()
    {
        var systemTime = new SystemTime(DateTime.Now);

        Console.WriteLine("ShortDatePattern (as reported by .NET): " + DateTimeFormatInfo.CurrentInfo.ShortDatePattern);
        var sbDate = new StringBuilder();
        GetDateFormat(0, 0, ref systemTime, null, sbDate, sbDate.Capacity);
        Console.WriteLine("Date string (as reported by kernel32) : " + sbDate);
        Console.WriteLine();

        Console.WriteLine("LongTimePattern (as reported by .NET) : " + DateTimeFormatInfo.CurrentInfo.LongTimePattern);
        var sbTime = new StringBuilder();
        GetTimeFormat(0, 0, ref systemTime, null, sbTime, sbTime.Capacity);
        Console.WriteLine("Time string (as reported by kernel32) : " + sbTime);

        Console.ReadKey();
    }

    [DllImport("kernel32.dll")]
    private static extern int GetDateFormat(int locale, uint dwFlags, ref SystemTime sysTime,
        string lpFormat, StringBuilder lpDateStr, int cchDate);

    [DllImport("kernel32.dll")]
    private static extern int GetTimeFormat(uint locale, uint dwFlags, ref SystemTime time, 
        string format, StringBuilder sb, int sbSize);


    [StructLayout(LayoutKind.Sequential)]
    private struct SystemTime
    {
        [MarshalAs(UnmanagedType.U2)] private readonly ushort Year;
        [MarshalAs(UnmanagedType.U2)] private readonly ushort Month;
        [MarshalAs(UnmanagedType.U2)] private readonly ushort DayOfWeek;
        [MarshalAs(UnmanagedType.U2)] private readonly ushort Day;
        [MarshalAs(UnmanagedType.U2)] private readonly ushort Hour;
        [MarshalAs(UnmanagedType.U2)] private readonly ushort Minute;
        [MarshalAs(UnmanagedType.U2)] private readonly ushort Second;
        [MarshalAs(UnmanagedType.U2)] private readonly ushort Milliseconds;

        public SystemTime(DateTime dateTime)
        {
            Year = (ushort) dateTime.Year;
            Month = (ushort) dateTime.Month;
            DayOfWeek = (ushort) dateTime.DayOfWeek;
            Day = (ushort) dateTime.Day;
            Hour = (ushort) dateTime.Hour;
            Minute = (ushort) dateTime.Minute;
            Second = (ushort) dateTime.Second;
            Milliseconds = (ushort) dateTime.Millisecond;
        }
    }
}

Ответ 2

Просто установите форматы в .NET по своему усмотрению. Например:

var clonedProvider = (CultureInfo)CultureInfo.CurrentCulture.Clone();

clonedProvider.DateTimeFormat.LongTimePattern = "HH-mm':'ss";
clonedProvider.DateTimeFormat.ShortDatePattern = "dd'/'MM-yyyy";

Тогда:

mydate.ToString("T", clonedProvider);
mydate.ToString("G", clonedProvider);

Обратите внимание, что я помещаю двоеточие : и косую черту / в одинарные кавычки (апострофы '), чтобы они не переводились в какой-либо разделитель, который имеет ваша культура с самого начала. Мне просто нужна буквальная двоеточие и слэш.

Если вы не хотите писать clonedProvider всюду, постоянно изменяйте культуру на текущем потоке:

Thread.CurrentThread.CurrentCulture = CultureInfo.ReadOnly(clonedProvider);

У вас есть много потоков в вашем приложении?


Изменить после комментария:

Если вы хотите увидеть, как настройки ОС повлияли на объект поставщика .NET-формата, просто проверьте строки:

DateTimeFormatInfo.CurrentInfo.ShortDatePattern
DateTimeFormatInfo.CurrentInfo.LongTimePattern

и т.д. Я полагаю, что у вашего провайдера текущего формата UseUserOverride установлено значение true, поэтому пользовательские настройки из Windows будут видны.

Нет ограничений на количество "разделителей", которые пользователь мог бы ввести. Например, кто-то может использовать "ddd;dd-MM,yyyy". Таким образом, есть три разделителя. Таким образом, вам придется самому исследовать строку, чтобы узнать, сколько там "разделителей" и "компонентов", и какие символы пользователь использует как разделитель в каждом месте.


Внимательно прочитав ваш вопрос и связавшись с вашим примером, я вижу, что вы набрали HH-mm:ss в настройках Windows. У этого есть проблема с этим. При переходе на синтаксис .NET первый разделитель - становится разделителем времени. Затем следующий разделитель, двоеточие, в .NET - это "подстановочный знак", означающий "заменить временным разделителем". Так что двоеточие также переведено в тире.

Вы должны были ввести в настройках Windows

HH-mm':'ss

где снова вы защищаете двоеточие с одинарными кавычками (апострофы).

Теперь, что если один из ваших пользователей сначала использует нестандартный разделитель, а затем использует стандартный разделитель : (или /) без цитирования последнего в одинарных кавычках? Ну, в таком случае вы правы, есть разница между поведением в Windows и тем, что в .NET. По-видимому, пользователям не следует вводить такие форматы. Вы можете назвать это ошибкой.