2016-04-08 35 views
1

.NETのフロントエンドでは、時間のローカライズを処理するためにティックを使用しています。データベースには、すべての値がUTCで格納されています。今日、データベースにレコードを格納するときに奇妙な丸め誤差が発生しました.Tickをdatetimeに変換するために次の式を使用しています。SQLのbigintのダーティ丸め誤差

DECLARE @ticks bigint = 635953248000000000; -- 2016-04-04​ 00:00:00.000 
SELECT CAST((@Ticks - 599266080000000000)/10000000/24/60/60 AS datetime) 
-- Results in 2016-04-03 23:59:59.997 

質問は次のとおりです:この丸め誤差を引き起こしていると何がベストプラクティスになります私たちは、次の丸めエラーを発見するまで

CAST((@ticks - 599266080000000000)/10000000/24/60/60 AS datetime) 

は、我々が試したほとんどの時間値のために正常に動作するように見えましたそれを修正するには?

+0

実際の日付と時刻の代わりにダニを使用すると、タイムゾーンの問題を*導入する方法のように聞こえますが、解決できません。 SQL Serverには、任意のベースティックと想定されるオフセットとの変換を必要とせずに、値自体にタイムゾーンを指定できる 'datetimeoffset'タイプがあります –

+0

データベースのフロントエンドとdatetimeoffsetでDateTimeOffsetを使用しないのはなぜですか?それは仮定されたUTCオフセットでダニを使用するときにまだ残っているあいまいさを取り除きます。 –

答えて

1

sql_variantで計算をキャプチャする場合は、そのタイプを判断できます。あなたのケースでは、計算は正確なデータ型ではない数値型を使用し、丸めが発生している場所です。私はこのコードが見出されている

BaseType Precisions Scale TotalBytes Collation MaxLengths 
numeric  38   18  17   NULL  13 

:次の出力を生成し

DECLARE @myVar sql_variant = (@Ticks - 599266080000000000)/10000000/24/60/60 

SELECT SQL_VARIANT_PROPERTY(@myVar,'BaseType') BaseType, 
SQL_VARIANT_PROPERTY(@myVar,'Precision') Precisions, 
SQL_VARIANT_PROPERTY(@myVar,'Scale') Scale, 
SQL_VARIANT_PROPERTY(@myVar,'TotalBytes') TotalBytes, 
SQL_VARIANT_PROPERTY(@myVar,'Collation') Collation, 
SQL_VARIANT_PROPERTY(@myVar,'MaxLength') MaxLengths 

Extended.Net link

DECLARE @ticks bigint = 635953248000000000 

-- First, we will convert the ticks into a datetime value with UTC time 
DECLARE @BaseDate datetime; 
SET @BaseDate = '01/01/1900'; 

DECLARE @NetFxTicksFromBaseDate bigint; 
SET @NetFxTicksFromBaseDate = @Ticks - 599266080000000000; 
-- The numeric constant is the number of .Net Ticks between the System.DateTime.MinValue (01/01/0001) and the SQL Server datetime base date (01/01/1900) 

DECLARE @DaysFromBaseDate int; 
SET @DaysFromBaseDate = @NetFxTicksFromBaseDate/864000000000; 
-- The numeric constant is the number of .Net Ticks in a single day. 

DECLARE @TimeOfDayInTicks bigint; 
SET @TimeOfDayInTicks = @NetFxTicksFromBaseDate - @DaysFromBaseDate * 864000000000; 

DECLARE @TimeOfDayInMilliseconds int; 
SET @TimeOfDayInMilliseconds = @TimeOfDayInTicks/10000; 
-- A Tick equals to 100 nanoseconds which is 0.0001 milliseconds 

DECLARE @UtcDate datetime; 
SET @UtcDate = DATEADD(ms, @TimeOfDayInMilliseconds, DATEADD(d, @DaysFromBaseDate, @BaseDate)); 
-- The @UtcDate is already useful. If you need the time in UTC, just return this value. 

SELECT @UtcDate; 
+0

これは問題を解決し、なぜそれが最初に起こったのかという良い理由も与えます。私はsql_variantについて知りませんでした。これらのタイプの計算には素晴らしい機能があります。 – Bonnoj

0

私は、次の作品を見つけました:

DECLARE @ticks bigint = 635953248000000000; -- 2016-04-04​ 00:00:00.000 

SELECT DATEADD(s,(@ticks %CONVERT(BIGINT,864000000000))/CONVERT(BIGINT,10000000),DATEADD(d,@ticks /CONVERT(BIGINT,864000000000)-CONVERT(BIGINT,639905),CONVERT(DATETIME,'1/1/1753'))) 
-- Results in 2016-04-04​ 00:00:00.000 

Source

丸め誤差がが発信する場所私はあなたを伝えることができませんでした。

0

問題はdatetimeはマイルまでの値を格納するための精度を持っていないため、約来てからの作品llisecond:

datetime values are rounded to increments of .000, .003, or .007 seconds

Source

あなたの価値42462.000000000000000000として出てくる、私はC#でのDateTimeに変換する場合は2016年4月3日23として出てくる:59:59.995で0.997に丸めてしまいますSQL。

+0

データベースでdatetimeoffsetを使用し、アプリケーションでDateTimeOffsetを使用すると、問題が解決されます*と*ダニを取り除きます –