Tại sao GETUTCDATE sớm hơn SYSDATETIMEOFFSET?


8

Ngoài ra, làm thế nào Microsoft làm cho du hành thời gian có thể?

Xem xét mã này:

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được đặt trước @UTC , nhưng đôi khi nó có giá trị sau . (Tôi đã thử điều này trên SQL Server 2008 R2 và SQL Server 2016. Bạn phải chạy nó một vài lần để bắt gặp sự cố nghi ngờ.)

Điều này dường như không chỉ đơn giản là vấn đề làm tròn hoặc thiếu độ chính xác. (Trên thực tế, tôi nghĩ rằng việc làm tròn là điều thỉnh thoảng "khắc phục" sự cố.) Các giá trị cho một lần chạy mẫu như sau:

  • Bù lại
    • 2017-06-07 12: 01: 58,8801139 -05: 00
  • UTC
    • 2017-06-07 17: 01: 58.877
  • UTC từ Offset:
    • 2017-06-07 17: 01: 58.880

Vì vậy, độ chính xác của datetime cho phép .880 là một giá trị hợp lệ.

Ngay cả các ví dụ GETUTCDATE của Microsoft cũng cho thấy các giá trị SYS * muộn hơn các phương thức cũ hơn, mặc dù đã được chọn trước đó :

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  
*/

Tôi đoán điều này là do chúng đến từ các thông tin hệ thống cơ bản khác nhau. Bất cứ ai có thể xác nhận và cung cấp chi tiết?

Tài liệu SYSDATETIMEOFFSET của Microsoft cho biết "SQL Server có được các giá trị ngày và thời gian bằng cách sử dụng API Windows GetSystemTimeAsFileTime ()" (cảm ơn srutzky), nhưng tài liệu GETUTCDATE của họ ít cụ thể hơn, chỉ nói rằng "giá trị được lấy từ hệ điều hành của máy tính mà phiên bản SQL Server đang chạy ".

(Điều này không hoàn toàn mang tính học thuật. Tôi gặp phải một vấn đề nhỏ do nguyên nhân này. Tôi đã nâng cấp một số quy trình để sử dụng SYSDATETIMEOFFSET thay vì GETUTCDATE, với hy vọng có độ chính xác cao hơn trong tương lai, nhưng tôi bắt đầu nhận được đơn đặt hàng kỳ lạ vì các quy trình khác là vẫn đang sử dụng GETUTCDATE và đôi khi "nhảy về phía trước" các quy trình được chuyển đổi của tôi trong nhật ký.)


1
Tôi không biết rằng có bất kỳ lời giải thích nào khác ngoài việc làm tròn hoặc làm tròn lên. Khi bạn phải làm tròn bất kỳ giá trị nào với độ chính xác thấp hơn và trong một số trường hợp bạn phải làm tròn xuống và các giá trị khác bạn phải làm tròn (quy tắc toán học đơn giản khi chơi ở đây), bạn có thể mong đợi rằng trong một số trường hợp, giá trị được làm tròn sẽ thấp hơn hoặc cao hơn giá trị ban đầu, chính xác hơn. Nếu bạn muốn đảm bảo rằng giá trị ít chính xác hơn luôn luôn ở trước (hoặc luôn luôn sau) chính xác hơn, bạn luôn có thể thao tác nó trong 3 mili giây bằng DATEADD ().
Aaron Bertrand

(Ngoài ra, tại sao không thay đổi TẤT CẢ các thủ tục cùng một lúc để sử dụng giá trị mới hơn, chính xác hơn?)
Aaron Bertrand

Tôi không nghĩ rằng nó có thể là một câu trả lời đơn giản như làm tròn bởi vì nếu bạn chuyển đổi giá trị datetimeoffset của 2017-06-07 12: 01: 58.8801139 -05: 00 trực tiếp sang datetime, nó sẽ chọn 2017-06-07 12:01 : 58.880, không phải 2017-06-07 12: 01: 58.877. Vì vậy, GETUTCDATE nội bộ làm tròn khác nhau hoặc chúng là các nguồn khác nhau.
Riley Major

Tôi muốn làm chúng dần dần để thay đổi ít nhất có thể tại một thời điểm. scribnasium.com/2017/05/deploying-the-old-f Fashioned -way Cuối cùng tôi sẽ thực hiện chúng trong một đợt hoặc sống với sự không nhất quán trong nhật ký.
Riley Major

2
Ngoài ra, tôi muốn thêm thời gian du hành luôn có thể có trong máy tính . Bạn không bao giờ nên viết mã mong đợi rằng thời gian sẽ luôn luôn tiếp tục. Lý do là trôi dạt và điều chỉnh thời gian hệ thống, chủ yếu được điều khiển bởi Dịch vụ thời gian Windows , nhưng cũng do điều chỉnh của người dùng. Milliseconds trôi và điều chỉnh thực sự rất thường gặp phải.
Remus Rusanu

