Как создать .NET DateTime из формата ISO 8601

130

Я нашел, как преобразовать DateTime в формат ISO 8601 , но не знаю, как сделать обратное в C #.

У меня есть 2010-08-20T15:00:00Z, и я хочу превратить это в DateTimeобъект.

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

Ripter
источник
1
возможный дубликат Convert String to Date в .NET
Абатищев
1
@Aidin: 24 авг., 12:02
Абатищев,
@Aidin: и да, это дубликат. Единственная разница в формате. В остальном то же самое.
Абатищев
6
@abatishchev, а потому и не дубликат. Ответ в «дубликате» не
касается
3
Да это не дубликат. Этот вопрос относится к разбору формата ISO 8601.
Хосе

Ответы:

143

Это решение использует перечисление DateTimeStyles , а также работает с Z.

DateTime d2 = DateTime.Parse("2010-08-20T15:00:00Z", null, System.Globalization.DateTimeStyles.RoundtripKind);

Это отлично печатает решение.

Мамта Д
источник
3
Отредактированное решение, DateTime d2= DateTime.Parse("2010-08-20T15:00:00Z", null, DateTimeStyles.RoundtripKind);похоже, работает хорошо.
j3ko
4
Любой желающий уточнить это DateTimeStyles.RoundtripKind? описание MSDN пусто.
Стив Пэриш
8
кажется, этот вопрос был отредактирован, чтобы отразить лучший ответ, но поскольку @MamtaD перезаписал исходный ответ, комментарии становятся очень вводящими в заблуждение. Вначале я не был уверен, что ответ правильный из-за комментариев вверху, но затем я понял, что неправильный ответ был позже заменен правильным
Айдин
5
У меня не работает с дробными цифрами. 2018-06-19T14:56:14.123Zанализируется как местное время, а не UTC. Я использую CultureInfo.InvariantCultureвместо null.
Эрик Харт
1
Для получения дополнительной информации DateTimeStyles.RoundTripKindсм. Stackoverflow.com/q/39572395/2014893
Роберт К. Белл
33

Хотя MSDN утверждает, что форматы «s» и «o» отражают стандарт, похоже, они могут анализировать только его ограниченное подмножество. Особенно проблема, если строка содержит указание часового пояса. (Ни для базовых форматов ISO8601, ни для форматов с пониженной точностью - однако это не совсем ваш случай.) Вот почему я использую строки настраиваемого формата, когда дело доходит до синтаксического анализа ISO8601. В настоящее время мой предпочтительный фрагмент:

static readonly string[] formats = { 
    // Basic formats
    "yyyyMMddTHHmmsszzz",
    "yyyyMMddTHHmmsszz",
    "yyyyMMddTHHmmssZ",
    // Extended formats
    "yyyy-MM-ddTHH:mm:sszzz",
    "yyyy-MM-ddTHH:mm:sszz",
    "yyyy-MM-ddTHH:mm:ssZ",
    // All of the above with reduced accuracy
    "yyyyMMddTHHmmzzz",
    "yyyyMMddTHHmmzz",
    "yyyyMMddTHHmmZ",
    "yyyy-MM-ddTHH:mmzzz",
    "yyyy-MM-ddTHH:mmzz",
    "yyyy-MM-ddTHH:mmZ",
    // Accuracy reduced to hours
    "yyyyMMddTHHzzz",
    "yyyyMMddTHHzz",
    "yyyyMMddTHHZ",
    "yyyy-MM-ddTHHzzz",
    "yyyy-MM-ddTHHzz",
    "yyyy-MM-ddTHHZ"
    };

public static DateTime ParseISO8601String ( string str )
{
    return DateTime.ParseExact ( str, formats, 
        CultureInfo.InvariantCulture, DateTimeStyles.None );
}

Если вы не возражаете против синтаксического анализа строк без TZ (я так и поступаю), вы можете добавить строку «s», чтобы значительно увеличить количество изменяемых форматов.

