Comment créer un .NET DateTime à partir du format ISO 8601

130

J'ai trouvé comment transformer un DateTime en un format ISO 8601 , mais rien sur la façon de faire l'inverse en C #.

J'ai 2010-08-20T15:00:00Z, et je veux en faire un DateTimeobjet.

Je pourrais séparer les parties de la corde moi-même, mais cela semble être beaucoup de travail pour quelque chose qui est déjà une norme internationale.

Ripter
la source
1
duplication possible de Convertir la chaîne en date en .NET
abatishchev
1
@Aidin: 24 août 10 à 12
h 02
@Aidin: et oui, c'est un doublon. La seule différence de format. Le reste est le même.
abatishchev
6
@abatishchev, et c'est pourquoi ce n'est pas un doublon. La réponse dans le "duplicata" ne gère pas 8601.
Spiralis
3
Oui, ce n'est pas un doublon. Cette question est spécifique à l'analyse du format ISO 8601.
Jose le

Réponses:

142

Cette solution utilise l' énumération DateTimeStyles et fonctionne également avec Z.

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

Cela imprime parfaitement la solution.

Mamta D
la source
3
La solution éditée de DateTime d2= DateTime.Parse("2010-08-20T15:00:00Z", null, DateTimeStyles.RoundtripKind);semble fonctionner correctement.
j3ko
4
Toute personne souhaitant développer cette DateTimeStyles.RoundtripKind? description MSDN est vide.
Steve Parish
8
il semble que cette question ait été modifiée pour refléter une meilleure réponse, mais comme @MamtaD a écrasé la réponse d'origine, les commentaires deviennent très trompeurs. Au début, je n'étais pas sûr que la réponse soit correcte en raison des commentaires en haut, mais j'ai ensuite réalisé que la réponse incorrecte avait été remplacée par la bonne
Aidin
5
Cela ne fonctionne pas pour moi avec des chiffres fractionnaires. 2018-06-19T14:56:14.123Zest analysé en tant qu'heure locale et non UTC. J'utilise CultureInfo.InvariantCultureau lieu de null.
Erik Hart
1
Pour plus d'informations sur DateTimeStyles.RoundTripKind, voir stackoverflow.com/q/39572395/2014893
Robert K. Bell
33

Bien que MSDN dise que les formats «s» et «o» reflètent la norme, ils semblent pouvoir en analyser seulement un sous-ensemble limité. En particulier, cela pose un problème si la chaîne contient une spécification de fuseau horaire. (Ni pour les formats de base ISO8601, ni pour les formats à précision réduite - mais ce n'est pas exactement votre cas.) C'est pourquoi j'utilise des chaînes de format personnalisées lorsqu'il s'agit d'analyser ISO8601. Actuellement, mon extrait préféré est:

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

Si cela ne vous dérange pas d'analyser les chaînes sans TZ (je le fais), vous pouvez ajouter une ligne «s» pour étendre considérablement le nombre de modifications de format couvertes.

Alexey Biryukov
la source
3
J'ajouterais "yyyyMMdd"dans le formatstableau une précision réduite à quelques jours, car c'est parfois le cas lorsqu'un RRULE RFC 5545 s'appuiera sur un DTSTART pour fournir l'heure.
Kyle Falconer
1
L'utilisation Kvous permet de regrouper vos différentes manipulations de fuseaux horaires. J'ai une variante plus étendue sur stackoverflow.com/a/31246449/400547 mais c'est trop complet (accepte des éléments valides ISO 8601 mais non utilisés dans les profils plus courants) mais cela montre comment Kréduire la taille d'un troisième.
Jon Hanna
20
using System.Globalization;

DateTime d;
DateTime.TryParseExact(
    "2010-08-20T15:00:00",
    "s",
    CultureInfo.InvariantCulture,
    DateTimeStyles.AssumeUniversal, out d);
abatishchev
la source
1
produit False et d ~~> "1/1/0001 12:00:00 AM" dans LinqPad :(
Reb.Cabin
@Reb: "2010-08-20T15: 00: 00" et "s", si aucun "Z" à la fin
abatishchev
corrigé :) le Z apparaît dans tous mes échantillons (qui proviennent de diverses unités GPS et fichiers GPX)
Reb.Cabin
découvert dans une autre référence ISO 8601 que le "Z" signifie Zone - comme dans Time Zone.
Reb.Cabin
30
Z signifie en fait l'heure zoulou ou UTC. en.wikipedia.org/wiki/ISO_8601#UTC
Peter Stephens
19

En voici un qui fonctionne mieux pour moi ( version 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()

produit

true
8/20/2010 8:00:00 AM
Reb.Cabin
la source
Je l'utilise actuellement pour vérifier dans mes tests unitaires que toutes les chaînes auxquelles je pense être des dates sont au format Iso8601. Merci!
anthv123
1
Pourquoi cela renvoie-t-il un horodatage non UTC?! Une violation assez importante du principe du moindre étonnement car "Culture invariante" avec "AssumeUniversal" ne devrait pas faire cela car l'heure d'été diffère si fortement à travers le monde, donc revenir dans votre fuseau horaire local peut introduire des bogues si vous commencez à exécuter le code sur un serveur avec des paramètres différents!
Elaskanator
7

Il semble important de faire correspondre exactement le format de la chaîne ISO pour TryParseExactfonctionner. Je suppose qu'exact est exact et cette réponse est évidente pour la plupart, mais de toute façon ...

Dans mon cas, la réponse de Reb.Cabin ne fonctionne pas car j'ai une entrée légèrement différente selon ma "valeur" ci-dessous.

Valeur: 2012-08-10T14:00:00.000Z

Il y a quelques milliers supplémentaires là-dedans pendant des millisecondes et il peut y en avoir plus.

Cependant, si j'en ajoute .fffau format comme indiqué ci-dessous, tout va bien.

Chaîne de format: @"yyyy-MM-dd\THH:mm:ss.fff\Z"

Dans la fenêtre immédiate de VS2010:

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

vrai

Vous devrez peut-être également l'utiliser DateTimeStyles.AssumeLocalen fonction du fuseau horaire de votre heure ...

Rob Von Nesselrode
la source
1
Cela a fonctionné pour moi, mais je devais aussi changer AssumeUniversalà AdjustToUniversal.
Augusto Barreto
4

Cela fonctionne très bien dans 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
la source
-1

DateTime.ParseExact(...) vous permet d'indiquer à l'analyseur ce que chaque caractère représente.

Jerod Houghtelling
la source