Pregunta DateTime versus DateTimeOffset


Actualmente, tenemos una forma estándar de tratar con .Net DateTimes en una forma consciente de TimeZone: siempre que generemos un DateTime lo hacemos en UTC (por ejemplo, usando DateTime.UtcNow), y siempre que mostramos uno, convertimos de nuevo desde UTC a la hora local del usuario.

Eso funciona bien, pero he estado leyendo sobre DateTimeOffset y cómo captura la hora local y UTC en el objeto mismo. Entonces la pregunta es, ¿cuáles serían las ventajas de usar DateTimeOffset vs lo que ya hemos estado haciendo?


531
2017-12-02 02:39


origen


Respuestas:


DateTimeOffset es una representación de tiempo instantáneo (también conocido como tiempo absoluto) Con eso, me refiero a un momento en el tiempo que es universal para todos (sin contar segundos de salto, o los efectos relativistas de dilatación del tiempo) Otra forma de representar el tiempo instantáneo es con un DateTime dónde .Kind es DateTimeKind.Utc.

Esto es distinto de tiempo calendario (también conocido como tiempo civil), que es una posición en el calendario de alguien, y hay muchos calendarios diferentes en todo el mundo. Llamamos a estos calendarios zonas horarias. La hora del calendario está representada por un DateTime dónde .Kind es DateTimeKind.Unspecified, o DateTimeKind.Local. Y .Local solo tiene sentido en escenarios en los que tiene una comprensión implícita de dónde está posicionada la computadora que está utilizando el resultado. (Por ejemplo, la estación de trabajo de un usuario)

Entonces, ¿por qué? DateTimeOffset en lugar de un UTC DateTime? Se trata de perspectiva.  Usemos una analogía: pretendemos ser fotógrafos.

Imagine que está de pie en una línea de tiempo del calendario, apuntando una cámara a una persona en la línea de tiempo instantánea dispuesta frente a usted. Alinee su cámara de acuerdo con las reglas de su zona horaria, que cambian periódicamente debido al horario de verano o debido a otros cambios en la definición legal de su zona horaria. (No tiene una mano firme, por lo que su cámara es inestable.)

La persona que está parada en la foto podría ver el ángulo desde el que salió su cámara. Si otros tomaran fotos, podrían ser desde diferentes ángulos. Esto es lo que Offset parte de DateTimeOffset representa

Por lo tanto, si etiqueta su cámara como "Hora del Este", a veces señala desde -5, y algunas veces señala desde -4. Hay cámaras en todo el mundo, todas etiquetan cosas diferentes, y todas apuntan a la misma línea de tiempo instantánea desde diferentes ángulos. Algunos de ellos están justo al lado (o encima de) el uno del otro, por lo que simplemente conocer el desplazamiento no es suficiente para determinar con qué zona horaria está relacionada la hora.

¿Y qué hay de UTC? Bueno, es la cámara que está garantizada para tener una mano firme. Está en un trípode, firmemente anclado en el suelo. No va a ir a ningún lado. Llamamos a su ángulo de perspectiva el desplazamiento cero.

Instantaneous Time vs Calendar Time Visualization

