¿Cómo funciona DateTime.ToUniversalTime ()?

¿Cómo funciona la conversión a UTC del formato estándar de DateTime ?

Más específicamente, si creo un objeto DateTime en una zona horaria y luego cambio a otra zona horaria y ejecuto ToUniversalTime() en ella, ¿cómo sabe que la conversión se realizó correctamente y que la hora todavía se representa con precisión?

No hay una zona horaria implícita asociada a un objeto DateTime . Si ejecuta ToUniversalTime() en él, utiliza la zona horaria del contexto en el que se está ejecutando el código.

Por ejemplo, si creo un DateTime de la época del 1/1/1970, me da el mismo objeto DateTime sin importar en qué parte del mundo estoy.

Si ejecuto ToUniversalTime() en él cuando estoy ejecutando el código en Greenwich, entonces obtengo el mismo tiempo. Si lo hago mientras vivo en Vancouver, obtengo un objeto DateTime compensado de -8 horas.

Es por eso que es importante almacenar información relacionada con el tiempo en su base de datos como horas UTC cuando necesita hacer algún tipo de conversión de fecha o localización. Considere si su base de código se movió a una instalación de servidor en otra zona horaria;)

Editar: nota de la respuesta de Joel: los objetos DateTime por defecto se escriben como DateTimeKind.Local . Si analiza una fecha y la configura como DateTimeKind.Utc , ToUniversalTime() no realiza ninguna conversión.

Y aquí hay un artículo sobre “Las mejores prácticas de encoding con fecha y hora” , y un artículo sobre la conversión de DateTimes con .Net .

En primer lugar, comprueba si ya se sabe que el Kind del DateTime es UTC. Si es así, devuelve el mismo valor.

De lo contrario, se supone que es una hora local, que es local para la computadora en la que se está ejecutando, y en particular en la zona horaria que la computadora estaba usando cuando se inicializó por primera vez una propiedad privada. Eso significa que si cambia la zona horaria después de que se inició su aplicación, es muy probable que siga utilizando la anterior.

La zona horaria contiene suficiente información para convertir una hora local a una hora UTC o viceversa, aunque hay veces en que eso es ambiguo o no válido. (Hay momentos locales que ocurren dos veces y horas locales que nunca ocurren debido al horario de verano). Las reglas para manejar estos casos se especifican en la documentación :

Si el valor de la instancia de fecha y hora es un tiempo ambiguo, este método supone que es una hora estándar. (Un tiempo ambiguo es aquel que puede asignarse a una hora estándar oa un horario de verano en la zona horaria local) Si el valor de instancia de fecha y hora no es válido, este método simplemente resta la hora local de la zona horaria local Desplazamiento UTC para devolver UTC. (Un tiempo no válido es aquel que no existe debido a la aplicación de las reglas de ajuste del horario de verano).

El valor devuelto tendrá una Kind de DateTimeKind.Utc , por lo tanto, si llama a ToUniveralTime , no volverá a aplicar el desplazamiento. (¡Esta es una gran mejora sobre .NET 1.1!)

Si desea una zona horaria no local, debe utilizar TimeZoneInfo que se introdujo en .NET 3.5 (hay soluciones hacky para versiones anteriores, pero no son agradables). Para representar un instante en el tiempo, debería considerar utilizar DateTimeOffset que se introdujo en .NET 2.0SP1, .NET3.0SP1 y .NET 3.5. Sin embargo, aún no tiene una zona horaria real asociada, solo una compensación de UTC. Eso significa que no sabe qué hora local será una hora más tarde, por ejemplo: las reglas de horario de verano pueden variar entre las zonas horarias que usaron la misma compensación para ese instante en particular. TimeZoneInfo está diseñado para tener en cuenta las reglas históricas y futuras, a diferencia de TimeZone que es algo simplista.

Básicamente, el soporte en .NET 3.5 es mucho mejor de lo que era, pero aún deja algo que desear para una aritmética de calendario adecuada. ¿Alguien quiere trasladar Joda Time a .NET? 😉

Lo que @womp dijo , con la adición de que verifica la propiedad Tipo de fecha y hora para ver si ya podría ser una fecha UTC.

DateTime.ToUniversalTime elimina el desplazamiento de la zona horaria de la zona horaria local para normalizar un DateTime a UTC. Si luego usa DateTime.ToLocalTime en el valor normalizado en otra zona horaria, el desplazamiento de la zona horaria de esa zona horaria se agregará al valor normalizado para la representación correcta en esa zona horaria.