【问题标题】:How to convert DATETIME to FILETIME value in T-SQL?如何在 T-SQL 中将 DATETIME 转换为 FILETIME 值?
【发布时间】:2011-02-24 20:03:07
【问题描述】:

我需要在 T-SQL SELECT 语句(在 SQL Server 2000 上)中将 SQL Server DATETIME转换为 FILETIME。是否有内置功能可以做到这一点?如果没有,有人可以帮我弄清楚如何将此转换例程实现为 UDF(或只是简单的 Transact-SQL)吗?以下是我所知道的:

  1. FILETIME 是 64 位值,表示自 1601 年 1 月 1 日(UTC)(根据MSDN: FILETIME Structure)。
  2. SQL Server 基准时间从 1900-01-01 00:00:00 开始(按 SELECT CAST(0 as DATETIME))。

我找到了几个示例,展示了如何将 FILETIME 值转换为 T-SQL DATETIME(不过,我不能 100% 确定它们是准确的),但找不到任何关于反向转换的信息。即使是一般的想法(或算法)也会有所帮助。

【问题讨论】:

    标签: tsql datetime filetime


    【解决方案1】:

    好的,我想我可以自己实现这个。这是函数:

    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 tool 来计算日期之间的持续时间。

    你怎么看?

    【讨论】:

      【解决方案2】:

      2 SQL Server 时代开始于 1900-01-01 00:00:00(每 SELECT CAST(0 作为日期时间)。

      不,那是基准日期,日期时间从 1753 开始

      运行这个

      select cast('17800122' as datetime) 
      

      输出

      1780-01-22 00:00:00.000

      但这仍然小于文件时间,因此您需要添加...但是请记住公历和儒略历(也是日期时间从 1753 开始的原因)

      【讨论】:

      • 哦,太好了,现在我更加困惑了。 :-)
      • 好吧,至少在 SQL Server 2008 中 datetime2 是第 1 年
      【解决方案3】:

      接受的答案效果很好,但会在 2038 年 1 月 19 日以上的日期崩溃。要么使用 如果您使用的是 SQL Server 2016 或更高版本,请使用 DATEDIFF_BIG 而不是 DATEDIFF,或者使用以下更正

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-11-29
        • 1970-01-01
        • 1970-01-01
        • 2020-03-04
        • 2019-12-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多