Алексей Бирюков
источник
3
Я бы добавил "yyyyMMdd"в formatsмассив для уменьшения точности до дней, так как это иногда бывает, когда RFC 5545 RRULE будет полагаться на DTSTART для предоставления времени.
Кайл Фалконер
1
Использование Kпозволяет объединить различные обработки часовых поясов. У меня есть более обширный вариант на stackoverflow.com/a/31246449/400547, но он, если что-то слишком обширный (принимает материалы, действующие в соответствии с ISO 8601, но не используемые в более распространенных профилях), но он показывает, как Kможно уменьшить размер на третий.
Джон Ханна
20
using System.Globalization;

DateTime d;
DateTime.TryParseExact(
    "2010-08-20T15:00:00",
    "s",
    CultureInfo.InvariantCulture,
    DateTimeStyles.AssumeUniversal, out d);
abatishchev
источник
1
выдает False и d ~~> "1/1/0001 12:00:00 AM" в LinqPad :(
Reb.Cabin
@Reb: «2010-08-20T15: 00: 00» и «s», если в конце нет «Z»
Абатищев
исправлено :) Z появляется во всех моих сэмплах (которые происходят из различных устройств GPS и файлов GPX)
Reb.Cabin
обнаружил в другой ссылке ISO 8601, что «Z» означает зону - как в часовом поясе.
Reb.Cabin
30
Z на самом деле означает зулусское время или UTC. en.wikipedia.org/wiki/ISO_8601#UTC
Питер Стивенс,
19

Вот тот, который мне больше подходит ( версия LINQPad ):

DateTime d;
DateTime.TryParseExact(
    "2010-08-20T15:00:00Z",
    @"yyyy-MM-dd\THH:mm:ss\Z",
    CultureInfo.InvariantCulture,
    DateTimeStyles.AssumeUniversal, 
    out d);
d.ToString()

производит

true
8/20/2010 8:00:00 AM
Reb.Cabin
источник
В настоящее время я использую это, чтобы проверить в своих модульных тестах, что все строки, которые я ожидаю, будут датами, имеют формат Iso8601. Спасибо!
anthv123
1
Почему это возвращает метку времени не в формате UTC ?! Довольно серьезное нарушение принципа наименьшего удивления, поскольку «инвариантная культура» с «AssumeUniversal» не должна этого делать, потому что летнее время сильно различается во всем мире, поэтому возвращение в местный преобладающий часовой пояс может привести к ошибкам, если вы начнете запускать код. на сервере с разными настройками!
Elaskanator
7

Кажется важным точно соответствовать формату строки ISO для TryParseExactработы. Я думаю, что Exact is Exact, и этот ответ очевиден для большинства, но в любом случае ...

В моем случае ответ Reb.Cabin не работает, так как у меня немного другой ввод в соответствии с моим «значением» ниже.

Стоимость: 2012-08-10T14:00:00.000Z

Там несколько лишних 000 за миллисекунды, а может быть и больше.

Однако, если я добавлю немного .fffв формат, как показано ниже, все будет в порядке.

Строка формата: @"yyyy-MM-dd\THH:mm:ss.fff\Z"

В немедленном окне VS2010:

DateTime.TryParseExact(value,@"yyyy-MM-dd\THH:mm:ss.fff\Z", CultureInfo.InvariantCulture,DateTimeStyles.AssumeUniversal, out d);

правда

Возможно, вам также придется использовать, DateTimeStyles.AssumeLocalв зависимости от того, в какой зоне ваше время ...

Роб фон Нессельроде
источник
1
Это сработало для меня, но мне также пришлось перейти AssumeUniversalна AdjustToUniversal.
Аугусто Баррето,
4

Это отлично работает в LINQPad4:

Console.WriteLine(DateTime.Parse("2010-08-20T15:00:00Z"));
Console.WriteLine(DateTime.Parse("2010-08-20T15:00:00"));
Console.WriteLine(DateTime.Parse("2010-08-20 15:00:00"));
Зар Шардан
источник
-1

DateTime.ParseExact(...) позволяет вам сообщить парсеру, что представляет каждый символ.

Джерод Хоутеллинг
источник