Using AT TIME ZONE to get current time in specified time zone - sql-server

I am trying to use the new AT TIME ZONE syntax in SQL Server 2016 and Azure SQL. I'm just trying to get the current time in London as a datetime, adjusted for daylight saving. At the time of running all of the commands below, the time in London was 3.27am.
The first step is to get a datetimeoffset, which I can successfully do as follows:
DECLARE #dto datetimeoffset
SET #dto = (SELECT GETUTCDATE() AT TIME ZONE 'GMT Standard Time')
SELECT #dto
This returns a value as I would expect:
2016-04-04 02:27:54.0200000 +01:00
Next, I want to convert that to a datetime, which is what my applications expect. I've tried three different approaches, none of which give me the result I'm looking for:
SELECT SWITCHOFFSET(#dto,'+00:00')
-- Returns 2016-04-04 01:27:54.0200000 +00:00
SELECT CONVERT(datetime, #dto)
-- Returns 2016-04-04 02:27:54.020
SELECT CONVERT(datetime2, #dto)
-- Returns 2016-04-04 02:27:54.0200000
I feel like I'm missing something obvious - is there an easy way to take a datetimeoffset and return just the date/time part at that offset?

The first line of your code contains the fault:
SELECT GETUTCDATE() AT TIME ZONE 'GMT Standard Time'
GETUTCDATE() returns a datetime, which has no time zone offset information. Thus as described in the MSDN documentation:
If inputdate is provided without offset information, the function applies the offset of the time zone assuming that inputdate value is provided in the target time zone.
So, even though you retrieved the UTC time, you erroneously asserted that the value was in London time (which is UTC+1 for daylight saving time at this date).
The easiest way to handle this is to just fetch the UTC time as a datetimeoffset to begin with.
SELECT SYSDATETIMEOFFSET() AT TIME ZONE 'GMT Standard Time'
This invokes the conversion functionality of AT TIME ZONE, which in the docs states:
If inputdate is provided as a datetimeoffset value, then AT TIME ZONE clause converts it into the target time zone using time zone conversion rules.
Consider that if your data actually comes from a datetime field somewhere, you might need to use both parts of the functionality, like this:
SELECT mydatetimefield AT TIME ZONE 'UTC' AT TIME ZONE 'GMT Standard Time'
The first call to AT TIME ZONE asserts the value is in UTC, giving a datetimeoffset to the second call, which converts it to London time.
The output of any of these is a datetimeoffset, which you can cast or convert to a datetime or datetime2 exactly as you showed in your original question. (Don't use switchoffset for this.)
Also, the Windows time zone identifier for London is always "GMT Standard Time". It is inclusive of both Greenwich Mean Time and British Summer Time, with the appropriate transitions between them. Do not try change it to "GMT Daylight Time" - that identifier doesn't exist. This is also covered in the timezone tag wiki, in the section on Windows time zones.

Since I was unable to find this anywhere else I thought I'd share. You can get the offset in minutes by using datepart (tz) with AT TIME ZONE.
datepart(tz,UTC_Date AT TIME ZONE 'Central Standard Time')
select dateadd(MINUTE,datepart(tz,cast('2018-07-02 17:54:41.537' as datetime) AT Time Zone 'Central Standard Time'),'2018-07-02 17:54:41.537') as CentralTime
returns
CentralTime
2018-07-02 12:54:41.537

I suggest you only store this as a string and qualify that it is a local time representation, otherwise the time SQL Server stores internally would be the wrong actual/physical time, if the server time is correct, but just not in the same time zone. It is why you cannot use convert to represent the same because you are actually changing the datetime value from the real time of occurrence and not just re-representing it i.e. Datetime is always stored as UTC, but entered and displayed in the timezone of the server, so if you enter a local time in a datetime field, the server interprets that time as the time in the server time zone and not the actual time of the event, which results in stored time navigation/deviation if the local time is not the same as the server> Should you then feed the same data to other systems in different time zones, they will have incorrect data and it can get messy. Store the right value in the datetime field and display it as you wish as a string.

Related

Aggregating Data by Month, but first converting timezones - SQL Server

I have a database that records time in UTC. I need to convert this time to Local Time, and then aggregate the data by Month on this new Local Time that I have created. Everything works fine if I just aggregate on the original UTC Time, but of course, the value that I get at the end is slightly off which does not work for me. However, if I try to aggregate on Local Time, I just can't get it to work.
I am unable to use TIMESTAMP_LOCAL in my GROUP BY Clause, and neither am I able to use the whole YEAR(CONVERT(DATETIME2(3), TIMESTAMP_UTC AT TIME ZONE 'UTC' AT TIME ZONE 'Central European Standard Time'). Same goes for the entire DATEFROMPARTS Function.
SELECT
SUM([VALUE]) AS Required_Value
,DATEFROMPARTS(YEAR(CONVERT(DATETIME2(3), TIMESTAMP_UTC AT TIME ZONE 'UTC' AT TIME ZONE 'Central European Standard Time')), MONTH(CONVERT(DATETIME2(3), TIMESTAMP_UTC AT TIME ZONE 'UTC' AT TIME ZONE 'Central European Standard Time')), 1) AS TIMESTAMP_LOCAL
FROM [DB].[TABLE]
GROUP BY ???

Add timezone offset retroactivly to SQL Server datetime entries

We have our time saved without a timezone offset inside our SQL Server database. The time is actually Central Europe Standard Time but due to no timezone offset in the time string it is treated as UTC. This now creates a bunch of problems regarding daylight saving times.
My question would be: is there a way to retroactively convert the Time with offset to the correct CEST Time.
For example my time string in my database is '2022-10-30 02:00' and should be converted to '2022-10-30 00:00+2' as well as '2022-10-30 03:00' to '2022-10-30 01:00+1'.
There is an option to convert a datetime object to another timezone with "AT TIME ZONE" but this didn't help much due to the date objects being treated as UTC in the database, but we need to convert them to the UTC+[offset] format. Also due to the daylight saving time changing timezone offsets during the year, we can't subtract the timezone offsets with a set value.

Convert UTC Time to Multiple Time zones

The below SQL will convert the UTC time to my local time in BRISBANE, Australia.
I would like to get the Local time in Sydney which is 1 hour ahead of Brisbane time considering the DST
SELECT
GETUTCDATE() AS UTCTime,
CAST(GETUTCDATE() AT TIME ZONE 'UTC' AT TIME ZONE 'E. Australia Standard Time' AS DATETIME2(2)) AS BrisbaneTime
Need some assistance in getting the local time in Sydney.
All of the possible timezones are defined in the sys.time_zone_info system table. From there you can select the appropriate timeone to use.
You would use the same query but with the other timezone

How to display local time in Excel when it is stored in UTC in SQL?

I'm trying to show a time in Excel that is part of a view in SQL Server. In SQL Server, it is a datetime2 column storing dates in UTC. The result should be a time in W. Europe Standard Time.
For example, when the SQL Server value is 2019-07-02 09:44:00, Excel should show 11:44.
The view I'm working on is used only for displaying the data in Excel to provide a detailed report on start/end times of employees and machines. I either have to provide the correct data from the view or configure the Excel sheet to do the translation. Both are acceptable, although it seems cleaner to do the translation in Excel.
I have tried using the datetime2 column value as-is, but I have also tried to convert the datetime2 to a datetimeoffset in my view using AT TIME ZONE. I can set the cell formatting to Time and even specify a location. However, this is not enough to translate the time. The result of the conversion is
2019-07-02 09:44:00.0000000 +02:00
In Excel, the result of both methods is 09:44:00.
I have also tried to pre-format the date in my view. While the result of AT TIME ZONE contains all the information needed, I failed to find a way to add the 2 hours to the time and format it in "hh:mm". Is there a way to do this?
My query so far is quite basic for this field:
SELECT TOP 100
dbo.tasks.EmployeeStart AT TIME ZONE 'W. Europe Standard Time' AS 'Start Employee'
This is connected to Excel by using the Data > retrieve data options in the ribbon.
Use the CONVERT function to convert from DatetimeOffset (the result of AT TIME ZONE) back to datetime.
select top 100
Convert(datetime, current_timestamp at time zone 'UTC' at time zone 'W. Europe Standard Time', 0) 'Start Employee'
You can test this if your SQL Version is >= 2016.
SELECT TOP 100 (dbo.tasks.EmployeeStart AT TIME ZONE 'UTC') AT TIME ZONE 'W. Europe Standard Time' AS 'Start Employee';

Azure SQL display local time

I'm trying to display datetime values in local time. By default, Azure SQL Database stores dates and times in UTC, there is no way around this. (This was a pain when migrating from on premise SQL Server.) I would like to display a stored time value in Central European time.
Now the local time is 11:30 (CET). UTC time is 10:30.
DECLARE #TestTime DATETIME;
SET #TestTime = '2016-11-02 10:30:00'
SELECT #TestTime
--Returns 2016-11-02 10:30:00
SELECT #TestTime AT TIME ZONE 'Central European Standard Time'
--Returns 02 November 2016 10:30:00 +01:00
I need to return 2016-11-02 11:30:00 somehow. Now for the fun part:
As has been suggested here:
SELECT convert(DATETIME,#TestTime AT TIME ZONE 'Central European Standard Time',1)
--Returns 2016-11-02 09:30:00 So instead of adding the timezonedifference it subtracts it.
This works, but makes me sick:
SELECT DATEADD(MINUTE,DATEPART(tz,#TestTime AT TIME ZONE 'Central European Standard Time'),#TestTime)
--Returns 2016-11-02 11:30:00
A similar solution has been suggested here, but it plays with string operations. My suspicion is that something is wrong in AT TIME ZONE; it should have displayed 11:30 +1, and not 10:30 +1, no?
Is there really no proper way to display a UTC time in local time? This "hacking around it" feels awfully dirty, especially since at any point in time it just might stop working (e.g. Microsoft fixes / introduces a bug).
Thanks!
The AT TIME ZONE statement performs two distinct operations:
To assert that a datetime (or datetime2) is in a particular time zone, thus looking up the correct offset for that zone and returning a datetimeoffset value with the correct offset applied.
To convert a datetimeoffset value to a different time zone, using the offset from the source value to pin down an exact point in time, then looking up the new offset for the requested time zone and applying it. This returns a datetimeoffset with a potentially different local time and offset than the original, but representing the same moment in time.
You are using the first part only. To convert a datetime from UTC to a specific time zone, you'll need to use both.
SELECT #TestTime AT TIME ZONE 'UTC' AT TIME ZONE 'Central European Standard Time'
The first AT TIME ZONE asserts that the input datetime is in UTC, resulting in a datetimeoffset that has +00:00 for the offset. The second AT TIME ZONE converts that to the time zone requested.

Resources