Jak utworzyć .NET DateTime z formatu ISO 8601

135

Dowiedziałem się, jak zamienić DateTime na format ISO 8601 , ale nie ma nic o tym, jak zrobić odwrotność w C #.

Mam 2010-08-20T15:00:00Zi chcę przekształcić go w DateTimeprzedmiot.

Mógłbym sam oddzielić części struny, ale wydaje się, że to dużo pracy jak na coś, co jest już międzynarodowym standardem.

Ripter
źródło
1
możliwy duplikat Convert String to Date w .NET
abatishchev
1
@Aidin: 24 sierpnia 10 o 12:02
abatishchev
@Aidin: i tak, to jest duplikat. Jedyna różnica w formacie. Reszta jest taka sama.
abatishchev
6
@abatishchev i dlatego nie jest duplikatem. Odpowiedź w „duplikat” nie obsługuje 8601.
spiralis
3
Tak, to nie jest duplikat. To pytanie jest specyficzne dla analizowania formatu ISO 8601.
Jose

Odpowiedzi:

149

To rozwiązanie korzysta z wyliczenia DateTimeStyles i działa również z Z.

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

To doskonale drukuje rozwiązanie.

Mamta D
źródło
3
Edytowane rozwiązanie DateTime d2= DateTime.Parse("2010-08-20T15:00:00Z", null, DateTimeStyles.RoundtripKind);wydaje się działać ładnie.
j3ko,
4
Każdy, kto chce rozwinąć ten DateTimeStyles.RoundtripKind? opis MSDN, jest pusty.
Steve Parish
8
wydaje się, że to pytanie zostało zredagowane, aby odzwierciedlić lepszą odpowiedź, ale ponieważ @MamtaD nadpisał oryginalną odpowiedź, komentarze stają się bardzo mylące. Na początku nie byłem pewien, czy odpowiedź jest poprawna ze względu na komentarze u góry, ale potem zdałem sobie sprawę, że błędna odpowiedź została zastąpiona poprawną
Aidin
5
Nie działa dla mnie z cyframi ułamkowymi. 2018-06-19T14:56:14.123Zjest analizowany jako czas lokalny, a nie UTC. Używam CultureInfo.InvariantCulturezamiast null.
Erik Hart
1
Aby uzyskać więcej informacji DateTimeStyles.RoundTripKind, zobacz stackoverflow.com/q/39572395/2014893
Robert K. Bell
35

Chociaż MSDN mówi, że formaty „s” i „o” odzwierciedlają standard, wydaje się, że są w stanie przeanalizować tylko ograniczony podzbiór. Szczególnie jest to problem, jeśli ciąg zawiera specyfikację strefy czasowej. (Ani dla podstawowych formatów ISO8601, ani dla formatów o zmniejszonej precyzji - jednak nie jest to dokładnie w twoim przypadku). Dlatego używam niestandardowych ciągów formatów, jeśli chodzi o parsowanie ISO8601. Obecnie mój preferowany fragment to:

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 );
}

Jeśli nie masz nic przeciwko analizowaniu ciągów bez TZ (tak robię), możesz dodać wiersz „s”, aby znacznie zwiększyć liczbę objętych zmian formatu.

Alexey Biryukov
źródło
3
Dodałbym "yyyyMMdd"w formatstablicy, aby dokładność została zredukowana do dni, ponieważ czasami ma to miejsce, gdy RRULE RFC 5545 będzie polegać na DTSTART, aby zapewnić czas.
Kyle Falconer
1
Użycie Kumożliwia jednoczesne łączenie obsługi różnych stref czasowych. Mam bardziej rozbudowany wariant na stackoverflow.com/a/31246449/400547, ale jest on zbyt obszerny (akceptuje rzeczy, które są zgodne z ISO 8601, ale nie są używane w bardziej popularnych profilach), ale pokazuje, jak Kmożna zmniejszyć rozmiar o trzeci.
Jon Hanna
21
using System.Globalization;

DateTime d;
DateTime.TryParseExact(
    "2010-08-20T15:00:00",
    "s",
    CultureInfo.InvariantCulture,
    DateTimeStyles.AssumeUniversal, out d);
abatishchev
źródło
1
generuje False id ~~> "1/1/0001 12:00:00 AM" w LinqPad :(
Reb.Cabin
@Reb: „2010-08-20T15: 00: 00” i „s”, jeśli na końcu nie ma „Z”
abatishchev
poprawione :) Z pojawia się we wszystkich moich próbkach (które zdarzają się pochodzić z różnych urządzeń GPS i plików GPX)
Reb.Cabin
dowiedziałem się w innym dokumencie ISO 8601, że „Z” oznacza strefę - jak w strefie czasowej.
Reb
31
Z faktycznie oznacza czas Zulu lub UTC. en.wikipedia.org/wiki/ISO_8601#UTC
Peter Stephens
19

Oto taki, który działa lepiej dla mnie ( wersja 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()

produkuje

true
8/20/2010 8:00:00 AM
Reb.Cabin
źródło
Obecnie używam tego do sprawdzenia w moich testach jednostkowych, że wszystkie ciągi, których oczekuję, że będą datami, mają format Iso8601. Dzięki!
anthv123
1
Dlaczego to zwraca sygnaturę czasową inną niż UTC ?! Dość duże naruszenie zasady najmniejszego zdumienia, ponieważ „Niezmienna kultura” z „AssumeUniversal” nie powinna tego robić, ponieważ czas letni różni się tak bardzo na całym świecie, więc powrót do lokalnej strefy czasowej może spowodować błędy, jeśli zaczniesz uruchamiać kod na serwerze z różnymi ustawieniami!
Elaskanator
7

Wydaje się ważne, aby dokładnie dopasować format ciągu ISO TryParseExactdo pracy. Myślę, że Exact to Exact i ta odpowiedź jest oczywista dla większości, ale w każdym razie ...

W moim przypadku odpowiedź Reb.Cabin nie działa, ponieważ mam nieco inne dane wejściowe, jak na moją „wartość” poniżej.

Wartość: 2012-08-10T14:00:00.000Z

Jest tam kilka dodatkowych tysięcy na milisekundy, a może być ich więcej.

Jeśli jednak dodam trochę .fffdo formatu, jak pokazano poniżej, wszystko jest w porządku.

Ciąg formatu: @"yyyy-MM-dd\THH:mm:ss.fff\Z"

W oknie bezpośrednim VS2010:

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

prawdziwe

Być może będziesz musiał użyć, DateTimeStyles.AssumeLocalw zależności od strefy, dla której jest twój czas ...

Rob Von Nesselrode
źródło
1
Ten pracował dla mnie, ale ja też musiałem zmienić AssumeUniversalna AdjustToUniversal.
Augusto Barreto
4

Działa to dobrze w 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"));
Zar Shardan
źródło
-2

DateTime.ParseExact(...) pozwala powiedzieć parserowi, co reprezentuje każdy znak.

Jerod Houghtelling
źródło