В качестве альтернативы, как Microsoft сделала возможным путешествие во времени?
Рассмотрим этот код:
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
устанавливается раньше @UTC
, но иногда имеет более позднее значение. (Я пробовал это на SQL Server 2008 R2 и SQL Server 2016. Вам нужно запустить его несколько раз, чтобы перехватить подозрительные случаи.)
Это не просто вопрос округления или отсутствия точности. (На самом деле, я думаю, что округление - это то, что «исправляет» проблему время от времени.) Ниже приведены значения для примера:
- офсет
- 2017-06-07 12: 01: 58.8801139 -05: 00
- универсальное глобальное время
- 2017-06-07 17: 01: 58.877
- UTC от смещения:
- 2017-06-07 17: 01: 58.880
Таким образом, точность даты и времени допускает 0,880 в качестве допустимого значения.
Даже примеры GETUTCDATE от Microsoft показывают, что значения SYS * позже, чем у более старых методов, несмотря на то, что они были ВЫБРАНЫ ранее :
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 */
Я предполагаю, что это происходит потому, что они исходят из различной базовой системной информации. Кто-нибудь может подтвердить и предоставить подробности?
Документация Microsoft SYSDATETIMEOFFSET гласит: «SQL Server получает значения даты и времени с помощью Windows API GetSystemTimeAsFileTime ()» (спасибо srutzky), но их документация GETUTCDATE гораздо менее конкретна, говоря только о том, что «значение получено из операционной системы компьютер, на котором работает экземпляр SQL Server ".
(Это не совсем академично. Я столкнулся с незначительной проблемой, вызванной этим. Я обновлял некоторые процедуры, чтобы использовать SYSDATETIMEOFFSET вместо GETUTCDATE, в надежде на большую точность в будущем, но я начал получать странный порядок, потому что другие процедуры были все еще использую GETUTCDATE и иногда "забегаю вперед" из моих преобразованных процедур в журналах.)
источник
Ответы:
Проблема заключается в сочетании гранулярности / точности типов данных и источника значений.
Во-первых,
DATETIME
это только точное / гранулированное значение каждые 3 миллисекунды. Следовательно, преобразование из более точного типа данных , такие какDATETIMEOFFSET
илиDATETIME2
не будет просто круглым вверх или вниз до ближайшей миллисекунды, это может быть 2 миллисекунды разные.Во-вторых, документация подразумевает разницу в том, откуда берутся значения. Функции SYS * используют высокоточные функции FileTime.
Документация SYSDATETIMEOFFSET гласит:
в то время как документация GETUTCDATE гласит:
Затем в документации « О времени» диаграмма показывает следующие два (из нескольких) типов:
Дополнительные подсказки находятся в документации .NET для
StopWatch
класса (выделение выделено жирным курсивом):Класс секундомера
Секундомер.IsHighResolution Field
Следовательно, существуют разные «типы» времен, которые имеют как разную точность, так и разные источники.
Но, даже если это очень слабая логика, тестирование обоих типов функций в качестве источников
DATETIME
значения доказывает это. Следующая адаптация запроса из вопроса показывает это поведение:Возвращает:
Как видно из приведенных выше результатов, UTC и UTC2 являются
DATETIME
типами данных.@UTC2
устанавливается черезSYSUTCDATETIME()
и устанавливается после@Offset
(также берется из функции SYS *), но до@UTC
которого устанавливается черезGETUTCDATE()
. Тем не менее,@UTC2
кажется, пришел раньше@UTC
. ОФСЕТНАЯ часть этого абсолютно не связана ни с чем.ОДНАКО, чтобы быть справедливым, это все еще не доказательство в строгом смысле. @MartinSmith отследил
GETUTCDATE()
звонок и обнаружил следующее:Я вижу три интересные вещи в этом стеке вызовов:
GetSystemTime()
который возвращает значение с точностью до миллисекунд.SYSDATETIMEOFFSET
.Здесь много полезной информации о различных типах времени, различных источниках, дрейфе и т. Д.: Получение меток времени с высоким разрешением .
Действительно, неуместно сравнивать значения разной точности. Например, если у вас есть
2017-06-07 12:01:58.8770011
и2017-06-07 12:01:58.877
, то как узнать, что значение с меньшей точностью больше, меньше или равно значению с большей точностью? Сравнение их предполагает, что на самом деле менее точный2017-06-07 12:01:58.8770000
, но кто знает, правда это или нет? Реальное время могло быть или2017-06-07 12:01:58.8770005
или2017-06-07 12:01:58.8770111
.Однако, если у вас есть
DATETIME
типы данных, вам следует использовать функции SYS * в качестве источника, поскольку они более точны, даже если вы теряете некоторую точность, поскольку значение принудительно переводится в менее точный тип. И в этом отношении, кажется, имеет больше смысла использовать,SYSUTCDATETIME()
а не вызыватьSYSDATETIMEOFFSET()
только для настройки черезSWITCHOFFSET(@Offset, 0)
.источник
GETUTCDATE
кажется, звонитGetSystemTime
иSYSDATETIMEOFFSET
звонит,GetSystemTimeAsFileTime
хотя оба, по-видимому, сводятся к одному и тому же: blogs.msdn.microsoft.com/oldnewthing/20131101-00/?p=2763GetSystemTime
вызовыGetSystemTimeAsFileTime
, но я обратил внимание на интересные вещи в стеке вызовов, который вы разместили в комментарии к вопросу: 1) он использует DateFromParts, так что, скорее всего, он усечен до мс (продолжение)