或者,微软是如何让时间旅行成为可能的?
考虑这段代码:
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
设置为before @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 作为有效值。
即使是Microsoft 的 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 文档没有那么具体,只说“值来自操作系统运行 SQL Server 实例的计算机”。
(这并不完全是学术性的。我遇到了一个由此引起的小问题。我正在升级一些程序以使用 SYSDATETIMEOFFSET 而不是 GETUTCDATE,希望将来能获得更高的精度,但我开始得到奇怪的排序,因为其他程序是仍在使用 GETUTCDATE 并且偶尔在日志中“跳过”我转换的过程。)
问题是数据类型粒度/准确性和值来源的组合。
首先,
DATETIME
仅准确/精细到每 3 毫秒。因此,从更精确的数据类型转换,例如DATETIMEOFFSET
或DATETIME2
不只是向上或向下舍入到最接近的毫秒,它可能有 2 毫秒的不同。其次,文档似乎暗示了值的来源不同。SYS* 函数使用高精度 FileTime 函数。
SYSDATETIMEOFFSET文档状态:
而GETUTCDATE文档指出:
然后,在About Time文档中,图表显示了以下两种(几种)类型:
其他线索在
StopWatch
该类的 .NET 文档中(我的粗斜体强调):秒表类
Stopwatch.IsHighResolution 字段
因此,有不同“类型”的时间具有不同的精度和不同的来源。
但是,即使这是一个非常松散的逻辑,测试这两种类型的函数作为
DATETIME
值的来源也证明了这一点。以下对来自问题的查询的改编显示了这种行为:回报:
正如您在上面的结果中看到的,UTC 和 UTC2 都是
DATETIME
数据类型。@UTC2
通过设置SYSUTCDATETIME()
并在之后设置@Offset
(也取自 SYS* 函数),但在@UTC
此之前设置通过GETUTCDATE()
. 然而,@UTC2
似乎来之前@UTC
。这里的偏移部分与任何东西完全无关。然而,公平地说,这仍然不是严格意义上的证明。@MartinSmith 跟踪
GETUTCDATE()
通话并发现以下内容:我在这个调用堆栈中看到了三个有趣的东西:
GetSystemTime()
返回一个精确到毫秒的值。SYSDATETIMEOFFSET
。这里有很多关于不同类型时间、不同来源、漂移等的好信息:Acquiring high-resolution time stamps。
确实,比较不同精度的值是不合适的。例如,如果你有
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)
。