2009-05-07 14 views
44

[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]¿Dónde está el especificador de formato DateTime 'Z'?

que he estado tratando de realizar conversiones DateTime de ida y vuelta con una cadena de formato que utiliza especificador de formato 'zzz', que sé que está ligado a la hora local. Por lo tanto, si intento 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, use el especificador de formato 'Z', que designa una hora UTC, o use 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 tengo que hacer para que mi código funcione es reemplazar 'zzz' con 'ZZZ' para que pueda estar 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?

Código Ejemplo:

// 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); 
+0

Quizás podría suministrar con un ejemplo de código real de lo que está tratando de hacer, así que no tienen que perder tiempo tratando de adivinar? –

+0

Acabo de actualizar con una muestra. Estoy confundido porque sé que 'Z' no es compatible de acuerdo con MSDN, pero la excepción me pide que lo use. Y lo que quiero hacer es convertir con esta cadena personalizada, incluso si es UTC, y obtener "+0000" en el resultado, como si obtuviera "-04: 00" si usaba 'zzz' con un hora local. –

+0

Veo tu problema ahora; gracias por la actualización del código Me zambulliré en él por un rato y publicaré algo si lo encuentro. –

Respuesta

48

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.

+2

Usando 'K' irónicamente se emite 'Z' en mi cadena en lugar de "+0000". –

+1

@Dimebrain, una "Z" literal en la cadena es directamente equivalente a "+0000" en lo que se refiere a ISO8601. Si necesita tener "+0000" en lugar de "Z", entonces sospecho que deberá hacer un reemplazo en la cadena formateada. – LukeH

+3

@Dimebrain, creo que hacer referencia a "Z" como especificador de formato en el mensaje de excepción es un error, "K" es casi con certeza el especificador que necesita. – LukeH

2

This page en MSDN enumera cadenas estándar de formato de fecha y hora, cadenas uncluding utilizando el 'Z'.

Actualización: deberá asegurarse de que el resto 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 el formato GMT funcione debe tener este aspecto: la salida

// yyyy'-'MM'-'dd HH':'mm':'ss'Z' 
DateTime utcTime = DateTime.Parse("2009-05-07 08:17:25Z"); 
+0

Sí, sé que hay una cadena de formato 'Z'. Pero esta pregunta se trata de por qué la excepción sugiere un especificador de formato 'Z', que es completamente diferente. –

+0

Creo que puedes mezclarlos; la cadena de formato para UTC no es 'Z', es 'u' (de acuerdo con la información en el enlace que publiqué). Entonces, si quiere una expresión DateTime formateada como el formato UTC estándar (como la cadena de formato en mi ejemplo anterior) puede usar .ToString ("u") –

+0

Estoy usando una cadena * custom * con especificadores que no son cadenas de formato. –

2
Label1.Text = dt.ToString("dd MMM yyyy | hh:mm | ff | zzz | zz | z"); 

voluntad:

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

estoy en Dinamarca, mis desplazada con respecto a GMT +2 horas es, bruja es correcta .

si necesita obtener CLIENTE Offset, le recomiendo que marque little trick 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 usted comenta, 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 me sale esto:

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

consigo no es una excepción, sólo ... no hace nada con el capital Z :(

Lo siento, pero ¿me falta algo?


leer detenidamente el MSDN en Custom Date and Time Format Strings

no hay apoyo para la 'Z' mayúscula.

+0

No es mi pregunta; Sé que los especificadores de formato 'zzz' funcionan con compensaciones locales. Estoy preguntando si reemplazar 'zzz' con 'ZZZ' como lo sugiere la excepción, si estoy lidiando con tiempos UTC. Si cambió ese dt a UTC con dt.ToUniversalTime(), luego trató de formatearlo con su cadena, se levantaría la excepción de la que estoy hablando. –

+0

No estoy seguro de por qué no recibió la excepción, ¿depuró el paso? Si no lo haces, no lo atraparás. Pero sí, es cierto que 'Z' no es compatible, pero si no es así, la Excepción no debería decirme que lo use. –

3

Las fechas de ida y vuelta 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); 
+0

Normalmente soy bueno con eso, pero no controlo el origen del DateTime, me viene en una llamada XML REST, y tengo que deserializarlo de mi lado, pero también puedo volver al modo exacto. formato utilizado, de lo contrario me quedaría con las mejores prácticas y usar 'o'. Este es un formato no estándar, y mi código funciona con las horas locales, pero no puedo hacer que esto funcione sin un hack, es decir, formato.Replace ("zzzzz", "+0000"); –

5

Cuando utiliza 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é sucede cuando imprimo DT1 y DT1 incluyendo 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 su máquina.

El compilador le indica que cuando utiliza 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 utilizará el desplazamiento de la máquina servidor que está ejecutando el código.

El compilador te dice "Advertencia: la salida de su código depende del reloj de la máquina offset"

Si corro este código en un servidor que está posicionada en Londres (1 GMT) el resultado será completamente diferente: en vez de "" se escribirá ""

... 
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 utilizar DateTimeOffset tipo de datos en lugar de DateTime. Está disponible en SQL Server a partir de la versión de 2008 y en el marco .Net a partir de la versión 3.5

0

que estaba tratando con DateTimeOffset y por desgracia la "o" imprime "0000" no "Z".

así que terminé con:

dateTimeOffset.UtcDateTime.ToString("o") 
Cuestiones relacionadas