为什么GETUTCDATE早于SYSDATETIMEOFFSET?


8

另外,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

因此,日期时间精度允许.880作为有效值。

即使是微软的GETUTCDATE例子显示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  
*/

我认为这是因为它们来自不同的基础系统信息。谁能确认并提供详细信息?

微软的SYSDATETIMEOFFSET文档说“ SQL Server通过使用GetSystemTimeAsFileTime()Windows API获得日期和时间值”(感谢srutzky),但是他们的GETUTCDATE文档没有那么具体,只说“值是从Windows 的操作系统派生的”。运行SQL Server实例的计算机”。

(这不完全是学术性的。因此,我遇到了一个小问题。我正在升级一些过程以使用SYSDATETIMEOFFSET而不是GETUTCDATE,以期将来获得更高的精度,但是由于其他过程被使用,我开始变得奇怪。仍在使用GETUTCDATE,并偶尔在日志中“跳过”转换后的过程。)


1
除四舍五入或四舍五入外,我不知道有任何其他解释。当您必须将任何值四舍五入到较低的精度之一,并且在某些情况下您必须四舍五入,而在其他情况下则必须四舍五入(此处使用简单的数学规则)时,您可以期望在某些情况下四舍五入的值会低于或高于原始的更精确的值。如果要确保精度较低的值始终在精度较高的值之前(或之后),则可以始终使用DATEADD()将其操纵3毫秒。
亚伦·伯特兰

(此外,为什么不同时更改所有程序以使用更新的,更精确的值?)
亚伦·伯特兰

我认为这不是四舍五入的简单答案,因为如果将2017-06-07 12:01:58.8801139 -05:00的datetimeoffset值直接转换为datetime,它将选择2017-06-07 12:01 :58.880,而不是2017-06-07 12:01:58.877。因此,要么GETUTCDATE内部舍入不同,要么它们是不同的来源。
莱利少校

我更愿意逐步执行它们,以一次更改尽可能少的内容。scribnasium.com/2017/05/deploying-the-old-fashioned-way 我最终将分批处理它们或忍受日志中的不一致之处。
莱利少校

2
另外,我想补充一点,在计算机中总是可以进行时间旅行。您永远不要编写代码,以期望时间永远在前进。原因是漂移和系统时间校正,这主要是由Windows时间服务驱动的,也来自用户调整。实际上经常会遇到毫秒级的漂移和校正。
Remus Rusanu

Answers:


9

问题是数据类型粒度/准确性和值来源的结合。

首先,DATETIME仅每3毫秒精确/精确一次。因此,从更精确的数据类型(例如DATETIMEOFFSETDATETIME2将不舍入或舍入到最接近的毫秒)进行转换,可能相差2毫秒。

其次,文档似乎暗示了值的来源有所不同。SYS *函数使用高精度FileTime函数。

SYSDATETIMEOFFSET文档指出:

SQL Server通过使用GetSystemTimeAsFileTime()Windows API获取日期和时间值。

GETUTCDATE文档指出:

此值是从运行SQL Server实例的计算机的操作系统派生的。

然后,在“ 关于时间”文档中,图表显示了以下两种(几种)类型:

  • 系统时间=“年,月,日,小时,秒和毫秒,从内部硬件时钟获取。”
  • File Time =“自1601年1月1日以来的100纳秒间隔数。”

StopWatch该类的.NET文档中,还有其他线索(以粗斜体表示):

  • 秒表类

    秒表通过计算基础计时器机制中的计时器滴答来测量经过的时间。如果安装的硬件和操作系统支持高分辨率性能计数器,则Stopwatch类将使用该计数器来测量经过的时间。否则,秒表类将使用系统计时器来测量经过的时间。

  • Stopwatch.IsHighResolution字段

    Stopwatch类使用的计时器取决于系统硬件和操作系统。如果秒表计时器基于高分辨率性能计数器,则IsHighResolution为true。否则,IsHighResolution为false,表示秒表计时器基于系统计时器

因此,存在不同的时间“类型”,它们具有不同的精度和不同的来源。

但是,即使这是一个非常宽松的逻辑,测试两种类型的函数作为DATETIME值的来源也证明了这一点。对问题的查询的以下修改显示了此行为:

