US timezone conversion into EST in sql server - sql-server

My table has 3 column, id, timezone, closingTime(HH:mm 24hrs format). I am trying to convert every entry in this table in EST format. I have already created a query and it is working fine. But, the issue is I am passing -5:00, -4:00 inside the query. Is there a way sql internally handles it return me the timezone in EST format. What about the day light saving, does sql server internally handles it?
with est_table as (select id, closingTime ,
case
when timeZone='EST' THEN cast(cast(CONVERT(VARCHAR, CONCAT(CAST( GETDATE() AS Date ),' ', closingTime, ' ','-5:00' ),100) as DATETIMEOFFSET)at time zone 'Eastern Standard Time' as datetime)
when timeZone='CST' THEN cast(cast(CONVERT(VARCHAR, CONCAT(CAST( GETDATE() AS Date ),' ', closingTime, ' ','-6:00' ),100) as DATETIMEOFFSET)at time zone 'Eastern Standard Time' as datetime)
when timeZone='MDT' THEN cast(cast(CONVERT(VARCHAR, CONCAT(CAST( GETDATE() AS Date ),' ', closingTime, ' ','-7:00' ),100) as DATETIMEOFFSET)at time zone 'Eastern Standard Time' as datetime)
when timeZone='PST' THEN cast(cast(CONVERT(VARCHAR, CONCAT(CAST( GETDATE() AS Date ),' ', closingTime, ' ','-8:00' ),100) as DATETIMEOFFSET)at time zone 'Eastern Standard Time' as datetime)
else CAST( GETDATE() AS Date )
END as EST
from test_timezone

Related

UTC time zone calculation

We have data stored in our timezone ('AUS Eastern Standard Time') into SQL Server database, and we have to do calculations. Is it possible to convert both dates to UTC for calculations, so that it calculates the right difference?
DECLARE #startdate DateTime = '2022-10-01 23:13:00.000'; --UTC 2022-10-01 13:13:00.00
DECLARE #enddate DateTime = '2022-10-02 12:08:00.000' --UTC 2022-10-02 01:08:00.00
select CAST((#enddate - #startdate) as time(0)) 'Difference'
Time difference: 12:55:00
Actual Time Difference: 11:55:00
Datetime values will be problematic without an offset as #Larnu suggest in comments. This will occur when clocks are set back and result in overlapping times before and after the time change. You'll need rules to determine the offset in that case.
In your example, where clocks are set forward, there should be no overlap or ambiguity. This allows one to use AT DATE TIME AUS Eastern Standard Time') to convert the datetime values to datetimeoffset for the duration calculation. If an invalid datetime value is stored (within the gap when clocks should have been change forward), AT DATE TIME will return the offset after the clock change.
DECLARE #startdate DateTime = '2022-10-01 23:13:00.000'; --UTC 2022-10-01 13:13:00.00
DECLARE #enddate DateTime = '2022-10-02 12:08:00.000'; --UTC 2022-10-02 01:08:00.00
SELECT DATEADD(second
, DATEDIFF(second, #startdate AT TIME ZONE 'AUS Eastern Standard Time'
, #enddate AT TIME ZONE 'AUS Eastern Standard Time')
, CAST('00:00:00' AS time(0)));
So you'll have one hour of the year you'll need to develop business rules for. AT DATE TIME will return the offset before the change during the ambiguous interval when clocks are set backwards.
DATETIME is a not recommended because it's a datatype with plenty of erronous features and it is not accurate.
Use DATETIME2 instaed (MS recommendation)
Then SET the TIMEZONE with the operator AT TIME ZONE :
Use ALSO DATETDIFF to get a difference because a minus sign must only apply to numbers not for date/time calculus...
Finally :
DECLARE #startdate DateTime2 = '2022-10-01 23:13:00.000';
DECLARE #enddate DateTime2 = '2022-10-02 12:08:00.000';
DECLARE #difsecond INT = DATEDIFF(s, #startdate AT TIME ZONE 'AUS Eastern Standard Time', #enddate AT TIME ZONE 'AUS Eastern Standard Time')
SELECT CAST(DATEADD(s, #difsecond, CAST('2000-01-01' AS DATETIME2(0))) AS TIME)

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.

Convert a date format as text - day month

Insert a column: received_by as text in the format of Day Month.
i.e. 25/06/2018 should be inserted as 25 June. The format dd/mm/yyyy should be converted into day month - whereby month should be written out.
You can use below select statement to get the desired return
select FORMAT(convert(datetime, '25/06/2018', 103), 'dd MMMM')
Or You can create the custom function in SQL server which will take a date in 'dd/mm/yyyy' format and return day and month as required. use below code to achieve the desired result.
create function GetDateDaynMonth(#date varchar(20))
returns varchar(20)
as
begin
declare #DaynMonth varchar (20)
SELECT #DaynMonth = FORMAT (convert(datetime, #date, 103), 'dd MMMM')
return #DaynMonth;
end
go
select dbo.GetDateDaynMonth('25/06/2018')

Using datetimeoffset dates in where clause date range filter

Using SQL Server 2016. This is in a SP for a report. When given date range of 02/22/2017, report is including items for 02/21/2017. Dates are stored in the db as DateTimeOffset.
In this query I am amtrying to return on the 22nd, but I am also getting 21st.
#start and #end represent the date range entered by the user. #storeddate is the date from the db used to filter the report. My plan is to convert the offset date then pull the 'date' part out to filter on, but it isnt working.
declare #start date = '2017-02-22';
declare #end date = '2017-02-22';
declare #storeddate datetimeoffset = '2017-02-22 00:00:19.0000000 +00:00';
;with dates as
(
select #storeddate as 'raw'
, #storeddate AT TIME ZONE 'Pacific Standard Time' as offset
, CONVERT(datetime, #storeddate) AT TIME ZONE 'Pacific Standard Time' as dt
, CONVERT(datetime, #storeddate AT TIME ZONE 'Pacific Standard Time') as d
, CAST(CONVERT(datetime, #storeddate) AT TIME ZONE 'Pacific Standard Time' as date) as 'casted'
, #start as 'start'
, #end as 'end'
)
select * from dates
WHERE (
CAST(CONVERT(datetime, #storeddate) AT TIME ZONE 'Pacific Standard Time' as date) >= #start
AND CAST(CONVERT(datetime, #storeddate) AT TIME ZONE 'Pacific Standard Time' as date) < DATEADD(day, 1, #end) )
Edit to add note:
Its kind of an odd scenario. This is an intranet web app used only in oregon. The web developer used some javascript datepicker library that changed all his dates to UTC and he couldn't figure out how to change them back so he just stored them in the db as datetimeoffset. So now I have to change all the reports to show the correct dates. The 'At Time Zone' fixed the displaying of the dates in the reports, but it is not working in the where clauses for the date range filters.
Does this have to do with your timezones? You're putting in #storeddate without an offset and then it looks like you're evaluating for Pacific time (-8:00, isn't it?). It seems like that would shift the returned data.
Edit: Try using dateadd to modify the date:
declare #start date = '2017-02-22';
declare #end date = '2017-02-22';
declare #storeddate table (rawdate datetimeoffset)
insert into #storeddate
values( '2017-02-22 00:00:19.0000000 +00:00')
,('2017-02-21 00:00:19.0000000 +00:00')
,('2017-02-22 00:18:19.0000000 +00:00')
,('2017-02-23 00:18:19.0000000 +00:00')
;with dates as
(
select rawdate as 'raw'
, cast(dateadd(hh,-8,rawdate) as date) as offset
, #start as 'start'
, #end as 'end'
from #storeddate
)
select * from dates
WHERE (
offset >= #start
and offset < DATEADD(day, 1, #end))
I had the closing bracket on the CONVERT in the wrong place, so I was converting the #storeddate to date instead of the #storedate AT TIME ZONE. So the answer was this:
WHERE CAST(CONVERT(datetime, #storeddate AT TIME ZONE 'Pacific Standard Time') as date)
instead of
WHERE CAST(CONVERT(datetime, #storeddate) AT TIME ZONE 'Pacific Standard Time' as date)
etc...

SQL Server: Convert a month name (string) to a date

I have a table with a month name in it as type varchar. e.g. "November". Can I convert this to a date field?
CONVERT(DATETIME,main.ReportMonth) AS ReportMonthDate
CAST(main.ReportMonth AS DATETIME) AS ReportMonthDate
both result in a conversion failure.
I'm using SQL Server 2008.
You can hard code a string value at the end of your input value. Something like this.
declare #ReportMonth varchar(10) = 'November'
select cast(#ReportMonth + ' 1, 2015' as date)
Or if you want to make the year portion be dynamic based on the current date you could modify that slightly like this.
select cast(#ReportMonth + ' 1, ' + cast(datepart(year, getdate()) as char(4)) as date)

Resources