DateTime conversion and comparison in SQL Query - sql

I have a sql query for pulling a report from a table. The idea is to pull the sum of counts, grouped by day of the week, in the local timezone. Dates in the table are stored in UTC.
SELECT (SUM(t.di1) + SUM(t.di2) + SUM(t.di3) + SUM(t.di4)) AS [ScanCount],
DATEPART(WEEKDAY, t.LocalTime) AS [weekday]
FROM (SELECT di1,
di2,
di3,
di4,
CreatedOnUTC AT TIME ZONE 'UTC' AT TIME ZONE 'Pacific Standard Time' AS LocalTime
FROM tableName
WHERE DeviceId = 649754) t
WHERE t.LocalTime > '03/16/2020 00:00'
AND t.LocalTime < '03/16/2020 23:59:59'
GROUP BY DATEPART(WEEKDAY, t.LocalTime)
ORDER BY DATEPART(WEEKDAY, t.LocalTime);
A query like this should only return a single day of the week count, but it returns 2 days. This obviously has something to do with the difference in time zones, whereas the UTC time contains dates from both 3/15 and 3/16. It seems that the conversion from UTC to Pacific Time works in the output but the UTC values are used in the where clause. How can I do this comparison to the new converted datetimes and not to the original UTC times?

Comparison between datetimeoffset and string literal works in UTC.
Simplest solution will be to convert datetimeoffset to datetime2.
Modify your inner query to:
cast(CreatedOnUTC AT TIME ZONE 'UTC' AT TIME ZONE 'Pacific Standard Time' as datetime2(0)) AS LocalTime

Related

Proper way of getting rows since a date accounting for DST?

I have a datetime column, changedate, that I need to use to get rows that have changed in the last week since 1PM. This column is unfortunately in local time (EST). The server is Microsoft SQL Server 2016.
Here is the query I have now:
DECLARE #since datetime = DATEADD(week,-1,SMALLDATETIMEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), DAY(GETDATE()), 13, 0)) AT TIME ZONE 'Eastern Standard Time'
SELECT * FROM table WHERE changedate AT TIME ZONE 'Eastern Standard Time' >= #since
Since I'm using AT TIME ZONE for both the column and #since, will this properly account for DST changes? That's my understanding per the documentation I've found, but I'm not 100% sure if that's how it works or if I'm missing something.
First, figure out the time you're wanting to compare against:
-- Get the current date in the given time zone
DECLARE #today date = convert(date, sysdatetimeoffset() AT TIME ZONE 'Eastern Standard Time')
-- Get the date one week ago
DECLARE #dateOneWeekAgo date = DATEADD(week, -1, #today)
-- Join the date with the desired time (local to the same time zone)
DECLARE #since datetime = convert(datetime, #dateOneWeekAgo) + convert(datetime, timefromparts(1, 0, 0, 0, 0))
Then just compare it:
SELECT * FROM table WHERE changedate >= #since
That assumes your changedate field is a datetime or datetime2. If it's a datetimeoffset, you should first convert the target value to a datetimeoffset in the same time zone and use that instead:
DECLARE #sinceDTO datetimeoffset = #since AT TIME ZONE 'Eastern Standard Time'
Regarding the approach you gave in the question, there two issues:
getdate() gives the time based on the server's local time zone. It's possible that it's not the same day in Eastern Time.
You should never apply a function (whether an intrinsic like AT TIME ZONE or something else) against a table field in a where clause, because it makes the query non-sargable. In other words, SQL would have to scan the entire table, rather than using an index. The bigger the table, the slower the query would take.

Convert date column to new UTC date column

I wrote the following query on a SQL Server DB to convert a datetime column to a UTC date column.
select datetime
, dateadd(minute,-datepart(tz,datetime),datetime) datetime_dt_utc
from table1
But I get the same same datetime for both columns.
What do I have to change to make it work?
Please try this:
DATEADD(hh, DATEDIFF(hh, GETDATE(), GETUTCDATE()), datetime)
The GETDATE() will return the current time
The GETUTCDATE() will retunr the current UTC time
The DATEDIFF function will calculate the difference between this two datetimes in hours
The DATEADD function will add this hours to your current datetime.
Another way is via usign the AT TIME ZONE
SELECT datetime AT TIME ZONE 'UTC' from table1
You can check your time zone with:
select CURRENT_TIMEZONE ( )
And use this:
SELECT datetime AT TIME ZONE 'Pacific Standard Time' AT TIME ZONE 'UTC'
from table1
If you are in 'Pacific Standard Time' timezone

Compare datetimeoffset with timezone SQL

I have a date emp_date which is of type datetime offset. This date is saved correctly in the database.
I want to retrieve all data having the date emp_date less than the actual time, including time zone.
For example, if the emp_date is as below:
2019-10-25 23:44:09.5798885 +14:00
And I am in a time zone of +2, then the data having the above date should not be retrieved.
Any idea of how to do this?
I want to pass the timezone of the user as parameter.
https://dzone.com/articles/dates-and-times-in-sql-server-at-time-zone
pass the needed time zone as parameter, then we can convert to the needed time zone.
DECLARE #TimeZone varchar(50)
SET #TimeZone = 'Central European Standard Time'
SELECT getdate() AT TIME ZONE #TimeZone AS date
SELECT getdate() AT TIME ZONE 'Central European Standard Time' AS date