Entonces, ¿qué nos dice esta analogía? Proporciona algunas pautas intuitivas.

  • Si representa el tiempo relativo a algún lugar en particular, represéntelo en tiempo calendario con un DateTime. Solo asegúrate de no confundir nunca un calendario con otro. Unspecified debería ser tu suposición. Local solo es útil viniendo de DateTime.Now. Por ejemplo, podría obtener DateTime.Now y guardarlo en una base de datos, pero cuando lo recupero, tengo que asumir que es Unspecified. No puedo confiar en que mi calendario local sea el mismo calendario del que se tomó originalmente.

  • Si siempre debe estar seguro del momento, asegúrese de representar el tiempo instantáneo. Utilizar DateTimeOffset para hacer cumplir, o usar UTC DateTime por convención.

  • Si necesita rastrear un momento de tiempo instantáneo, pero también desea saber "¿A qué hora pensó el usuario que estaba en su calendario local?" - entonces tú debe usar una DateTimeOffset. Esto es muy importante para los sistemas de control de tiempo, por ejemplo, tanto para cuestiones técnicas como legales.

  • Si alguna vez necesita modificar un registro previo DateTimeOffset - no tiene suficiente información en el desplazamiento solo para garantizar que el nuevo desplazamiento sigue siendo relevante para el usuario. Debes además almacene un identificador de zona horaria (piense: necesito el nombre de esa cámara para poder tomar una nueva foto, incluso si la posición ha cambiado).

    También se debe señalar que Hora de Noda tiene una representación llamada ZonedDateTime para esto, mientras que la biblioteca de clases base .Net no tiene nada similar. Necesitarías almacenar un DateTimeOffset y un TimeZoneInfo.Id valor.

  • Ocasionalmente, querrás representar un tiempo calendario que sea local para "quien lo esté mirando". Por ejemplo, al definir qué hoy medio. Hoy es siempre de medianoche a medianoche, pero representa una cantidad casi infinita de rangos superpuestos en la línea de tiempo instantánea. (En la práctica, tenemos un número finito de zonas horarias, pero puede expresar compensaciones hasta la marca) Entonces, en estas situaciones, asegúrese de entender cómo limitar el "¿quién pregunta?" pregunta hacia una zona horaria única, o trate de traducirlas a la hora instantánea según corresponda.

Aquí hay algunos otros pequeños detalles sobre DateTimeOffset que respalda esta analogía, y algunos consejos para mantenerlo en línea:

  • Si compara dos DateTimeOffset valores, primero se normalizan a cero de compensación antes de comparar. En otras palabras, 2012-01-01T00:00:00+00:00 y 2012-01-01T02:00:00+02:00 se refieren al mismo momento instantáneo, y por lo tanto son equivalentes.

  • Si está haciendo una prueba unitaria y necesita estar seguro de la compensación, pruebe ambos el DateTimeOffset valor, y el .Offset propiedad por separado.

  • Hay una conversión implícita unidireccional integrada en .Net Framework que le permite pasar un DateTime en cualquier DateTimeOffset parámetro o variable. Al hacerlo, el .Kind asuntos. Si pasa un tipo UTC, se llevará con un desplazamiento cero, pero si pasa cualquiera .Local o .Unspecified, supondrá que es local. El marco básicamente dice: "Bueno, me pediste que convirtiera el tiempo del calendario en tiempo instantáneo, pero no tengo idea de dónde vino esto, así que solo voy a usar el calendario local". Este es un gran problema si carga un no especificado DateTimeen una computadora con una zona horaria diferente. (En mi humilde opinión, eso debería arrojar una excepción, pero no es así).

Plug desvergonzado:

Muchas personas han compartido conmigo que consideran que esta analogía es extremadamente valiosa, así que la incluí en mi curso de Pluralsight. Fundamentos de fecha y hora. Encontrará un recorrido paso a paso de la analogía de la cámara en el segundo módulo, "Context Matters", en el clip titulado "Calendar Time vs. Instant Time".


856
2018-01-10 22:09



De Microsoft:

Estos usos para los valores de DateTimeOffset son mucho más comunes que los de los valores de DateTime. Como resultado, DateTimeOffset se debe considerar el tipo de fecha y hora predeterminado para el desarrollo de la aplicación.

fuente: "Elegir entre DateTime, DateTimeOffset, TimeSpan y TimeZoneInfo", MSDN

Usamos DateTimeOffset para casi todo, ya que nuestra aplicación trata puntos específicos en el tiempo (por ejemplo, cuando se creó / actualizó un registro). Como nota al margen, usamos DATETIMEOFFSET en SQL Server 2008 también.

Ya veo DateTime como útil cuando desea tratar fechas solo, veces solamente, o tratar con cualquiera en un sentido genérico. Por ejemplo, si tiene una alarma que desea apagar todos los días a las 7 a. M., Puede almacenarla en un DateTime utilizando un DateTimeKind de Unspecified porque quieres que suene a las 7 a.m., independientemente del horario de verano. Pero si quieres representar el historial de ocurrencias de alarma, usarías DateTimeOffset.

