将SQL Server DateTime对象转换为BIGINT(.Net ticks)

时间:2011-09-12 10:42:37

标签: .net sql sql-server

我需要将.Net ticks格式的DateTime类型值转换为BIGINT类型(自0001年1月1日午夜12:00:00起经过的100纳秒间隔的数量)。

应使用T-SQL查询在Sql server 2008中执行转换

例如:

DateTime value - 12/09/2011 00:00:00

将转换为:

BIGINT value - 634513824000000000

8 个答案:

答案 0 :(得分:21)

我讨论是否发布这个因为它取决于SQL Server中二进制级别的日期存储方式,因此它是一个非常脆弱的解决方案。对于除了一次性转换之外的任何事情,我会使用类似@Solution Evangelist发布的答案。不过,你可能会在学术上找到这种有趣的方式,所以无论如何我都会发布它。

利用DateTime2的准确性与.NET中的刻度持续时间匹配并且两者都基于01-01-0001 00:00:00.0000000的开始日期这一事实,您可以强制转换DateTimeDateTime2,然后将其投放到binary(9)0x07F06C999F3CB7340B

日期时间信息以RTL格式存储,因此,我们将获得0x0B34B73C9F996CF007

前三个字节存储自01-01-0001以来的天数,接下来的5个字节存储自那天午夜起的100ns刻度,因此我们可以计算天数,乘以一天的刻度和添加表示当天经过的时间的刻度。

执行以下代码:

set @date = getdate()
set @ticksPerDay = 864000000000

declare @date2 datetime2 = @date

declare @dateBinary binary(9) = cast(reverse(cast(@date2 as binary(9))) as binary(9))
declare @days bigint = cast(substring(@dateBinary, 1, 3) as bigint)
declare @time bigint = cast(substring(@dateBinary, 4, 5) as bigint)

select @date as [DateTime], @date2 as [DateTime2], @days * @ticksPerDay + @time as [Ticks]

返回以下结果:

DateTime                DateTime2              Ticks
----------------------- ---------------------- --------------------
2011-09-12 07:20:32.587 2011-09-12 07:20:32.58 634514088325870000

获取返回的Ticks数并转换回.NET中的DateTime:

DateTime dt = new DateTime(634514088325870000);
dt.ToString("yyyy-MM-dd HH:mm:ss.fffffff").Dump();

从sql server获取日期:

2011-09-12 07:20:32.5870000

答案 1 :(得分:17)

我找到了一篇可能有帮助的CodeProject文章:Convert DateTime To .NET Ticks Using T-SQL

我附上上面文章中的SQL函数(我希望这样可以吗?因为它需要注册。)

CREATE FUNCTION [dbo].[MonthToDays365] (@month int)
RETURNS int
WITH SCHEMABINDING
AS
-- converts the given month (0-12) to the corresponding number of days into the year (by end of month)
-- this function is for non-leap years
BEGIN 
RETURN
    CASE @month
        WHEN 0 THEN 0
        WHEN 1 THEN 31
        WHEN 2 THEN 59
        WHEN 3 THEN 90
        WHEN 4 THEN 120
        WHEN 5 THEN 151
        WHEN 6 THEN 181
        WHEN 7 THEN 212
        WHEN 8 THEN 243
        WHEN 9 THEN 273
        WHEN 10 THEN 304
        WHEN 11 THEN 334
        WHEN 12 THEN 365
        ELSE 0
    END
END

GO

CREATE FUNCTION [dbo].[MonthToDays366] (@month int)
RETURNS int 
WITH SCHEMABINDING
AS
-- converts the given month (0-12) to the corresponding number of days into the year (by end of month)
-- this function is for leap years
BEGIN 
RETURN
    CASE @month
        WHEN 0 THEN 0
        WHEN 1 THEN 31
        WHEN 2 THEN 60
        WHEN 3 THEN 91
        WHEN 4 THEN 121
        WHEN 5 THEN 152
        WHEN 6 THEN 182
        WHEN 7 THEN 213
        WHEN 8 THEN 244
        WHEN 9 THEN 274
        WHEN 10 THEN 305
        WHEN 11 THEN 335
        WHEN 12 THEN 366
        ELSE 0
    END
