UNIX_TIMESTAMP in SQL Server - sql

I need to create a function in SQL Server 2008 that will mimic mysql's UNIX_TIMESTAMP().

If you're not bothered about dates before 1970, or millisecond precision, just do:
-- SQL Server
SELECT DATEDIFF(s, '1970-01-01 00:00:00', DateField)
Almost as simple as MySQL's built-in function:
-- MySQL
SELECT UNIX_TIMESTAMP(DateField);
Other languages (Oracle, PostgreSQL, etc): How to get the current epoch time in ...
If you need millisecond precision (SQL Server 2016/13.x and later):
SELECT DATEDIFF_BIG(ms, '1970-01-01 00:00:00', DateField)

Try this post:
https://web.archive.org/web/20141216081938/http://skinn3r.wordpress.com/2009/01/26/t-sql-datetime-to-unix-timestamp/
CREATE FUNCTION UNIX_TIMESTAMP (
#ctimestamp datetime
)
RETURNS integer
AS
BEGIN
/* Function body */
declare #return integer
SELECT #return = DATEDIFF(SECOND,{d '1970-01-01'}, #ctimestamp)
return #return
END
or this post:
http://mysql.databases.aspfaq.com/how-do-i-convert-a-sql-server-datetime-value-to-a-unix-timestamp.html
code is as follows:
CREATE FUNCTION dbo.DTtoUnixTS
(
#dt DATETIME
)
RETURNS BIGINT
AS
BEGIN
DECLARE #diff BIGINT
IF #dt >= '20380119'
BEGIN
SET #diff = CONVERT(BIGINT, DATEDIFF(S, '19700101', '20380119'))
+ CONVERT(BIGINT, DATEDIFF(S, '20380119', #dt))
END
ELSE
SET #diff = DATEDIFF(S, '19700101', #dt)
RETURN #diff
END
Sample usage:
SELECT dbo.DTtoUnixTS(GETDATE())
-- or
SELECT UnixTimestamp = dbo.DTtoUnixTS(someColumn)
FROM someTable

Sql Server 2016 and later have a DATEDIFF_BIG function that can be used to get the milliseconds.
SELECT DATEDIFF_BIG(millisecond, '1970-01-01 00:00:00', GETUTCDATE())
Create a function
CREATE FUNCTION UNIX_TIMESTAMP()
RETURNS BIGINT
AS
BEGIN
RETURN DATEDIFF_BIG(millisecond, '1970-01-01 00:00:00', GETUTCDATE())
END
And execute it
SELECT dbo.UNIX_TIMESTAMP()

I often need a unix timestamp with millisecond precision. The following will give you the current unixtime as FLOAT; wrap per answers above to get a function or convert arbitrary strings.
The DATETIME datatype on SQL Server is only good to 3 msec, so I have different examples for SQL Server 2005 and 2008+. Sadly there is no DATEDIFF2 function, so various tricks are required to avoid DATEDIFF integer overflow even with 2008+. (I can't believe they introduced a whole new DATETIME2 datatype without fixing this.)
For regular old DATETIME, I just use a sleazy cast to float, which returns (floating point) number of days since 1900.
Now I know at this point, you are thinking WHAT ABOUT LEAP SECONDS?!?! Neither Windows time nor unixtime really believe in leap seconds: a day is always 1.00000 days long to SQL Server, and 86400 seconds long to unixtime. This wikipedia article discusses how unixtime behaves during leap seconds; Windows I believe just views leap seconds like any other clock error. So while there is no systematic drift between the two systems when a leap second occurs, they will not agree at the sub-second level during and immediately following a leap second.
-- the right way, for sql server 2008 and greater
declare #unixepoch2 datetime2;
declare #now2 Datetime2;
declare #days int;
declare #millisec int;
declare #today datetime2;
set #unixepoch2 = '1970-01-01 00:00:00.0000';
set #now2 = SYSUTCDATETIME();
set #days = DATEDIFF(DAY,#unixepoch2,#now2);
set #today = DATEADD(DAY,#days,#unixepoch2);
set #millisec = DATEDIFF(MILLISECOND,#today,#now2);
select (CAST (#days as float) * 86400) + (CAST(#millisec as float ) / 1000)
as UnixTimeFloatSQL2008
-- Note datetimes are only accurate to 3 msec, so this is less precise
-- than above, but works on any edition of SQL Server.
declare #sqlepoch datetime;
declare #unixepoch datetime;
declare #offset float;
set #sqlepoch = '1900-01-01 00:00:00';
set #unixepoch = '1970-01-01 00:00:00';
set #offset = cast (#sqlepoch as float) - cast (#unixepoch as float);
select ( cast (GetUTCDate() as float) + #offset) * 86400
as UnixTimeFloatSQL2005;
-- Future developers may hate you, but you can put the offset in
-- as a const because it isn't going to change.
declare #sql_to_unix_epoch_in_days float;
set #sql_to_unix_epoch_in_days = 25567.0;
select ( cast (GetUTCDate() as float) - #sql_to_unix_epoch_in_days) * 86400.0
as UnixTimeFloatSQL2005MagicNumber;
FLOATs actually default to 8-byte doubles on SQL Server, and therefore superior to 32-bit INT for many use cases. (For example, they won't roll over in 2038.)

For timestamp with milliseconds result I found this solution from here https://gist.github.com/rsim/d11652a8336137832df9:
SELECT (cast(DATEDIFF(s, '1970-01-01', GETUTCDATE()) as bigint)*1000+datepart(ms,getutcdate()))
Answer from #Rafe didn't work for me correctly (MSSQL 20212) - I got 9 seconds of difference.

Necromancing.
The ODBC-way:
DECLARE #unix_timestamp varchar(20)
-- SET #unix_timestamp = CAST({fn timestampdiff(SQL_TSI_SECOND,{d '1970-01-01'}, CURRENT_TIMESTAMP)} AS varchar(20))
IF CURRENT_TIMESTAMP >= '20380119'
BEGIN
SET #unix_timestamp = CAST
(
CAST
(
{fn timestampdiff(SQL_TSI_SECOND,{d '1970-01-01'}, {d '2038-01-19'})}
AS bigint
)
+
CAST
(
{fn timestampdiff(SQL_TSI_SECOND,{d '2038-01-19'}, CURRENT_TIMESTAMP)}
AS bigint
)
AS varchar(20)
)
END
ELSE
SET #unix_timestamp = CAST({fn timestampdiff(SQL_TSI_SECOND,{d '1970-01-01'}, CURRENT_TIMESTAMP)} AS varchar(20))
PRINT #unix_timestamp

Here's a single-line solution without declaring any function or variable:
SELECT CAST(CAST(GETUTCDATE()-'1970-01-01' AS decimal(38,10))*86400000.5 as bigint)

If you have to deal with previous versions of SQL Server (<2016) and you only care for positive timestamps, I post here the solution I found for very distant dates (so you can get rid of the IF from #rkosegi's answer.
What I did was first calculating the difference in days and then adding the difference in seconds left.
CREATE FUNCTION [dbo].[UNIX_TIMESTAMP]
(
#inputDate DATETIME
)
RETURNS BIGINT
AS
BEGIN
DECLARE #differenceInDays BIGINT, #result BIGINT;
SET #differenceInDays = DATEDIFF(DAY, '19700101', #inputDate)
IF #differenceInDays >= 0
SET #result = (#differenceInDays * 86400) + DATEDIFF(SECOND, DATEADD(DAY, 0, DATEDIFF(DAY, 0, #inputDate)), #inputDate)
ELSE
SET #result = 0
RETURN #result
END

When called to Scalar-valued Functions can use following syntax
Function Script :
USE [Database]
GO
/****** Object: UserDefinedFunction [dbo].[UNIX_TIMESTAMP] ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[UNIX_TIMESTAMP] (
#ctimestamp datetime
)
RETURNS integer
AS
BEGIN
/* Function body */
declare #return integer
SELECT #return = DATEDIFF(SECOND,{d '1970-01-01'}, #ctimestamp)
return #return
END
GO
Call Function :
SELECT dbo.UNIX_TIMESTAMP(GETDATE());

Related

Is there a way to preserve locale when format a datetime in SQL? [duplicate]

I have an sql DateTime (ms sql server) and want to extract the same date without the seconds:
e.g. 2011-11-22 12:14:58.000 to become: 2011-11-22 12:14:00.000
How can I do this? I was thinking to use DATEADD in combination with DATEPART but seems very error prone (besides performance issues)
SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, 0, yourcolumn), 0) FROM yourtable
This will be effective, if you don't want a slow conversion between datatypes.
For a solution that truncates using strings try this:
SELECT CAST(CONVERT(CHAR(16), GetDate(),20) AS datetime)
CHAR(16) works only if our variable is converted to ODBC canonical format, as shown above by using 20 as the format specifier.
DECLARE #date DateTime = '2011 Nov 22 12:14:55';
SELECT CONVERT(Char(16), #date ,20) AS datetime
Results:
| datetime |
|------------------|
| 2011-11-22 12:14 |
Then you simply cast back to a DateTime type to continue using the value.
NOTE: This is only viable for data types that do not carry TimeZone info.
Also type conversions to VarChar and back are usually LESS performant than using DateTime functions that use numeric operations internally.
Consider other solutions posted if performance is a concern or if you must retain timezone information.
DECLARE #TheDate DATETIME
SET #TheDate = '2011-11-22 12:14:58.000'
DATEADD(mi, DATEDIFF(mi, 0, #TheDate), 0)
In queries
/* ...all records in that minute; index-friendly expression */
WHERE TheDate BETWEEN DATEADD(mi, DATEDIFF(mi, 0, #TheDate), 0)
AND DATEADD(mi, DATEDIFF(mi, 0, #TheDate) + 1, 0)
Date and time needs carefully and not being converted as TEXT.
My personal solution:
CREATE FUNCTION [dbo].[fnDateTimeTruncated]
(
#datetime DATETIME
)
RETURNS DATETIME
AS
BEGIN
RETURN DATETIMEFROMPARTS ( year(#datetime), month(#datetime), day(#datetime), DATEPART(hh,#datetime), DATEPART(mi,#datetime), 0, 0)
END
Edited:
Regarding http://blog.waynesheffield.com/wayne/archive/2012/03/truncate-a-date-time-to-different-part/, DateAdd has a better performance.
Thanks to t-clausen.dk
With a little fiddling around, this seems to work well:
SELECT CAST(CONVERT(CHAR(17), bl.[time],113) AS varchar(17))
Result given: 2011-11-22 12:14
The exact way I'm using it in my query as part of the selection list :
,CAST(CONVERT(CHAR(17), bl.[time],113) AS varchar(17))
+ ' (UTC +0)' AS [TIME]
Gives me the result: 15 Dec 2017 06:43 (UTC +0)
From SQL Server 2014, You can use Format function for this.
for Ex.
declare #Startdate datetime = '2020-11-07 15:27:50.713'
set #Startdate = Convert(datetime,FORMAT(#Startdate, 'yyyy-MM-dd HH:mm'))
> Result is
2020-11-07 15:27:00.000
If there is no milliseconds, than
DECLARE #dt datetime2 = '2011-11-22 12:14:58.000';
DECLARE #goalDt datetime2 = DATEADD(second,-DATEPART(second,#dt), #dt);
To remove a milliseconds part, add
SET #goalDt = DATEADD(millisecond,-DATEPART(millisecond,#goalDt ), goalDt dt);
To Round Off it:
DECLARE #TheDate DATETIME;
SET #TheDate = '2019-1-2 12:14:58.400';
SELECT CAST(#TheDate AS SMALLDATETIME);
To just Truncate:
DECLARE #TruncTheDate DATETIME;
SET #TruncTheDate = '2019-1-2 12:14:58.400';
SELECT DATEADD(mi, DATEDIFF(mi, 0, #TruncTheDate), 0);
select substring(cast(cast(getdate() as time(0)) as char(8)),0,6)

Create a procedure to return 32bit ID(nvarchar) with date time when user logs in to a web application

I am trying to write a Microsoft SQL server procedure to return a 32bit ID and date time when the user logs in to an application through a website.I have PHP on the front end to call for the procedure. The Id would be the session used to evaluate the session of the user and update that in the table.
I am having a problem coming up with a procedure that can generate a 32bit id and date time to return to PHP call.I am new to MS SQL I need help.
For your Integer:
You can use the built in RAND() function to generate a positive decimal between 0 and 1 and multiply that by the max 32 bit INT value 2147483647.
Example:
If you're using a Scalar-Valued Function:
...
DECLARE #return INT
SET #return = FLOOR(RAND() * 2147483647)
RETURN(#return)
For your Datetime:
You would have to use a date range that you would want to generate a date for. Additionally, you would have to decide the increment you would want to generate that datetime in (by DAY, SECOND, etc.). You can also use RAND() similar to the previous example with INT.
Example:
Also assuming you're using a Scalar-Valued Function:
...
DECLARE #beginDate DATETIME
DECLARE #endDate DATETIME
DECLARE #return DATETIME
SET #beginDate = '2000-01-01 00:00:00'
-- Gets current Datetime
SET #endDate = GETDATE()
-- Days difference in the range
-- Using RAND again, generate a random date in the range
DECLARE #days INT = DATEDIFF(DAY, #beginDate, #endDate)
DECLARE #random INT = ROUND(((#days - 1) * RAND()), 0)
SET #return = DATEADD(DAY, #random, #endDate)
RETURN(#return)
You can simply use NEWID() and GETDATE() functions for that and convert to the desired format afterwards:
DECLARE #myID uniqueidentifier = NEWID();
DECLARE #myDate datetime = GETDATE();
SELECT #myID, #myDate;

Direct access slower than using functions?

I was conducting some performance testing and have discovered something quite strange. I have set up a short script to time how long it takes to perform certain actions.
declare #date date
declare #someint int
declare #start datetime
declare #ended datetime
set #date = GETDATE()
DECLARE #count INT
SET #count = 0
set #start = GETDATE()
WHILE (#count < 1000)
BEGIN
--Insert test script here
END
set #ended = GETDATE()
select DATEDIFF( MILLISECOND, #start, #ended)
The table I was running tests againsts contains 3 columns, MDay, and CalDate. Every calendar date has a corresponding M(Manufacturing)Day. The table may look something like this:
MDay | CalDate
1 | 1970-01-01
2 | 1970-01-02
I wanted to test how efficient one of our functions was. This function simply takes in a date and returns the int MDay value. I used direct access, basically the same thing without the function, and tests resulted in this method take twice as long! Code I inserted into the loop is provided below. I used a random date in an attempt to eliminate caching (if exist).
Function
select #someint = Reference.GetMDay(DATEADD( D, convert(int, RAND() * 1000) , #date))
Definition for above
create Function [Reference].[GetMDay]
(#pCaLDate smalldatetime
)
Returns int
as
Begin
Declare #Mday int
Select #Mday = Mday
from Reference.MDay
where Caldate = #pCaLDate
Direct
select #someint = MDay from Reference.MDay where CalDate = DATEADD( D, convert(int, RAND() * 1000) , #date)
I even tried using a static #date for my direct code and the difference in times are negligible, so I know the convert call isn't holding it back.
What the heck is going on here?
Take a look at http://msdn.microsoft.com/en-us/library/ms178071%28v=sql.105%29.aspx is the execution plan the same on your sql server for both methods?

A way to extract from a DateTime value data without seconds

I have an sql DateTime (ms sql server) and want to extract the same date without the seconds:
e.g. 2011-11-22 12:14:58.000 to become: 2011-11-22 12:14:00.000
How can I do this? I was thinking to use DATEADD in combination with DATEPART but seems very error prone (besides performance issues)
SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, 0, yourcolumn), 0) FROM yourtable
This will be effective, if you don't want a slow conversion between datatypes.
For a solution that truncates using strings try this:
SELECT CAST(CONVERT(CHAR(16), GetDate(),20) AS datetime)
CHAR(16) works only if our variable is converted to ODBC canonical format, as shown above by using 20 as the format specifier.
DECLARE #date DateTime = '2011 Nov 22 12:14:55';
SELECT CONVERT(Char(16), #date ,20) AS datetime
Results:
| datetime |
|------------------|
| 2011-11-22 12:14 |
Then you simply cast back to a DateTime type to continue using the value.
NOTE: This is only viable for data types that do not carry TimeZone info.
Also type conversions to VarChar and back are usually LESS performant than using DateTime functions that use numeric operations internally.
Consider other solutions posted if performance is a concern or if you must retain timezone information.
DECLARE #TheDate DATETIME
SET #TheDate = '2011-11-22 12:14:58.000'
DATEADD(mi, DATEDIFF(mi, 0, #TheDate), 0)
In queries
/* ...all records in that minute; index-friendly expression */
WHERE TheDate BETWEEN DATEADD(mi, DATEDIFF(mi, 0, #TheDate), 0)
AND DATEADD(mi, DATEDIFF(mi, 0, #TheDate) + 1, 0)
Date and time needs carefully and not being converted as TEXT.
My personal solution:
CREATE FUNCTION [dbo].[fnDateTimeTruncated]
(
#datetime DATETIME
)
RETURNS DATETIME
AS
BEGIN
RETURN DATETIMEFROMPARTS ( year(#datetime), month(#datetime), day(#datetime), DATEPART(hh,#datetime), DATEPART(mi,#datetime), 0, 0)
END
Edited:
Regarding http://blog.waynesheffield.com/wayne/archive/2012/03/truncate-a-date-time-to-different-part/, DateAdd has a better performance.
Thanks to t-clausen.dk
With a little fiddling around, this seems to work well:
SELECT CAST(CONVERT(CHAR(17), bl.[time],113) AS varchar(17))
Result given: 2011-11-22 12:14
The exact way I'm using it in my query as part of the selection list :
,CAST(CONVERT(CHAR(17), bl.[time],113) AS varchar(17))
+ ' (UTC +0)' AS [TIME]
Gives me the result: 15 Dec 2017 06:43 (UTC +0)
From SQL Server 2014, You can use Format function for this.
for Ex.
declare #Startdate datetime = '2020-11-07 15:27:50.713'
set #Startdate = Convert(datetime,FORMAT(#Startdate, 'yyyy-MM-dd HH:mm'))
> Result is
2020-11-07 15:27:00.000
If there is no milliseconds, than
DECLARE #dt datetime2 = '2011-11-22 12:14:58.000';
DECLARE #goalDt datetime2 = DATEADD(second,-DATEPART(second,#dt), #dt);
To remove a milliseconds part, add
SET #goalDt = DATEADD(millisecond,-DATEPART(millisecond,#goalDt ), goalDt dt);
To Round Off it:
DECLARE #TheDate DATETIME;
SET #TheDate = '2019-1-2 12:14:58.400';
SELECT CAST(#TheDate AS SMALLDATETIME);
To just Truncate:
DECLARE #TruncTheDate DATETIME;
SET #TruncTheDate = '2019-1-2 12:14:58.400';
SELECT DATEADD(mi, DATEDIFF(mi, 0, #TruncTheDate), 0);
select substring(cast(cast(getdate() as time(0)) as char(8)),0,6)

Transforming nvarchar day duration setting into datetime

I have a SQL Server function which converts a nvarchar day duration setting into a datetime value.
The day duration format is >days<.>hours<:>minutes<, for instance 1.2:00 for one day and two hours.
The format of the day duration setting can not be changed, and we can be sure that all data is correctly formatted and present.
Giving the function a start time and the day duration setting it should return the end time.
For instance: 2010-01-02 13:30 ==> 2010-01-03 2:00
I'm using a combination of charindex, substring and convert methods to calculate the value,
which is kind of slow and akward. Is there any other way to directly convert this day duration setting into a datetime value?
Not from what I can see. I would end up with a similar bit of SQL like you, using charindex etc. Unfortunately it's down to the format the day duration is stored in. I know you can't change it, but if it was in a different format then it would be a lot easier - the way I'd usually do this for example, is to rationalise the duration down to a base unit like minutes.
Instead of storing 1.2:00 for 1 day and 2 hours, it would be (1 * 24 * 60) + (2 * 60) = 1560. This could then be used in a straightforward DATEADD on the original date (date part only).
With the format you have, all approaches I can think of involve using CHARINDEX etc.
One alternative would be to build a string with the calculation. Then you can run the generated SQL with sp_executesql, specifying #enddate as an output parameter:
declare #startdate datetime
declare #duration varchar(10)
declare #enddate datetime
set #startdate = '2010-01-02 13:30'
set #duration = '0.12:30'
declare #sql nvarchar(max)
set #sql = 'set #enddate = dateadd(mi,24*60*' +
replace(replace(#duration,'.','+60*'),':','+') + ', #startdate)'
exec sp_executesql #sql,
N'#startdate datetime, #enddate datetime out',
#startdate, #enddate out
This creates a string containing set #enddate = dateadd(mi,24*60*0+60*12+30, #startdate) and then runs it.
I doubt this is faster than the regular charindex way:
declare #pos_dot int
declare #day int
declare #hour int
declare #minute int
select
#pos_dot = charindex('.',#duration),
#day = cast(left(#duration, #pos_dot-1) as int),
#hour = cast(left(right(#duration, 5), 2) as int),
#minute = cast(right(#duration, 2) as int),
#enddate = dateadd(mi, 24*60*#day + 60*#hour + #minute, #startdate)