4

T-SQL SELECT ステートメントで SQL Server DATETIMEを FILETIMEに変換する必要があります(SQL Server 2000 上)。これを行う組み込み関数はありますか?そうでない場合、誰かがこの変換ルーチンを UDF (または単純な Transact-SQL) として実装する方法を理解するのを手伝ってくれますか? これが私が知っていることです:

  1. FILETIME は、1601 年 1 月 1 日 (UTC) からの 100 ナノ秒間隔の数を表す 64 ビット値です ( MSDN によると: FILETIME Structure )。
  2. SQL Server の基本時間は 1900-01-01 00:00:00 から始まります (SELECT CAST (0 as DATETIME) による)。

FILETIME 値を T-SQL DATETIME に変換する方法を示すいくつかの例を見つけました (ただし、それらが正確であると 100% 確信しているわけではありません) が、逆変換については何も見つかりませんでした。一般的なアイデア(またはアルゴリズム)でさえ役立ちます。

4

3 に答える 3

4

さて、私はこれを自分で実装できたと思います。関数は次のとおりです。

IF EXISTS 
(
    SELECT 1
    FROM   sysobjects 
    WHERE  id   = OBJECT_ID('[dbo].[fnDateTimeToFileTime]')
      AND  type = 'FN'
)
BEGIN
    DROP FUNCTION [dbo].[fnDateTimeToFileTime]
END
GO

-- Create function.
CREATE FUNCTION [dbo].[fnDateTimeToFileTime]
(
    @DateTime AS DATETIME
)
RETURNS
    BIGINT
BEGIN

IF @DateTime IS NULL
    RETURN NULL

DECLARE @MsecBetween1601And1970 BIGINT
DECLARE @MsecBetween1970AndDate BIGINT

SET @MsecBetween1601And1970 = 11644473600000

SET @MsecBetween1970AndDate = 
    DATEDIFF(ss, CAST('1970-01-01 00:00:00' as DATETIME), @DateTime) * 
        CAST(1000 AS BIGINT)

RETURN (@MsecBetween1601And1970 + @MsecBetween1970AndDate) * CAST(10000 AS BIGINT)  
END
GO

IF @@ERROR = 0
    GRANT EXECUTE ON [dbo].[fnDateTimeToFileTime] TO Public 
GO

1 秒までは正確なようですが、これで問題ありません (データがオーバーフローしたため、これ以上正確にすることはできませんでした)。TimeAndDate Web ツールを使用して、日付間の期間を計算しました。

どう思いますか?

于 2010-06-01T18:03:06.210 に答える
2

2 SQL Server の時代は 1900-01-01 00:00:00 に始まります (SELECT CAST(0 as DATETIME) による)。

いいえ、それは基準日です。datetime は 1753 から始まります

これを実行

select cast('17800122' as datetime) 

出力

1780-01-22 00:00:00.000

しかし、これはまだ filetime よりも短いので、それを追加する必要があります...ただし、グレゴリオ暦とユリウス暦を覚えておいてください (datetime が 1753 から始まる理由でもあります)。

于 2010-05-27T19:27:39.803 に答える
1

受け入れられた回答はうまく機能しますが、2038 年 1 月 19 日より後の日付でクラッシュします。SQL Server 2016 以降を使用している場合は、DATEDIFF の代わりに DATEDIFF_BIG を使用するか、次の修正を使用します。

CREATE FUNCTION [dbo].[fnDateTimeToFileTime]
(
    @DateTime AS DATETIME
)
RETURNS
    BIGINT
BEGIN

IF @DateTime IS NULL
    RETURN NULL

DECLARE @MsecBetween1601And1970 BIGINT
DECLARE @MsecBetween1970AndDate BIGINT

DECLARE @MaxNumberDayBeforeOverflowDateDiff int;
SET @MaxNumberDayBeforeOverflowDateDiff  = 24855; --SELECT DATEDIFF(day, CAST('1970-01-01 00:00:00' as DATETIME), CAST('2038-01-19 00:00:00' as DATETIME))

DECLARE @nbMaxDaysBetween1970AndDate int;
SET @nbMaxDaysBetween1970AndDate = DATEDIFF(day, CAST('1970-01-01 00:00:00' as DATETIME), @DateTime) / @MaxNumberDayBeforeOverflowDateDiff;

DECLARE @moduloResteDay int
SET @moduloResteDay = DATEDIFF(day, CAST('1970-01-01 00:00:00' as DATETIME), @DateTime) % @MaxNumberDayBeforeOverflowDateDiff;

DECLARE @nbSecondBefore19700101And20380119 bigint = 2147472000;
SET @MsecBetween1601And1970 = 11644473600000;

DECLARE @DateTimeModulo datetime;
SET @DateTimeModulo = DATEADD(day, -@nbMaxDaysBetween1970AndDate * @MaxNumberDayBeforeOverflowDateDiff, @DateTime)


SET @MsecBetween1970AndDate = CAST(CAST(@nbMaxDaysBetween1970AndDate as bigint) * @nbSecondBefore19700101And20380119 + 
    DATEDIFF(ss, CAST('1970-01-01 00:00:00' as DATETIME), @DateTimeModulo) as bigint)* 
        CAST(1000 AS BIGINT)

RETURN (@MsecBetween1601And1970 + @MsecBetween1970AndDate) * CAST(10000 AS BIGINT) 
END
于 2020-10-26T15:10:28.693 に答える