SQL Server Timezone in where clause

I am storing datetime field with UTC time. We have a requirement to filter the records with CST timezone.
I have tried this query:
select id, CreatedOn,
CreatedOn AT TIME ZONE 'UTC' AT TIME ZONE 'Central Standard Time' AS LocalTime
from Status
WHERE CAST((CreatedOn AT TIME ZONE 'Central Standard Time') AS date) = '2018-09-06'
order by CreatedOn desc;
The issue is that it is also bringing those records which were saved on September 5th CST time in the evening when the UTC time was changed to 6th September. What is the correct way to filter out only September 6th records in CST time?
I found an issue with the query, I was missing 'UTC' AT TIME ZONE in the where clause. Here is the correct query which works:
select id, CreatedOn,
       CreatedOn AT TIME ZONE 'UTC' AT TIME ZONE 'Central Standard Time' AS LocalTime from CosmoStatus WHERE CAST((CreatedOn AT TIME ZONE
'UTC' AT TIME ZONE 'Central Standard Time') AS date) = '2018-09-06'
order by CreatedOn desc;

SQL DateTimes stored in UTC get beginning of day based on offset

This may be the most pathetic question ever asked related to SQL and date/time values, but I could use some help...
Trying to setup a function/job that will run at a specified time or times in eastern, mountain, central, and pacific time zones (in theory other zones would work too). The system will identify which users belong to each timezone and then output data from the system highlighting what they've accomplished for the current day.
Here is my challenge, I know all date/time values are stored on the SQL DB in UTC. I can apply the offset and convert those times to local time zones. Rather than convert tens of thousands of date/time values to local time and make comparisons there, it'd be cleaner (I think) to simply adjust the beginning and ending date/time values of UTC within the stored procedure.
On the west coast it is currently just about 2017-09-30 14:30:00 and in UTC is 2017-09-30 21:30:00, this clearly demonstrates a 7 hour time zone difference right now which means "today" from a user perspective technically started at 2017-09-30 07:00:00 and will end on 2017-10-01 06:59:999 in UTC.
What is the best way of establishing these date/time values for a users beginning of day and ending of day values?
UPDATES
I currently have this code...
DECLARE #InputDate as DateTime
DECLARE #InputEndDate as DateTime
DECLARE #InputDateWithOffset as DateTimeOffSet
DECLARE #InputEndDateWithOffset as DateTimeOffSet
SET #InputDate = '2017-09-28'
SET #InputDateWithOffset = #InputDate AT TIME ZONE 'UTC' AT TIME ZONE 'Pacific Standard Time'
SET #InputEndDate = DATEADD(day, 1, DATEADD(ms, -3, #InputDate))
SET #InputEndDateWithOffset = #InputEndDate AT TIME ZONE 'UTC' AT TIME ZONE 'Pacific Standard Time'
SELECT
#InputDate AS InputDate, #InputEndDate AS InputEndDate,
#InputDateWithOffset AS InputDateWithOffset,
#InputEndDateWithOffset AS InputEndDateWithOffset
Which outputs the following:
The last two columns appear to be correct as it would represent both the beginning of the Input Date and the ending of the Input Date as the Input Date is going to be the local date of the execution...
When I take the #InputDateWithOffset and #InputEndDateWithOffset against my table values with datetimes in UTC, it appears the only dates being returned are those that fall on 2017-09-28 and seems to disregard the comparisons to the Offset date/times.
Your update is mostly correct. However, you're missing a "start of day" operation, which needs to be done in local time.
Consider:
DECLARE #InputStartUTC as DATETIME, #InputEndUTC as DATETIME
DECLARE #InputStartDTO as DATETIMEOFFSET, #InputEndDTO as DATETIMEOFFSET
DECLARE #InputStartDTOatStartOfDay as DATETIMEOFFSET,
#InputEndDTOatStartOfDay as DATETIMEOFFSET
DECLARE #tz as VARCHAR(50) = 'Pacific Standard Time'
SET #InputStartUTC = '2017-09-28 00:00:00'
SET #InputStartDTO = #InputStartUTC AT TIME ZONE 'UTC' AT TIME ZONE #tz
SET #InputStartDTOatStartOfDay = CAST(CAST(#InputStartDTO as DATE) as DATETIME)
AT TIME ZONE #tz
SET #InputEndUTC = DATEADD(day, 1, #InputStartUTC)
SET #InputEndDTO = #InputEndUTC AT TIME ZONE 'UTC' AT TIME ZONE #tz
SET #InputEndDTOatStartOfDay = CAST(CAST(#InputEndDTO as DATE) as DATETIME)
AT TIME ZONE #tz
SELECT
#InputStartUTC as InputStartUTC, #InputEndUTC as InputEndUTC,
#InputStartDTO as InputStartDTO, #InputEndDTO as InputEndDTO,
#InputStartDTOatStartOfDay as InputStartDTOatStartOfDay,
#InputEndDTOatStartOfDay as InputEndDTOatStartOfDay
Also, notice I did not subtract three milliseconds from your end date. Rather than trying to figure out .997 or .999 or whatever, the better approach is to query using a half-open interval. In other words, start <= value AND end > value.