sql >> Base de Datos >  >> RDS >> Sqlserver

datetime vs datetimeoffset en SQL Server:¿Cuál es la diferencia?

Este artículo destaca las principales diferencias entre datetime y desplazamiento de fecha y hora tipos de datos en SQL Server.

Ambos tipos de datos se utilizan para almacenar valores de fecha y hora. Pero hay diferencias significativas entre los dos.

Quizás la diferencia más obvia es que el datetimeoffset almacena el desplazamiento de la zona horaria, mientras que datetime no.

Otra diferencia importante es que datetimeoffset le permite especificar la precisión (hasta 7 decimales). Esto significa que datetimeoffset los valores pueden variar en su tamaño de almacenamiento, dependiendo de la precisión que se utilice.

La fechahora type por otro lado, tiene un tamaño de almacenamiento fijo y precisión.

En general, debe evitar usar datetime a menos que tenga una buena razón para usarlo (como admitir un sistema heredado). Además, el datetime2 tipo es una coincidencia más cercana que datetimeoffset , por lo que es mejor que lo use si no necesita una compensación de zona horaria.

De cualquier manera, aquí hay una tabla que compara datetime y desplazamiento de fecha y hora :

Característica datetimeoffset fechahora
Cumple con SQL (ANSI e ISO 8601) No
Intervalo de fechas 0001-01-01 a 9999-12-31 1753-01-01 a 9999-12-31
Intervalo de tiempo 00:00:00 a 23:59:59.9999999 00:00:00 a 23:59:59.997
Longitud de caracteres 26 posiciones mínimo
34 máximo
19 posiciones mínimo
23 máximo
Tamaño de almacenamiento 8 a 10 bytes, dependiendo de la precisión*

* Más 1 byte para almacenar la precisión en algunos casos. Consulte a continuación para obtener más información.

8 bytes
Precisión 100 nanosegundos Redondeado a incrementos de .000, .003 o .007 segundos
Precisión fraccionaria de segundo definida por el usuario No
Rango de compensación de zona horaria -14:00 a +14:00 Ninguno
Reconocimiento y preservación de la compensación de zona horaria No
Consciente del horario de verano No No

Ejemplo 1:comparación básica

En cualquier caso, aquí hay un ejemplo rápido para demostrar la diferencia básica entre datetime y desplazamiento de fecha y hora .

DECLARE 
  @thedatetimeoffset datetimeoffset(7), 
  @thedatetime datetime;
SET @thedatetimeoffset = '2025-05-21 10:15:30.5555555 +07:30';
SET @thedatetime = @thedatetimeoffset;
SELECT 
  @thedatetimeoffset AS 'datetimeoffset',
  @thedatetime AS 'datetime';

Resultado:

+------------------------------------+-------------------------+
| datetimeoffset                     | datetime                |
|------------------------------------+-------------------------|
| 2025-05-21 10:15:30.5555555 +07:30 | 2025-05-21 10:15:30.557 |
+------------------------------------+-------------------------+

Aquí, configuro una fecha y hora variable al mismo valor que el datetimeoffset variable. Esto hace que el valor se convierta a datetime y luego podemos usar un SELECT instrucción para ver el valor de cada variable.

En este caso, el datetimeoffset el valor incluye el desplazamiento de la zona horaria y 7 lugares decimales. La fechahora el valor por otro lado, no incluye el desplazamiento de la zona horaria y solo tiene 3 decimales. Además, su tercer dígito fraccionario se redondea hacia arriba. Esto se debe a que su precisión siempre se redondea a incrementos de 0,000, 0,003 o 0,007 segundos.

Ejemplo 2:configuración de valores a partir de literales de cadena

En el ejemplo anterior, la fecha y hora el valor se asignó estableciéndolo en el mismo valor que el datetimeoffset valor. Cuando hacemos eso, SQL Server realiza una conversión implícita para que los datos "encajen" en el nuevo tipo de datos.

Si tratamos de asignar el mismo valor directamente a la fecha y hora variable obtenemos un error:

DECLARE 
  @thedatetimeoffset datetimeoffset(7), 
  @thedatetime datetime;
SET @thedatetimeoffset = '2025-05-21 10:15:30.5555555 +07:30';
SET @thedatetime = '2025-05-21 10:15:30.5555555 +07:30';
SELECT 
  @thedatetimeoffset AS 'datetimeoffset',
  @thedatetime AS 'datetime';

Resultado:

Msg 241, Level 16, State 1, Line 5
Conversion failed when converting date and/or time from character string.

Esto se debe a que la fecha y hora el tipo de datos no admite un literal de cadena con un desplazamiento de zona horaria. Además, no admite literales de cadena con más de 3 decimales.

Entonces, si eliminamos el desplazamiento de la zona horaria, pero mantenemos todas las fracciones de segundo, aún obtendremos un error:

DECLARE 
  @thedatetimeoffset datetimeoffset(7), 
  @thedatetime datetime;
SET @thedatetimeoffset = '2025-05-21 10:15:30.5555555 +07:30';
SET @thedatetime = '2025-05-21 10:15:30.5555555';
SELECT 
  @thedatetimeoffset AS 'datetimeoffset',
  @thedatetime AS 'datetime';

Resultado:

Msg 241, Level 16, State 1, Line 5
Conversion failed when converting date and/or time from character string.

Para que funcione, debemos asignar un valor con no más de 3 decimales:

DECLARE 
  @thedatetimeoffset datetimeoffset(7), 
  @thedatetime datetime;
