¿Dónde está el especificador de formato DateTime ‘Z’?

[ Actualización : los especificadores de formato no son lo mismo que las cadenas de formato; un especificador de formato es una parte de una cadena de formato personalizado, donde una cadena de formato es ‘stock’ y no proporciona personalización. Mi problema es con especificadores no formatos ]

He intentado realizar conversiones DateTime de ida y vuelta con una cadena de formato que utiliza el especificador de formato ‘zzz’, que sé que está vinculado a la hora local. Por lo tanto, si bash realizar un viaje de ida y vuelta con una fecha UTC, arroja una excepción DateTimeInvalidLocalFormat, que debería, con este texto:

Un UTC DateTime se está convirtiendo a texto en un formato que solo es correcto para las horas locales. Esto puede suceder cuando se llama a DateTime.ToString utilizando el especificador de formato ‘z’, que incluirá un desplazamiento de zona horaria local en la salida. En ese caso, utilice el especificador de formato ‘Z’, que designa una hora UTC , o utilice la cadena de formato ‘o’, que es la forma recomendada de conservar un DateTime en el texto. Esto también puede ocurrir al pasar un DateTime para ser serializado por XmlConvert o DataSet. Si usa XmlConvert.ToString, pase XmlDateTimeSerializationMode.RoundtripKind para serializar correctamente. Si usa DataSet, configure DateTimeMode en el objeto DataColumn en DataSetDateTime.Utc.

Basado en esta sugerencia, todo lo que necesito hacer para que mi código funcione es reemplazar ‘zzz’ con ‘ZZZ’ para que pueda estar parado en un formato UTC. El problema es que ‘Z’ no se encuentra en ninguna parte de la documentación y cualquier combinación de formato ‘Z’ que intente, es decir, ‘Z’, ‘ZZ’, ‘ZZZ’, siempre convierte la instancia de DateTime con esas Z tratadas como literales .

¿Alguien se olvidó de implementar ‘Z’ sin decirle al autor del mensaje de excepción, o me falta cómo cambiar un desplazamiento de hora local válido con “+0000” sin piratear?

Ejemplo de código:

// This is the format with 'zzzzz' representing local time offset const string format = "ddd MMM dd HH:mm:ss zzzzz yyyy"; // create a UTC time const string expected = "Fri Dec 19 17:24:18 +0000 2008"; var time = new DateTime(2008, 12, 19, 17, 24, 18, 0, DateTimeKind.Utc); // If you're using a debugger this will rightfully throw an exception // with .NET 3.5 SP1 because 'z' is for local time only; however, the exception // asks me to use the 'Z' specifier for UTC times, but it doesn't exist, so it // just spits out 'Z' as a literal. var actual = time.ToString(format, CultureInfo.InvariantCulture); Assert.AreEqual(expected, actual); 

Tal vez el especificador de formato “K” sería de alguna utilidad. Este es el único que parece mencionar el uso del capital “Z”.

“Z” es una especie de caso único para DateTimes. La “Z” literal es en realidad parte del estándar de fecha y hora ISO 8601 para horas UTC. Cuando “Z” (Zulu) se vira al final de un tiempo, indica que ese tiempo es UTC, entonces realmente el literal Z es parte del tiempo. Esto probablemente crea algunos problemas para la biblioteca de formato de fecha en .NET, ya que en realidad es un literal, en lugar de un especificador de formato.

Cuando usa DateTime, puede almacenar una fecha y una hora dentro de una variable.

La fecha puede ser una hora local o una hora UTC, depende de usted.

Por ejemplo, estoy en Italia (+2 UTC)

 var dt1 = new DateTime(2011, 6, 27, 12, 0, 0); // store 2011-06-27 12:00:00 var dt2 = dt1.ToUniversalTime() // store 2011-06-27 10:00:00 

Entonces, ¿qué ocurre cuando imprimo dt1 y dt2, incluida la zona horaria?

 dt1.ToString("MM/dd/yyyy hh:mm:ss z") // Compiler alert... // Output: 06/27/2011 12:00:00 +2 dt2.ToString("MM/dd/yyyy hh:mm:ss z") // Compiler alert... // Output: 06/27/2011 10:00:00 +2 

dt1 y dt2 contienen solo una información de fecha y hora. dt1 y dt2 no contienen el desplazamiento de la zona horaria.

