【问题标题】:Convert .NET Ticks to SQL Server DateTime将 .NET 刻度转换为 SQL Server 日期时间
【发布时间】:2010-02-22 18:43:22
【问题描述】:

我在我的数据库中将TimeSpan(来自.NET)值保存为SQL Server 中的BIGINT(保存Ticks 属性)。我想知道如何在 SQL Server(不在 .NET 中)中将此 BIGINT 值转换为 DATETIME 值。有什么想法吗?

干杯

编辑:

我正在使用 NHibernate 来映射我拥有的 TimeSpan 属性,并且它保留了 Ticks 属性。我用它来控制某个日期的相对小时(或分钟)。

在系统内部一切正常,不需要这种转换。但是,在 SQL Server 中执行随机查询时,很难理解 TimeSpan 的持久形式。因此,我传递Ticks 值并返回DateTime 的函数将给出TimeSpan 所代表的小时、分钟和秒数。

【问题讨论】:

    标签: sql-server-2005 nhibernate datetime timespan


    【解决方案1】:

    我不太了解 SQL Server,但今天我的一位同事遇到了同样的问题,我认为我找到了这样的解决方案:

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

    其中 599266080000000000 是 01/01/1900 00:00:00 的刻度值。

    【讨论】:

    • 这确实是对之前答案的重申,但我认为它有一些优点,因为它对我来说看起来更简单。
    【解决方案2】:

    我不确定几秒钟的准确度,但您可以尝试以下方法:

    Declare @TickValue bigint
    Declare @Days float
    
    Set @TickValue = 634024345696365272 
    Select @Days = @TickValue * POWER(10.00000000000,-7) / 60 / 60 / 24
    
    Select DATEADD(d, Cast(@Days As int), Cast('0001-01-01' As DATE)) 
        + Cast( (@Days - FLOOR(@Days)) As DateTime)
    

    实际上,在 SQL 2005 中可以使用的另一种方法是注意从 0001-01-01 到 1900-01-01 的刻度数是 599266080000000000。这样你可以这样做:

    Declare @TickOf19000101 bigint
    Declare @TickValue bigint
    Declare @Minutes float
    
    Set @TickOf19000101  = 599266080000000000
    Set @TickValue = DATEDIFF(mi, 0 ,CURRENT_TIMESTAMP) * Cast(60 As BigInt) 
                       * POWER(10.00000000000,7) + @TickOf19000101
    
    Select @TickValue
    Select @Minutes = (@TickValue - @TickOf19000101) * POWER(10.00000000000,-7) / 60
    
    Select @Minutes
    Select DATEADD(MI, @Minutes, '1900-01-01')
    

    【讨论】:

    • 授予..这需要 SQL 2008 及其 DATE 数据类型。
    • 顺便说一句,还应该注意,Ticks 值从'0001-01-01'开始不是'1900-01-01'。
    • 代码很棒,但如果时间跨度仅表示小时而不是完整日期,则生成的日期太低而无法表示为日期时间。但是代码是一个好的开始,我会将其标记为答案并继续处理。非常感谢!
    • 这非常适合查看我的 quartz.net 触发器:DATEADD(hour,@EST_offset_h,DATEADD(mi,(a.START_TIME - 599266080000000000) * POWER(10.00000000000,-7) / 60, ' 1900-01-01')) 作为'StartDateTimeEST-4'
    【解决方案3】:

    您可以使用取自 Pavel Gatilov's blog 的此函数将 64 位整数转换为服务器本地时间毫秒精度的日期时间值:

    CREATE FUNCTION NetFxUtcTicksToDateTime
    (
       @Ticks bigint
    )
    RETURNS datetime
    AS
    BEGIN
    
    -- 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.
    
    -- Now, some magic to get the local time
    RETURN @UtcDate + GETDATE() - GETUTCDATE();
    END
    GO
    

    适合内联使用的替代代码:

    DECLARE @Ticks bigint
    set @Ticks = 634899090000000000
    select DATEADD(ms, ((@Ticks - 599266080000000000) - 
       FLOOR((@Ticks - 599266080000000000) / 864000000000) * 864000000000) / 10000,
       DATEADD(d, (@Ticks - 599266080000000000) / 864000000000, '01/01/1900')) +
       GETDATE() - GETUTCDATE()
    

    【讨论】:

    • 请注意,对于相同的输入刻度值,此函数可能会返回相差几毫秒的日期时间。 return 语句进行两次不同的调用以获取当前日期时间,并且时间可以(有时)在这些调用之间移动。
    • +1 表示相当出色的 UTC -> 本地转换。网上有一篇又一篇文章告诉人们创建时区偏移表之类的,然后就是这个,如此简单,并埋在一个看似无关的话题中。
    • UTC -> 本地转换仅在您比较的日期是最近的情况下才有用。如果您使用的日期不确定是否在夏令时(A.K.A. 夏季时间),那么您仍然需要进行一些比较才能正确转换。 Here is a good example to get started with that
    【解决方案4】:

    TimeSpan 不是日期,因此保存它可能会在将来引起混淆。

    您是否有理由不能简单地将刻度保存到整数字段而不更改其含义?

    【讨论】:

      【解决方案5】:

      在 .NET 中获取 TimeSpan.TicksPerSecond 的值(写下来)。

      然后,在您的 SQL 中,您可以将滴答计数除以该数字,得出秒数。

      然后您可以将其除以 60 得到分钟等。

      【讨论】:

        【解决方案6】:

        您应该能够使用 SQL Server 中内置的 CAST 函数。

        SELECT(CAST(CAST(CAST ('02/02/10' AS datetime) AS BIGINT) AS datetime)) 
        

        你得到 2010-02-02 00:00:00.000

        【讨论】:

        • 奇怪地从刻度开始并使用 select (CAST(CAST(633979266000000000 AS BIGINT) as datetime)) 给出算术溢出错误。
        【解决方案7】:

        我自己想通了:

        288,000,000,000 个滴答表示 8 小时,因此以下 SELECT 返回一个具有指定小时数的虚拟日期...

        SELECT DATEADD(millisecond, 288000000000/10000, CAST('1900-01-01' AS DATETIME))
        

        感谢大家的努力。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-06-01
          • 2011-04-22
          • 1970-01-01
          • 2011-11-15
          • 2019-03-07
          • 2019-10-03
          • 1970-01-01
          相关资源
          最近更新 更多