94

We are dealing with an application that needs to handle global time data from different time zones and daylight savings time settings. The idea is to store everything in UTC format internally and only convert back and forth for the localized user interfaces. Does the SQL Server offer any mechanisms for dealing with the translations given a time, a country and a timezone?

This must be a common problem, so I'm surprised google wouldn't turn up anything usable.

Any pointers?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
BuschnicK
  • 5,304
  • 8
  • 37
  • 49
  • I have my mssql-server linked to a mysql-server. I wonder if it is possible to run mysql CONVERT_TZ(time,srczone,dstzone) on the queries :-) Strange this function is missing; it is built into linux. – Leif Neland Aug 27 '15 at 10:28
  • 1
    @BuschnicK See my answer below. Actually I think you can accept it even so it's easier for others to find. – Piotr Owsiak Oct 28 '16 at 07:52
  • The short answer: there is no built-in way to do this before SQL Server 2016, so it will require custom code on earlier versions. – lehiester Sep 10 '18 at 17:18
  • The medium answer: all time offset functionality before SQL Server 2016 only worked with absolute offsets, with no support for the variable offsets that occur in most time zones due to Daylight Saving Time. Queries do not have any way of accessing time offsets except for whatever happens to be the current offset of the server's local time, which is useless for trying to automate conversion. – lehiester Sep 10 '18 at 17:28

11 Answers11

73

This works for dates that currently have the same UTC offset as SQL Server's host; it doesn't account for daylight savings changes. Replace YOUR_DATE with the local date to convert.

SELECT DATEADD(second, DATEDIFF(second, GETDATE(), GETUTCDATE()), YOUR_DATE);

Sam
  • 40,644
  • 36
  • 176
  • 219
shaig
  • 829
  • 1
  • 6
  • 2
  • 2
    Thanks, this is a good idea but it only works for exactly one timezone - the local machine's. We need it to work for arbitrary timezones though... – BuschnicK Nov 10 '10 at 14:48
  • clever but yeah, watch out for timezones. – Krip Apr 04 '11 at 13:15
  • 54
    This doesn't account for daylight savings time – Gabriel McAdams Aug 29 '11 at 23:16
  • 11
    No! The difference is dependent on the exact date. It depends on daylight-savings. – usr Oct 17 '11 at 14:40
  • 3
    In my case I just needed the 1 timezone, and this worked great! Thanks. – M Thelen Dec 07 '11 at 15:14
  • 2
    I also only use one timezone and the times I'm converting are all today so I don't need to worry about DST issues, but I found I had to use select dateadd(second, (-1 * datediff(second, getutcdate(), getdate())), YOUR_DATE), otherwise I was 2 * my difference off. – grahamesd Jan 09 '12 at 17:45
  • 11
    This works well if you know your running today not historically, thanks – David Adlington May 17 '12 at 12:24
  • The question was about multiple time zones. This only talks about a single one - whichever TZ the server is in. – Rob Farley Mar 23 '13 at 11:46
67

7 years passed and...
actually there's this new SQL Server 2016 feature that does exactly what you need.
It is called AT TIME ZONE and it converts date to a specified time zone considering DST (daylight saving time) changes.
More info here: https://msdn.microsoft.com/en-us/library/mt612795.aspx

Piotr Owsiak
  • 6,081
  • 8
  • 39
  • 42
  • 23
    No longer working on this - not at that project, not at SQL Server, not at the same company and not even in the same country ;-) So it won't help me, but I'll upvote it for people finding this question now. – BuschnicK Dec 04 '16 at 09:07
  • 2
    @BuschnicK yeah, I figure, but I got here looking for a solution to the same problem you had so I decided to post an aswer now that there is a real solution :D – Piotr Owsiak Feb 20 '17 at 13:33
  • 5
    The question is for SQL Server 2008. Please update the question if you accept this answer. Thx – Robert Apr 10 '17 at 04:06
  • How do you use this to convert to UTC, which is not a time zone? – xr280xr Aug 07 '17 at 22:29
  • 3
    To convert to UTC you can do 'AT TIME ZONE 'UTC'. – Krzyserious Mar 21 '18 at 13:24
  • 2
    @Piotr Owsiak: Yea, but it assumes that the input time is UTC, which is pointless if you want to convert from local time to UTC... So 7 years passed, and they still don't think it's worth handling this properly... – Stefan Steiger Mar 01 '19 at 08:49
22

While a few of these answers will get you in the ballpark, you cannot do what you're trying to do with arbitrary dates for SqlServer 2005 and earlier because of daylight savings time. Using the difference between the current local and current UTC will give me the offset as it exists today. I have not found a way to determine what the offset would have been for the date in question.

That said, I know that SqlServer 2008 provides some new date functions that may address that issue, but folks using an earlier version need to be aware of the limitations.

Our approach is to persist UTC and perform the conversion on the client side where we have more control over the conversion's accuracy.

user890155
  • 429
  • 4
  • 4
21

Here is the code to convert one zone DateTime to another zone DateTime