Câu trả lời:


9

Vấn đề là sự kết hợp giữa độ chi tiết / độ chính xác của kiểu dữ liệu và nguồn của các giá trị.

Đầu tiên, DATETIMEchỉ chính xác / chi tiết cho mỗi 3 mili giây. Do đó, chuyển đổi từ một kiểu dữ liệu chính xác hơn như DATETIMEOFFSEThoặc DATETIME2sẽ không làm tròn lên hoặc xuống đến mili giây gần nhất, nó có thể khác nhau 2 mili giây.

Thứ hai, tài liệu dường như ngụ ý một sự khác biệt trong đó các giá trị đến từ đâu. Các hàm SYS * sử dụng các hàm FileTime có độ chính xác cao.

Tài liệu của SYSDATETIMEOFFSET nêu rõ:

SQL Server có được các giá trị ngày và thời gian bằng cách sử dụng API Windows GetSystemTimeAsFileTime ().

trong khi tài liệu GETUTCDATE nêu rõ:

Giá trị này được lấy từ hệ điều hành của máy tính mà phiên bản SQL Server đang chạy.

Sau đó, trong tài liệu Giới thiệu về Thời gian , biểu đồ hiển thị hai loại (trong số một số) sau:

  • Thời gian hệ thống = "Năm, tháng, ngày, giờ, giây và mili giây, được lấy từ đồng hồ phần cứng bên trong."
  • Thời gian tệp = "Số lượng khoảng thời gian 100 nano giây kể từ ngày 1 tháng 1 năm 1601."

Các manh mối bổ sung có trong tài liệu .NET cho StopWatchlớp (nhấn mạnh vào chữ in nghiêng in đậm):

  • Lớp đồng hồ bấm giờ

    Đồng hồ bấm giờ đo thời gian đã trôi qua bằng cách đếm các đồng hồ bấm giờ trong cơ chế hẹn giờ bên dưới. Nếu phần cứng và hệ điều hành được cài đặt hỗ trợ bộ đếm hiệu suất có độ phân giải cao, thì lớp Đồng hồ bấm giờ sử dụng bộ đếm đó để đo thời gian đã trôi qua. Mặt khác, lớp Đồng hồ bấm giờ sử dụng bộ đếm thời gian hệ thống để đo thời gian đã trôi qua.

  • Đồng hồ bấm giờ.IsHighResolution

    Đồng hồ bấm giờ được sử dụng bởi lớp Đồng hồ bấm giờ phụ thuộc vào phần cứng hệ điều hành và hệ điều hành. IsHighResolution là đúng nếu đồng hồ bấm giờ dựa trên bộ đếm hiệu suất độ phân giải cao. Mặt khác, IsHighResolution là sai , điều này cho biết rằng đồng hồ bấm giờ Đồng hồ bấm giờ dựa trên bộ hẹn giờ hệ thống .

Do đó, có nhiều "loại" thời gian khác nhau có cả các quy tắc khác nhau và các nguồn khác nhau.

Nhưng, ngay cả khi đó là một logic rất lỏng lẻo, việc kiểm tra cả hai loại hàm làm nguồn cho DATETIMEgiá trị đã chứng minh điều đó. Các điều chỉnh sau đây của truy vấn từ câu hỏi cho thấy hành vi này:

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;

Trả về:

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

Như bạn có thể thấy trong các kết quả trên, UTC và UTC2 đều là DATETIMEkiểu dữ liệu. @UTC2được đặt qua SYSUTCDATETIME()và được đặt sau @Offset(cũng được lấy từ hàm SYS *), nhưng trước @UTCđó được đặt qua GETUTCDATE(). Tuy nhiên, @UTC2dường như đến trước @UTC. Phần OFFSET này hoàn toàn không liên quan đến bất cứ điều gì.

TUY NHIÊN, công bằng mà nói, đây vẫn không phải là bằng chứng theo nghĩa chặt chẽ. @MartinSmith theo dõi GETUTCDATE()cuộc gọi và tìm thấy như sau:

nhập mô tả hình ảnh ở đây