DECLARE @Offset DATETIMEOFFSET = SYSDATETIMEOFFSET(),
        @UTC2 DATETIME = SYSUTCDATETIME(),
        @UTC DATETIME = GETUTCDATE();
DECLARE @UTCFromOffset DATETIME = CONVERT(DATETIME, SWITCHOFFSET(@Offset, 0));
SELECT
    Offset = @Offset,
    UTC2 = @UTC2,
    UTC = @UTC,
    UTCFromOffset = @UTCFromOffset,
    TimeTravelPossible = CASE WHEN @UTC < @UTCFromOffset THEN 1 ELSE 0 END,
    TimeTravelPossible2 = CASE WHEN @UTC2 < @UTCFromOffset THEN 1 ELSE 0 END;

返回值:

Offset                 2017-06-07 17:50:49.6729691 -04:00
UTC2                   2017-06-07 21:50:49.673
UTC                    2017-06-07 21:50:49.670
UTCFromOffset          2017-06-07 21:50:49.673
TimeTravelPossible     1
TimeTravelPossible2    0

从上面的结果中可以看到,UTC和UTC2都是DATETIME数据类型。@UTC2通过进行设置,SYSUTCDATETIME()并在之后进行设置@Offset(也来自SYS *函数),但在之前进行@UTC设置GETUTCDATE()。然而,@UTC2似乎早已到来@UTC。偏移部分与此完全无关。

但是,公平地说,从严格意义上讲,这还不是证明。@MartinSmith跟踪了该GETUTCDATE()呼叫并找到以下内容:

在此处输入图片说明

我在此调用堆栈中看到了三个有趣的东西:

  1. Is从调用开始GetSystemTime(),返回的值仅精确到毫秒。
  2. 它使用DateFromParts(即没有公式进行转换,仅使用各个部分)。
  3. 底部有一个对QueryPerformanceCounter的调用。这是一种高分辨率差分计数器,可以用作“校正”手段。我已经看到了一些建议,可以使用一种类型来随着时间的推移调整另一种类型,因为长间隔会逐渐变得不同步(请参见如何在SO上的.NET / C#中获取刻度精度的时间戳)。呼叫时不会显示SYSDATETIMEOFFSET

关于不同类型的时间,不同的来源,漂移等,这里有很多很好的信息:获取高分辨率时间戳

确实,不适合比较不同精度的值。例如,如果您有2017-06-07 12:01:58.87700112017-06-07 12:01:58.877,那么您如何知道精度较低的值大于,小于或等于精度较高的值?比较它们是假设实际上不是那么精确2017-06-07 12:01:58.8770000,但是谁知道那是真的呢?实时可能是2017-06-07 12:01:58.87700052017-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=2763
Martin Smith

1
@MartinSmith(1/2)我已经花了一些时间在这个广告上,今天没有时间做更多的事情。我没有一种简单的方法来测试您提到的该博客中所引用和引用的C ++ Win API。我可以在.NET中在FileTime和DateTime之间来回转换,但是DateTime不是SystemTime,因为它可以处理完全的精度,但是它是无损的。我无法证明/拒绝该GetSystemTime调用GetSystemTimeAsFileTime ,但是我确实注意到了在问题注释中发布的调用堆栈中的有趣内容:1)它使用DateFromParts,因此很可能被截断为ms(续)
Solomon Rutzky

@MartinSmith(2/2)而不是四舍五入的(因为SystemTime仅下降到毫秒),并且2)底部调用了QueryPerformanceCounter。这是一种高分辨率差分计数器,可以用作“校正”的手段。我看到了一些建议,随着时间的推移,长时间间隔逐渐不同步,请使用一种类型来调整另一种类型。这里有很多很好的信息:获取高分辨率时间戳。如果它们确实同时启动,则损失是上述2个步骤之一。
所罗门·鲁兹基

我认为Offset是一个红色的鲱鱼,但没有创建更抽象的测试。我应该一次看到Microsoft文档显示出差异。感谢您确认。
莱利少校

我的挑战是我要执行多个proc。他们现在都使用GETUTCDATE。如果我将某些功能更改为任何SYS *功能(直接使用Offset或UTC),则它们会使用GET *功能与其他功能开始混乱。但我可以忍受它,也可以立即更改它们。没什么大不了的。只是激发了我的好奇心。
莱利少校
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.