(T-SQL) Did daylight savings occur in the last hour? - sql-server

I have a process that is going to use table partitioning (each partition is 1 hour) and I need to handle the daylight savings flips when archiving out the data.
For instance, this past weekend it went from 1:59:59 to 1:00:00, so the second time that the partitioning code ran at 1:05am nothing would occur - the midnight hour had already been SWITCHed out.
However, when spring rolls around, the time goes from 1:59am to 3:00am, so when the job runs at 3:05, it would SWITCH out 2am.... leaving the data from the 1am hour in the original table.
In theory I can just look for the oldest not-current partition with data and flip that one (partition key is a default getdate() constraint), but I was wondering if there was some way to use AT TIME ZONE to determine that daylight savings had "occurred", so that we could have different code to handle that older hour still being out there.
Thanks.

Maybe something like this. Basically take your current getdate() and use AT TIME ZONE with any time zone that observes. Then use DATEPART tz and compare your current and before.
Regardless of what the server time zone is, using AT TIME ZONE will get you the offset for a particular datetime value in that time zone.
For comparison of before I think you'd need to use 2 hours, 1 for the switch and 1 for how far back you want to check.
Give this a look:
DECLARE #BeforeDate DATETIME = '2018-11-04 1:59' --Before the change
DECLARE #AfterDate DATETIME = '2018-11-04 3:00' --After the change
--use can use AT TIME ZONE with DATEPART tz which tells you offset in minutes
--I'm in central, any should work for any that observe the time change
--Your offset is different because of the change.
SELECT DATEPART(tz, #BeforeDate AT TIME ZONE 'Central Standard Time')
SELECT DATEPART(tz, #AfterDate AT TIME ZONE 'Central Standard Time')
--using the above you could possibly compare current offset to 2 hours prior to see if they changed. 2 hours, 1 for the switch and 1 for how far back you want to compare.
DECLARE #CurrentDate DATETIME = '2018-11-04 3:00' --"simulate" getdate() as of the time change
DECLARE #PriorOffSet INT = (SELECT DATEPART(tz, DATEADD(HOUR, -2, #CurrentDate) AT TIME ZONE 'Central Standard Time')) --You'd have to subtract 2 hours to account for the hour shift and the hour back you want to check.
DECLARE #CurrentOffset INT = (SELECT DATEPART(tz, #CurrentDate AT TIME ZONE 'Central Standard Time'))
SELECT #PriorOffSet, #CurrentOffset
IF #PriorOffSet <> #CurrentOffset
SELECT 'Time changed in the last hour'
ELSE
SELECT 'No time change in the last hour'

Related

Time difference calculation based on timezone and offset

We transfer data from Oracle SQL to SQL Server in the near real-time data warehouse. (Oracle is a transactional database) The time changes twice a year for the data we use. We have been having issues when calculating DateTime differences and I have to work on a short-term and long-term solutions where I need advice.
New data coming in :
I can create tables from scratch and use datetimeoffset data field for all the time variables. Would this
display dates in SQL Server queries in the local time zone and save them in their actual UTC time? And when I
calculate the differences, would it give the correct difference?
Existing data without offset information:
I already have data converted into the current timezone. We have 2 timezones AEST and AEDT.
When we calculate time differences for KPIs it gives incorrect times. Any ideas on how it can be resolved?
Can creating a function that converts the dates and times into UTC and subtracts the difference and returns the value would work?
An example below :
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
For your example, if you include the time zone info with the data, the date calculations work out
DECLARE #startdate datetimeoffset(0) = '2022-10-01 23:13:00 +10:00'; --UTC 2022-10-01 13:13:00.00
DECLARE #enddate datetimeoffset(0) = '2022-10-02 12:08:00 +11:00' --UTC 2022-10-02 01:08:00.00
declare #minutes int = datediff(minute, #startdate, #enddate);
select #startdate at time zone 'utc',
#enddate at time zone 'utc';
select [hours] = #minutes / 60,
[minutes] = #minutes - 60 * (#minutes / 60)
To address the existing data, you can do a one-time conversion to datetimeoffset by running it through at time zone with the appropriate time zone name. In my example above, I used UTC but you can find all of the supported time zones in sys.time_zone_info.
As to your first question about the internal storage format and display, I'm not sure if it uses UTC internally or not. As to display, it will, by default, output them with the time zone that you stored them with. That is, if you stored a given value with +10:00 as the offset, that's how it will display. You can convert via methods already described above or with switchoffset(). Lastly, if it matters, you can get just the offset with datepart noting that it returns the offset in number of minutes (to conform with datepart needing to return an int).

SQL Server UTC to Local time 'at time zone' not offseting time

I am trying to convert from UTC to Eastern Standard Time. But the code below is not producing the result that I expect.
SELECT
GETUTCDATE() AS UTC_Date, --original datetime value
GETUTCDATE() AT TIME ZONE 'Eastern Standard Time' AS ETZ
The result is
UTC_Date = 2022-01-16 01:15:39.920
ETZ =2022-01-16 01:15:39.920 -05:00
How can I get a simple date time with the offset applied instead of just added on to the end of the string?

Snowflake CONVERT_TIMEZONE bug?

In Snowflake, when converting some local dates to UTC and then back to local timezone using CONVERT_TIMEZONE function, the end result is off by one hour. For example:
ALTER SESSION SET TIMEZONE = 'Canada/Eastern';
select
cast('1949-04-24' as timestamp) as date_local -- because TIMEZONE = 'Canada/Eastern'
, convert_timezone('Canada/Eastern', 'UTC', '1949-04-24') as date_utc
, convert_timezone('UTC', 'Canada/Eastern', convert_timezone('Canada/Eastern', 'UTC', '1949-04-24')) as date_local1
The results are:
DATE_LOCAL
DATE_UTC
DATE_LOCAL1
1949-04-24 00:00:00.0
1949-04-24 05:00:00.0
1949-04-24 01:00:00.0
I expect both DATE_LOCAL and DATE_LOCAL1 to be identical, however DATE_LOCAL1 is one hour off.
Is this expected? Am I missing something or there is a bug in the CONVERT_TIMEZONE function?
This is not a bug. This is about Daylight Saving Time (DST). In 1949, DST started on Sunday, 24 April, 00:00. So when you convert it to UTC, It becomes 1949-04-24 05:00:00.0. When you convert it back, it becomes 01:00 because of DST, because it's the time that the clocks were turned forward one hour.

Convert UTC on Azure to EST Time (including Daylight saving Time-dynamic version)

I have a question about converting UTC time zone to EST time zone since I am still new to SQL language and Azure platform. In log file, I used system built-in function "GetDate()" in log file to get the Date/Time. However, while accessing Azure database on my SSMS, using system-built in function (GetDate()) gives me the datetime in UTC time zone, which is 4 hours ahead of Eastern Time Zone (EST). I have asked similar question before for converting UTC to EST here
, and #DanGuzman helped me fix my code. But this question is more about converting UTC to EST (considering Daylight saving time dynamically). Below is my code so far, and I used this link as a reference. However, I would like to make my code dynamic so that I can keep using it for 2020, 2021 as well.
Below code works ONLY for 2019 (since Daylight Saving starts on March 10,2019 until November 3, 2019. Within the date range, below code forward one hour of EST time during daylight saving time range.
CREATE FUNCTION [dbo].[EST_GetDateTime]
(
-- no parameter
)
RETURNS datetime
AS
BEGIN
DECLARE
#EST datetime,
#StandardOffset int,
#DST datetime, -- Daylight Saving Time
#SSM datetime, -- Second Sunday in March
#FSN datetime -- First Sunday in November
-- get DST Range
set #EST = CAST(DATEADD(hh,-5,GETDATE()) AS DATETIME)
set #StandardOffset = 0
set #SSM = datename(year,#EST) + '0310' -- Set which day daylight saving start (for 2019, March 10)
set #SSM = dateadd(hour,2,dateadd(day,datepart(dw,#SSM)*-1+1,#SSM))
set #FSN = datename(year,#EST) + '1103' -- Set which day daylight saving start (for 2019, March 10)
set #FSN = dateadd(second,-1,dateadd(hour,2,dateadd(day,datepart(dw,#FSN)*-1+1,#FSN)))
-- add an hour to #StandardOffset if #EST is in DST range
if #EST between #SSM and #FSN
set #StandardOffset = #StandardOffset + 1
-- convert to DST
set #EST = CAST(DATEADD(hh,-5+#StandardOffset ,GETDATE()) AS DATETIME)
RETURN #EST
END
GO
Can someone please give any suggestions how to improve my existing code (able to work dynamically) so that I do not need to change the function every single year for adjusting day light saving.
Since Azure SQL is ahead of the on-premises version, I think you can use the syntax select getutcdate() at time zone 'UTC' at time zone 'Eastern Standard Time'. Also, I'd switch to getutcdate() as that should be invariant regardless of the server's TZ. More information on at time zone here.
One method is a function that converts SYSDATETIMEOFFSET() to EST and converts the result to datetime:
CREATE FUNCTION dbo.EST_GetDateTime()
RETURNS datetime
AS
BEGIN
RETURN(SELECT CAST(SYSDATETIMEOFFSET() AT TIME ZONE 'Eastern Standard Time' AS datetime));
END
GO
--example usage
SELECT dbo.EST_GetDateTime();
GO
This should do exactly what you need.
SELECT CONVERT(DATETIME,GETDATE() AT TIME ZONE (SELECT CURRENT_TIMEZONE_ID()) AT TIME ZONE 'Eastern Standard Time')
Explanation: This gets the current datetime in the server's local timezone using CURRENT_TIMEZONE_ID(). We then use AT TIME ZONE to make it a datetimeoffset, then we cut that datetimeoffset over to requested timezone... here 'Eastern Standard Time'. Lastly the whole thing is wrapped in a CONVERT() to cut the datetimeoffset objects over to a proper datetime datatype.

t-SQL Selecting a records where an event happened today accounting for midnight and timezones

I have a table with a date column and time column and it gets populated whenever the user performs a scheduled task. They generally have a 2hr window to perform the task and I can't seem to get my mind around how to see if that task was done today or not.
For example, if it is 12:30 AM, I need to check whether it was done at 11:30 PM on the prior day, or at 12:01 AM today. I store all the time and dates as central time and have an offset number to adjust for other timezones.
Here is where I'm stuck, this works for something done last night, but not if it was done today after midnight (#DatePartOfDateTime is the date I am checking (it has already been corrected for timezone) and #EndTime is the time I am checking to see if it happened):
select 1 from tblMedsDispensed
where DatePassed =
case when (#EndTime = '23:59' or #Offset < 0 and #EndTime < '21:59')
and TimePassed < '01:00'
-- For midnight, if the time passed is < 01:00 then roll it back a day
then dateadd(DAY,1,#DatePartOfDateTime )
else #DatePartOfDateTime end
and tblMedsDispensed.patdrugs_fk = tblPatDrugs.PatDrugs_id
Any ideas?
Here's a guess. I don't think you need to mess around with all those variables and time components. What you want to do is sort of rotate the clock so that your logical start of day lines up with a calendar day so the date comparisons are easy.
select *
from tblMedsDispensed
where
cast(dateadd(hh, #Offset - 1, DatePassed) as date) = cast('<literal date>' as date)
EDIT: I looked over your question again and I see you've got two columns. The idea here is the adjust the date value backward one day when the hour component is less than 1. I'm still not clear whether your tolerance is one hour or two hours.
dateadd(
hh,
case when hour(dateadd(hh, #Offset, <TimeColumn>)) < 1 then -1 else 0 end,
<DateColumn>
)

Resources