DECLARE @UTCDateTime DATETIME = GETUTCDATE();
DECLARE @ConvertedZoneDateTime DATETIME;

-- 'UTC' to 'India Standard Time' DATETIME
SET @ConvertedZoneDateTime = @UTCDateTime AT TIME ZONE 'UTC' AT TIME ZONE 'India Standard Time'
SELECT @UTCDateTime AS UTCDATE,@ConvertedZoneDateTime AS IndiaStandardTime

-- 'India Standard Time' to 'UTC' DATETIME
SET @UTCDateTime = @ConvertedZoneDateTime AT TIME ZONE 'India Standard Time' AT TIME ZONE 'UTC'
SELECT @ConvertedZoneDateTime AS IndiaStandardTime,@UTCDateTime AS UTCDATE

Note: AT TIME ZONE works only on SQL Server 2016+ and the advantage is that it automatically considers Daylight when converting to a particular Time zone

Seva
  • 1,631
  • 2
  • 18
  • 23
KarthikeyanMlp
  • 333
  • 3
  • 9
  • 4
    I love this, if for no other reason than the fact that it shows you can chain multiple `AT TIME ZONE` calls (phrases?) together! Simply elegant. I said earlier that https://stackoverflow.com/a/44579178/112764 met my needs, but this is even better. Major kudos. – NateJ Jul 19 '18 at 17:05
  • `DECLARE @UTCDateTime DATETIME = GETUTCDATE();` `DECLARE @ConvertedZoneDateTime DATETIME;` `-- 'UTC' to 'India Standard Time' to 'Eastern Standard Time' DATETIME` `SET @ConvertedZoneDateTime = @UTCDateTime AT TIME ZONE 'UTC' AT TIME ZONE 'India Standard Time' AT TIME ZONE 'Eastern Standard Time'` `SELECT @UTCDateTime AS UTCDATE,@ConvertedZoneDateTime AS EasternStandardTime` Yes, you can chain multiple `AT TIME ZONE` calls, but **From** and **To** is enough for any conversion and most we needed – KarthikeyanMlp Nov 21 '18 at 04:43
17

For SQL Server 2016 and newer, and Azure SQL Database, use the built in AT TIME ZONE statement.

For older editions of SQL Server, you can use my SQL Server Time Zone Support project to convert between IANA standard time zones, as listed here.

UTC to Local is like this:

SELECT Tzdb.UtcToLocal('2015-07-01 00:00:00', 'America/Los_Angeles')

Local to UTC is like this:

SELECT Tzdb.LocalToUtc('2015-07-01 00:00:00', 'America/Los_Angeles', 1, 1)

The numeric options are flag for controlling the behavior when the local time values are affected by daylight saving time. These are described in detail in the project's documentation.

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • 1
    How man... this project of Matt is great and does not require the CLR. Matt deserves much credit for this +100 – buckley Apr 29 '16 at 15:23
14

SQL Server 2008 has a type called datetimeoffset. It's really useful for this type of stuff.

http://msdn.microsoft.com/en-us/library/bb630289.aspx

Then you can use the function SWITCHOFFSET to move it from one timezone to another, but still keeping the same UTC value.

http://msdn.microsoft.com/en-us/library/bb677244.aspx

Rob

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Rob Farley
  • 15,625
  • 5
  • 44
  • 58
  • 6
    SWITCHOFFSET doesn't account for daylight savings, so it is only useful in some situations. – robocat Mar 18 '13 at 01:14
  • 2
    No. But the question was about being able to handle switching to whichever time zone was requested. – Rob Farley Mar 23 '13 at 11:48
  • From the question "different time zones and daylight savings time settings". We too are looking for a solution for local times. Your suggestion doesn't solve the daylight savings issue does it? – robocat Mar 25 '13 at 01:10
  • Any time when you need to detect the time zone at a client and then have the database return times in the specified time zone, the SWITCHOFFSET function is very useful. – Rob Farley Apr 01 '13 at 10:11
  • 4
    @RobFarley Detecting the time zone at the client and using SWITCHOFFSET can still get things wrong. You need to know whether or not the date and time you are converting has Daylight Savings Time applied or not. Simply detecting the time zone and applying the current offset to UTC can be an hour off -- and that's in a simple case where all your conversions are in the same country. Not every country switches to/from daylight savings time on the same dates. SWITCHOFFSET does work nicely if you store local time and know the difference between the original and target zone within the same country. – JamieSee Mar 02 '15 at 16:41
  • Yes. If you know you want to switch from GMT-5 to GMT+930, then you can do that, keeping the order of times unchanged. But you need to know that you're interested in 0930 and not 1030. – Rob Farley Mar 02 '15 at 20:17
4

I tend to lean towards using DateTimeOffset for all date-time storage that isn't related to a local event (ie: meeting/party, etc, 12pm-3pm at the museum).

To get the current DTO as UTC:

