Convert date in millisecond to date - sql-server

In my table, I have a column that contains date in millisecond like this:
table a
dateinmili
1440301846096 //first six month date
1443589721039 //second six month date
I use that for my Android device and it works fine. When I want to use this time in a PROCEDURE in SQL Server and convert this time to human time (understandable for human) and date I have a problem.
I'm in Iran which uses UTC time in first six Persian date month 4.30 and 3.30 in second six month.
For convert date in PROCEDURE I use this code:
CONVERT(nVARCHAR(10),DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), GETDATE()), DATEADD(ss,dateinmili/1000,'1970-01-01')),8) as date
DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), GETDATE()), DATEADD(ss,dateinmili/1000,'1970-01-01')) as time
and here is my problem:
When I convert date in second six month and date registered in first six month of year, I get 1 hour difference between real time and converted time. I know that is because
DATEDIFF(mi, GETUTCDATE(), GETDATE())
method which return different between UTC time and local time when ever its called (in my example return 3:30 not 4:30 ) but I don't know how can I fix that?
I can add column which contain current UTC time but I am looking for another way.
update
I see this question and it's not my problem convert long to date.
My problem is in my country UTC time is not constant in whole year and change between 3.30 and 4.30, for example I have date registered in first six month (Persian six month) like 1440271800000 and convert it now which we are in second six month (Persian six month) and use this code for convert.
declare #unixTS bigint
set #unixTS = 1440271800000
select dateadd(ms, #unixTS%(3600*24*1000),
dateadd(day, #unixTS/(3600*24*1000), '1970-01-01 03:30:00.0')
)
I get this
2015-08-22 23:00:00.000
but it's not right date; the right date is:
2015-08-23 00:00:00.000
because when time registered UTC was 4.30 and not 3.30 but know when I convert
it UTC is 3.30.
I wish if there was a method in SQL which return past UTC time different; I mean put a date to that and return that time different between local time and gmt time my problem solved.
I hope you understand my problem.

In the US we have Daylight Savings Time in the summer, in most areas that means that we are also not fixed offset from UTC. Older versions of MS Dynamics CRM used to save everything in UTC, so when we wanted to export data in local time, we had a similar exercise. I created a set of SQL functions that would take the standard GMT offset and the datetime to I wanted to convert and figure out whether to apply the standard or DST offset and return the local datetime. If your offset follows a set of rules, then you can modify this:
CREATE function [dbo].[DC_GMTtoLocal]
(#OrigGMT datetime,
#StandardOffset int)
RETURNS datetime
AS
BEGIN
DECLARE #RevDate datetime
set #RevDate = CASE dbo.DC_DaylightSavingTime_IsInEffect(#OrigGMT)
WHEN 1 THEN DATEADD(hour, - #StandardOffset + 1, #OrigGMT) -- in DST
ELSE DATEADD(hour, - #StandardOffset, #OrigGMT) -- Not In DST
END
return #RevDate
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE function [dbo].[DC_DaylightSavingTime_IsInEffect]
(#DtTime datetime)
RETURNS tinyint
AS
BEGIN
DECLARE #DLSStart datetime
, #DLSEnd datetime
, #DLSActive tinyint
SET #DLSActive = 0
If DATEADD(YEAR,3,GETDATE()) > #DtTime
BEGIN
SET #DLSStart =(SELECT dbo.DC_GetDaylightSavingsTimeStart(CONVERT(varchar,DATEPART(YEAR,#DtTime))))
SET #DLSEnd =(SELECT dbo.DC_GetDaylightSavingsTimeEnd(CONVERT(varchar,DATEPART(YEAR,#DtTime))))
IF #DtTime BETWEEN #DLSStart AND #DLSEnd
BEGIN
SET #DLSActive = 1
END
--SET #DLSActive = 0
END
RETURN #DLSActive
END
GO
CREATE function [dbo].[DC_GetDaylightSavingsTimeStart]
(#Year varchar(4))
RETURNS smalldatetime
as
--Start date: We evaluate the day of the week corresponding to the first day of the month and find the second Sunday of March using a Case statement
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
GO
CREATE function [dbo].[DC_GetDaylightSavingsTimeEnd]
(#Year varchar(4))
RETURNS smalldatetime
as
-- End date: We evaluate the day of the week corresponding to the first day of the month and find the first Sunday of March using a Case statement
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
GO

Related

How to use a User Defined Function to iterate through column values

I have a function which converts UTC time into local time based on a certain office location.
The two parameters for the function are the UTC as datetime2 data type and Office as int data type.
SELECT [fn].[ConvertFromUTC]('2021-03-14 07:00:00', 5740)
Result: 2021-03-14 03:00:00
Here is the a table that I am wanting to convert all the UTC times to their local times based on the Support Site location (office).
What would be the best way to go about doing this? I tried using something like this but am not sure how to iterate through each support site and UTC time. Suggestions? Would a Cursor be ideal in this scenario?
DECLARE #meh nvarchar(50)
DECLARE #x datetime2
DECLARE #y int
Set #x = '2021-03-14 07:00:00'
Set #y = 20608
EXEC #meh = fn.ConvertFromUTC
#DateTime = #x,
#Office = #y
SELECT #meh
Here is the code for the function.
ALTER Function [fn].[ConvertFromUTC]
/*This function converts a DateTime from UTC to local time at each office.*/
(
#DateTime DATETIME2(0),
#Office int
)
RETURNS DATETIME2(0)
AS
BEGIN
DECLARE #TimeZone nvarchar(50)
DECLARE #Result DATETIME2(0)
IF #Office IN ('20608','5740')
BEGIN
SET #TimeZone = 'US Eastern Standard Time'
SET #Result = #DateTime at time zone 'UTC' at time zone #TimeZone
END
ELSE IF #Office = '597'
BEGIN
SET #TimeZone = 'W. Europe Standard Time'
SET #Result = #DateTime at time zone 'UTC' at time zone #TimeZone
SET #Result = DATEADD(HOUR,-1,#Result) /* 'AT TIME ZONE' functionality for Europe uses the wrong offset. This corrects it.*/
END
ELSE IF #Office = '6179' /*Not using 'AT TIME ZONE' functionality because it doesn't recognize that Australia observes Daylight Savings Time*/
BEGIN
/*DateTime is the end and beginning of Sydney daylight savings time in UTC.
Ends first Sunday in April at 2:00:00 and begins first Sunday in October at 3:00:00
In UTC, Ends first Saturday in April at 16:00:00 and begins first Saturday in October 16:00:00*/
IF #DateTime >= DATEADD(dd, (5-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(#DateTime)-1900) * 12 + 3,0))%7)),DATEADD(mm,(YEAR(#DateTime)-1900) * 12 + 3,0))+'16:00:00'
AND #DateTime < DATEADD(dd,0 + (5-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(#DateTime)-1900) * 12 + 9,0))%7)),DATEADD(mm,(YEAR(#DateTime)-1900) * 12 + 9,0))+'16:00:00'
BEGIN
SET #Result = DATEADD(hour,10,#DateTime)
END
ELSE
BEGIN
SET #Result = DATEADD(hour,11,#DateTime)
END
END
RETURN #Result
END
Firstly, what you have currently is termed a scalar User Defined Function (UDF).
You can use it quite simply like this:
SELECT fn.ConvertFromUTC(t.YourDate, 5740)
FROM YourTable t;
Scalar UDFs are slow for various reasons and should be avoided, and although most are helped by SQL Server 2019's UDF inlining, it is normally better to rewrite this as an inline Table Valued Function. This returns a rowset, and is a bit like a parameterized view.
To make it inline, it must be a single RETURN (SELECT statement
CREATE OR ALTER Function [fn].[ConvertFromUTC]
/*This function converts a DateTime from UTC to local time at each office.*/
(
#DateTime DATETIME2(0),
#Office int
)
RETURNS TABLE
AS RETURN
(
SELECT Result = CASE
WHEN #Office IN ('20608', '5740')
THEN #DateTime AT TIME ZONE 'UTC' AT TIME ZONE 'US Eastern Standard Time'
WHEN #Office = '597'
THEN DATEADD(HOUR, -1, #DateTime AT TIME ZONE 'UTC' AT TIME ZONE 'W. Europe Standard Time')
WHEN #Office = '6179'
THEN
/*DateTime is the end and beginning of Sydney daylight savings time in UTC.
Ends first Sunday in April at 2:00:00 and begins first Sunday in October at 3:00:00
In UTC, Ends first Saturday in April at 16:00:00 and begins first Saturday in October 16:00:00*/
CASE WHEN #DateTime >= DATEADD(dd, (5-(DATEDIFF(dd, 0, DATEADD(mm, (YEAR(#DateTime) - 1900) * 12 + 3, 0)) %7)), DATEADD(mm,(YEAR(#DateTime) - 1900) * 12 + 3, 0)) + '16:00:00'
AND #DateTime < DATEADD(dd, 0 + (5 - (DATEDIFF(dd, 0, DATEADD(mm, (YEAR(#DateTime)-1900) * 12 + 9, 0)) % 7)), DATEADD(mm,(YEAR(#DateTime) - 1900) * 12 + 9, 0)) + '16:00:00'
THEN DATEADD(hour,10,#DateTime)
ELSE DATEADD(hour,11,#DateTime)
END
END
);
GO
You can use it like this:
SELECT utc.Result
FROM YourTable t
CROSS APPLY fn.ConvertFromUTC(t.YourDate, 5740) utc;
-- Because it's only one value you can also do this
SELECT
(SELECT utc.Result FROM fn.ConvertFromUTC(t.YourDate, 5740))
FROM YourTable t;
I must say, I take issue with the original writer of this function, who clearly knows about AT TIME ZONE, but thinks it doesn't work properly.
SET #TimeZone = 'W. Europe Standard Time'
SET #Result = #DateTime at time zone 'UTC' at time zone #TimeZone
SET #Result = DATEADD(HOUR,-1,#Result) /* 'AT TIME ZONE' functionality for Europe uses the wrong offset. This corrects it.*/
Europe is not monolithic, presumably the correct time zone should have been 'GMT Standard Time'.
ELSE IF #Office = '6179' /*Not using 'AT TIME ZONE' functionality because it doesn't recognize that Australia observes Daylight Savings Time*/
BEGIN
/*DateTime is the end and beginning of Sydney daylight savings time in UTC.
Again, Australia is not one time zone, and I suspect that E. Australia Standard Time was used instead of 'AUS Eastern Standard Time'.
You can see all the available time zones on the server with select * from sys.time_zone_info, you can also add more time zones via Windows.
Another thing is that this function is supposed to converts a DateTime from UTC to local time, but using AT TIME ZONE twice is used only when converting from one time zone to another, it should only be done once if the time is already in 'UTC'.
One further point: I suggest you actually store the correct time zone within the table you are querying, then you can pass through the time zone instead of #Office and avoid a bunch of CASE expressions.

Declaring a date as being the last month day of another date

I'm trying to write a financial report on our SAP B1 system using SQL Server Studio.
In my report I want the information to be calculated on a month to month basis. In my report I have #start date as #Startofcurrentfinancialyear, and my end as DD+30 (because there are 31 days in the month) However I am wanting to have mm+1 and dd-1 to bring me to the last day in the month.
I plan on changing the report for each month to give me the following.
MM+1 (for month 2) and MM+2 - DD 1 to give me the date range for month 2 etc.
Currently, I can make this go based on the following: MM+0, DD+30, then going ahead doing DD+60 etc and calculating for each month how many days they are, but this will give me problems with leap years.
DECLARE #Start DATETIME = DATEADD(MM,-0,#StartOfCurrentFinancialYear)
DECLARE #End DATETIME = DATEADD(DD,+30,#StartOfCurrentFinancialYear)
I expect to be able to define a month for each section and give the last day of the defined month based on the parameters given above.
If you want the end of month, then in all supported versions of SQL Server, you can do:
DECLARE #Start DATETIME = DATEADD(MONTH, -0, #StartOfCurrentFinancialYear);
DECLARE #End DATETIME = EOMONTH(#StartOfCurrentFinancialYear);
If you are using an unsupported version, you can do date arithmetic:
DECLARE #End DATETIME = DATEADD(day, -1,
DATEADD(month, 1,
DATEADD(day,
1 - DAY(#StartOfCurrentFinancialYear),
#StartOfCurrentFinancialYear
),
)
);
This does the following calculation:
Innermost subquery dateadd() moves to the first day of the month.
Middle subquery adds one month.
Outer query subtracts one day.
If you want start of the month and end of the month you can get it by using DateAdd function like :
DECLARE #Start DATETIME = DATEADD(DAY, (DAY(GETDATE()) * -1) + 1, GETDATE())
DECLARE #End DATETIME = DATEADD(MONTH,1,DATEADD(DAY, DAY(GETDATE()) * -1, GETDATE()))
SELECT #Start, #End
you can replace GETDATE() with your date variable so after your replacement the code should be something like this :
DECLARE #StartOfCurrentFinancialYear AS DATE = '31/aug/2019'
DECLARE #Start DATETIME = DATEADD(DAY, (DAY(#StartOfCurrentFinancialYear) * -1) + 1, #StartOfCurrentFinancialYear)
DECLARE #End DATETIME = DATEADD(MONTH,1,DATEADD(DAY, DAY(#StartOfCurrentFinancialYear) * -1, #StartOfCurrentFinancialYear))
SELECT #Start, #End

How to get date from yyyy-mm-dd to yyyy-mm-dd in SQL?

I want to get date from yyyy-mm-dd to yyyy-mm-dd in SQL.
Example: I have two parameter #startdate : 2015-12-28 and #enddate : 2016-01-02, and database in SQLServer, datatype is varchar(10)
DATE_ORDER
28-12-2015
30-12-1996
29-12-2016
30-12-1997
24-12-2015
27-12-1993
03-01-2016
01-01-1992
02-01-2016
etc...
Ok,now I want to get data from #startdate : 2015-12-28 and #enddate : 2016-01-02. I use SELECT * FROM TABLE_X WHERE DATE_ORDER >= #startdate AND DATE_ORDER <= #enddate . But the results are not what I expected. Here are the results I want
28-12-2015
30-12-1996
29-12-2016
30-12-1997
01-01-1992
02-01-2016
I think to solve this problem, I need to do two things :
First, get date range from #startdate to #enddate , in here 28/12/2015, 29/12/2015, 30/12/2015, 31/12/2015, 01/01/2016, 02/01/2016.
The second: get the date in database same in range 28/12, 29/12, 30/12, 31/12, 01/01, 02/01, ignoring the year.
Can you give me some ideas about this ?
Your actual format is "105-italian" find details here.
You can convert your existing VARCHAR(10)-values with this line to real datetime
SELECT CONVERT(DATETIME,YourColumn,105)
Next thing to know is, that you should not use BETWEEN but rather >=StartDate AND < NakedDateOfTheFollowingDay to check date ranges
So to solve your need Get date-range from 2015-12-28 to 2016-01-02 you might do something like this:
DECLARE #Start DATETIME={d'2015-12-28'};
DECLARE #End DATETIME={d'2016-01-02'};
SELECT *
FROM YourTable
WHERE CONVERT(DATETIME,YourDateColumn,105)>=#Start AND CONVERT(DATETIME,YourDateColumn,105)<#End+1
Attention Be aware, that the conversion lets your expression be not sargable. No index will be used.
Better was to store your date as correctly typed data to avoid conversions...
Try this query
SET DATEFIRST 1
DECLARE #wk int SET #wk = 2
DECLARE #yr int SET #yr = 2011
--define start and end limits
DECLARE #todate datetime, #fromdate datetime
SELECT #fromdate = dateadd (week, #wk, dateadd (YEAR, #yr-1900, 0)) - 4 -
datepart(dw, dateadd (week, #wk, dateadd (YEAR, #yr-1900, 0)) - 4) + 1
SELECT #todate = #fromdate + 6
;WITH DateSequence( Date ) AS
(
SELECT #fromdate AS Date
UNION ALL
SELECT dateadd(DAY, 1, Date)
FROM DateSequence
WHERE Date < #todate
)
--select result
SELECT * FROM DateSequence OPTION (MaxRecursion 1000)
So, after the 2nd or 3rd edit, it slowly becomes clear, what you want (i hope).
So you REALLY WANT to get the dates with the year beeing ignored.
As someone pointed out already, date-values are stored internally not as string, but as internal datatype date (whatever that is in memory, i don't know).
If you want to compare DATES, you cannot do that with ignorance of any part. If you want to, you have to build a NEW date value of day and month of given row and a hard coded year (2000 or 1 or whatever) for EVERY row.
SELECT * FROM TABLE_X WHERE convert(date,'2000' + substring(convert(char(8),convert(datetime, 'DATE_ORDER', 105),112),5,4),112) >= #startdate AND convert(date,'2000' + substring(convert(char(8),convert(datetime, 'DATE_ORDER', 105),112),5,4),112) <= #enddate
If your startdate and enddate go OVER sylvester, you have to do 2 queries, on from startdate to 1231, one from 0101 to enddate.

How to get the year of a date, depending on the weeknumber

I know how to get the year out of a date. But how can I get the year of a date, depending on the week number of that date?
Example: 28-12-2015 has week number 1 (ISO_WEEK). In this case, the week number does not belong to 2015, but 2016. Also: 1-1-2016: Weeknumber 53, so 1-1-2016 does not belong to 2016, but to 2015.
Is there a function to do this easily in SQL?
No there isn't, but I created it myself.
CREATE FUNCTION [dbo].[ISO_YEAR](#date DATETIME)
returns SMALLINT
AS
BEGIN
DECLARE #ISO_YEAR SMALLINT = CASE
WHEN DATEPART(ISO_WEEK, #date)=1
AND MONTH(#date)=12 THEN YEAR(#date)+1
WHEN DATEPART(ISO_WEEK, #date)=53
AND MONTH(#date)=1 THEN YEAR(#date)-1
WHEN DATEPART(ISO_WEEK, #date)=52
AND Month(#date)=1 THEN YEAR(#date)-1
ELSE YEAR(#date)
END;
RETURN #ISO_YEAR;
END;
As per your question it seems you have two different dates and you are not able to identify in which year this date belongs to.
I have a small example here:
DECLARE #Date datetime, #Date1 datetime
SET #Date = '2015-12-28'
SET #Date1 = '2016-01-01'
SELECT CASE WHEN datepart(Wk , #Date ) < datepart(Wk , #Date1 ) THEN (datepart(YEAR , #Date1 ) + 1) ELSE (datepart(YEAR , #Date1 )) END AS GETYEAR
OUTPUT:
2016
I storngly recommend that this is not a correct way to know the year. it is not a +/- 1 logic here. there are max 53 weeks every year and you should always get the year from date which you have.
refer fiddle:
http://sqlfiddle.com/#!3/9eecb7/4510

SQl Server Timezone offset Calculation

I am storing all my dates in SQL Server Datetime fields in UTC Date.
There is a requirement where by I have to calculate local Datetime in a procedure from the UTC Date field, and for that i have the Time zone offset of the local datetime.
For ex. my Timezone offset is:'05:30:00'
and UTC Date is: 2013-02-09 08:34:12.037
Desired output: 2013-02-09 14:04:12.037
Now is there a simple way where of doing this without DateAdd and splitting the offset in hours and minutes.
You should be able to use the SWITCHOFFSET function. Here is an example:
declare #dt datetime;
set #dt = '2013-02-09 08:34:12.037';
select SWITCHOFFSET(CONVERT(datetimeoffset, #dt), '+05:30') as 'DATETIMEOFFSET',
CAST(SWITCHOFFSET(CONVERT(datetimeoffset, #dt), '+05:30') as datetime) as 'DATETIME'
-- Outputs:
--
-- 2013-02-09 14:04:12.0370000 +05:30 2013-02-09 14:04:12.037
Use the convert with 112:
declare #d datetime
set #d = getdate()
declare #s nvarchar(20)
set #s = convert(varchar, #d, 112)
print #s
That string will have the year, month, seconds etc.. always on the same position
Extract the desired part with substring:
print substring(#s, 1, 4) -- the year
Now recalculate the entire thing to minutes, by multiplying the hours by 60 and adding the minutes. Now substract your minutes delta from that number. Build a new string with the adjusted date-time, and convert that back to datetime. But... if you need to know the date as well, and you want to do it correct.... there is some coding left.
My advice: do use dateadd it's simple and correct (substract minutes is my advice).

Resources