SET @thedatetimeoffset = '2025-05-21 10:15:30.5555555 +07:30';
SET @thedatetime = '2025-05-21 10:15:30.555';
SELECT 
  @thedatetimeoffset AS 'datetimeoffset',
  @thedatetime AS 'datetime';

Resultado:

+------------------------------------+-------------------------+
| datetimeoffset                     | datetime                |
|------------------------------------+-------------------------|
| 2025-05-21 10:15:30.5555555 +07:30 | 2025-05-21 10:15:30.557 |
+------------------------------------+-------------------------+

De cualquier manera, fecha y hora siempre tendrá un valor diferente a datetimeoffset , porque no incluye el desplazamiento de la zona horaria. Esto será cierto incluso si usamos la misma precisión de segundos fraccionarios y el mismo valor de segundos fraccionarios.

Para demostrar esto, esto es lo que sucede si asignamos el mismo valor a datetimeoffset :

DECLARE 
  @thedatetimeoffset datetimeoffset(3), 
  @thedatetime datetime;
SET @thedatetimeoffset = '2025-05-21 10:15:30.123';
SET @thedatetime = '2025-05-21 10:15:30.123';
SELECT 
  @thedatetimeoffset AS 'datetimeoffset',
  @thedatetime AS 'datetime';

Resultado:

+------------------------------------+-------------------------+
| datetimeoffset                     | datetime                |
|------------------------------------+-------------------------|
| 2025-05-21 10:15:30.1230000 +00:00 | 2025-05-21 10:15:30.123 |
+------------------------------------+-------------------------+

En este caso datetimeoffset usa una escala de 3, lo que le da 3 decimales (lo mismo que datetime ). Esto se hace usando datetimeoffset(3) al declarar la variable.

También cambié las fracciones de segundo para que datetime no los redondearía (por lo que ambos valores comparten exactamente la misma parte fraccionaria).

Independientemente, datetimeoffset aún agrega un desplazamiento de zona horaria, establecido en su valor predeterminado de +00:00.

Tenga en cuenta que mi sistema muestra ceros al final en datetimeoffset es parte fraccionaria, pero el valor solo usa 3 lugares decimales.

Ejemplo 3:tamaño de almacenamiento

La fechahora tipo de datos utiliza 8 bytes.

El desplazamiento de fecha y hora el tipo de datos usa 8, 9 o 10 bytes, dependiendo de su precisión.

Por lo tanto, no está ahorrando ningún tamaño de almacenamiento al usar datetime .

Sin embargo, si convierte un datetimeoffset valor a una constante binaria, agrega 1 byte para almacenar precisión.

Esto es lo que sucede si usamos DATALENGTH() función para devolver el número de bytes utilizados para cada uno de nuestros valores:

DECLARE 
  @thedatetimeoffset datetimeoffset(7), 
  @thedatetime datetime;
SET @thedatetimeoffset = '2025-05-21 10:15:30.5555555 +07:30';
SET @thedatetime = @thedatetimeoffset;
SELECT 
  DATALENGTH(@thedatetimeoffset) AS 'datetimeoffset',
  DATALENGTH(@thedatetime) AS 'datetime';

Resultado

+------------------+------------+
| datetimeoffset   | datetime   |
|------------------+------------|
| 10               | 8          |
+------------------+------------+

Como era de esperar, 10 bytes para datetimeoffset y 8 bytes para datetime .

Pero si los convertimos a varbinary , obtenemos lo siguiente:

DECLARE 
  @thedatetimeoffset datetimeoffset(7), 
  @thedatetime datetime;
SET @thedatetimeoffset = '2025-05-21 10:15:30.5555555 +07:30';
SET @thedatetime = @thedatetimeoffset;
SELECT 
  DATALENGTH(CAST(@thedatetimeoffset AS varbinary(16))) AS 'datetimeoffset',
  DATALENGTH(CAST(@thedatetime AS varbinary(16))) AS 'datetime';

Resultado

+------------------+------------+
| datetimeoffset   | datetime   |
|------------------+------------|
| 11               | 8          |
+------------------+------------+

Se agrega un byte adicional al datetimeoffset valor pero no a la fecha y hora valor. Esto se debe a que datetimeoffset El valor necesita un byte adicional para almacenar la precisión (porque la precisión está definida por el usuario). La fechahora El valor, por otro lado, tiene una precisión fija, por lo que no es necesario que la precisión se almacene con el valor.

Muchos desarrolladores asumen que convertir a varbinary es representativo de cómo SQL Server realmente almacena los valores de fecha y hora. Sin embargo, esto es sólo parcialmente cierto.

Si bien es cierto que SQL Server almacena sus valores de fecha y hora en hexadecimal, ese valor hexadecimal en realidad no incluye la precisión al almacenar datetimeoffset valores. Esto se debe a que la precisión está incluida en la definición de la columna.

Para obtener más detalles sobre cómo se almacena este tipo de datos en la base de datos, consulte Comprender el tamaño de almacenamiento 'datetimeoffset' en SQL Server.

¿Debería usar 'datetime' o 'datetimeoffset'?

Si necesita incluir una compensación de zona horaria, deberá usar datetimeoffset . Si no, entonces fecha y hora puede ser suficiente.

Sin embargo, Microsoft recomienda que utilice datetime2 para el nuevo trabajo, ya que tiene muchos beneficios sobre datetime .

Ver datetime vs datetime2 para una comparación de estos tipos de datos.