Где указатель формата DateTime 'Z'? - PullRequest
54 голосов
/ 07 мая 2009

[ Обновление : Спецификаторы формата - это не то же самое, что строки формата; спецификатор формата - это часть строки пользовательского формата, где строка формата - «сток» и не обеспечивает настройку. Моя проблема со спецификаторами, а не с форматами ]

Я пытался выполнить преобразование DateTime в обе стороны с помощью строки формата, которая использует спецификатор формата 'zzz', который, я знаю, привязан к местному времени. Таким образом, если я пытаюсь выполнить обратную поездку с указанием времени UTC, он выдает исключение DateTimeInvalidLocalFormat, которое должно произойти, с таким текстом:

Дата и время по Гринвичу преобразуются в текст в формате, который подходит только для местного времени. Это может произойти при вызове DateTime.ToString с использованием спецификатора формата 'z', который будет включать в вывод смещение местного часового пояса. В этом случае либо используйте спецификатор формата 'Z', который обозначает время UTC , либо используйте строку формата 'o', которая является рекомендуемым способом сохранения DateTime в тексте. Это также может происходить при передаче DateTime для сериализации с помощью XmlConvert или DataSet. Если используется XmlConvert.ToString, передайте XmlDateTimeSerializationMode.RoundtripKind для правильной сериализации. При использовании DataSet установите для DateTimeMode объекта DataColumn значение DataSetDateTime.Utc.

Исходя из этого предложения, все, что мне нужно, чтобы заставить мой код работать, - это заменить 'zzz' на 'ZZZ', чтобы я мог стоять в формате UTC. Проблема в том, что «Z» нигде не встречается в документации, и любая комбинация форматов «Z», которую я пробую, то есть «Z», «ZZ», «ZZZ», всегда просто конвертирует экземпляр DateTime с теми, которые Z обрабатываются как литералы. .

Кто-то забыл внедрить 'Z', не сообщая об этом автору сообщения об исключении, или мне не хватает, как поменять местами корректное местное время с "+0000" без взлома?

Пример кода:

// This is the format with 'zzzzz' representing local time offset
const string format = "ddd MMM dd HH:mm:ss zzzzz yyyy";

// create a UTC time
const string expected = "Fri Dec 19 17:24:18 +0000 2008";
var time = new DateTime(2008, 12, 19, 17, 24, 18, 0, DateTimeKind.Utc);

// If you're using a debugger this will rightfully throw an exception
// with .NET 3.5 SP1 because 'z' is for local time only; however, the exception
// asks me to use the 'Z' specifier for UTC times, but it doesn't exist, so it
// just spits out 'Z' as a literal.
var actual = time.ToString(format, CultureInfo.InvariantCulture);

Assert.AreEqual(expected, actual);

Ответы [ 6 ]

58 голосов
/ 07 мая 2009

Возможно, спецификатор формата "K" будет полезен. Это единственный, в котором упоминается использование заглавной буквы "Z".

«Z» является своего рода уникальным случаем для DateTimes. Буква «Z» фактически является частью стандарта даты и времени ISO 8601 для времени UTC. Когда «Z» (Zulu) привязан к концу времени, это указывает на то, что это время UTC, поэтому в действительности литерал Z является частью времени. Это, вероятно, создает несколько проблем для библиотеки формата даты в .NET, поскольку она на самом деле является литералом, а не спецификатором формата.

7 голосов
/ 29 июня 2011

Когда вы используете DateTime, вы можете хранить дату и время внутри переменной.

Дата может быть по местному или UTC времени, это зависит от вас.

Например, я в Италии (+2 UTC)

var dt1 = new DateTime(2011, 6, 27, 12, 0, 0); // store 2011-06-27 12:00:00
var dt2 = dt1.ToUniversalTime()  // store 2011-06-27 10:00:00

Итак, что происходит, когда я печатаю dt1 и dt2, включая часовой пояс?

dt1.ToString("MM/dd/yyyy hh:mm:ss z") 
// Compiler alert...
// Output: 06/27/2011 12:00:00 +2

dt2.ToString("MM/dd/yyyy hh:mm:ss z") 
// Compiler alert...
// Output: 06/27/2011 10:00:00 +2