DECLARE @utcNow DATETIMEOFFSET = CONVERT(DATETIMEOFFSET, SYSUTCDATETIME())
DECLARE @utcToday DATE = CONVERT(DATE, @utcNow);
DECLARE @utcTomorrow DATE = DATEADD(D, 1, @utcNow);
SELECT  @utcToday [today]
        ,@utcTomorrow [tomorrow]
        ,@utcNow [utcNow]

NOTE: I will always use UTC when sending over the wire... client-side JS can easily get to/from local UTC. See: new Date().toJSON() ...

The following JS will handle parsing a UTC/GMT date in ISO8601 format to a local datetime.

if (typeof Date.fromISOString != 'function') {
  //method to handle conversion from an ISO-8601 style string to a Date object
  //  Date.fromISOString("2009-07-03T16:09:45Z")
  //    Fri Jul 03 2009 09:09:45 GMT-0700
  Date.fromISOString = function(input) {
    var date = new Date(input); //EcmaScript5 includes ISO-8601 style parsing
    if (!isNaN(date)) return date;

    //early shorting of invalid input
    if (typeof input !== "string" || input.length < 10 || input.length > 40) return null;

    var iso8601Format = /^(\d{4})-(\d{2})-(\d{2})((([T ](\d{2}):(\d{2})(:(\d{2})(\.(\d{1,12}))?)?)?)?)?([Zz]|([-+])(\d{2})\:?(\d{2}))?$/;

    //normalize input
    var input = input.toString().replace(/^\s+/,'').replace(/\s+$/,'');

    if (!iso8601Format.test(input))
      return null; //invalid format

    var d = input.match(iso8601Format);
    var offset = 0;

    date = new Date(+d[1], +d[2]-1, +d[3], +d[7] || 0, +d[8] || 0, +d[10] || 0, Math.round(+("0." + (d[12] || 0)) * 1000));

    //use specified offset
    if (d[13] == 'Z') offset = 0-date.getTimezoneOffset();
    else if (d[13]) offset = ((parseInt(d[15],10) * 60) + (parseInt(d[16],10)) * ((d[14] == '-') ? 1 : -1)) - date.getTimezoneOffset();

    date.setTime(date.getTime() + (offset * 60000));

    if (date.getTime() <= new Date(-62135571600000).getTime()) // CLR DateTime.MinValue
      return null;

    return date;
  };
}
Tracker1
  • 19,103
  • 12
  • 80
  • 106
  • +1 I've too switched to DateTimeOffset. It avoids a number of issues with UTC + local conversions. However, for similar reasons, I recommend sending values with an offset over-the-wire (via JSON) as well. – user2864740 Dec 07 '13 at 00:41
  • As a note, I do what you do *exactly* the other way around. For DateTimes that are tied to a local event, I store a DateTimeOffset. For a DateTime that is not tied to a local event, I store a DateTime in UTC. The former has two relevant datapoints (when is it in local time, and what local time is that), the latter only one (when is it) – Martijn Jan 23 '14 at 12:44
  • @Martijn But the timezone doesn't give you the location, and you have to stored separately anyhow. – Tracker1 Feb 05 '14 at 19:00
  • @tracker1 That only works when the location has a way of knowing its timezone, and even then it's a complete pain to convert. – Martijn Feb 06 '14 at 08:38
3

Yes, to some degree as detailed here.
The approach I've used (pre-2008) is to do the conversion in the .NET business logic before inserting into the DB.

AdaTheDev
  • 142,592
  • 28
  • 206
  • 200
1

You can use GETUTCDATE() function to get UTC datetime Probably you can select difference between GETUTCDATE() and GETDATE() and use this difference to ajust your dates to UTC

But I agree with previous message, that it is much easier to control right datetime in the business layer (in .NET, for example).

Bogdan_Ch
  • 3,328
  • 4
  • 23
  • 39
  • 12
    No! The difference is dependent on the exact date. It depends on daylight-savings. – usr Oct 17 '11 at 14:40
  • 3
    Does not account for daylight savings. I was using solutions like this for some time and it caused major issues. You have to determine if the date you are comparing against is in DST. – Jeff Davis May 29 '12 at 15:14
0

SUBSTRING(CONVERT(VARCHAR(34), SYSDATETIMEOFFSET()), 29, 5)

Returns (for example):

-06:0

Not 100% positive this will always work.

Jared Beach
  • 2,635
  • 34
  • 38
-1

Sample usage:

SELECT
    Getdate=GETDATE()
    ,SysDateTimeOffset=SYSDATETIMEOFFSET()
    ,SWITCHOFFSET=SWITCHOFFSET(SYSDATETIMEOFFSET(),0)
    ,GetutcDate=GETUTCDATE()
GO

Returns:

Getdate SysDateTimeOffset   SWITCHOFFSET    GetutcDate
2013-12-06 15:54:55.373 2013-12-06 15:54:55.3765498 -08:00  2013-12-06 23:54:55.3765498 +00:00  2013-12-06 23:54:55.373
rgettman
  • 176,041
  • 30
  • 275
  • 357