c# - example - DateTime vs DateTimeOffset
difference between timespan and datetime in c# (9)
DateTime es capaz de almacenar solo dos tiempos distintos, la hora local y UTC. La propiedad Kind indica cuál.
DateTimeOffset amplía esto al poder almacenar horas locales desde cualquier parte del mundo. También almacena el desplazamiento entre esa hora local y UTC. Tenga en cuenta que DateTime no puede hacer esto a menos que agregue un miembro adicional a su clase para almacenar ese desplazamiento UTC. O solo trabajar con UTC. Lo que en sí mismo es una buena idea por cierto.
Actualmente, tenemos una forma estándar de tratar con .net DateTimes de una forma consciente de TimeZone: cada vez que producimos un DateTime
lo hacemos en UTC (por ejemplo, usando DateTime.UtcNow
), y cada vez que mostramos uno, volvemos a convertir de UTC a 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 en sí. Entonces, la pregunta es, ¿cuáles serían las ventajas de usar DateTimeOffset
frente a lo que ya hemos estado haciendo?
Desde Microsoft:
Estos usos para los valores de DateTimeOffset son mucho más comunes que aquellos para los valores de DateTime. Como resultado, DateTimeOffset debe considerarse 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 con puntos específicos en el tiempo (por ejemplo, cuando se creó / actualizó un registro). Como nota al margen, también usamos DATETIMEOFFSET
en SQL Server 2008.
Considero que DateTime
es útil cuando desea tratar fechas solo, solo horas, o tratar cualquiera de ellas en un sentido genérico. Por ejemplo, si tiene una alarma que desea que suene todos los días a las 7 am, puede guardarla en un DateTime
utilizando un DateTimeKind
de Unspecified
porque desea que suene a las 7 am, independientemente del horario de verano. Pero si desea representar el historial de ocurrencias de alarmas, debe usar DateTimeOffset
.
Tenga cuidado al usar una combinación de DateTimeOffset
y DateTime
especialmente al asignar y comparar entre los tipos. Además, solo compare las instancias de DateTime
que son el mismo DateTimeKind
porque DateTime
ignora el desplazamiento de la zona horaria cuando se compara.
El único lado negativo de DateTimeOffset que veo es que Microsoft "olvidó" (por diseño) para admitirlo en su clase XmlSerializer. Pero desde entonces se ha agregado a la clase de utilidad XmlConvert.
Digo que siga adelante y use DateTimeOffset y TimeZoneInfo debido a todos los beneficios, solo tenga cuidado al crear entidades que serán o pueden ser serializadas hacia o desde XML (todos los objetos comerciales entonces).
Este fragmento de código de Microsoft explica todo:
// Find difference between Date.Now and Date.UtcNow
date1 = DateTime.Now;
date2 = DateTime.UtcNow;
difference = date1 - date2;
Console.WriteLine("{0} - {1} = {2}", date1, date2, difference);
// Find difference between Now and UtcNow using DateTimeOffset
dateOffset1 = DateTimeOffset.Now;
dateOffset2 = DateTimeOffset.UtcNow;
difference = dateOffset1 - dateOffset2;
Console.WriteLine("{0} - {1} = {2}",
dateOffset1, dateOffset2, difference);
// If run in the Pacific Standard time zone on 4/2/2007, the example
// displays the following output to the console:
// 4/2/2007 7:23:57 PM - 4/3/2007 2:23:57 AM = -07:00:00
// 4/2/2007 7:23:57 PM -07:00 - 4/3/2007 2:23:57 AM +00:00 = 00:00:00
Hay algunos lugares donde DateTimeOffset
tiene sentido. Una es cuando estás lidiando con eventos recurrentes y el horario de verano. Digamos que quiero programar una alarma para que suene a las 9 am todos los días. Si utilizo la regla "almacenar como UTC, mostrar como hora local", la alarma se activará a una hora diferente cuando el horario de verano esté vigente.
Probablemente hay otros, pero el ejemplo anterior es uno que encontré en el pasado (esto fue antes de agregar DateTimeOffset
al BCL; mi solución en ese momento era almacenar explícitamente la hora en la zona horaria local, y guarde la información de la zona horaria junto a ella: básicamente lo que DateTimeOffset
hace internamente).
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 ningún desplazamiento de zona horaria explícito asociado a él. Si realiza cualquier 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 cualquier compensación explícita en DateTime) utilizará la compensación de zona horaria del servidor.
Por ejemplo, si serializa un valor 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 su DateTime ni con ningún desplazamiento que usó para calcularlo ... es simplemente el desplazamiento de la zona horaria del servidor.
Mientras tanto, DateTimeOffset incluye explícitamente el desplazamiento. Puede que no incluya el nombre de la zona horaria, pero al menos incluye el desplazamiento, y si lo serializa, obtendrá el desplazamiento incluido explícitamente en su valor en lugar de lo que sea la hora local del servidor.
La mayoría de las respuestas son buenas, pero pensé en agregar más enlaces de MSDN para obtener más información.
- Una breve historia de DateTime: por Anthony Moore por el equipo de BCL
- Elección entre fecha y hora y la fecha y hora Offset - por MSDN
- No olvide que SQL Server 2008 en adelante tiene un nuevo tipo de datos como DateTimeOffset
- .NET Framework incluye los tipos DateTime , DateTimeOffset y TimeZoneInfo , todos los cuales se pueden usar para crear aplicaciones que funcionan con fechas y horas.
- Realización de operaciones aritméticas con fechas y horas-MSDN
Una diferencia importante es que DateTimeOffset
se puede usar junto con TimeZoneInfo
para convertir a horas locales en zonas horarias distintas de la actual.
Esto es útil en una aplicación de servidor (por ejemplo, ASP.NET) a la que los usuarios acceden en diferentes zonas horarias.
DateTimeOffset
es una representación del 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 tener en cuenta los segundos de salto , o los efectos relativistas de la dilatación del tiempo ). Otra forma de representar el tiempo instantáneo es con un DateTime
donde .Kind
es DateTimeKind.Utc
.
Esto es distinto del tiempo del 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
donde .Kind
es DateTimeKind.Unspecified
, o DateTimeKind.Local
. Y .Local
solo tiene sentido en los escenarios en los que tiene una comprensión implícita de dónde se ubica la computadora que utiliza el resultado. (Por ejemplo, la estación de trabajo de un usuario)
Entonces, ¿por qué DateTimeOffset
lugar de un DateTime
UTC? Se trata de la perspectiva. Usemos una analogía: fingiremos ser fotógrafos.
Imagina que estás parado en una línea de tiempo del calendario, apuntando una cámara a una persona en la línea de tiempo instantánea que se muestra frente a ti. Usted alinea 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 tienes una mano firme, así que tu cámara está temblorosa).
La persona que está en la foto verá el ángulo en el que proviene la cámara. Si otros estuvieran tomando fotos, podrían ser desde diferentes ángulos. Esto es lo que representa la parte de Offset
de DateTimeOffset
.
Entonces, si etiqueta su cámara como "Hora del Este", a veces apunta desde -5, y otras apunta desde -4. Hay cámaras en todo el mundo, todas etiquetadas como cosas diferentes y todas apuntando a la misma línea de tiempo instantánea desde diferentes ángulos. Algunos de ellos están uno al lado del otro (o uno encima del otro), por lo que el simple hecho de saber el desplazamiento no es suficiente para determinar con qué zona horaria está relacionado el tiempo.
¿Y qué pasa con UTC? Bueno, es la única cámara que está garantizada para tener una mano firme. Está en un trípode, firmemente anclado en el suelo. No va a ninguna parte. Llamamos a su ángulo de perspectiva el offset cero.
Entonces, ¿qué nos dice esta analogía? Proporciona algunas pautas intuitivas.
Si está representando el tiempo relativo a algún lugar en particular, represéntelo en tiempo calendario con un
DateTime
. Solo asegúrate de no confundir un calendario con otro.Unspecified
debe ser su suposición.Local
solo es útil a partir deDateTime.Now
. Por ejemplo, podría obtenerDateTime.Now
y guardarlo en una base de datos, pero cuando lo recupero, debo asumir que no estáUnspecified
. No puedo confiar en que mi calendario local es el mismo calendario del que se tomó originalmente.Si siempre debe estar seguro del momento, asegúrese de estar representando el tiempo instantáneo. Use
DateTimeOffset
para imponerlo o use UTCDateTime
por convención.Si necesita rastrear un momento de tiempo instantáneo, pero también quiere saber "¿A qué hora pensó el usuario que estaba en su calendario local?" - entonces debes usar un
DateTimeOffset
. Esto es muy importante para los sistemas de cronometraje, por ejemplo, tanto para asuntos técnicos como legales.Si alguna vez necesita modificar un
DateTimeOffset
previamente grabado, no tiene suficiente información solo en el offset para garantizar que el nuevo offset aún sea relevante para el usuario. También debe almacenar un identificador de zona horaria (piense: necesito el nombre de esa cámara para poder tomar una nueva imagen incluso si la posición ha cambiado).También debe señalarse que Noda Time tiene una representación llamada
ZonedDateTime
para esto, mientras que la biblioteca de clases base .Net no tiene nada similar. Necesitaría almacenar tanto unDateTimeOffset
como un valorTimeZoneInfo.Id
.Ocasionalmente, querrá representar una hora del calendario que sea local para "quien la esté mirando". Por ejemplo, al definir lo que hoy significa. Hoy es siempre de medianoche a medianoche, pero estos representan un número casi infinito 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 las compensaciones hasta el final) Entonces, en estas situaciones, asegúrese de entender cómo limitar el "¿quién pregunta?" cuestione a una sola zona horaria, o trate de traducirlos al tiempo instantáneo, según corresponda.
Aquí hay algunos otros pequeños detalles sobre DateTimeOffset
que respaldan esta analogía, y algunos consejos para mantenerlo en orden:
Si compara dos valores de
DateTimeOffset
, primero se normalizan en cero antes de compararlos. En otras palabras,2012-01-01T00:00:00+00:00
y2012-01-01T02:00:00+02:00
refieren al mismo momento instantáneo y, por lo tanto, son equivalentes.Si está realizando una prueba de unidad y necesita estar seguro del desplazamiento, pruebe el valor de
DateTimeOffset
y la propiedad.Offset
separado.Existe una conversión implícita unidireccional integrada en el marco .Net que le permite pasar un
DateTime
a cualquier parámetro o variableDateTimeOffset
. Al hacerlo, el.Kind
importa . Si pasa un tipo UTC, se cargará con una compensación de cero, pero si pasa.Local
o.Unspecified
, asumirá que es local . Básicamente, el marco 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 unDateTime
no especificado en una computadora con una zona horaria diferente. (IMHO - eso debería lanzar una excepción, pero no lo hace).
Enchufe desvergonzado:
Muchas personas han compartido conmigo que encuentran esta analogía extremadamente valiosa, así que la incluí en mi curso de Pluralsight, Fundamentos de fecha y hora . Encontrará un tutorial 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. Instantaneous Time".