dt1 и dt2 содержат только информацию о дате и времени. dt1 и dt2 не содержат смещения часового пояса.

Так откуда же взято "+2", если оно не содержится в переменных dt1 и dt2?

Он исходит из настроек часов вашей машины.

Компилятор говорит вам, что когда вы используете формат 'zzz', вы пишете строку, которая объединяет «DATE + TIME» (которые хранятся в dt1 и dt2) + «TIMEZONE OFFSET» (это содержится в dt1 и dt2, потому что они имеют тип DateTyme) , и он будет использовать смещение сервера, на котором выполняется код.

Компилятор сообщает вам: «Предупреждение: вывод вашего кода зависит от смещения часов машины»

Если я выполню этот код на сервере, который расположен в Лондоне (+1 UTC), результат будет совершенно другим: вместо « + 2 » будет написано « + 1"

...
dt1.ToString("MM/dd/yyyy hh:mm:ss z") 
// Output: 06/27/2011 12:00:00 +1

dt2.ToString("MM/dd/yyyy hh:mm:ss z") 
// Output: 06/27/2011 10:00:00 +1

Правильное решение - использовать тип данных DateTimeOffset вместо DateTime. Он доступен в SQL Server, начиная с версии 2008, и в .Net Framework, начиная с версии 3.5

.
5 голосов
/ 07 мая 2009

Даты кругового отключения через строки всегда были болезненными ... но документы, указывающие на то, что спецификатор 'o' - это тот, который используется для кругового отключения, который фиксирует состояние UTC. При анализе результат будет обычно иметь Kind == Utc, если оригинал был UTC. Я обнаружил, что лучше всего всегда нормализовать даты в UTC или локально перед сериализацией, а затем дать парсеру указание, какую нормализацию вы выбрали.

DateTime now = DateTime.Now;
DateTime utcNow = now.ToUniversalTime();

string nowStr = now.ToString( "o" );
string utcNowStr = utcNow.ToString( "o" );

now = DateTime.Parse( nowStr );
utcNow = DateTime.Parse( nowStr, null, DateTimeStyles.AdjustToUniversal );

Debug.Assert( now == utcNow );
2 голосов
/ 07 мая 2009
Label1.Text = dt.ToString("dd MMM yyyy | hh:mm | ff | zzz | zz | z");

выведет:

07 Mai 2009 | 08:16 | 13 | +02:00 | +02 | +2

Я в Дании, мое смещение от GMT +2 часа, ведьма верна.

если вам нужно получить Смещение КЛИЕНТА , я рекомендую вам проверить маленький трюк , который я сделал. Страница находится на сервере в Великобритании, где время по Гринвичу +00: 00, и, как вы можете видеть, вы получите местное смещение по Гринвичу.


Что касается вашего комментария, я сделал:

DateTime dt1 = DateTime.Now;
DateTime dt2 = dt1.ToUniversalTime();

Label1.Text = dt1.ToString("dd MMM yyyy | hh:mm | ff | zzz | zz | z");
Label2.Text = dt2.ToString("dd MMM yyyy | hh:mm | FF | ZZZ | ZZ | Z");

и я получаю это:

07 Mai 2009 | 08:24 | 14 | +02:00 | +02 | +2
07 Mai 2009 | 06:24 | 14 | ZZZ | ZZ | Z 

Я не получаю никаких исключений, просто ... он ничего не делает с заглавной Z: (

Извините, но я что-то упустил?


Внимательно читая MSDN на Пользовательские строки формата даты и времени

отсутствует поддержка заглавных букв 'Z'.

2 голосов
/ 07 мая 2009

На этой странице в MSDN перечислены строки стандартного формата DateTime, за исключением строк, использующих 'Z'.

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

// yyyy'-'MM'-'dd HH':'mm':'ss'Z'
DateTime utcTime = DateTime.Parse("2009-05-07 08:17:25Z");
0 голосов
/ 13 марта 2014

Я имел дело с DateTimeOffset и, к сожалению, "o" выводит "+0000", а не "Z".

Итак, я закончил с:

dateTimeOffset.UtcDateTime.ToString("o")
...