我需要将.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
答案 0 :(得分:21)
我讨论是否发布这个因为它取决于SQL Server中二进制级别的日期存储方式,因此它是一个非常脆弱的解决方案。对于除了一次性转换之外的任何事情,我会使用类似@Solution Evangelist发布的答案。不过,你可能会在学术上找到这种有趣的方式,所以无论如何我都会发布它。
利用DateTime2
的准确性与.NET中的刻度持续时间匹配并且两者都基于01-01-0001 00:00:00.0000000
的开始日期这一事实,您可以强制转换DateTime
到DateTime2
,然后将其投放到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纳秒。显然链接到已在其他帖子中提供的算法本身。
单个刻度表示一百纳秒或一千万分之一 一秒钟一毫秒内有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