END

GO

CREATE FUNCTION [dbo].[MonthToDays] (@year int, @month int)
RETURNS int
WITH SCHEMABINDING
AS
-- converts the given month (0-12) to the corresponding number of days into the year (by end of month)
-- this function is for non-leap years
BEGIN 
RETURN 
    -- determine whether the given year is a leap year
    CASE 
        WHEN (@year % 4 = 0) and ((@year % 100  != 0) or ((@year % 100 = 0) and (@year % 400 = 0))) THEN dbo.MonthToDays366(@month)
        ELSE dbo.MonthToDays365(@month)
    END
END

GO

CREATE FUNCTION [dbo].[TimeToTicks] (@hour int, @minute int, @second int)  
RETURNS bigint 
WITH SCHEMABINDING
AS 
-- converts the given hour/minute/second to the corresponding ticks
BEGIN 
RETURN (((@hour * 3600) + CONVERT(bigint, @minute) * 60) + CONVERT(bigint, @second)) * 10000000
END

GO

CREATE FUNCTION [dbo].[DateToTicks] (@year int, @month int, @day int)
RETURNS bigint
WITH SCHEMABINDING
AS
-- converts the given year/month/day to the corresponding ticks
BEGIN 
RETURN CONVERT(bigint, (((((((@year - 1) * 365) + ((@year - 1) / 4)) - ((@year - 1) / 100)) + ((@year - 1) / 400)) + dbo.MonthToDays(@year, @month - 1)) + @day) - 1) * 864000000000;
END

GO

CREATE FUNCTION [dbo].[DateTimeToTicks] (@d datetime)
RETURNS bigint
WITH SCHEMABINDING
AS
-- converts the given datetime to .NET-compatible ticks
-- see https://msdn.microsoft.com/en-us/library/system.datetime.ticks(v=vs.110).aspx
BEGIN 
RETURN 
    dbo.DateToTicks(DATEPART(yyyy, @d), DATEPART(mm, @d), DATEPART(dd, @d)) +
    dbo.TimeToTicks(DATEPART(hh, @d), DATEPART(mi, @d), DATEPART(ss, @d)) +
    (CONVERT(bigint, DATEPART(ms, @d)) * CONVERT(bigint,10000));
END

GO

答案 2 :(得分:3)

啧,

这是简化的#1推荐,但我不相信这是最好的结果。当然这是内置的,但在这一点上,无论我在截止日期。重构花了不到1分钟,但也许它会帮助其他寻求一站式解决方案的人。

CREATE FUNCTION [dbo].[Ticks] (@dt DATETIME)
RETURNS BIGINT
WITH SCHEMABINDING
AS
BEGIN 
DECLARE @year INT = DATEPART(yyyy, @dt)
DECLARE @month INT = DATEPART(mm, @dt)
DECLARE @day INT = DATEPART(dd, @dt)
DECLARE @hour INT = DATEPART(hh, @dt)
DECLARE @min INT = DATEPART(mi, @dt)
DECLARE @sec INT = DATEPART(ss, @dt)

DECLARE @days INT =
    CASE @month - 1
        WHEN 0 THEN 0
        WHEN 1 THEN 31
        WHEN 2 THEN 59
        WHEN 3 THEN 90
        WHEN 4 THEN 120
        WHEN 5 THEN 151
        WHEN 6 THEN 181
        WHEN 7 THEN 212
        WHEN 8 THEN 243
        WHEN 9 THEN 273
        WHEN 10 THEN 304
        WHEN 11 THEN 334
        WHEN 12 THEN 365
    END
    IF  @year % 4 = 0 AND (@year % 100  != 0 OR (@year % 100 = 0 AND @year % 400 = 0)) AND @month > 2 BEGIN
        SET @days = @days + 1
    END
