Alternativamente, ¿cómo hizo Microsoft posible viajar en el tiempo?
Considera este código:
DECLARE @Offset datetimeoffset = sysdatetimeoffset();
DECLARE @UTC datetime = getUTCdate();
DECLARE @UTCFromOffset datetime = CONVERT(datetime,SWITCHOFFSET(@Offset,0));
SELECT
Offset = @Offset,
UTC = @UTC,
UTCFromOffset = @UTCFromOffset,
TimeTravelPossible = CASE WHEN @UTC < @UTCFromOffset THEN 1 ELSE 0 END;
@Offset
se establece antes @UTC
, pero a veces tiene un valor posterior . (He intentado esto en SQL Server 2008 R2 y SQL Server 2016. Debe ejecutarlo varias veces para detectar los casos sospechosos).
Esto no parece ser simplemente una cuestión de redondeo o falta de precisión. (De hecho, creo que el redondeo es lo que "soluciona" el problema ocasionalmente). Los valores para una ejecución de muestra son los siguientes:
- Compensar
- 2017-06-07 12: 01: 58.8801139 -05: 00
- UTC
- 2017-06-07 17: 01: 58.877
- UTC desde compensación:
- 2017-06-07 17: 01: 58.880
Entonces, la precisión de fecha y hora permite que el .880 sea un valor válido.
Incluso los ejemplos GETUTCDATE de Microsoft muestran que los valores SYS * son posteriores a los métodos anteriores, a pesar de haber sido SELECCIONADOS anteriormente :
SELECT 'SYSDATETIME() ', SYSDATETIME(); SELECT 'SYSDATETIMEOFFSET()', SYSDATETIMEOFFSET(); SELECT 'SYSUTCDATETIME() ', SYSUTCDATETIME(); SELECT 'CURRENT_TIMESTAMP ', CURRENT_TIMESTAMP; SELECT 'GETDATE() ', GETDATE(); SELECT 'GETUTCDATE() ', GETUTCDATE(); /* Returned: SYSDATETIME() 2007-05-03 18:34:11.9351421 SYSDATETIMEOFFSET() 2007-05-03 18:34:11.9351421 -07:00 SYSUTCDATETIME() 2007-05-04 01:34:11.9351421 CURRENT_TIMESTAMP 2007-05-03 18:34:11.933 GETDATE() 2007-05-03 18:34:11.933 GETUTCDATE() 2007-05-04 01:34:11.933 */
Supongo que esto se debe a que provienen de información del sistema subyacente diferente. ¿Alguien puede confirmar y proporcionar detalles?
La documentación SYSDATETIMEOFFSET de Microsoft dice que "SQL Server obtiene los valores de fecha y hora utilizando la API de Windows GetSystemTimeAsFileTime ()" (gracias srutzky), pero su documentación GETUTCDATE es mucho menos específica, diciendo solo que el "valor se deriva del sistema operativo del equipo en el que se ejecuta la instancia de SQL Server ".
(Esto no es completamente académico. Me encontré con un problema menor causado por esto. Estaba actualizando algunos procedimientos para usar SYSDATETIMEOFFSET en lugar de GETUTCDATE, con la esperanza de una mayor precisión en el futuro, pero comencé a obtener pedidos extraños porque otros procedimientos eran sigo usando GETUTCDATE y ocasionalmente "saltando" de mis procedimientos convertidos en los registros).
fuente
Respuestas:
El problema es una combinación de granularidad / precisión de tipo de datos y fuente de los valores.
Primero,
DATETIME
solo es preciso / granular cada 3 milisegundos. Por lo tanto, la conversión de un tipo de datos más precisos tales comoDATETIMEOFFSET
oDATETIME2
no a la vuelta de arriba o hacia abajo al milisegundo más cercano, que podría ser de 2 milisegundos diferente.Segundo, la documentación parece implicar una diferencia en el origen de los valores. Las funciones SYS * utilizan las funciones FileTime de alta precisión.
La documentación de SYSDATETIMEOFFSET establece:
mientras que la documentación de GETUTCDATE establece:
Luego, en la documentación Acerca del tiempo , un gráfico muestra los siguientes dos (de varios) tipos:
Hay pistas adicionales en la documentación de .NET para la
StopWatch
clase (énfasis en negrita cursiva mía):Clase de cronómetro
Stopwatch.IsHighResolution Field
Por lo tanto, hay diferentes "tipos" de veces que tienen tanto precisiones diferentes como diferentes fuentes.
Pero, incluso si esa es una lógica muy flexible, probar ambos tipos de funciones como fuentes para el
DATETIME
valor lo demuestra. La siguiente adaptación de la consulta de la pregunta muestra este comportamiento:Devoluciones:
Como puede ver en los resultados anteriores, UTC y UTC2 son ambos
DATETIME
tipos de datos.@UTC2
se establece a través deSYSUTCDATETIME()
y se establece después@Offset
(también tomado de una función SYS *), pero antes de lo@UTC
cual se establece a través deGETUTCDATE()
. Sin embargo,@UTC2
parece venir antes@UTC
. La parte OFFSET de esto no tiene nada que ver con nada.SIN EMBARGO, para ser justos, esto todavía no es una prueba en sentido estricto. @MartinSmith rastreó la
GETUTCDATE()
llamada y encontró lo siguiente:Veo tres cosas interesantes en esta pila de llamadas:
GetSystemTime()
que devuelve un valor que solo es preciso hasta los milisegundos.SYSDATETIMEOFFSET
.Aquí hay mucha buena información sobre los diferentes tipos de tiempo, diferentes fuentes, deriva, etc.: Adquisición de marcas de tiempo de alta resolución .
Realmente, no es apropiado comparar valores de diferentes precisiones. Por ejemplo, si tiene
2017-06-07 12:01:58.8770011
y2017-06-07 12:01:58.877
, entonces, ¿cómo sabe que el que tiene menos precisión es mayor, menor o igual que el valor con mayor precisión? Compararlos supone que el menos preciso es en realidad2017-06-07 12:01:58.8770000
, pero ¿quién sabe si eso es cierto o no? El tiempo real podría haber sido2017-06-07 12:01:58.8770005
o2017-06-07 12:01:58.8770111
.Sin embargo, si tiene
DATETIME
tipos de datos, debe usar las funciones SYS * como fuente, ya que son más precisas, incluso si pierde algo de precisión, ya que el valor se fuerza a un tipo menos preciso. Y en ese sentido, parece tener más sentido usar enSYSUTCDATETIME()
lugar de llamarSYSDATETIMEOFFSET()
solo para ajustarlo a través deSWITCHOFFSET(@Offset, 0)
.fuente
GETUTCDATE
parece llamarGetSystemTime
ySYSDATETIMEOFFSET
llamar,GetSystemTimeAsFileTime
aunque aparentemente ambos se reducen a lo mismo blogs.msdn.microsoft.com/oldnewthing/20131101-00/?p=2763GetSystemTime
llamadasGetSystemTimeAsFileTime
, pero noté cosas interesantes en la pila de llamadas que publicaste en el comentario de la pregunta: 1) usa DateFromParts, por lo que probablemente se truncó en ms (cont)