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

DateTime.Parse( "2012-09-30T23: 00: 00.0000000Z" ) всегда преобразуется в DateTimeKind.Local

Я хочу проанализировать строку, которая представляет DateTime в формате UTC.

В моем строковом представлении содержится спецификация времени Зулу, которая должна указывать, что строка представляет время UTC.

var myDate = DateTime.Parse("2012-09-30T23:00:00.0000000Z");    

Из вышеизложенного я ожидал бы, что myDate.Kind будет DateTimeKind.Utc, вместо этого это DatetimeKind.Local.

Что я делаю неправильно и как анализировать строку, представляющую время UTC?

Большое спасибо!

4b9b3361

Ответ 1

Я бы использовал Noda Time лично, но если вы не можете этого сделать...

Либо используйте DateTime.ParseExact, указав точный формат, который вы ожидаете, и включите DateTimeStyles.AssumeUniversal и DateTimeStyles.AdjustToUniversal в код синтаксического анализа:

using System;
using System.Globalization;

class Test
{
    static void Main()        
    {
        var date = DateTime.ParseExact("2012-09-30T23:00:00.0000000Z",
                                       "yyyy-MM-dd'T'HH:mm:ss.fffffff'Z'",
                                       CultureInfo.InvariantCulture,
                                       DateTimeStyles.AssumeUniversal |
                                       DateTimeStyles.AdjustToUniversal);
        Console.WriteLine(date);
        Console.WriteLine(date.Kind);
    }
}

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

EDIT: Чтобы расширить мои возражения против предложения mattytommo, я стремился доказать, что он потеряет информацию. Я провалился до сих пор, но очень своеобразно. Взгляните на это - работайте в европейском/лондонском часовом поясе, где часы вернутся 28 октября 2012 года, в 2 часа по местному времени (1:00 по UTC):

DateTime local1 = DateTime.Parse("2012-10-28T00:30:00.0000000Z");
DateTime local2 = DateTime.Parse("2012-10-28T01:30:00.0000000Z");
Console.WriteLine(local1 == local2); // True

DateTime utc1 = TimeZoneInfo.ConvertTimeToUtc(local1);
DateTime utc2 = TimeZoneInfo.ConvertTimeToUtc(local2);
Console.WriteLine(utc1 == utc2); // False. Hmm.

Похоже, что там где-то где-то где-то хранится флаг "с или без DST", но я буду взорван, если смогу поработать где. Документы для TimeZoneInfo.ConvertTimeToUtc state

Если dateTime соответствует неоднозначному времени, этот метод предполагает, что это стандартное время в исходном часовом поясе.

Это не выглядит здесь при преобразовании local2...

EDIT: Хорошо, это становится еще более странным - это зависит от того, какую версию используемой структуры вы используете. Рассмотрим эту программу:

using System;
using System.Globalization;

class Test
{
    static void Main()        
    {
        DateTime local1 = DateTime.Parse("2012-10-28T00:30:00.0000000Z");
        DateTime local2 = DateTime.Parse("2012-10-28T01:30:00.0000000Z");

        DateTime utc1 = TimeZoneInfo.ConvertTimeToUtc(local1);
        DateTime utc2 = TimeZoneInfo.ConvertTimeToUtc(local2);
        Console.WriteLine(utc1);
        Console.WriteLine(utc2);

        DateTime utc3 = local1.ToUniversalTime();
        DateTime utc4 = local2.ToUniversalTime();
        Console.WriteLine(utc3);
        Console.WriteLine(utc4);
    }
}

Таким образом, это принимает два разных значения UTC, анализирует их с помощью DateTime.Parse, затем преобразует их обратно в UTC двумя разными способами.

Результаты в .NET 3.5:

28/10/2012 01:30:00 // Look - we've lost information
28/10/2012 01:30:00
28/10/2012 00:30:00 // But ToUniversalTime() seems okay...
28/10/2012 01:30:00

Результаты в .NET 4.5 beta:

28/10/2012 00:30:00 // It okay!
28/10/2012 01:30:00
28/10/2012 00:30:00
28/10/2012 01:30:00

Ответ 2

Как обычно, ответ Джона очень всеобъемлющий. Тем не менее, никто еще не упомянул DateTimeStyles.RoundtripKind. Если вы хотите преобразовать DateTime в строку и вернуться к тому же DateTime (включая сохранение параметра DateTime.Kind), используйте флаг DateTimeStyles.RoundtripKind.

Как сказал Джон, правильная вещь - использовать форматер "O" при преобразовании объекта DateTime в строку. Это сохраняет информацию о точности и часовом поясе. Опять же, как сказал Джон, используйте DateTime.ParseExact при обратном преобразовании. Но если вы используете DateTimeStyles.RoundtripKind, вы всегда получите то, что вы положили:

var now = DateTime.UtcNow;
var strNow = now.ToString("O");
var newNow = DateTime.ParseExact(strNow, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);

В приведенном выше коде newNow точно совпадает с now, включая тот факт, что это UTC. Если вы используете тот же код, за исключением замены DateTime.Now для DateTime.UtcNow, вы получите точную копию now назад как newNow, но на этот раз как локальное время.

В моих целях это было правильно, так как я хотел убедиться, что все, что было передано и преобразовано, преобразуется обратно в ту же самую вещь.

Ответ 3

Используйте класс TimeZoneInfo, используя следующее:

var myDate = TimeZoneInfo.ConvertTimeToUtc(DateTime.Parse("2012-09-30T23:00:00.0000000Z"));

Ответ 4

Вы можете использовать следующий формат для метода парсера: yyyy-MM-ddTHH:mm:ss.ffffffK

Это должно правильно обрабатывать информацию о часовом поясе в конце (начиная с .NET 2.0).

RE: ISO 8601

Ответ 5

Ранься в аналогичную проблему до и несколько часов (и вытащил волосы) позже закончил использование DateTime.SpecifyKind:

DateTime.SpecifyKind(inputDate, DateTimeKind.Utc);

Я считаю, что кто-то также избегал этого в комментарии выше.