GETUTCDATEがSYSDATETIMEOFFSETよりも早いのはなぜですか?


8

あるいは、マイクロソフトはどのようにしてタイムトラベルを可能にしたのですか?

このコードを考えてみましょう:

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

これは、根本的なシステム情報が異なるためだと思います。誰かが詳細を確認して提供できますか?

MicrosoftのSYSDATETIMEOFFSETのドキュメントは、(感謝srutzky)「SQL ServerがGetSystemTimeAsFileTime()は、Windows APIを使用して、日付と時刻の値を取得」と言うが、そのGETUTCDATEのドキュメントは、「値がのオペレーティングシステムに由来していることだけ言って、あまり具体的ですSQL Serverのインスタンスが実行されているコンピューター」。

(これは完全に学術的なものではありません。これによって引き起こされた小さな問題に遭遇しました。将来の精度向上を期待して、GETUTCDATEの代わりにSYSDATETIMEOFFSETを使用するようにいくつかのプロシージャをアップグレードしましたが、他のプロシージャがまだ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 2017年

回答:


9

問題は、データ型の粒度/精度と値のソースの組み合わせです。

1つ目は、DATETIME3ミリ秒ごとにのみ正確/粒度です。したがって、例えば、より正確なデータ型からの変換DATETIMEOFFSETまたはDATETIME2最寄りミリ秒にしないであろうだけラウンドアップまたはダウン、それが異なる2ミリ秒とすることができます。

第二に、ドキュメントは値がどこから来たのか違いを示唆しているようです。SYS *関数は、高精度のFileTime関数を使用します。

SYSDATETIMEOFFSETドキュメントには次のように記載されています。

SQL Serverは、GetSystemTimeAsFileTime()Windows APIを使用して日付と時刻の値を取得します。

GETUTCDATEのドキュメントには次のように記載されています。

この値は、SQL Serverのインスタンスが実行されているコンピューターのオペレーティングシステムから取得されます。

次に、About Timeのドキュメントでは、グラフに次の2つ(いくつか)のタイプが表示されます。

  • システム時刻= "内部ハードウェアクロックから取得した年、月、日、時、秒、ミリ秒。"
  • ファイル時間= "1601年1月1日からの100ナノ秒間隔の数。"

追加の手がかりは、StopWatchクラスの.NETドキュメントにあります(太字の斜体で強調):

  • ストップウォッチクラス

    ストップウォッチは、基になるタイマーメカニズムのタイマーティックをカウントすることによって経過時間を測定します。インストールされているハードウェアとオペレーティングシステムが高解像度のパフォーマンスカウンターをサポートしている場合、Stopwatchクラスはそのカウンターを使用して経過時間を測定します。それ以外の場合、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()呼び出しをトレースしたところ、次のことがわかりました。

ここに画像の説明を入力してください

このコールスタックには3つの興味深いものがあります。

  1. IsはGetSystemTime()、ミリ秒まで正確な値を返す呼び出しで始まります。
  2. これはDateFromPartsを使用します(つまり、変換する数式はなく、個々のピースを使用します)。
  3. 下の方にQueryPerformanceCounterへの呼び出しがあります。これは、「修正」の手段として使用できる高解像度の差分カウンターです。長い間隔がゆっくりと同期しなくなると、一方のタイプを使用してもう一方のタイプを徐々に調整するという提案がいくつかあります(SOの.NET / C#でティック精度のタイムスタンプを取得する方法を参照)。これはを呼び出しても表示されませんSYSDATETIMEOFFSET

さまざまなタイプの時間、さまざまなソース、ドリフトなどに関して、ここには多くの優れた情報があります。高解像度のタイムスタンプの取得

実際には、異なる精度の値を比較することは適切ではありません。あなたが持っている場合たとえば、2017-06-07 12:01:58.8770011そして2017-06-07 12:01:58.877、その後、どのように少ない精度の1未満、より大きいことを知っています、またはそれ以上の精度で値に等しいですか?それらを比較することは、あまり正確でない方が実際2017-06-07 12:01:58.8770000にはであると仮定しますが、それが本当かどうかは誰が知っていますか?リアルタイムはまたはのいずれ2017-06-07 12:01:58.87700052017-06-07 12:01:58.8770111でした。

ただし、DATETIMEデータ型がある場合は、SYS *関数の方がソースとして使用する必要があります。SYS*関数の方が精度が高いため、値の精度が低くなり、精度が低下した場合でも使用できます。そしてそれらの線に沿って、それを介してそれを調整するためだけにSYSUTCDATETIME()呼び出すのSYSDATETIMEOFFSET()ではなく、使用する方が理にかなっているようSWITCHOFFSET(@Offset, 0)です。


私のシステムでGETUTCDATE呼び出しているようだGetSystemTimeSYSDATETIMEOFFSETの通話GetSystemTimeAsFileTimeの両方が明らかに同じものを煮詰めるのにblogs.msdn.microsoft.com/oldnewthing/20131101-00/?p=2763
マーティン・スミス

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つのステップのうちの1つによるものです。
ソロモンルツキー2017年

私はオフセットが赤いニシンだと思ったが、より抽象的なテストを作成しなかった。Microsoftのドキュメントに不一致が見られたら、私はそれを確認する必要があります。確認していただきありがとうございます。
ライリー少佐

私の課題は、これを行う複数のプロシージャがあることです。現在、すべてGETUTCDATEを使用しています。一部をSYS *関数(OffsetまたはUTCを直接使用)に変更すると、GET *関数を使用して他の関数と順序が乱れ始めます。しかし、私はそれと一緒に暮らすことも、すべてを一度に変更することもできます。大したことではない。それはちょうど私の好奇心を刺激しました。
ライリー少佐
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.