Actualmente, tenemos una forma estándar de tratar con .NET DateTime
's de una manera consciente de TimeZone: cada vez que producimos una DateTime
lo hacemos en UTC (por ejemplo, usando DateTime.UtcNow
), y cada vez que mostramos una, volvemos de UTC a la hora local del usuario .
Eso funciona bien, pero he estado leyendo 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 en DateTimeOffset
comparación con lo que ya hemos estado haciendo?
Respuestas:
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 bisiestos o los efectos relativistas de la dilatación del tiempo ). Otra forma de representar el tiempo instantáneo es con un lugarDateTime
donde.Kind
estáDateTimeKind.Utc
.Esto es distinto del tiempo de 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 . El tiempo del calendario está representado por un lugar
DateTime
donde.Kind
estáDateTimeKind.Unspecified
, oDateTimeKind.Local
. Y.Local
solo es significativo 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é en
DateTimeOffset
lugar de un UTCDateTime
? Se trata de perspectiva. Usemos una analogía: fingiremos ser fotógrafos.Imagine que está parado en una línea de tiempo del calendario, apuntando con una cámara a una persona en la línea de tiempo instantánea que se encuentra 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 tienes una mano firme, por lo que tu cámara está temblorosa).
La persona de pie en la foto vería el ángulo desde el cual proviene su cámara. Si otros tomaran fotos, podrían ser desde diferentes ángulos. Esto es lo que representa la
Offset
parte deDateTimeOffset
.Por lo tanto, si etiqueta su cámara como "Hora del Este", a veces apunta desde -5 y otras 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 conocer el desplazamiento no es suficiente para determinar con qué zona horaria está relacionada la hora.
¿Y qué hay de UTC? Bueno, es la única cámara que garantiza 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 desplazamiento cero.
Entonces, ¿qué nos dice esta analogía? Proporciona algunas pautas intuitivas.
Si está representando el tiempo en relación con algún lugar en particular, hágalo en tiempo calendario con a
DateTime
. Solo asegúrese de no confundir nunca un calendario con otro.Unspecified
Debería ser su suposición.Local
solo es útil viniendo deDateTime.Now
. Por ejemplo, podría obtenerloDateTime.Now
y guardarlo en una base de datos, pero cuando lo recupere, debo asumir que es asíUnspecified
. No puedo confiar en que mi calendario local sea el mismo calendario del que fue tomado originalmente.Si siempre debe estar seguro del momento, asegúrese de representar el tiempo instantáneo. Úselo
DateTimeOffset
para aplicarlo, o use UTCDateTime
por convención.Si necesita realizar un seguimiento de un momento de tiempo instantáneo, pero también desea saber "¿A qué hora pensó el usuario que estaba en su calendario local?" - entonces debes usar a
DateTimeOffset
. Esto es muy importante para los sistemas de cronometraje, por ejemplo, tanto por cuestiones técnicas como legales.Si alguna vez necesita modificar un registro anterior
DateTimeOffset
, no tiene suficiente información solo en el desplazamiento para garantizar que el nuevo desplazamiento aún sea relevante para el usuario. Debe también almacenar un identificador de zona horaria (piensa - Necesito el nombre de esa cámara para que pueda 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 clase base .Net no tiene nada similar. Tendría que almacenar tanto unDateTimeOffset
como unTimeZoneInfo.Id
valor.Ocasionalmente, querrá representar un tiempo de calendario que sea local para "quien lo esté mirando". Por ejemplo, al definir lo que significa hoy . Hoy siempre es 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 la marca). En estas situaciones, asegúrese de comprender cómo limitar el "¿quién pregunta?" pregunta a una sola zona horaria, o trata de traducirlos nuevamente al tiempo instantáneo según corresponda.
Aquí hay algunos otros pequeños detalles sobre
DateTimeOffset
eso que respaldan esta analogía, y algunos consejos para mantenerla recta:Si compara dos
DateTimeOffset
valores, primero se normalizan a cero antes de comparar. En otras palabras,2012-01-01T00:00:00+00:00
y se2012-01-01T02:00:00+02:00
refieren al mismo momento instantáneo, y por lo tanto son equivalentes.Si está haciendo alguna prueba unitaria y necesita asegurarse del desplazamiento, pruebe tanto el
DateTimeOffset
valor como la.Offset
propiedad por separado.Hay una conversión implícita unidireccional integrada en el marco .Net que le permite pasar
DateTime
a cualquierDateTimeOffset
parámetro o variable. Al hacerlo, los.Kind
asuntos . Si pasa un tipo GMT, llevará con un desplazamiento cero, pero si pasa o bien.Local
o.Unspecified
, asumirá ser locales . El marco básicamente dice: "Bueno, me pediste que convirtiera el tiempo del calendario a 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 especificadoDateTime
en una computadora con una zona horaria diferente. (En mi humilde opinión, eso debería arrojar una excepción, pero no lo hace).Enchufe desvergonzado:
Muchas personas han compartido conmigo que encuentran esta analogía extremadamente valiosa, por lo que la incluí en mi curso 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. Instant Time".
fuente
DateTimeOffset
en C #, entonces debe persistirlo en unDATETIMEOFFSET
en SQL Server.DATETIME2
o simplementeDATETIME
(dependiendo del rango requerido) están bien paraDateTime
valores regulares . Sí, puede resolver una hora local desde cualquier emparejamiento de zona horaria + dto o utc. La diferencia es: ¿siempre desea calcular las reglas con cada resolución, o desea calcularlas previamente? En muchos casos (a veces por cuestiones legales) un DTO es una mejor opción.DateTimeOffset.Now
en el servidor, obtendrá el desplazamiento del servidor. El punto es que elDateTimeOffset
tipo puede retener ese desplazamiento. Puede hacer eso con la misma facilidad en el cliente, enviarlo al servidor y luego su servidor conocerá el desplazamiento del cliente.De Microsoft:
fuente: "Elegir entre DateTime, DateTimeOffset, TimeSpan y TimeZoneInfo" , MSDN
Usamos
DateTimeOffset
para casi todo, ya que nuestra aplicación se ocupa de puntos particulares en el tiempo (por ejemplo, cuando se creó / actualizó un registro). Como nota al margen, también utilizamosDATETIMEOFFSET
en SQL Server 2008.Veo
DateTime
que es útil cuando quieres tratar solo con fechas, solo horas o tratar en un sentido genérico. Por ejemplo, si usted tiene una alarma que desea salir todos los días a las 7 am, podría almacenar que en unaDateTime
utilización de unaDateTimeKind
deUnspecified
porque usted quiere que vaya a las 7 am sin importar el horario de verano. Pero si desea representar el historial de eventos de alarma, lo usaríaDateTimeOffset
.Tenga precaución cuando use una combinación de
DateTimeOffset
yDateTime
especialmente cuando asigne y compare entre los tipos. Además, solo compareDateTime
instancias que sean igualesDateTimeKind
porqueDateTime
ignora el desplazamiento de la zona horaria al comparar.fuente
Kind
sean iguales, la comparación podría ser un error. Si ambos lados lo tienenDateTimeKind.Unspecified
, realmente no saben que provienen de la misma zona horaria. Si ambos lados lo estánDateTimeKind.Local
, la mayoría de las comparaciones estarán bien, pero aún podría tener errores si un lado es ambiguo en la zona horaria local. Realmente solo lasDateTimeKind.Utc
comparaciones son infalibles, y sí,DateTimeOffset
generalmente se prefiere. (¡Salud!)DateTime es capaz de almacenar solo dos horas distintas, 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 trabajas con UTC. Lo cual en sí mismo es una buena idea por cierto.
fuente
Hay algunos lugares donde
DateTimeOffset
tiene sentido. Una es cuando se trata de eventos recurrentes y el horario de verano. Digamos que quiero configurar una alarma para que suene a las 9 am todos los días. Si uso la regla "almacenar como UTC, mostrar como hora local", la alarma se activará en un momento diferente cuando el horario de verano esté vigente.Probablemente hay 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
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 lo queDateTimeOffset
hace internamente).fuente
La distinción más importante es que DateTime no almacena información de zona horaria, mientras que DateTimeOffset sí.
Aunque DateTime distingue entre UTC y Local, no hay absolutamente ningún desplazamiento explícito de zona horaria asociado con él. 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 una compensación explícita en DateTime) usará 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 o cualquier compensación que utilizó para calcularlo ... es solo la compensación de zona horaria del servidor.Mientras tanto, DateTimeOffset incluye explícitamente el desplazamiento. Es posible 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 la hora local del servidor.
fuente
The most important distinction is that DateTime does not store time zone information, while DateTimeOffset does.
Este código de Microsoft explica todo:
fuente
La mayoría de las respuestas son buenas, pero pensé en agregar algunos enlaces más de MSDN para obtener más información
fuente
Una diferencia importante es que
DateTimeOffset
se puede usar junto con laTimeZoneInfo
conversión 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 acceden los usuarios en diferentes zonas horarias.
fuente
El único lado negativo de DateTimeOffset que veo es que Microsoft "olvidó" (por diseño) admitirlo en su clase XmlSerializer. Pero desde entonces se ha agregado a la clase de utilidad XmlConvert.
XmlConvert.ToDateTimeOffset
XmlConvert.ToString
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).
fuente