Tôi thấy ba điều thú vị trong ngăn xếp cuộc gọi này:

  1. Được bắt đầu bằng một cuộc gọi GetSystemTime()trả về một giá trị chỉ chính xác đến từng mili giây.
  2. Nó sử dụng DateFromParts (tức là không có công thức để chuyển đổi, chỉ sử dụng các phần riêng lẻ).
  3. Có một cuộc gọi đến QueryPerformanceCorer về phía dưới. Đây là một bộ đếm chênh lệch độ phân giải cao có thể được sử dụng như một phương tiện "hiệu chỉnh". Tôi đã thấy một số gợi ý về việc sử dụng một loại để điều chỉnh loại kia theo thời gian khi các khoảng thời gian dài dần dần không đồng bộ hóa (xem Cách lấy dấu thời gian của độ chính xác đánh dấu trong .NET / C #? Trên SO). Điều này không hiển thị khi gọi SYSDATETIMEOFFSET.

Có nhiều thông tin tốt ở đây liên quan đến các loại thời gian khác nhau, các nguồn khác nhau, trôi dạt, v.v .: Nhận được tem thời gian có độ phân giải cao .

Thực sự, không phù hợp để so sánh các giá trị của các khu vực khác nhau. Ví dụ: nếu bạn có 2017-06-07 12:01:58.87700112017-06-07 12:01:58.877, thì làm sao bạn biết rằng cái có độ chính xác thấp hơn lớn hơn, nhỏ hơn hoặc bằng giá trị với độ chính xác cao hơn? So sánh họ giả định rằng thực tế ít chính xác hơn 2017-06-07 12:01:58.8770000, nhưng ai biết liệu điều đó có đúng hay không? Thời gian thực có thể là một trong hai 2017-06-07 12:01:58.8770005hoặc 2017-06-07 12:01:58.8770111.

Tuy nhiên, nếu bạn có DATETIMEkiểu dữ liệu, thì bạn nên sử dụng các hàm SYS * làm nguồn vì chúng chính xác hơn, ngay cả khi bạn mất một số độ chính xác vì giá trị được buộc thành một loại ít chính xác hơn. Và dọc theo những dòng đó, nó dường như có ý nghĩa hơn để sử dụng SYSUTCDATETIME()thay vì SYSDATETIMEOFFSET()chỉ gọi để điều chỉnh nó thông qua SWITCHOFFSET(@Offset, 0).


Trên hệ thống của tôi GETUTCDATEdường như gọi GetSystemTimeSYSDATETIMEOFFSETgọi GetSystemTimeAsFileTimemặc dù cả hai dường như sôi sục với cùng một blog.msdn.microsoft.com/oldnewthing/20131101-00/?p=2763
Martin Smith

1
@MartinSmith (1/2) Tôi đã dành một chút thời gian cho quảng cáo này đã hết thời gian hôm nay để làm thêm nữa. Tôi không có cách dễ dàng để kiểm tra API C ++ Win đang được gọi và được tham chiếu trong blog mà bạn đã đề cập. Tôi có thể chuyển đổi qua lại giữa FileTime và DateTime trong .NET, nhưng DateTime không phải là SystemTime vì nó có thể xử lý chính xác hoàn toàn, nhưng nó đã bị thất bại. Tôi không thể chứng minh / từ chối GetSystemTimecác cuộc gọi đó GetSystemTimeAsFileTime , nhưng tôi đã chú ý đến những điều thú vị trong ngăn xếp cuộc gọi mà bạn đã đăng trong bình luận câu hỏi: 1) nó sử dụng DateFromParts nên rất có thể bị cắt thành ms (tiếp)
Solomon Rutzky

@MartinSmith (2/2) thay vì làm tròn (vì SystemTime chỉ giảm xuống còn mili giây) và 2) có một lệnh gọi QueryPerformanceCorer về phía dưới. Đây là một bộ đếm chênh lệch độ phân giải cao thể được sử dụng như một phương tiện "hiệu chỉnh". Tôi đã thấy một số gợi ý về việc sử dụng một loại để điều chỉnh loại kia theo thời gian khi các khoảng thời gian dài dần dần không đồng bộ. Có nhiều thông tin tốt ở đây: Có được tem thời gian độ phân giải cao . Nếu họ bắt đầu cùng một lúc, mất mát là từ 1 trong 2 bước trên.
Solomon Rutzky

Tôi cho rằng Offset là cá trích đỏ nhưng không tạo ra một thử nghiệm trừu tượng hơn. Tôi nên có một khi tôi thấy các tài liệu của Microsoft cho thấy sự khác biệt. Cảm ơn đã xác nhận điều đó.
Riley Major

Thách thức của tôi là tôi có nhiều procs làm điều này. Tất cả đều sử dụng GETUTCDATE ngay bây giờ. Nếu tôi thay đổi một số thành bất kỳ chức năng SYS * nào (với Offset hoặc UTC trực tiếp), chúng sẽ bắt đầu mất trật tự với các chức năng khác bằng cách sử dụng các hàm GET *. Nhưng tôi có thể sống với nó hoặc thay đổi tất cả chúng cùng một lúc. Không phải là một thỏa thuận lớn. Nó chỉ khơi dậy sự tò mò của tôi.
Riley Major
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.