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.
Related
I have a date column called actual_cargo_ready_date_local, it is of type TIMESTAMP_NTZ(9).
Next we're trying to compare a column with UTC values, say the column name is booked_at.
Is it correct to do the comparison as follows;
booked_at > actual_cargo_ready_date_local::timestamp_ntz
Essentially, my question is can we compare a column with values in UTC with timestamp column with values in local timezone.
And is it true that using the command actual_cargo_ready_date_local::timestamp_ntz coverts the actual_cargo_ready_date_local into UTC.
Thanks
TLDR: Use CONVERT_TIMEZONE.
If actual_cargo_ready_date_local and booked_at are both of type TIMESTAMP_NTZ, there is no notion of the "origin" timezone here at all, so the system can not automatically make the comparisons right.
Casting actual_cargo_ready_date_local::timestamp_ntz does not have any effect, as the datatype doesn't change.
However, if you know that actual_cargo_ready_date_local is in some timezone, let's say America/New_York, and booked_at is in UTC, you can use CONVERT_TIMEZONE to convert the times into UTC:
booked_at > CONVERT_TIMEZONE(actual_cargo_ready_date_local, 'America/New_York', 'UTC')
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.
I'm connecting to an MS SQL database via Power Query (Version: 2.10.3598.81) in Excel 2010.
My dates are stored in UTC in the database, however I would like to show them as local in the spreadsheet.
DateTimeZone.From() - converts to a DateTimeZone type but assumes the input date is in the Local timezone. This is the exact opposite of what I want.
DateTimeZone.ToLocal() - gives an error, I think because there's no timezone information in the source date.
Local in my case is Australian EST, however it would be great if Excel just picked up the local timezone. (It appears to do this already)
I think I've discovered the answer.
The function DateTime.AddZone() which I thought was used to convert between timezones is actually used to add timezone information to an existing date. If your date is UTC you would use DateTime.AddZone([date_created],0) and if your date was already in AEST then you would use DateTime.AddZone([date_created],10).
So I can now use:
DateTimeZone.ToLocal(DateTime.AddZone([date_created_UTC],0))
and Power Query will correctly convert my date created from UTC to AEST (or whatever is local).
Using SQL Server 2008+. SQLCLR is not an option.
I have a situation where I have data all stored in UTC. I need to do a comparison against that data by determining what a certain local time, let's say 8am, is in UTC. The timezone for the local time will vary on a row by row basis. (The timezone for each row is stored, so that's not an issue.) That certain local time has no date associated with it. It's always just "8am".
I have timezone data in the database, and this tells me the base UTC offset as well if the timezone follows daylight savings time.
But now I'm kind of stuck.
My problem is that in order to do a daylight savings time adjustment, I need to know if the current date/time in a particular timezone falls within certain ranges, but I can only convert to the appropriate local time to do that check if I know if it's daylight savings! In other words, how can I check to see if it's daylight savings unless I know whether a UTC offset is off due to daylight savings?
It's a chicken and egg problem.
It seems to me that the only solution is to be able to have a table that calculates daylight-savings aware offsets on a per-timezone basis.
Ideas?
You do have an ambiguity problem here, but it's not a chicken and egg issue.
The piece of information you are missing is, "what defines a day?" I know, it sounds crazy, but a "day" is not a universal concept. It's a local one.
For just a minute, put aside issues of time zones, DST and UTC. If I ask you, "How many hours are we apart from 8 AM right now?" You could give me two different answers. It's 7PM right now, so you might say "11 hours" - since that's how much time we are from 8 AM today. But I could also have said "13 hours" - since that's how much time we are from 8 AM tomorrow. Now in this very simplistic sample, you could disambiguate in one of two different ways. You might say "the last 8AM" or "the next 8AM". Or you might say "whichever happened today."
Now go back to the concept of UTC. What is a "UTC day?" Well, we know it's 24 hours, since UTC doesn't follow any daylight savings time. But saying that it runs "midnight to midnight UTC", isn't a very meaningful measure. Sure, there are some places that use this definition (for example, StackOverflow's stats engine). But for most people, we think of "today" in our own local time.
So I can't really say "whichever 8AM happened today". The only date measurement you have is a UTC date. You won't know which local date you should be looking at. Let's take a real example:
I live in Phoenix, Arizona, so my time zone offset is UTC-7. We don't have DST here.
It is currently June 14th 2013, 7 PM local time.
So that's June 15th 2013, 2 AM UTC.
Now I record that time in the database, and later I ask:
"How far away are we from 8 AM Arizona time?"
With the information I have, I don't know if I should be looking for 8 AM on June 14th, or 8 AM on June 15th. Only the latter falls on the same UTC date, but I certainly could be interested in either one of them.
If you can decide in your business logic that you want the last time, or the next time, then you can resolve this. Simply convert the UTC datetime to the local time zone. Then roll forward or backward to the desired time. If your time zone has DST and you cross a transition date along the way, you can adjust for that.
You could also pick the nearest time of the two, but of course that all depends on your business logic.
Another approach would be to figure out which local today you are in, using the UTC time you are comparing. So in my example above, Arizona's local June 14th runs from June 13th 17:00 UTC to June 14th 17:00 UTC.
So to summarize, you wanted to know "Is 8 AM in DST surrounding this UTC datetime?", and you can't answer that without more information, either the date of the 8AM, or some logical relationship to follow, of which there are several options available. Pick a strategy that works for your needs.
UPDATE
You asked in comments:
How can I know if right now in UTC is in dst in X time zone so I can adjust accordingly?
This is where the datetimeoffset type can be helpful. You don't just want to track "is DST in effect", you want to track the precise offset for the target time zone, including any DST that might be in effect. The difference is subtle, but it comes down to tracking a full offset rather than just a boolean yes/no.
So, let's pretend I live in New York City. Checking this site, we know that EDT went into effect on March 10th 2013 at 2AM local time, and it will go back to EST on November 3rd 2013 at 2AM local time.
So we have the following:
UTC Local datetimeofffset
2013-03-10T05:00:00Z 2013-03-10T00:00:00-05:00
2013-03-10T06:00:00Z 2013-03-10T01:00:00-05:00
2013-03-10T07:00:00Z 2013-03-10T03:00:00-04:00 <--- transition
2013-03-10T08:00:00Z 2013-03-10T04:00:00-04:00
...
2013-11-03T04:00:00Z 2013-11-03T00:00:00-04:00
2013-11-03T05:00:00Z 2013-11-03T01:00:00-04:00
2013-11-03T06:00:00Z 2013-11-03T01:00:00-05:00 <--- transition
2013-11-03T07:00:00Z 2013-11-03T02:00:00-05:00
Now notice that if you strip off the offset, you only have a one-way function. In other words, you can always determine the correct local time for the UTC time, but you can't go the other direction unless you know the offset during the fall-back transition (or unless you are willing to live with ambiguity).
So the algorithm for going from UTC to local time should be something like this:
Starting with the UTC datetime: 2013-11-03T05:30:00Z
Apply the standard offset (-5) 2013-11-03T00:30:00-05:00
Apply the daylight offset (-4) 2013-11-03T01:30:00-04:00
Which one is valid according to the time zone rules?
In this case, the daylight offset is valid.
Your time zone data should have this information.
If not, then you need to reconsider the source of your time zone tables.
Let's try it again with the other 1:30 time:
Starting with the UTC datetime: 2013-11-03T06:30:00Z
Apply the standard offset (-5) 2013-11-03T01:30:00-05:00
Apply the daylight offset (-4) 2013-11-03T02:30:00-04:00
Which one is valid according to the time zone rules?
In this case, the standard offset is valid.
How do we know? Because -4 is the daylight offset, and DST is supposed to be over at 2:00 local time. We have 2:30 local time associated with that offset, so only the standard one is valid in this time zone.
So can you convert from UTC to local? Yes. Always.
But you also said that the local value in the other column is just something like 8AM. So if it was 1:30AM, then certainly you would have an ambiguity during the fall-back transition. There is no way to resolve this, other than just picking one.
Sometimes, you might want to just pick one or the other, but sometimes you might want to error. And sometimes you might want to let your user pick which of the two they were interested in. It's not unheard of to see a dialog such as the following:
DAYLIGHT SAVING TIME
We're sorry, but there are two different instances of 1:30 AM on this day.
Which did you mean?
[1:30 AM Eastern Daylight Time] [1:30 AM Eastern Standard Time]
...those are buttons, if you couldn't tell. :)
According to Microsoft's page on the datetimeoffset data type (see here):
A time zone offset specifies the zone offset from UTC for a time or datetime value.
But nowhere does it say whether the datetime part of the datetimeoffset string literal format is showing either:
UTC time, with the timezone offset being what to apply to that time to get to localtime, or;
localtime, with the timezone offset being what to apply to that time to get back to UTC.
This is the string literal format for datetimeoffset: YYYY-MM-DD hh:mm:ss[.nnnnnnn] [{+|-}hh:mm].
My question therefore is, is the YYYY-MM-DD hh:mm:ss[.nnnnnnn] bit of that string literal in localtime or in UTC?
The YYY-MM-DD hh:mm:ss[.nnnnnnn] [{+|-}hh:mm] format is an ISO8601 format. The ISO page gives
Coordinated universal time (UTC)
Local time with offset to UTC
As this format has the offset then the base time is local. See wikipedia for other examples.