RETURN CONVERT(bigint, 
    ((((((((@year - 1) * 365) + ((@year - 1) / 4)) - ((@year - 1) / 100)) + ((@year - 1) / 400)) + @days) + @day) - 1) * 864000000000) +
    ((((@hour * 3600) + CONVERT(bigint, @min) * 60) + CONVERT(bigint, @sec)) * 10000000) + (CONVERT(bigint, DATEPART(ms, @dt)) * CONVERT(bigint,10000));

END
GO

答案 3 :(得分:2)

您可以使用以下sql将日期或utcdate转换为ticks

declare @date datetime2 = GETUTCDATE() or getdate()
declare @dateBinary binary(9) = cast(reverse(cast(@date as binary(9))) as binary(9))
declare @days bigint = cast(substring(@dateBinary, 1, 3) as bigint)
declare @time bigint = cast(substring(@dateBinary, 4, 5) as bigint)

select @date as [DateTime],  @days * 864000000000 + @time as [Ticks]

并使用下面的sql将tick转换为日期

SELECT Converted = CAST(635324318540000000/864000000000.0 - 693595.0 AS DATETIME)

答案 4 :(得分:1)

编辑:由于有关原始问题的更新详细信息而更新

要提供100ns的精度,您应该使用新的日期类型 - datetime2在SQL Server 2008上执行算法,其精度为100纳秒。显然链接到已在其他帖子中提供的算法本身。

DateTime.Ticks Property

  

单个刻度表示一百纳秒或一千万分之一   一秒钟一毫秒内有10,000个刻度。

     

此属性的值表示100纳秒的数量   自0001年1月1日午夜12:00:00起经过的间隔,   代表DateTime.MinValue。它不包括数量   可归因于闰秒的滴答声。

DateTime dateTime = DateTime.Now;
System.Int64 ticks = dateTime.Ticks;

答案 5 :(得分:1)

更高效的解决方案可能是:(注意前两行仅用于测试)

DECLARE @YourDate DATETIME

SET @YourDate = GETDATE()

SELECT 
(
  (
  -- seconds since 1970
  CAST(DATEDIFF(s,'1970-01-01 12:00:00',@YourDate) As BIGINT)
  )
-- seconds from 0001 to 1970 (approximate)
+ 62125920000
) 
-- make those seconds nanoseconds
* 1000000000

鉴于你的输入日期只有几秒钟,我们只需要在几秒钟内完成它并乘以1000000000即可得到纳秒。

答案 6 :(得分:1)

我错过了这个问题的毫米精确的单线解决方案,所以这里有一个:

SELECT ROUND(CAST(CAST(GETUTCDATE() AS FLOAT)*8.64e8 AS BIGINT),-1)*1000+599266080000000000

8.64e8 = TimeSpan.TicksPerDay / 1000
599266080000000000 = DateTime.Parse('1900-01-01')。Ticks

这适用于DATETIME类型,但不适用于DATETIME2。 DATETIME的4/3 ms分辨率使得必须涉及ROUND(...,-1):在乘以8.64e8之后,浮点结果总是以0或33.3或66.6结束。这将四舍五入为0,30或70。

答案 7 :(得分:0)

此函数返回unix时间:

alter FUNCTION [dbo].[GETTICKS] (@datetime datetime)
RETURNS bigint
WITH SCHEMABINDING
AS
BEGIN 
    RETURN 
        DATEPART(millisecond,@datetime) +
        CONVERT(bigint, 
                    1000 * (
                        DATEPART(second,@datetime) +
                        60 * DATEPART(minute,@datetime) +
                        3600 * DATEPART(hour,@datetime) +
                        3600 * 24 * DATEPART(DAYOFYEAR,@datetime) +
                        convert(bigint, 3600 * 24 * (((DATEPART(year,@datetime) - 1970) * 365) + ((DATEPART(year,@datetime) - 1972) / 4)))
                ) )
END