Tenga cuidado al usar una mezcla de DateTimeOffset y DateTime especialmente al asignar y comparar entre los tipos. Además, solo compare DateTime instancias que son lo mismo DateTimeKind porque DateTime ignora el desplazamiento de la zona horaria al comparar.


201
2018-01-09 19:42



DateTime es capaz de almacenar solo dos horarios distintos, la hora local y el UTC. los Tipo propiedad indica cual.

DateTimeOffset amplía esto al poder almacenar horas locales desde cualquier lugar del mundo. También almacena el compensar entre esa hora local y UTC. Observe cómo DateTime no puede hacer esto a menos que agregue un miembro adicional a su clase para almacenar ese desplazamiento UTC. O solo trabaje con UTC. Lo cual en sí mismo es una buena idea por cierto.


54
2017-12-02 17:47



Hay algunos lugares donde DateTimeOffset tiene sentido. Una es cuando se trata de eventos recurrentes y horario de verano. Digamos que quiero configurar una alarma para que suene a las 9 a.m. todos los días. Si utilizo la regla "almacenar como UTC, mostrar como hora local", la alarma se activará a la diferente hora en que está vigente el horario de verano.

Probablemente haya otros, pero el ejemplo anterior es en realidad uno con el que me he encontrado en el pasado (esto fue antes de la adición de DateTimeOffset al BCL: mi solución en ese momento era almacenar explícitamente la hora en la zona horaria local, y guardar la información de la zona horaria junto a ella: básicamente, DateTimeOffset lo hace internamente).


31
2017-12-02 02:59



La distinción más importante es que DateTime no almacena información de zona horaria, mientras que DateTimeOffset sí lo hace.

Aunque DateTime distingue entre UTC y Local, no hay absolutamente ninguna compensación de zona horaria explícita asociada. Si realiza algún tipo de serialización o conversión, se utilizará la zona horaria del servidor. Incluso si crea manualmente una hora local agregando minutos para compensar una hora UTC, aún puede obtener un bit en el paso de serialización, porque (debido a la falta de un desplazamiento explícito en DateTime) usará el desplazamiento de la zona horaria del servidor.

Por ejemplo, si serializa un valor de DateTime con Kind = Local usando Json.Net y un formato de fecha ISO, obtendrá una cadena como 2015-08-05T07:00:00-04. Tenga en cuenta que la última parte (-04) no tuvo nada que ver con DateTime ni con ninguna compensación que haya utilizado para calcularla ... simplemente es la compensación de la zona horaria del servidor.

Mientras tanto, DateTimeOffset incluye explícitamente el desplazamiento. Puede que no incluya el nombre del huso horario, pero al menos incluye el desplazamiento, y si lo serializa, obtendrá el desplazamiento explícitamente incluido en su valor en lugar de la hora local del servidor.


16
2017-08-05 23:00



La mayoría de las respuestas son buenas, pero pensé en agregar algunos más enlaces de MSDN para obtener más información


9
2018-04-29 00:41



Una gran diferencia es que DateTimeOffset se puede usar junto con TimeZoneInfo para convertir a horas locales en zonas horarias distintas a la actual.

Esto es útil en una aplicación de servidor (por ejemplo, ASP.NET) a la que acceden los usuarios en diferentes zonas horarias.


7
2017-12-02 13:03



El único aspecto negativo de DateTimeOffset que veo es que Microsoft "olvidó" (por diseño) admitirlo en su clase XmlSerializer. No hay problema con DataContractSerializer (y tal vez otros, pero necesita verificar eso). De todos modos hay una solución que expliqué en el problema oficial de MS Connect aquí:

https://connect.microsoft.com/VisualStudio/feedback/details/288349/datetimeoffset-is-not-serialized-by-a-xmlserializer

Como puede ver, Microsoft no arreglará esto a pesar de que es un tipo incorporado y recomiendan que lo usemos por defecto; loco pero cierto! Sigo diciendo que continúe y use DateTimeOffset y TimeZoneInfo debido a todos los beneficios, solo tenga cuidado al crear entidades que se serializarán en XML (todos los objetos comerciales).


2
2018-04-09 11:11