Convierta la hora UTC / GMT en hora local

Estamos desarrollando una aplicación C # para un cliente de servicio web. Esto se ejecutará en PC con Windows XP.

Uno de los campos devueltos por el servicio web es un campo DateTime. El servidor devuelve un campo en formato GMT, es decir, con una “Z” al final.

Sin embargo, encontramos que .NET parece hacer algún tipo de conversión implícita y el tiempo siempre fue de 12 horas.

El siguiente ejemplo de código resuelve esto hasta cierto punto, ya que la diferencia de 12 horas se ha ido, pero no tiene en cuenta el horario de verano de Nueva Zelanda.

CultureInfo ci = new CultureInfo("en-NZ"); string date = "Web service date".ToString("R", ci); DateTime convertedDate = DateTime.Parse(date); 

Según este sitio de fecha :

Desplazamiento UTC / GMT

Zona horaria estándar: UTC / GMT +12 horas
Horario de verano: +1 hora
Offset actual de zona horaria: UTC / GMT +13 horas

¿Cómo nos ajustamos por la hora extra? ¿Se puede hacer esto de forma programática o se trata de algún tipo de ajuste en la PC?

Para cadenas como 2012-09-19 01:27:30.000 , DateTime.Parse no puede decir de qué zona horaria son la fecha y la hora.

DateTime tiene una propiedad Kind , que puede tener una de tres opciones de zona horaria:

  • Sin especificar
  • Local
  • UTC

NOTA Si desea representar una fecha / hora que no sea UTC o su zona horaria local, entonces debe usar DateTimeOffset .


Entonces, para el código en su pregunta:

 DateTime convertedDate = DateTime.Parse(dateStr); var kind = convertedDate.Kind; // will equal DateTimeKind.Unspecified 

Usted dice que sabe de qué tipo es, así que cuéntelo.

 DateTime convertedDate = DateTime.SpecifyKind( DateTime.Parse(dateStr), DateTimeKind.Utc); var kind = convertedDate.Kind; // will equal DateTimeKind.Utc 

Ahora, una vez que el sistema sabe que está en hora UTC, puede simplemente llamar a ToLocalTime :

 DateTime dt = convertedDate.ToLocalTime(); 

Esto le dará el resultado que necesita.

Consideraría usar la clase System.TimeZoneInfo si estás en .NET 3.5. Ver http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx . Esto debería tener en cuenta los cambios de horario de verano correctamente.

 // Coordinated Universal Time string from // DateTime.Now.ToUniversalTime().ToString("u"); string date = "2009-02-25 16:13:00Z"; // Local .NET timeZone. DateTime localDateTime = DateTime.Parse(date); DateTime utcDateTime = localDateTime.ToUniversalTime(); // ID from: // "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Time Zone" // See http://msdn.microsoft.com/en-us/library/system.timezoneinfo.id.aspx string nzTimeZoneKey = "New Zealand Standard Time"; TimeZoneInfo nzTimeZone = TimeZoneInfo.FindSystemTimeZoneById(nzTimeZoneKey); DateTime nzDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, nzTimeZone); 
 TimeZone.CurrentTimeZone.ToLocalTime(date); 

DateTime objetos DateTime tienen el Kind de Unspecified por defecto, que para los fines de ToLocalTime se supone que es UTC .

Para obtener la hora local de un objeto DateTime Unspecified , solo necesita hacer esto:

 convertedDate.ToLocalTime(); 

El paso de cambiar el Kind de DateTime de Unspecified a UTC es necesario. Unspecified se supone que es UTC a los efectos de ToLocalTime : http://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime.aspx

Sé que esta es una pregunta anterior, pero me encontré con una situación similar, y quería compartir lo que había encontrado para los buscadores futuros, posiblemente incluyéndome a mí mismo :).

DateTime.Parse() puede ser complicado; consulte aquí, por ejemplo.

