Cómo crear un .NET DateTime a partir del formato ISO 8601

130

He encontrado cómo convertir un DateTime en un formato ISO 8601 , pero nada sobre cómo hacer lo contrario en C #.

Tengo 2010-08-20T15:00:00Z, y quiero convertirlo en un DateTimeobjeto.

Podría separar las partes de la cadena yo mismo, pero eso parece mucho trabajo para algo que ya es un estándar internacional.

Ripter
fuente
1
posible duplicado de Convertir cadena a fecha en .NET
abatishchev
1
@Aidin: 24 de agosto de 10 a las 12:02
abatishchev
@Aidin: y sí, este es un duplicado. La única diferencia en formato. El resto es igual.
abatishchev
66
@abatishchev, y es por eso que no es un duplicado. La respuesta en el "duplicado" no maneja 8601.
Spiralis
3
Sí, esto no es un duplicado. Esta pregunta es específica para analizar el formato ISO 8601.
José

Respuestas:

142

Esta solución hace uso de la enumeración DateTimeStyles y también funciona con Z.

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

Esto imprime la solución perfectamente.

Mamta D
fuente
3
La solución editada de DateTime d2= DateTime.Parse("2010-08-20T15:00:00Z", null, DateTimeStyles.RoundtripKind);parece funcionar bien.
j3ko
44
Cualquier persona que desee elaborar esta DateTimeStyles.RoundtripKind? descripción de MSDN está en blanco.
Steve Parish
8
parece que esta pregunta fue editada para reflejar una mejor respuesta, pero como @MamtaD sobrescribió la respuesta original, los comentarios se vuelven muy engañosos. Al principio no estaba seguro de que la respuesta fuera correcta debido a los comentarios en la parte superior, pero luego me di cuenta de que la respuesta incorrecta fue reemplazada por la correcta
Aidin
55
No me funciona con dígitos fraccionarios. 2018-06-19T14:56:14.123Zse analiza como hora local, no UTC. Yo uso en CultureInfo.InvariantCulturelugar de nulo.
Erik Hart
1
Para obtener más información sobre DateTimeStyles.RoundTripKind, consulte stackoverflow.com/q/39572395/2014893
Robert K. Bell el
33

Aunque MSDN dice que los formatos "s" y "o" reflejan el estándar, parecen ser capaces de analizar solo un subconjunto limitado del mismo. Especialmente es un problema si la cadena contiene la especificación de zona horaria. (Tampoco lo hace para formatos básicos ISO8601 o formatos de precisión reducida; sin embargo, este no es exactamente su caso). Es por eso que utilizo cadenas de formato personalizadas cuando se trata de analizar ISO8601. Actualmente mi fragmento preferido es:

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 no le importa analizar cadenas sin TZ (sí), puede agregar una línea "s" para ampliar en gran medida la cantidad de alteraciones de formato cubiertas.

Alexey Biryukov
fuente
3
Yo añadiría "yyyyMMdd"en la formatsmatriz para una precisión reducida a día, ya que este suele ser el caso cuando un RRULE RFC 5545 se basará en una DTSTART para proporcionar el tiempo.
Kyle Falconer
1
El uso le Kpermite combinar sus diferentes manejos de zona horaria. Tengo una variante más extensa en stackoverflow.com/a/31246449/400547, pero es demasiado extensa (acepta cosas que son válidas ISO 8601 pero no se usan en los perfiles más comunes) pero muestra cómo Kpuede reducir el tamaño en un tercero.
Jon Hanna
20
using System.Globalization;

DateTime d;
DateTime.TryParseExact(
    "2010-08-20T15:00:00",
    "s",
    CultureInfo.InvariantCulture,
    DateTimeStyles.AssumeUniversal, out d);
abatishchev
fuente
1
produce False and d ~~> "1/1/0001 12:00:00 AM" en LinqPad :(
Reb.Cabin
@Reb: "2010-08-20T15: 00: 00" y "s", si no
aparece
corregido :) la Z aparece en todas mis muestras (que provienen de varias unidades GPS y archivos GPX)
Reb.Cabin
descubrió en otra referencia ISO 8601 que la "Z" significa Zona, como en la Zona horaria.
Reb.Cabin
30
Z en realidad significa hora zulú o UTC. en.wikipedia.org/wiki/ISO_8601#UTC
Peter Stephens
19

Aquí hay uno que funciona mejor para mí ( versión 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()

produce

true
8/20/2010 8:00:00 AM
Cabaña Reb.
fuente
Actualmente, uso esto para verificar en mi unidad las pruebas de que todas las cadenas que espero sean fechas tienen el formato Iso8601. ¡Gracias!
anthv123
1
¿Por qué esto devuelve una marca de tiempo que no está en UTC? Una violación bastante grande del Principio de Menos Asombro ya que la "Cultura Invariante" con "AssumeUniversal" no debería estar haciendo esto porque el horario de verano es muy diferente en todo el mundo, por lo que regresar a su zona horaria local podría introducir errores si comienza a ejecutar el código en un servidor con diferentes configuraciones!
Elaskanator
7

Parece importante hacer coincidir exactamente el formato de la cadena ISO para TryParseExactque funcione. Supongo que Exacto es Exacto y esta respuesta es obvia para la mayoría, pero de todos modos ...

En mi caso, la respuesta de Reb.Cabin no funciona ya que tengo una entrada ligeramente diferente según mi "valor" a continuación.

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

Hay algunos miles adicionales allí por milisegundos y puede haber más.

Sin embargo, si agrego algo .fffal formato como se muestra a continuación, todo está bien.

Cadena de formato: @"yyyy-MM-dd\THH:mm:ss.fff\Z"

En la ventana inmediata VS2010:

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

cierto

Es posible que tenga que usar DateTimeStyles.AssumeLocaltambién dependiendo de la zona para la que sea su tiempo ...

Rob Von Nesselrode
fuente
1
Esto funcionó para mí, pero también tuvo que cambiar AssumeUniversala AdjustToUniversal.
Augusto Barreto
4

Esto funciona bien en 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
fuente
-1

DateTime.ParseExact(...) le permite decirle al analizador qué representa cada personaje.

Jerod Houghtelling
fuente