Entonces, ¿de dónde viene el “+2” si no está contenido en las variables dt1 y dt2?

Viene de la configuración del reloj de tu máquina.

El comstackdor le dice que cuando usa el formato ‘zzz’ está escribiendo una cadena que combina “FECHA + HORA” (que se almacenan en dt1 y dt2) + “DESPLAZAMIENTO DE TIMERÍA” (que no está contenido en dt1 y dt2 porque son del tipo DateTyme) y usará el desplazamiento de la máquina servidor que está ejecutando el código.

El comstackdor te dice “Advertencia: la salida de tu código depende del offset del reloj de la máquina”

Si ejecuto este código en un servidor que está ubicado en Londres (+1 UTC), el resultado será completamente diferente: en lugar de ” +2 ” escribirá ” +1

 ... dt1.ToString("MM/dd/yyyy hh:mm:ss z") // Output: 06/27/2011 12:00:00 +1 dt2.ToString("MM/dd/yyyy hh:mm:ss z") // Output: 06/27/2011 10:00:00 +1 

La solución correcta es usar el tipo de datos DateTimeOffset en lugar de DateTime. Está disponible en el servidor sql a partir de la versión 2008 y en el marco .Net a partir de la versión 3.5

Las fechas de disparo redondo a través de cadenas siempre han sido un problema … pero los documentos indican que el especificador ‘o’ es el que se utiliza para el disparo circular que captura el estado UTC. Cuando se analiza, el resultado generalmente tendrá Kind == Utc si el original fue UTC. Descubrí que lo mejor que se puede hacer es siempre normalizar las fechas en UTC o locales antes de la serialización, y luego instruir al analizador sobre la normalización que ha elegido.

 DateTime now = DateTime.Now; DateTime utcNow = now.ToUniversalTime(); string nowStr = now.ToString( "o" ); string utcNowStr = utcNow.ToString( "o" ); now = DateTime.Parse( nowStr ); utcNow = DateTime.Parse( nowStr, null, DateTimeStyles.AdjustToUniversal ); Debug.Assert( now == utcNow ); 

Esta página en MSDN enumera cadenas de formato de fecha y hora estándar, sin excluir cadenas usando la ‘Z’.

Actualización: deberá asegurarse de que el rest de la cadena de fecha también sigue el patrón correcto (no ha proporcionado un ejemplo de lo que envía, por lo que es difícil decir si lo hizo o no). Para que funcione el formato UTC, debería verse así:

 // yyyy'-'MM'-'dd HH':'mm':'ss'Z' DateTime utcTime = DateTime.Parse("2009-05-07 08:17:25Z"); 
 Label1.Text = dt.ToString("dd MMM yyyy | hh:mm | ff | zzz | zz | z"); 

dará salida:

 07 Mai 2009 | 08:16 | 13 | +02:00 | +02 | +2 

Estoy en Dinamarca, mi compensación de GMT es de +2 horas, que es correcta.

si necesita obtener el DESPLAZADOR DEL CLIENTE , le recomiendo que compruebe un pequeño truco que hice. La página está en un servidor en el Reino Unido donde GMT es +00: 00 y, como puede ver, obtendrá su compensación GMT local.


En cuanto a tu comentario, lo hice:

 DateTime dt1 = DateTime.Now; DateTime dt2 = dt1.ToUniversalTime(); Label1.Text = dt1.ToString("dd MMM yyyy | hh:mm | ff | zzz | zz | z"); Label2.Text = dt2.ToString("dd MMM yyyy | hh:mm | FF | ZZZ | ZZ | Z"); 

y entiendo esto:

 07 Mai 2009 | 08:24 | 14 | +02:00 | +02 | +2 07 Mai 2009 | 06:24 | 14 | ZZZ | ZZ | Z 

No recibo ninguna excepción, solo … no hace nada con la capital Z 🙁

Lo siento, pero ¿me estoy perdiendo algo?


Leer detenidamente el MSDN en cadenas de formato de fecha y hora personalizadas

no hay soporte para ‘Z’ en mayúsculas.

Estaba lidiando con DateTimeOffset y desafortunadamente la “o” imprime “+0000” no “Z”.

Así que terminé con:

 dateTimeOffset.UtcDateTime.ToString("o")