Si DateTime proviene de un servicio web u otro origen con un formato conocido, es posible que desee considerar algo como

 DateTime.ParseExact(dateString, "MM/dd/yyyy HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal) 

o mejor,

 DateTime.TryParseExact(...) 

El indicador AssumeUniversal le dice al analizador que la fecha / hora ya es UTC; la combinación de AssumeUniversal y AssumeUniversal le dice que no convierta el resultado a la hora “local”, que intentará hacer de forma predeterminada. (Personalmente trato de tratar exclusivamente con UTC en la (s) capa (s) de negocio / aplicación / servicio de todos modos. Pero eludir la conversión a la hora local también acelera las cosas: en un 50% o más en mis pruebas, ver más abajo).

Esto es lo que estábamos haciendo antes:

 DateTime.Parse(dateString, new CultureInfo("en-US")) 

Hicimos un perfil de la aplicación y descubrimos que DateTime.Parse representaba un porcentaje significativo del uso de la CPU. (Por cierto, el constructor CultureInfo no contribuyó significativamente al uso de la CPU).

Así que configuré una aplicación de consola para analizar una cadena de fecha / hora 10000 veces en una variedad de formas. Línea de fondo:
Parse() 10 sec
ParseExact() (convirtiendo a local) 20-45 ms
ParseExact() (no convierte a local) 10-15 ms
… y sí, los resultados para Parse() están en segundos , mientras que los demás están en milisegundos .

Solo me gustaría agregar una nota general de precaución.

Si todo lo que hace es obtener la hora actual del reloj interno de la computadora para poner una fecha / hora en la pantalla o un informe, entonces todo está bien. Pero si está guardando la información de fecha / hora para referencia posterior o está calculando fecha / hora, ¡tenga cuidado!

Digamos que usted determina que un crucero llegó a Honolulu el 20 de diciembre de 2007 a las 15:00 UTC. Y quieres saber qué hora local fue esa.
1. Probablemente haya al menos tres “lugareños” involucrados. Local puede significar Honolulu, o puede significar dónde se encuentra su computadora, o puede significar la ubicación donde se encuentra su cliente.
2. Si usa las funciones incorporadas para realizar la conversión, probablemente sea incorrecta. Esto se debe a que el horario de verano es (probablemente) actualmente en vigencia en su computadora, pero NO estuvo en vigencia en diciembre. Pero Windows no lo sabe … todo lo que tiene es una bandera para determinar si el horario de verano está vigente. Y si está actualmente en vigencia, agregará felizmente una hora incluso a una fecha en diciembre.
3. El horario de verano se implementa de manera diferente (o no se aplica) en varias subdivisiones políticas. No piense que solo porque su país cambia en una fecha específica, también lo harán otros países.

No olvide que si ya tiene un objeto DateTime y no está seguro si es UTC o Local, es bastante fácil usar los métodos en el objeto directamente:

 DateTime convertedDate = DateTime.Parse(date); DateTime localDate = convertedDate.ToLocalTime(); 

¿Cómo nos ajustamos por la hora extra?

A menos que se especifique .net usará la configuración de la PC local. Tendría una lectura de: http://msdn.microsoft.com/en-us/library/system.globalization.daylighttime.aspx

Por apariencia, el código podría ser algo así como:

 DaylightTime daylight = TimeZone.CurrentTimeZone.GetDaylightChanges( year ); 

Y como se mencionó anteriormente, compruebe qué configuración de zona horaria tiene su servidor. Hay artículos en la red sobre cómo afectar de forma segura los cambios en IIS.

En respuesta a la sugerencia de Dana:

El ejemplo del código ahora se ve así:

 string date = "Web service date"..ToString("R", ci); DateTime convertedDate = DateTime.Parse(date); DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(convertedDate); 

La fecha original fue 20/08/08; el tipo era UTC.

Tanto “convertedDate” como “dt” son los mismos:

21/08/08 10:00:26; el tipo era local

Tuve el problema de que estaba en un conjunto de datos que se enviaba a través del cable (servicio web al cliente) que cambiaría automáticamente porque el campo DateType de DataColumn se estableció en local. Asegúrese de verificar qué es DateType si está presionando DataSets.

Si no desea que cambie, configúrelo en No especificado

Me encontré con esta pregunta porque tenía un problema con las fechas UTC que recibes a través de la API de Twitter (campo created_at en un estado); Necesito convertirlos a DateTime. Ninguna de las respuestas / muestras de código en las respuestas de esta página fueron suficientes para evitar que obtuviera el error “Cadena no reconocida como fecha y hora válida” (pero es lo más cercano que tengo para encontrar la respuesta correcta en SO)

Publicando este enlace aquí en caso de que esto ayude a alguien más – la respuesta que necesitaba se encontró en esta publicación del blog: http://www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse-doesnt-work/ – básicamente use DateTime.ParseExact con una cadena de formato en lugar de DateTime.Parse

 @TimeZoneInfo.ConvertTimeFromUtc(timeUtc, TimeZoneInfo.Local)