Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

datetime vs datetimeoffset in SQL Server:qual è la differenza?

Questo articolo evidenzia le principali differenze tra datetime e datetimeoffset tipi di dati in SQL Server.

Entrambi i tipi di dati vengono utilizzati per memorizzare i valori di data e ora. Ma ci sono differenze significative tra i due.

Forse la differenza più ovvia è che datetimeoffset memorizza l'offset del fuso orario, mentre datetime no.

Un'altra importante differenza è che datetimeoffset consente di specificare la precisione (fino a 7 cifre decimali). Ciò significa che datetimeoffset i valori possono variare nella loro dimensione di archiviazione, a seconda della precisione utilizzata.

La data e ora tipo d'altra parte, ha una dimensione e una precisione di archiviazione fisse.

In generale, dovresti evitare di utilizzare datetime a meno che tu non abbia una buona ragione per usarlo (come supportare un sistema legacy). Inoltre, il datetime2 type è una corrispondenza più stretta di datetimeoffset , quindi è meglio usarlo se non hai bisogno di un offset di fuso orario.

Ad ogni modo, ecco una tabella che confronta datetime e datetimeoffset :

Funzione datetimeoffset datetime
Conforme a SQL (ANSI e ISO 8601) No
Intervallo di date 0001-01-01 a 9999-12-31 01-01-1753 a 31-12-9999
Intervallo di tempo 00:00:00 fino alle 23:59:59.9999999 00:00:00 fino alle 23:59:59.997
Lunghezza del personaggio 26 posizioni minimo
34 massimo
19 posizioni minimo
23 massimo
Dimensioni di archiviazione Da 8 a 10 byte, a seconda della precisione*

* Più 1 byte per memorizzare la precisione in alcuni casi. Vedi sotto per maggiori informazioni.

8 byte
Precisione 100 nanosecondi Arrotondato a incrementi di .000, .003 o .007 secondi
Precisione al secondo frazionario definita dall'utente No
Intervallo di offset del fuso orario -14:00 a +14:00 Nessuno
Consapevolezza e conservazione della differenza di fuso orario No
Attenzione all'ora legale No No

Esempio 1 – Confronto di base

In ogni caso, ecco un rapido esempio per dimostrare la differenza fondamentale tra datetime e datetimeoffset .

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';

Risultato:

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

Qui, ho impostato una data e ora variabile allo stesso valore di datetimeoffset variabile. Questo fa sì che il valore venga convertito in datetime e possiamo quindi utilizzare un SELECT istruzione per vedere il valore di ciascuna variabile.

In questo caso, il datetimeoffset il valore include l'offset del fuso orario e 7 cifre decimali. La data e ora valore d'altra parte, non include l'offset del fuso orario e ha solo 3 cifre decimali. Inoltre, la sua terza cifra frazionaria viene arrotondata per eccesso. Questo perché la sua precisione è sempre arrotondata a incrementi di .000, .003 o .007 secondi.

Esempio 2 – Impostazione di valori da stringhe letterali

Nell'esempio precedente, datetime il valore è stato assegnato impostandolo sullo stesso valore di datetimeoffset valore. Quando lo facciamo, SQL Server esegue una conversione implicita in modo che i dati "si adattino" al nuovo tipo di dati.

Se proviamo ad assegnare lo stesso valore direttamente a datetime variabile otteniamo un errore:

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';

Risultato:

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

Questo perché il datetime il tipo di dati non supporta una stringa letterale con un offset di fuso orario. Inoltre, non supporta le stringhe letterali con più di 3 cifre decimali.

Quindi, se rimuoviamo l'offset del fuso orario, ma manteniamo tutti i secondi frazionari, riceveremo comunque un errore:

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';

Risultato:

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

Per farlo funzionare, dovremmo assegnare un valore con non più di 3 cifre decimali:

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';

Risultato:

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

In ogni caso, data e ora conterrà sempre un valore diverso da datetimeoffset , perché non include l'offset del fuso orario. Questo sarà vero anche se utilizziamo la stessa precisione dei secondi frazionari e il valore dei secondi frazionari.

Per dimostrarlo, ecco cosa succede se assegniamo lo stesso valore 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';

Risultato:

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

In questo caso datetimeoffset utilizza una scala di 3, che fornisce 3 cifre decimali (lo stesso di datetime ). Questo viene fatto utilizzando datetimeoffset(3) quando si dichiara la variabile.

Ho anche modificato i secondi frazionari in modo che datetime non li arrotonda (in modo che entrambi i valori condividano esattamente la stessa parte frazionaria).

Indipendentemente da ciò, datetimeoffset aggiunge ancora un fuso orario, impostato sul valore predefinito di +00:00.

Tieni presente che il mio sistema mostra zeri finali su datetimeoffset 's parte frazionaria, ma il valore utilizza solo 3 cifre decimali.

Esempio 3 – Dimensioni di archiviazione

La data e ora il tipo di dati utilizza 8 byte.

Il datatimeoffset il tipo di dati utilizza 8, 9 o 10 byte, a seconda della precisione.

Pertanto, non stai salvando alcuna dimensione di archiviazione utilizzando datetime .

Tuttavia, se converti un datetimeoffset valore a una costante binaria, aggiunge 1 byte per memorizzare la precisione.

Ecco cosa succede se utilizziamo DATALENGTH() funzione per restituire il numero di byte utilizzati per ciascuno dei nostri valori:

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';

Risultato

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

Come previsto, 10 byte per datetimeoffset e 8 byte per datetime .

Ma se li convertiamo in varbinary , otteniamo quanto segue:

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';

Risultato

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

Un byte aggiuntivo viene aggiunto a datetimeoffset valore ma non al datetime valore. Ciò è dovuto al datetimeoffset value richiede un byte aggiuntivo per memorizzare la precisione (perché la precisione è definita dall'utente). La data e ora value invece ha una precisione fissa, quindi non è necessario che la precisione venga memorizzata con il valore.

Molti sviluppatori presumono che la conversione in varbinary è rappresentativo del modo in cui SQL Server archivia effettivamente i valori di data e ora. Tuttavia questo è vero solo in parte.

Sebbene sia vero che SQL Server archivia i valori di data e ora in formato esadecimale, tale valore esadecimale in realtà non include la precisione durante l'archiviazione di datetimeoffset valori. Questo perché la precisione è inclusa nella definizione della colonna.

Per maggiori dettagli su come questo tipo di dati viene archiviato nel database, vedere Informazioni sulle dimensioni di archiviazione "datetimeoffset" in SQL Server.

Devo usare "datetime" o "datetimeoffset"?

Se devi includere una differenza di fuso orario, dovrai utilizzare datetimeoffset . In caso contrario, datetime può bastare.

Tuttavia, Microsoft consiglia di utilizzare datetime2 per un nuovo lavoro, poiché offre molti vantaggi rispetto a datetime .

Vedi datetime vs datetime2 per un confronto su questi tipi di dati.