Convert Epoch to DateTime SQL Server (Exceeds Year 2038) - sql-server

How to convert Epoch to DateTime SQL Server if epoch exceeds the year 2038?
Answer in Convert Epoch to DateTime SQL Server will not work.
Example:
SELECT DATEADD(ss, 2713795200000 / 1000, '19700101')
Thu, 30 Dec 2055 16:00:00 GMT

DATEADD function assumes an INT as an increment to your date, to bypass the limitation of INT you can either reduce the precision of your epoch, or do a slightly complex code to retain the precision of your epoch.
This reduces the precision to minutes:
SELECT DATEADD(MINUTE,#YourEpoch/60/1000, '1/1/1970')
This one splits your epoch to days and milliseconds and then combines them in a datetime
CREATE FUNCTION [dbo].[fn_EpochToDatetime] (#Epoch BIGINT)
RETURNS DATETIME
AS
BEGIN
DECLARE #Days AS INT, #MilliSeconds AS INT
SET #Days = #Epoch / (1000*60*60*24)
SET #MilliSeconds = #Epoch % (1000*60*60*24)
RETURN (SELECT DATEADD(MILLISECOND, #MilliSeconds, DATEADD(DAY, #Days, '1/1/1970')))
END;
However, I'm not quite sure why the 2nd solution is not as precise as I expect it to be.

Building on the response above, the solution provided works but does not protect from trying to convert to a date that is out of bounds for SQL server.
create function dbo.unixTimestampConversion (
#unixTime bigInt
)
returns dateTime2(7)
as
begin
declare
#output dateTime2(7)
, #days int
, #ms int
, #x int = (1000 * 60 * 60 * 24)
;
set #days = #unixTime / #x
;
set #ms = #unixTime % #x
;
if (#unixTime < 32503593600000 and #unixTime > -2208988800000)
begin
set #output = dateAdd (millisecond, #ms, dateAdd (day, #days, '1/1/1970'))
;
end
;
else if (#unixTime <= -2208988800000)
begin
set #output = '1/1/1900'
;
end
;
else if (#unixTime >= 32503593600000)
begin
set #output = '12/31/2999'
;
end
;
return #output
;
end
;

You can assign the epoch time to your datetime directly (I tried this on SQL Server 15.0). Although it considers the number as the number of days since 1900-1-1 00:00:00 so you have to add 2208988800 (the number of seconds in 70 years) and then divide by 86400(number of seconds in a day).
DECLARE #time DATETIME = (2208988800.0 + [your epoch time in seconds])/86400;
However, it seems to be 0.007s or 0.003s behind the given epoch. Also, I'm not sure if this is faster than the DATEADD() function.

create a function to convert epoch to datetime and use them in your query like
below
create FUNCTION [dbo].[from_unixtime] (#Datetime BIGINT)
RETURNS DATETIME
AS
BEGIN
DECLARE #LocalTimeOffset BIGINT
,#AdjustedLocalDatetime BIGINT;
SET #LocalTimeOffset = DATEDIFF(second,GETDATE(),GETUTCDATE())
SET #AdjustedLocalDatetime = #Datetime - #LocalTimeOffset
RETURN (SELECT DATEADD(second,#AdjustedLocalDatetime, CAST('1970-01-01 00:00:00' AS datetime)))
END;
and then use this function in your query

Related

"Conversion failed when converting date and/or time from character string." error

I have the following query:
DECLARE #Month int
DECLARE #Year int
set #Month = 2
set #Year = 2004
Declare #MonthStartDate datetime
declare #MonthEndDate datetime
set #MonthStartDate = 'select DATEADD(month,#Month-1,DATEADD(year,#Year-1900,0))'
set #MonthEndDate = 'select DATEADD(day,-1,DATEADD(month,#Month,DATEADD(year,#Year-1900,0)))'
return #MonthStartDate , #MonthEndDate
But it returns:
"Conversion failed when converting date and/or time from character string."
What's wrong here?
Alternatively, you can also use as follow..
select #MonthStartDate = DATEADD(month,#Month-1,DATEADD(year,#Year-1900,0))
select #MonthEndDate = DATEADD(day,-1,DATEADD(month,#Month,DATEADD(year,#Year-1900,0)))
You should use DateTime expression instead of string literal. Just remove quotes:
DECLARE #Month int
DECLARE #Year int
set #Month = 2
set #Year = 2004
Declare #MonthStartDate datetime
declare #MonthEndDate datetime
set #MonthStartDate = DATEADD(month,#Month-1,DATEADD(year,#Year-1900,0))
set #MonthEndDate = DATEADD(day,-1,DATEADD(month,#Month,DATEADD(year,#Year-1900,0)))
Looking at your Query (Since you don't have enough Description on the Question ) What I understood is that you are trying to get the First and Last day of a Given month. If you are using a SQL Server Version 2012 or Above, then you have an Inbuild Function called EOMONTH() which can be used to calculate the End of any given month. Otherwise, you may try the below method on any Version on SQL Server
Declare #MonthStartDate datetime,
#MonthEndDate datetime,
#Year int,
#Month int --It's Better to Declare all the variables in the same space for easy handling
SELECT
#Month = 2,
#Year = 2004 -- Common Assignments can be done together
;WITH MNT
AS
(
SELECT
MonthStartDate = CAST(#Month AS VARCHAR(20))+'/01/'+CAST(#Year AS VARCHAR(20)) -- Hardcoded Day as 1 since Month Start always at 1
)
SELECT
#MonthStartDate = MonthStartDate,
#MonthEndDate = DATEADD(DAY,-1,DATEADD(MONTH,1,MonthStartDate))
FROM MNT

Convert epoch to datetime

On a table I have a bigint column that stores a timestamp with a microsecond precision like:
636453251396217655
636453251398405201
636453251592389899
636453251668326820
I have to build a script that, if that date is older than a week, the row must moved to another table.
I tried to convert to date using:
CREATE FUNCTION [dbo].[UNIXToDateTime] (#timestamp bigint)
RETURNS datetime
AS
BEGIN
DECLARE #ret datetime
SELECT #ret = DATEADD(second, #timestamp, '1970/01/01 00:00:00')
RETURN #ret
END
and used like:
select dbo.UNIXToDateTime(636453251396217655)
but because of the bigint my script crash because:
Arithmetic overflow error during expression conversion in int data
type
I can lose precision, the important is the date part that is the main part of the sql filter.
Demo: http://sqlfiddle.com/#!6/24f05/1
There's an answer here for converting with epoch values:
CREATE FUNCTION [dbo].[fn_EpochToDatetime] (#Epoch BIGINT)
RETURNS DATETIME
AS
BEGIN
DECLARE #Days AS INT, #MilliSeconds AS INT
SET #Days = #Epoch / (1000*60*60*24)
SET #MilliSeconds = #Epoch % (1000*60*60*24)
RETURN (SELECT DATEADD(MILLISECOND, #MilliSeconds, DATEADD(DAY, #Days, '1/1/1970')))
END;
You can use that function but simply divide your epoch values. As you are fine with the loss of fidelity, this will suit your needs perfectly. For example:
DECLARE #epoch BIGINT = 636453251396217655
SELECT dbo.[fn_EpochToDatetime](#epoch/100000)

ISDATE Function for different date formats in TSQL

I need to convert VARCHAR values into DATETIME in multiple columns of a view for sorting and formatting (displaying in locale format) purposes in another application on SQL Server 2008.
There are currently two problems.
The input format of the VARCHAR values differ (but consistent at
column level)
Also there may be faulty values (e.g.: 20..05.2015)
Unfortunately the TRY_CONVERT function is available just for SQL Server 2012 and later.
ISDATE does not work because the view contains different date formats and I can neither set the language inside user defined functions nor in views, which would cause ISDATE to work with german date formats for example.
Is there any easier solution for my problem?
My first thought was to write a function like
FUNCTION TryConvertStringAsDatetime ( #value VARCHAR(MAX),
#format INT
)
that uses the format numbers of the CONVERT function, but checking for every possible format manually scares me a bit.
Example: TryConvertStringAsDatetime('20.05.2015', 104) (with some pseudocode)
SET #day = character 1 and 2
SET #month = character 4 and 5
SET #year = character 7, 8, 9 and 10
SET #dateODBCFormat = #year - #month - #day (concatenated with hyphen and not subtracted :)
IF ISDATE(#dateODBCFormat ) = 1
RETURN CONVERT(DATETIME, #dateODBCFormat, 120)
ELSE
RETURN CONVERT(DATETIME, 0) (does the job)
This is the function I now came up with:
CREATE
FUNCTION TryConvertStringAsDatetime ( #value VARCHAR(MAX),
#format INT
)
RETURNS DATETIME
AS
/*
Tries to convert a given VARCHAR value to DATETIME.
Returns NULL if no value was specified or the value is not in the correct format.
*/
BEGIN
DECLARE #length INT = LEN(#value)
IF #length IS NULL OR #length < 10 OR #length > 23
RETURN NULL
DECLARE #day VARCHAR(2),
#month VARCHAR(2),
#year VARCHAR(4),
#time VARCHAR(9)
IF #format = 104 --dd.mm.yyyy hh:mi:ss(24h)
BEGIN
SET #day = SUBSTRING(#value, 1, 2)
SET #month = SUBSTRING(#value, 4, 2)
SET #year = SUBSTRING(#value, 7, 4)
END
ELSE IF #format IN (120, 121) --yyyy-mm-dd hh:mi:ss(24h)
BEGIN
SET #year = SUBSTRING(#value, 1, 4)
SET #month = SUBSTRING(#value, 6, 2)
SET #day = SUBSTRING(#value, 9, 2)
END
ELSE
RETURN NULL -- currently only german and ODBC supported
IF #length > 11
SET #time = SUBSTRING(#value, 12, #length - 11)
SET #value = #year + '-' + #month + '-' + #day + ISNULL(' ' + #time, '')
IF ISDATE(#value) = 1
RETURN CONVERT(DATETIME, #value, 121)
RETURN NULL
END
I would probably go with something like this:
CREATE FUNCTION TryConvertToDate
(
#InputString varchar(20)
)
RETURNS Datetime
BEGIN
DECLARE #DateTime datetime = NULL
SET #DateTime =
CASE
WHEN LEN(#InputString) = 10 AND PATINDEX('[0-9][0-9].[0-9][0-9].[0-9][0-9][0-9][0-9]', #InputString)=1 THEN
CONVERT(DateTime, #InputString, 104) -- German
WHEN LEN(#InputString) = 10 AND PATINDEX('[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]', #InputString)=1 THEN
CONVERT(DateTime, #InputString, 120) -- ODBC
ELSE
NULL -- unsuported format
END
RETURN #DateTime
END
Note: Testing for length and using patindex ensures only general format, so you need the call this function inside a try block in case the days and months are inverted and will cause a conversion error.
On the other hand, adding supported formats to this function is very easy - all you have to do is add a when clause with the correct patindex and length and the correct convert style.
Another option is to ensure the string can actually be converted to date.
This will make your function more complicated and thus harder to write, but will be easier to work with as it will reduce to minimum the chance of raising a conversion error:
CREATE FUNCTION TryConvertToDate
(
#InputString varchar(20)
)
RETURNS Datetime
BEGIN
DECLARE #DateValue date, #Days int, #Months int, #Years int
IF LEN(#DateString) = 10 AND PATINDEX('[0-9][0-9].[0-9][0-9].[0-9][0-9][0-9][0-9]', #InputString)=1 -- German format
BEGIN
SELECT #Days = CAST(LEFT(#InputString, 2) As int),
#Months = CAST(SUBSTRING(#InputString, 4, 2) as int),
#Years = CAST(RIGHT(#InputString, 4) as int)
-- NOTE: you will need to add a condition for leap years
IF (#Days < 31 AND #Months IN(4,6,9,12)) OR (#Days < 30 AND #Months = 2)
SET #DateValue = convert(date, #InputString, 104)
END
IF LEN(#InputString) = 10 AND PATINDEX('[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]', #InputString)=1 -- ODBC format
BEGIN
SELECT #Days = CAST(RIGHT(#InputString, 2) As int),
#Months = CAST(SUBSTRING(#InputString, 6, 2) as int),
#Years = CAST(LEFT(#InputString, 4) as int)
-- NOTE: you will need to add a condition for leap years
IF (#Days < 31 AND #Months IN(4,6,9,12)) OR (#Days < 30 AND #Months = 2)
SET #DateValue = convert(date, #InputString, 120)
END
RETURN #DateValue
END
You might have better luck in terms of both speed and functionality doing this in SQLCLR (as noted by #Tab and #Zohar in various comments).
.NET / C# code:
using System;
using System.Data.SqlTypes;
using System.Globalization;
using Microsoft.SqlServer.Server;
public class TryConvertStuff
{
[SqlFunction(IsDeterministic = true, IsPrecise = true)]
public static SqlDateTime TryConvertDateTime([SqlFacet(MaxSize = 50)] SqlString
StringDate, [SqlFacet(MaxSize = 10)] SqlString Culture)
{
CultureInfo _Culture = CultureInfo.CurrentCulture;
if (!Culture.IsNull && Culture.Value.Trim() != String.Empty)
{
_Culture = CultureInfo.GetCultureInfo(Culture.Value);
}
DateTime _RealDate;
if (DateTime.TryParse(StringDate.Value, _Culture,
DateTimeStyles.None, out _RealDate))
{
return _RealDate;
};
return SqlDateTime.Null;
}
}
Tests:
SELECT dbo.TryConvertDateTime(N'2019-04-20', N'en'); -- 2019-04-20 00:00:00.000
SELECT dbo.TryConvertDateTime(N'2019-04-20f', N'en'); -- NULL
SELECT dbo.TryConvertDateTime(N'2019.04.20', N'en'); -- 2019-04-20 00:00:00.000
SELECT dbo.TryConvertDateTime(N'20.04.2019', N'en'); -- NULL
SELECT dbo.TryConvertDateTime(N'20.04.2019', N'de'); -- 2019-04-20 00:00:00.000
SELECT dbo.TryConvertDateTime(N'20.04.2019', NULL); -- NULL

sql server 2005 convert time to minutes [duplicate]

This question already has answers here:
datetime to totalminute in sql
(3 answers)
Closed 7 years ago.
I have a few date time functions within my database, but I need to add one that takes the time portion of a datetime field and then converts the time into minutes
I have a function that get the minutes between two times, but not just the minutes of a single time.
ALTER FUNCTION [dbo].[fn_MinutesBetween]
( #fStart datetime, #fEnd datetime )
RETURNS int
AS
BEGIN
RETURN DateDiff(minute, #fStart, #fEnd)
and another one that gets just the time portion
ALTER function [dbo].[fn_ReturnTimeOnly]
(#DateTime smallDateTime)
returns nvarchar(50)
as
begin
Return substring(cast(#DateTime as varchar),12,len(#DateTime))
end
How can I just get the minutes of the time. Like 1:00 am would be 60, 2:00 am would be 120
12:00 pm would be 720 etc.
Thanks
I was given a link in comments to datetime to totalminute
and used that to come up with a solution.
ALTER FUNCTION [dbo].[fn_ReturnMinutesOnly]
( #dt smalldatetime )
RETURNS int
AS
BEGIN
RETURN DATEDIFF(MINUTE, DATEADD(DAY, DATEDIFF(DAY, 0, #dt), 0), #dt)
END
Get the number of hours, cast to int, multiply by 60, get the number of mins, cast to int, add these two.
ALTER function [dbo].[fn_ReturnMinutesOnly]
(#DateTime smallDateTime)
returns INT
as
begin
Return
cast(substring(cast(#DateTime as varchar),13,2) as INT) * 60 +
cast(substring(cast(#DateTime as varchar),16,2) as INT)
end
Casting to string is expensive (I talk about the opposite scenario here, but the concept is the same). Try this instead:
DECLARE #DateTime DATETIME;
SET #DateTime = GETDATE();
SELECT DATEDIFF(MINUTE, DATEDIFF(DAY, 0, #DateTime), #DateTime);
In your function, this would be:
ALTER function [dbo].[fn_ReturnTimeOnly]
(#DateTime smallDateTime)
returns INT
as
begin
Return (SELECT DATEDIFF(MINUTE, DATEDIFF(DAY, 0, #DateTime), #DateTime));
end

Check whether daylight savings is on or off

In a scenario, I have to check whether daylight savings is in effect or not. Based on the outcome of the check, I have to do some calculations in a SQL server procedure.
What is the best way to quickly check if daylight savings is currently in effect?
I used this Daylight Savings Time Functions in SQL Server that is created by Tim Cullen.
Specifically, the code that I used was:
Start Date Function
CREATE function [dbo].[fn_GetDaylightSavingsTimeStart]
(#Year varchar(4))
RETURNS smalldatetime
as
begin
declare #DTSStartWeek smalldatetime, #DTSEndWeek smalldatetime
set #DTSStartWeek = '03/01/' + convert(varchar,#Year)
return case datepart(dw,#DTSStartWeek)
when 1 then
dateadd(hour,170,#DTSStartWeek)
when 2 then
dateadd(hour,314,#DTSStartWeek)
when 3 then
dateadd(hour,290,#DTSStartWeek)
when 4 then
dateadd(hour,266,#DTSStartWeek)
when 5 then
dateadd(hour,242,#DTSStartWeek)
when 6 then
dateadd(hour,218,#DTSStartWeek)
when 7 then
dateadd(hour,194,#DTSStartWeek)
end
end
End Date Function
CREATE function [dbo].[fn_GetDaylightSavingsTimeEnd]
(#Year varchar(4))
RETURNS smalldatetime
as
begin
declare #DTSEndWeek smalldatetime
set #DTSEndWeek = '11/01/' + convert(varchar,#Year)
return case datepart(dw,dateadd(week,1,#DTSEndWeek))
when 1 then
dateadd(hour,2,#DTSEndWeek)
when 2 then
dateadd(hour,146,#DTSEndWeek)
when 3 then
dateadd(hour,122,#DTSEndWeek)
when 4 then
dateadd(hour,98,#DTSEndWeek)
when 5 then
dateadd(hour,74,#DTSEndWeek)
when 6 then
dateadd(hour,50,#DTSEndWeek)
when 7 then
dateadd(hour,26,#DTSEndWeek)
end
end
I then use the functions like this in my query:
declare #DLSStart smalldatetime
, #DLSEnd smalldatetime
, #DLSActive tinyint
set #DLSStart = (select MSSQLTIPS.dbo.fn_GetDaylightSavingsTimeStart(convert(varchar,datepart(year,getdate()))))
set #DLSEnd = (select MSSQLTIPS.dbo.fn_GetDaylightSavingsTimeEnd(convert(varchar,datepart(year,getdate()))))
if #Date between #DLSStart and #DLSEnd
begin
set #DLSActive = 1
end
else
begin
set #DLSActive = 0
end
select #DLSActive
In Western Europe, the summer time starts the last Sunday of March at 02:00
select
DATEADD(
day,
DATEDIFF(
day,
'1900-01-07',
DATEADD(month,DATEDIFF(MONTH,0,concat(year(getdate()),'-03-01')),30)
)/7*7,
'1900-01-07 02:00'
) as SummerTimeStarts
and ends the last Sunday of October at 03:00
Select
DATEADD(
day,
DATEDIFF(
day,
'1900-01-07',
DATEADD(month,DATEDIFF(MONTH,0,concat(year(getdate()),'-10-01')),30)
)/7*7,
'1900-01-07 03:00'
) as SummerTimeEnds
It gives as en function :
CREATE function [dbo].[DateIsSummerTime]
(#datetime datetime)
RETURNS bit
as
begin
declare #SummerTimeStarts datetime, #SummerTimeEnds datetime
set #SummerTimeStarts = (select DATEADD(day,DATEDIFF(day,'1900-01-07',DATEADD(month,DATEDIFF(MONTH,0,concat(year(getdate()),'-03-01')),30))/7*7,'1900-01-07 02:00'))
set #SummerTimeEnds = (Select DATEADD(day,DATEDIFF(day,'1900-01-07',DATEADD(month,DATEDIFF(MONTH,0,concat(year(getdate()),'-10-01')),30))/7*7,'1900-01-07 03:00'))
Return Case when #datetime > #SummerTimeStarts and #datetime < #SummerTimeEnds then 1 else 0 end
end

Resources