297

I'm doing a few SQL select queries and would like to convert my UTC datetime column into local time to be displayed as local time in my query results. Note, I am NOT looking to do this conversion via code but rather when I am doing manual and random SQL queries against my databases.

Luke Girvin
  • 13,221
  • 9
  • 64
  • 84
Nugs
  • 5,503
  • 7
  • 36
  • 57
  • Does this question seem similar to your circumstances? http://stackoverflow.com/questions/3404646/how-to-calculate-the-local-datetime-from-a-utc-datetime-in-tsql-sql-2005 – Taryn East Nov 07 '11 at 15:46
  • possible duplicate of [TSQL: How to convert local time to UTC? (SQL Server 2008)](http://stackoverflow.com/questions/1205142/tsql-how-to-convert-local-time-to-utc-sql-server-2008) – Reinstate Monica Please Mar 06 '15 at 02:12

30 Answers30

417

You can do this as follows on SQL Server 2008 or greater:

SELECT CONVERT(datetime, 
               SWITCHOFFSET(CONVERT(datetimeoffset, 
                                    MyTable.UtcColumn), 
                            DATENAME(TzOffset, SYSDATETIMEOFFSET()))) 
       AS ColumnInLocalTime
FROM MyTable

You can also do the less verbose:

SELECT DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), GETDATE()), MyTable.UtcColumn) 
       AS ColumnInLocalTime
FROM MyTable

Whatever you do, do not use - to subtract dates, because the operation is not atomic, and you will on occasion get indeterminate results due to race conditions between the system datetime and the local datetime being checked at different times (i.e., non-atomically).

Please note that this answer does not take DST into account. If you want to include a DST adjustment, please also see the following SO question:

How to create Daylight Savings time Start and End function in SQL Server

Community
  • 1
  • 1
Michael Goldshteyn
  • 71,784
  • 24
  • 131
  • 181
  • Missing something: The switchoffset function requires 2 argument(s). – Nugs Nov 07 '11 at 15:54
  • @Nugs, you have a good eye, I was missing a right parenthesis after `UtcColumn`... – Michael Goldshteyn Nov 07 '11 at 16:02
  • 46
    Is there a way to make this account for day light savings? – Steve Mar 30 '12 at 18:53
  • 1
    @Steve, see my answer below for a DST solution. – Ron Smith Feb 26 '14 at 18:36
  • 1
    That DST solution is only for current US rules, uses hardcoded dates and is a scalar function which can cause performance overhead if called many times over the course of a query. – Michael Goldshteyn Feb 27 '14 at 17:50
  • 22
    I don't see the name of a time zone here so this is incorrect. It's a really bad idea to assume you can convert to local time by doing arithmetic – JonnyRaa Sep 03 '14 at 16:33
  • 3
    @Jonny, the question as I understood it was to convert from UTC to local time, which given the SQL Server functions employed does not need any mention of time zone. There was no mention of converting from UTC to an arbitrary time zone, in which case the time zone (or better yet, its offset) would need to be mentioned. – Michael Goldshteyn Apr 17 '15 at 13:10
  • 9
    @MichaelGoldshteyn if you have a timezone offset you still dont know what logical timezone a time is in. Timezones are social things and can be changed by governments at different points. The offset for a timezone can be different at different points in time/history (summer time being a common one). You are offsetting a time by the current offset from UTC... a further point is that this will give the timezone of the server and not the client, which could be a further problem if you are hosting in a different country. – JonnyRaa Apr 17 '15 at 14:15
  • @MichaelGoldshteyn btw I'm not trying to have a go, I just had to do a load of work with a Swedish company (I work in England) and had to go to pains to get this stuff right. You need a string like 'sweden/stockholm' in there somewhere basically. There are good libraries for doing this in most languages (moment.js, nodatime + jodatime etc) but I dont think databases can cope – JonnyRaa Apr 17 '15 at 14:18
  • some of these are good examples of changes that you need a good library to be keeping track of http://www.thrillist.com/travel/nation/world-time-zones-insane-facts-you-didn-t-know-about-time-zones – JonnyRaa Apr 17 '15 at 14:26
  • hi what does local time mean? suppose sql server is running in Japan, webserver is in china and client is in France. if we do this convert in our program or in a stored procedure to which local time is it converted? – Niloofar Mar 03 '16 at 16:33
  • 7
    @Niloofar The simple answer is that you don't, and shouldn't. Datetimes should always be stored in UTC (preferably based on the database server's clock, not the webserver's, the application server's, and definitely not the client's clock). Displaying that datetime is a function for the UI layer, and it is responsible for converting the datetime into the format you want (including a timezone if necessary). – Robert McKee Jun 09 '16 at 17:42
  • @RobertMcKee yes you are definitely right. but my question was "what local time is here?" is it the sql server local time? – Niloofar Jun 11 '16 at 08:20
  • 16
    For anyone using sql azure this approach won't work because the date/time functions all return UTC, so comparing GETDATE() to GETUTCDATE() doesn't give you anything to work with and your result is the same as you started with. – Brian Surowiec Nov 09 '16 at 20:55
  • 1
    Does not work for me. I have dates in CET (Central Europe Time) and CEST (Central Europe Summer Time). This solution always return hours diff compare to now. Today on september I always have 2 hours but if I get a date on `15/01/2017` in the DB it's not 2 hours but only 1 hour. – Maxence Sep 13 '17 at 10:04
  • 1
    If you replace "SYSDATETIMEOFFSET()" with "CONVERT(DATETIMEOFFSET, MyTable.UtcColumn, 120)" it seems to account for DST – hobwell Feb 13 '20 at 16:53
  • @hobwell Yes, easy and fast. – langeleppel Apr 23 '21 at 10:01
105

If your local date time is say Eastern Standard Time and you want to convert from UTC to that, then in Azure SQL and SQL Server 2016 and above, you can do:

SELECT YourUtcColumn AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time' AS
       LocalTime
FROM   YourTable

The full list of timezone names can be found with:

SELECT * FROM sys.time_zone_info 

And yes, the timezones are badly named - even though it is Eastern Standard Time, daylight savings is taken into account.

Matt Frear
  • 52,283
  • 12
  • 78
  • 86
  • 3
    Timezones and offsets can also be found in SQL Server here: SELECT * FROM sys.time_zone_info – rayzinnz Feb 25 '20 at 03:24
  • 2
    Isn't the inclusion of `AT TIME ZONE 'UTC'` Redundant? I'd leave it out entirely. The other answers do not take into account DST (Daylight Savings Time), so when looking at DateTimes (before the recent Daylight Savings) they will appear wrong. This however works great. For those who say to leave the date alone and to display the local DST on the Front End instead, I'd say I agree - however - when you are working in SSMS running queries in the Back End and studying raw data, sometimes you need to convert these UTC's to troubleshoot when exactly something happened relative to your Time Zone. – MikeTeeVee Dec 09 '20 at 22:05
  • 1
    @MikeTeeVee the OP was asking about converting from UTC to local time, so yes, it is needed. – Matt Frear Dec 13 '20 at 22:56
  • 1
    Sorry about that. You are right, it is necessary and not redundant. For DateTime (what the OP was using) it is needed. I realize now I was using it with DateTimeOffset(3), so it already knew it was in UTC, which is why I was able to skip that step. My bad. – MikeTeeVee Dec 14 '20 at 17:19
  • This is a simple and logical solution that's easy to follow. Thanks. All this convert offset carry on is less intuitive than this solution. – Jayden Mar 16 '22 at 19:07
  • If your front end tool doesn't support datetimes with timezones, you can remove the timezone information `Select cast(YourUtcColumn AT TIME ZONE 'UTC' AT TIME ZONE 'Mountain Standard Time' as datetime) AS Local_Time` – Craig Wilson May 12 '22 at 20:09
  • This really should be marked the answer, as it does what the OP asks and at works everywhere, including Azure, which is where I'm working. AND it takes care of DST, I had not had the worked out before. – Hawkeye Jan 27 '23 at 21:33
104

I didn't find any of these example helpful in getting a datetime stored as UTC to a datetime in a specified timezone (NOT the timezone of the server because Azure SQL databases run as UTC). This is how I handled it. It's not elegant but it's simple and gives you the right answer without maintaining other tables:

select CONVERT(datetime, SWITCHOFFSET(dateTimeField, DATEPART(TZOFFSET, 
dateTimeField AT TIME ZONE 'Eastern Standard Time')))
Aiden Kaskela
  • 1,074
  • 1
  • 7
  • 4
  • 2
    So far this is the only one that seems to work for the Azure issue – Jeff Davis Jun 29 '17 at 15:36
  • 6
    Works for 2016 only and uses system registry. But great solution for Azure. – Dan Cundy Sep 20 '17 at 09:03
  • 4
    This one works for azure times stored in UTC, thanks guys – Null Dec 20 '17 at 10:44
  • 3
    Here's a list of the strings you can use for the time zones: https://stackoverflow.com/a/7908482/631277 – Matt Kemp Feb 19 '18 at 05:46
  • 2
    This is extra clever because it works for older (historical) dates which could have been during DST. The `AT TIME ZONE` syntax is only valid in SQL 2016 or higher (and Azure SQL DB of course!). Personally, of all the various answers and proposed solutions in StackOverflow for this kind of problem, I chose this one for my use-case. – NateJ Jul 19 '18 at 16:47
  • 3
    If using SQL 2016 and the `AT TIME ZONE` syntax, consider https://stackoverflow.com/a/44941536/112764 -- you can **chain** multiple conversions together by simply concatenating multiple `at time zone `s. – NateJ Jul 19 '18 at 17:07
  • 5
    This is a little odd, too, but some very basic testing looks like this might work, too - `dateTimeField AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time'` - just chaining the `AT TIME ZONE` statements. (@NateJ mentioned this above I see now) – David Mohundro Apr 25 '19 at 21:37
  • I'm not sure why - I'm no SQL expert - but I am working on Azure and this is the only one that worked for me. – marky Dec 15 '22 at 13:34
21

If you need a conversion other than your server's location, here is a function that allows you to pass a standard offset and accounts for US Daylight Savings Times:

-- =============================================
-- Author:      Ron Smith
-- Create date: 2013-10-23
-- Description: Converts UTC to DST
--              based on passed Standard offset
-- =============================================
CREATE FUNCTION [dbo].[fn_UTC_to_DST]
(
    @UTC datetime,
    @StandardOffset int
)
RETURNS datetime
AS
BEGIN

    declare 
        @DST datetime,
        @SSM datetime, -- Second Sunday in March
        @FSN datetime  -- First Sunday in November

    -- get DST Range
    set @SSM = datename(year,@UTC) + '0314' 
    set @SSM = dateadd(hour,2,dateadd(day,datepart(dw,@SSM)*-1+1,@SSM))
    set @FSN = datename(year,@UTC) + '1107'
    set @FSN = dateadd(second,-1,dateadd(hour,2,dateadd(day,datepart(dw,@FSN)*-1+1,@FSN)))

    -- add an hour to @StandardOffset if @UTC is in DST range
    if @UTC between @SSM and @FSN
        set @StandardOffset = @StandardOffset + 1

    -- convert to DST
    set @DST = dateadd(hour,@StandardOffset,@UTC)

    -- return converted datetime
    return @DST

END

GO
Ron Smith
  • 3,241
  • 1
  • 13
  • 16
  • 2
    Ron Smith I know this is an old post but I was curious what the hard coded '0314' and '1107' represent in the getting of DST range. It appears to be hard coded days which change due to DTS. Why would you hard code this when it should be a calculated date because the days change based on where on the calendar the second Sunday of march and the first Sunday of November falls. The hard coded days would make this code fundamentally flawed. – Mike Aug 16 '17 at 17:40
  • 2
    Good question :) Those are the max dates where the second Sunday in March and the first Sunday in November can occur. The following lines set the variables to the actual date. – Ron Smith Aug 18 '17 at 00:25
10

Using new SQL Server 2016 opportunities:

CREATE FUNCTION ToLocalTime(@dtUtc datetime, @timezoneId nvarchar(256))
RETURNS datetime
AS BEGIN

return @dtUtc AT TIME ZONE 'UTC' AT TIME ZONE @timezoneId

/* -- second way, faster

return SWITCHOFFSET(@dtUtc , DATENAME(tz, @dtUtc AT TIME ZONE @timezoneId))

*/

/* -- third way

declare @dtLocal datetimeoffset
set @dtLocal = @dtUtc AT TIME ZONE @timezoneId
return dateadd(minute, DATEPART (TZoffset, @dtLocal), @dtUtc)

*/

END
GO

But clr procedure works in 5 times faster :'-(

Pay attention that Offset for one TimeZone can change to winter or summer time. For example

select cast('2017-02-08 09:00:00.000' as datetime) AT TIME ZONE 'Eastern Standard Time'
select cast('2017-08-08 09:00:00.000' as datetime) AT TIME ZONE 'Eastern Standard Time'

results:

2017-02-08 09:00:00.000 -05:00
2017-08-08 09:00:00.000 -04:00

You can't just add constant offset.

Pavel Samoylenko
  • 491
  • 7
  • 14
8

Well if you store the data as UTC date in the database you can do something as simple as

select 
 [MyUtcDate] + getdate() - getutcdate()
from [dbo].[mytable]

this was it's always local from the point of the server and you are not fumbling with AT TIME ZONE 'your time zone name', if your database get moved to another time zone like a client installation a hard coded time zone might bite you.

Walter Verhoeven
  • 3,867
  • 27
  • 36
  • 2
    whilst it might be risky as you mention, I can't believe you don't have any up votes on this - saved me a lot of time – happs Dec 01 '20 at 13:32
6

If enabling CLR on your database is an option as well as using the sql server's timezone, it can be written in .Net quite easily.

public partial class UserDefinedFunctions
{
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlDateTime fn_GetLocalFromUTC(SqlDateTime UTC)
    {
        if (UTC.IsNull)
            return UTC;

        return new SqlDateTime(UTC.Value.ToLocalTime());
    }
}

A UTC datetime value goes in and the local datetime value relative to the server comes out. Null values return null.

JGates
  • 69
  • 1
  • 2
6

None of these worked for me but this below worked 100%. Hope this can help others trying to convert it like I was.

CREATE FUNCTION [dbo].[fn_UTC_to_EST]
(
    @UTC datetime,
    @StandardOffset int
)
RETURNS datetime
AS
BEGIN

declare 
    @DST datetime,
    @SSM datetime, -- Second Sunday in March
    @FSN datetime  -- First Sunday in November
-- get DST Range
set @SSM = DATEADD(dd,7 + (6-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(GETDATE())-1900) * 12 + 2,0))%7)),DATEADD(mm,(YEAR(GETDATE())-1900) * 12 + 2,0))+'02:00:00' 
set @FSN = DATEADD(dd, (6-(DATEDIFF(dd,0,DATEADD(mm,(YEAR(GETDATE())-1900) * 12 + 10,0))%7)),DATEADD(mm,(YEAR(GETDATE())-1900) * 12 + 10,0)) +'02:00:00'

-- add an hour to @StandardOffset if @UTC is in DST range
if @UTC between @SSM and @FSN
    set @StandardOffset = @StandardOffset + 1

-- convert to DST
set @DST = dateadd(hour,@StandardOffset,@UTC)

-- return converted datetime
return @DST

END
Mike
  • 419
  • 1
  • 6
  • 22
  • 1
    This should be the accepted answer. The only thing I would change is the name as it implies it is to EST time, when really it is to Local Time and the StandardOffset is passed as a parameter. – Greg Gum Aug 01 '18 at 00:23
  • 1
    Agree, best answer ... but instead of having to pass in the offset as a parameter, I added this within the function body: `declare @StandardOffset int = datediff (hh, GETUTCDATE(), GETDATE()) ` – Tom Warfield Nov 06 '18 at 11:46
  • Following up on my previous suggetion - If you calculate @StandardOffset, then you don't need to do the DST correction. – Tom Warfield Jun 20 '19 at 00:42
6

The easiest answer is not always at the bottom, but this time it is, and can be seen already somewhere hidden in above comments. Take your own 'AT TIME ZONE' to capture the TzOffset for your column/data field, and not the current SYSDATETIME. In below data, 2 queries, one on feb data (DST is off, winter in Amsterdam) +1 diff and 2nd query on april data in Amsterdam, so +2 hour diff.

    select top 2 month(receiveTimeUTC) as MonthInWinterOrSpring
   ,  receiveTimeUTC
   ,  CONVERT(datetime,  SWITCHOFFSET(CONVERT(datetimeoffset,  receiveTimeUTC),  DATENAME(TzOffset, SYSDATETIMEOFFSET()))) as LocalTimeWrongNoDST
   ,  CONVERT(datetime,  SWITCHOFFSET(CONVERT(datetimeoffset,  receiveTimeUTC),  DATENAME(TzOffset, receiveTimeUTC  AT TIME ZONE 'Central European Standard Time' ))) as LocalTimeWithDST
       from sensordetails order by id

    select top 2 month(receiveTimeUTC) as MonthInWinterOrSpring, receiveTimeUTC
,  CONVERT(datetime,  SWITCHOFFSET(CONVERT(datetimeoffset,  receiveTimeUTC),  DATENAME(TzOffset, SYSDATETIMEOFFSET()))) as LocalTimeWrongNoDST
,  CONVERT(datetime,  SWITCHOFFSET(CONVERT(datetimeoffset,  receiveTimeUTC),  DATENAME(TzOffset, receiveTimeUTC  AT TIME ZONE 'Central European Standard Time' ))) as LocalTimeWithDST
       from sensordetails order by id desc

Results: Formated results, indicating the red wrongNoDST on februari data.

So this is a T-SQL (SQL Server Answer), no need for storedproc of functions.

langeleppel
  • 121
  • 2
  • 3
  • The conversion can be done easier: `Convert(DATETIME, receiveTimeUTC AT TIME ZONE 'UTC' AT TIME ZONE 'Central European Standard Time')` – Patrick Koorevaar Mar 20 '23 at 13:02
5

There is no simple way to do this in a correct AND generic way.

First of all it must be understood that the offset depends on the date in question, the Time Zone AND DST. GetDate()-GetUTCDate only gives you the offset today at the server's TZ, which is not relevant.

I have seen only two working solution and I have search a lot.

1) A custom SQL function with a a couple of tables of base data such as Time Zones and DST rules per TZ. Working but not very elegant. I can't post it since I don't own the code.

EDIT: Here is an example of this method https://gist.github.com/drumsta/16b79cee6bc195cd89c8

2) Add a .net assembly to the db, .Net can do this very easily. This is working very well but the downside is that you need to configure several parameters on server level and the config is easily broken e.g. if you restore the database. I use this method but I cant post it since I don't own the code.

vikjon0
  • 182
  • 1
  • 12
5

This function will convert a UTC time to EST time with DST adjustment. You can change your designed time zone name in this function, or get it from registry:

Create Function fnConvertUTCTimetoESTTime(
    @UTCTime as datetime
)
returns datetime
as
begin
    return convert(datetime, convert(varchar(23), @UTCTime AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time', 121), 121)
end
go

select dbo.fnConvertUTCTimetoESTTime ('2020-3-8 5:00:00.000')
    , dbo.fnConvertUTCTimetoESTTime ('2020-3-8 6:00:00.000')
    , dbo.fnConvertUTCTimetoESTTime ('2020-3-8 7:00:00.000')
    , dbo.fnConvertUTCTimetoESTTime ('2020-3-8 8:00:00.000')

--returns 0:00am, 1:00am, 3:00am, 4:00am

select dbo.fnConvertUTCTimetoESTTime ('2020-11-1 4:00:00.000')
    , dbo.fnConvertUTCTimetoESTTime ('2020-11-1 5:00:00.000')
    , dbo.fnConvertUTCTimetoESTTime ('2020-11-1 6:00:00.000')
    , dbo.fnConvertUTCTimetoESTTime ('2020-11-1 7:00:00.000')

--returns 0:00am, 1:00am, 1:00am, 2:00am

Note you can not just return "@UTCTime AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time'" as the result because this result is actually a UTC time in EST format (when you compare this "fake" EST time or include it in an order clause it will be converted back to a UTC time).

Mike
  • 91
  • 1
  • 2
4

For Azure SQL and @@Version >= SQL Server 2016 users, Below is a simple function using AT TIME ZONE.

CREATE FUNCTION [dbo].[Global_Convert_UTCTimeTo_LocalTime]
(
   @LocalTimeZone        VARCHAR(50),
   @UTCDateTime          DATETIME
)
RETURNS DATETIME
AS
BEGIN
   DECLARE @ConvertedDateTime DATETIME;

   SELECT @ConvertedDateTime = @UTCDateTime AT TIME ZONE 'UTC' AT TIME ZONE @LocalTimeZone
   RETURN @ConvertedDateTime

END
GO

For types of values that @LocalTimeZone can take, please go to this link or Go to KEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones

Ranger
  • 95
  • 7
3

Here's a version that accounts for daylight savings, UTC offset, and is not locked into a particular year.

---------------------------------------------------------------------------------------------------
--Name:     udfToLocalTime.sql
--Purpose:  To convert UTC to local US time accounting for DST
--Author:   Patrick Slesicki
--Date:     3/25/2014
--Notes:    Works on SQL Server 2008R2 and later, maybe SQL Server 2008 as well.
--          Good only for US States observing the Energy Policy Act of 2005.
--          Function doesn't apply for years prior to 2007.
--          Function assumes that the 1st day of the week is Sunday.
--Tests:        
--          SELECT dbo.udfToLocalTime('2014-03-09 9:00', DEFAULT)
--          SELECT dbo.udfToLocalTime('2014-03-09 10:00', DEFAULT)
--          SELECT dbo.udfToLocalTime('2014-11-02 8:00', DEFAULT)
--          SELECT dbo.udfToLocalTime('2014-11-02 9:00', DEFAULT)
---------------------------------------------------------------------------------------------------
ALTER FUNCTION udfToLocalTime
    (
    @UtcDateTime    AS DATETIME
    ,@UtcOffset     AS INT = -8 --PST
    )
RETURNS DATETIME
AS 
BEGIN
    DECLARE 
        @PstDateTime    AS DATETIME
        ,@Year          AS CHAR(4)
        ,@DstStart      AS DATETIME
        ,@DstEnd        AS DATETIME
        ,@Mar1          AS DATETIME
        ,@Nov1          AS DATETIME
        ,@MarTime       AS TIME
        ,@NovTime       AS TIME
        ,@Mar1Day       AS INT
        ,@Nov1Day       AS INT
        ,@MarDiff       AS INT
        ,@NovDiff       AS INT

    SELECT
        @Year       = YEAR(@UtcDateTime)
        ,@MarTime   = CONVERT(TIME, DATEADD(HOUR, -@UtcOffset, '1900-01-01 02:00'))
        ,@NovTime   = CONVERT(TIME, DATEADD(HOUR, -@UtcOffset - 1, '1900-01-01 02:00'))
        ,@Mar1      = CONVERT(CHAR(16), @Year + '-03-01 ' + CONVERT(CHAR(5), @MarTime), 126)
        ,@Nov1      = CONVERT(CHAR(16), @Year + '-11-01 ' + CONVERT(CHAR(5), @NovTime), 126)
        ,@Mar1Day   = DATEPART(WEEKDAY, @Mar1)
        ,@Nov1Day   = DATEPART(WEEKDAY, @Nov1)

    --Get number of days between Mar 1 and DST start date
    IF @Mar1Day = 1 SET @MarDiff = 7
    ELSE SET @MarDiff = 15 - @Mar1Day

    --Get number of days between Nov 1 and DST end date
    IF @Nov1Day = 1 SET @NovDiff = 0
    ELSE SET @NovDiff = 8 - @Nov1Day

    --Get DST start and end dates
    SELECT 
        @DstStart   = DATEADD(DAY, @MarDiff, @Mar1)
        ,@DstEnd    = DATEADD(DAY, @NovDiff, @Nov1)

    --Change UTC offset if @UtcDateTime is in DST Range
    IF @UtcDateTime >= @DstStart AND @UtcDateTime < @DstEnd SET @UtcOffset = @UtcOffset + 1

    --Get Conversion
    SET @PstDateTime = DATEADD(HOUR, @UtcOffset, @UtcDateTime)
    RETURN @PstDateTime
END
GO
3

I found the one off function way to be too slow when there is a lot of data. So I did it through joining to a table function that would allow for a calculation of the hour diff. It is basically datetime segments with the hour offset. A year would be 4 rows. So the table function

dbo.fn_getTimeZoneOffsets('3/1/2007 7:00am', '11/5/2007 9:00am', 'EPT')

would return this table:

startTime          endTime   offset  isHr2
3/1/07 7:00     3/11/07 6:59    -5    0
3/11/07 7:00    11/4/07 6:59    -4    0
11/4/07 7:00    11/4/07 7:59    -5    1
11/4/07 8:00    11/5/07 9:00    -5    0

It does account for daylight savings. A sample of how it is uses is below and the full blog post is here.

select mt.startTime as startUTC, 
    dateadd(hh, tzStart.offset, mt.startTime) as startLocal, 
    tzStart.isHr2
from MyTable mt 
inner join dbo.fn_getTimeZoneOffsets(@startViewUTC, @endViewUTC, @timeZone)  tzStart
on mt.startTime between tzStart.startTime and tzStart.endTime
JBrooks
  • 9,901
  • 2
  • 28
  • 32
2
 declare @mydate2 datetime
 set @mydate2=Getdate()
 select @mydate2 as mydate,
 dateadd(minute, datediff(minute,getdate(),@mydate2),getutcdate())
Looking_for_answers
  • 343
  • 1
  • 6
  • 20
2

In postgres this works very nicely..Tell the server the time at which the time is saved, 'utc', and then ask it to convert to a specific timezone, in this case 'Brazil/East'

quiz_step_progresses.created_at  at time zone 'utc' at time zone 'Brazil/East'

Get a complete list of timezones with the following select;

select * from pg_timezone_names;

See details here.

https://popsql.com/learn-sql/postgresql/how-to-convert-utc-to-local-time-zone-in-postgresql

HHarada
  • 21
  • 1
1

Ron's answer contains an error. It uses 2:00 AM local time where the UTC equivalent is required. I don't have enough reputation points to comment on Ron's answer so a corrected version appears below:

-- =============================================
-- Author:      Ron Smith
-- Create date: 2013-10-23
-- Description: Converts UTC to DST
--              based on passed Standard offset
-- =============================================
CREATE FUNCTION [dbo].[fn_UTC_to_DST]
(
    @UTC datetime,
    @StandardOffset int
)
RETURNS datetime
AS
BEGIN

declare 
    @DST datetime,
    @SSM datetime, -- Second Sunday in March
    @FSN datetime  -- First Sunday in November
-- get DST Range
set @SSM = datename(year,@UTC) + '0314' 
set @SSM = dateadd(hour,2 - @StandardOffset,dateadd(day,datepart(dw,@SSM)*-1+1,@SSM))
set @FSN = datename(year,@UTC) + '1107'
set @FSN = dateadd(second,-1,dateadd(hour,2 - (@StandardOffset + 1),dateadd(day,datepart(dw,@FSN)*-1+1,@FSN)))

-- add an hour to @StandardOffset if @UTC is in DST range
if @UTC between @SSM and @FSN
    set @StandardOffset = @StandardOffset + 1

-- convert to DST
set @DST = dateadd(hour,@StandardOffset,@UTC)

-- return converted datetime
return @DST

END
mxix
  • 3,539
  • 1
  • 16
  • 23
jlspublic
  • 143
  • 1
  • 6
  • It's a feature, not a bug :) Most of the United States begins Daylight Saving Time at 2:00 a.m https://en.wikipedia.org/wiki/Daylight_saving_time – Ron Smith Aug 18 '17 at 00:36
  • @RonSmith Yes, at 2:00 a.m. local time, which we have to convert to UTC to detect if the given UTC time is in the DST range. – jlspublic Nov 18 '17 at 21:15
1

The UNIX timestamp is merely the number of seconds between a particular date and the Unix Epoch,

SELECT DATEDIFF(SECOND,{d '1970-01-01'},GETDATE()) // This Will Return the UNIX timestamp In SQL server

you can create a function for local date time to Unix UTC conversion using Country Offset Function to Unix Time Stamp In SQL server

1

It's simple. Try this for Azure SQL Server:

SELECT YourDateTimeColumn AT TIME ZONE 'Eastern Standard Time' FROM YourTable

For Local SQL Server :

SELECT CONVERT(datetime2, SWITCHOFFSET(CONVERT(datetimeoffset, gETDATE()), DATENAME(TzOffset, gETDATE() AT TIME ZONE 'Eastern Standard Time'))) FROM YourTable
Ashiquzzaman
  • 5,129
  • 3
  • 27
  • 38
lijuthomas
  • 11
  • 2
  • 2
    What happens with this during daylight savings time (since the time zone specifically says "Eastern Standard Time")? – Mark May 17 '19 at 12:11
  • @Mark depends on what zone you're using. There are many depending on choice from https://learn.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-time-zone-info-transact-sql?view=sql-server-2017 – Fandango68 Jul 24 '23 at 06:39
1

For anyone still trying to solve this issue, here's a proof of concept that works in SQL Server 2017

 declare
    @StartDate date = '2020-01-01'

;with cte_utc as
(
    select 
         1 as i
        ,CONVERT(datetime, @StartDate) AS UTC
        ,datepart(weekday, CONVERT(datetime, @StartDate)) as Weekday
        ,datepart(month, CONVERT(datetime, @StartDate)) as [Month]
        ,datepart(YEAR, CONVERT(datetime, @StartDate)) as [Year]
        
    union all

    Select
         i + 1
        ,dateadd(d, 1, utc)
        ,datepart(weekday, CONVERT(datetime, dateadd(d, 1, utc))) as Weekday
        ,datepart(month, CONVERT(datetime, dateadd(d, 1, utc))) as [Month]
        ,datepart(YEAR, CONVERT(datetime, dateadd(d, 1, utc))) as [Year]
    from    
        cte_utc
    where
        (i + 1) < 32767

), cte_utc_dates as 
(
    select 
        *,
        DENSE_RANK()OVER(PARTITION BY [Year], [Month], [Weekday] ORDER BY Utc) WeekDayIndex
    from
        cte_utc

), cte_hours as (
    select 0 as [Hour]
    union all
    select [Hour] + 1 from cte_hours where [Hour] < 23
)

select
    d.*
    , DATEADD(hour, h.Hour, d.UTC) AS UtcTime
    ,CONVERT(datetime, DATEADD(hour, h.Hour, d.UTC) AT TIME ZONE 'UTC' AT TIME ZONE 'Central Standard Time') CST
    ,CONVERT(datetime, DATEADD(hour, h.Hour, d.UTC) AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time') EST
from
    cte_utc_dates d, cte_hours h
where
    ([Month] = 3 and [Weekday] = 1 and WeekDayIndex = 2 )-- dst start
    or 
    ([Month] = 11 and [Weekday] = 1 and WeekDayIndex = 1 )-- dst end
order by
    utc
OPTION (MAXRECURSION 32767)

GO
Nicholas Ibarra
  • 494
  • 4
  • 11
0

As a warning - if you're going to use the following (note the milliseconds instead of minutes):

    SELECT DATEADD(ms, DATEDIFF(ms, GETUTCDATE(), GETDATE()), MyTable.UtcColumn) 
    AS ColumnInLocalTime
    FROM MyTable

Keep in mind that the DATEDIFF part will not always return the same number. So don't use it to compare DateTimes down to milliseconds.

Sasquatch
  • 49
  • 1
  • 4
0

This should be able to get server time with DST

declare @dt datetime
set @dt = getutcdate() -- GMT equivalent

sysdatetimeoffset takes DST into account

select [InputTime] = @dt
       , [LocalTime2] = dateadd(mi, datediff(mi, sysdatetimeoffset(),getdate()), @dt) 
svgrafov
  • 1,970
  • 4
  • 16
  • 32
DiAm
  • 1
0

First function: configured for italian time zone (+1, +2), switch dates: last sunday of march and october, return the difference between the current time zone and the datetime as parameter.

Returns:
current timezone < parameter timezone ==> +1
current timezone > parameter timezone ==> -1
else 0

The code is:

CREATE FUNCTION [dbo].[UF_ADJUST_OFFSET]
(
    @dt_utc datetime2(7)
)
RETURNS INT
AS
BEGIN


declare @month int,
        @year int,
        @current_offset int,
        @offset_since int,
        @offset int,
        @yearmonth varchar(8),
        @changeoffsetdate datetime2(7)

declare @lastweek table(giorno datetime2(7))

select @current_offset = DATEDIFF(hh, GETUTCDATE(), GETDATE())

select @month = datepart(month, @dt_utc)

if @month < 3 or @month > 10 Begin Set @offset_since = 1 Goto JMP End

if @month > 3 and @month < 10 Begin Set @offset_since = 2 Goto JMP End

--If i'm here is march or october
select @year = datepart(yyyy, @dt_utc)

if @month = 3
Begin

Set @yearmonth = cast(@year as varchar) + '-03-'

Insert Into @lastweek Values(@yearmonth + '31 03:00:00.000000'),(@yearmonth + '30 03:00:00.000000'),(@yearmonth + '29 03:00:00.000000'),(@yearmonth + '28 03:00:00.000000'),
                         (@yearmonth + '27 03:00:00.000000'),(@yearmonth + '26 03:00:00.000000'),(@yearmonth + '25 03:00:00.000000')

--Last week of march
Select @changeoffsetdate = giorno From @lastweek Where  datepart(weekday, giorno) = 1

    if @dt_utc < @changeoffsetdate 
    Begin 
        Set @offset_since = 1 
    End Else Begin
        Set @offset_since = 2
    End
End

if @month = 10
Begin

Set @yearmonth = cast(@year as varchar) + '-10-'

Insert Into @lastweek Values(@yearmonth + '31 03:00:00.000000'),(@yearmonth + '30 03:00:00.000000'),(@yearmonth + '29 03:00:00.000000'),(@yearmonth + '28 03:00:00.000000'),
                         (@yearmonth + '27 03:00:00.000000'),(@yearmonth + '26 03:00:00.000000'),(@yearmonth + '25 03:00:00.000000')

--Last week of october
Select @changeoffsetdate = giorno From @lastweek Where  datepart(weekday, giorno) = 1

    if @dt_utc > @changeoffsetdate 
    Begin 
        Set @offset_since = 1 
    End Else Begin
        Set @offset_since = 2
    End
End

JMP:

if @current_offset < @offset_since Begin
    Set @offset = 1
End Else if @current_offset > @offset_since Set @offset = -1 Else Set @offset = 0

Return @offset

END

Then the function that convert date

CREATE FUNCTION [dbo].[UF_CONVERT]
(
    @dt_utc datetime2(7)
)
RETURNS datetime
AS
BEGIN

    declare @offset int


    Select @offset = dbo.UF_ADJUST_OFFSET(@dt_utc)

    if @dt_utc >= '9999-12-31 22:59:59.9999999'
        set @dt_utc = '9999-12-31 23:59:59.9999999'
    Else
        set @dt_utc = (SELECT DATEADD(mi, DATEDIFF(mi, GETUTCDATE(), GETDATE()), @dt_utc) )

    if @offset <> 0
        Set @dt_utc = dateadd(hh, @offset, @dt_utc)

    RETURN @dt_utc

END
GigiS
  • 66
  • 5
0

-- get indian standard time from utc

CREATE FUNCTION dbo.getISTTime
(
@UTCDate datetime
)
RETURNS datetime
AS
BEGIN

    RETURN dateadd(minute,330,@UTCDate)

END
GO
Karan Harsh Wardhan
  • 1,096
  • 9
  • 22
0

You have to reformat the string as well as converting to the correct time. In this case I needed Zulu time.

Declare @Date datetime;
Declare @DateString varchar(50);
set @Date = GETDATE(); 
declare @ZuluTime datetime;

Declare @DateFrom varchar (50);
Declare @DateTo varchar (50);
set @ZuluTime = DATEADD(second, DATEDIFF(second, GETDATE(), GETUTCDATE()), @Date);
set @DateString =  FORMAT(@ZuluTime, 'yyyy-MM-ddThh:mm:ssZ', 'en-US' )  
select @DateString;
Tommyz
  • 1
0

Best way for Oracle:

With hardcoded datetime:

SELECT TO_CHAR(CAST((FROM_TZ(CAST(TO_DATE('2018-10-27 21:00', 'YYYY-MM-DD HH24:MI') AS TIMESTAMP), 'UTC') AT  TIME ZONE 'EET') AS DATE), 'YYYY-MM-DD HH24:MI') UTC_TO_EET FROM DUAL

Result: 2018-10-28 00:00

With column and table names:

SELECT TO_CHAR(CAST((FROM_TZ(CAST(COLUMN_NAME AS TIMESTAMP), 'UTC') AT  TIME ZONE 'EET') AS DATE), 'YYYY-MM-DD HH24:MI') UTC_TO_EET FROM TABLE_NAME
BOB
  • 700
  • 2
  • 16
  • 35
0

I have code to perform UTC to Local and Local to UTC times which allows conversion using code like this

DECLARE @usersTimezone VARCHAR(32)='Europe/London'
DECLARE @utcDT DATETIME=GetUTCDate()
DECLARE @userDT DATETIME=[dbo].[funcUTCtoLocal](@utcDT, @usersTimezone)

and

DECLARE @usersTimezone VARCHAR(32)='Europe/London'
DECLARE @userDT DATETIME=GetDate()
DECLARE @utcDT DATETIME=[dbo].[funcLocaltoUTC](@userDT, @usersTimezone)

The functions can support all or a subset of timezones in the IANA/TZDB as provided by NodaTime - see the full list at https://nodatime.org/TimeZones

Be aware that my use case means I only need a 'current' window, allowing the conversion of times within the range of about +/- 5 years from now. This means that the method I've used probably isn't suitable for you if you need a very wide period of time, due to the way it generates code for each timezone interval in a given date range.

The project is on GitHub: https://github.com/elliveny/SQLServerTimeConversion

This generates SQL function code as per this example

Elliveny
  • 2,159
  • 1
  • 20
  • 28
-1

Here's a simpler one that takes dst in to account

CREATE FUNCTION [dbo].[UtcToLocal] 
(
    @p_utcDatetime DATETIME 
)
RETURNS DATETIME
AS
BEGIN
    RETURN DATEADD(MINUTE, DATEDIFF(MINUTE, GETUTCDATE(), @p_utcDatetime), GETDATE())
END
  • 8
    This doesn't actually take DST into account. Just try it: `SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, GETUTCDATE(), '20150101'), GETDATE())`. I'm currently in CEST (UTC+2), but DST will not be in effect on New Year's day, so the correct answer for me would be 1 January 2015 01:00. Your answer, like the accepted answer, returns 1 January 2015 02:00. –  Aug 26 '14 at 10:13
-1

I've found that this function is faster than other solutions using a separate table or loops. It's just a basic case statement. Given that all months between April and October have a -4-hour offset (Eastern Time) we just need to add a few more case lines for the fringe days. Otherwise, the offset is -5 hours.

This is specific to a conversion from UTC to Eastern time, but additional time zone functions can be added as needed.

USE [YourDatabaseName]
GO

/****** Object:  UserDefinedFunction [dbo].[ConvertUTCtoEastern]    Script Date: 11/2/2016 5:21:52 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO


CREATE FUNCTION [dbo].[ConvertUTCtoEastern]
(
@dtStartDate DATETIME
)
RETURNS DATETIME
AS
BEGIN
DECLARE @Working DATETIME
DECLARE @Returned DATETIME

SET @Working = @dtStartDate
SET @Working = 
case when month(@Working) between 4 and 10 then dateadd(HH,-4,@Working) 
     when @Working between '2017-03-12' and '2017-11-05' then dateadd(HH,-4,@Working) 
     when @Working between '2016-03-13' and '2016-11-06' then dateadd(HH,-4,@Working) 
     when @Working between '2015-03-08' and '2015-11-01' then dateadd(HH,-4,@Working) 
     when @Working between '2014-03-09' and '2014-11-02' then dateadd(HH,-4,@Working) 
     when @Working between '2013-03-10' and '2013-11-03' then dateadd(HH,-4,@Working) 
     when @Working between '2012-03-11' and '2012-11-04' then dateadd(HH,-4,@Working) 
else dateadd(HH,-5,@Working) end

SET @Returned = @Working

RETURN @Returned

END


GO
  • 1
    Hi. Just to note, these rules for DST [might soon be changing](https://www.cnn.com/2022/03/15/politics/senate-daylight-saving-time-permanent/index.html). If you're using this function, you'll need to update it if this law is enacted. Preferably, one should not rely on hard-coded rules like that. Modern SQL Server can use [`AT TIME ZONE`](https://learn.microsoft.com/sql/t-sql/queries/at-time-zone-transact-sql) instead. Thanks. – Matt Johnson-Pint Mar 16 '22 at 18:32
-1

This can be done without a function. Code below will convert a UTC time to Mountain time accounting for daylight savings. Adjust all the -6 and -7 numbers to your timezone accordingly (i.e. for EST you would adjust to -4 and -5 respectively)

--Adjust a UTC value, in the example the UTC field is identified as UTC.Field, to account for daylight savings time when converting out of UTC to Mountain time.
CASE
    --When it's between March and November, it is summer time which is -6 from UTC
    WHEN MONTH ( UTC.Field ) > 3 AND MONTH ( UTC.Field ) < 11 
        THEN DATEADD ( HOUR , -6 , UTC.Field )
    --When its March and the day is greater than the 14, you know it's summer (-6)
    WHEN MONTH ( UTC.Field ) = 3
        AND DATEPART ( DAY , UTC.Field ) >= 14 
        THEN
            --However, if UTC is before 9am on that Sunday, then it's before 2am Mountain which means it's still Winter daylight time.
            CASE 
                WHEN DATEPART ( WEEKDAY , UTC.Field ) = 1 
                    AND UTC.Field < '9:00'
                    --Before 2am mountain time so it's winter, -7 hours for Winter daylight time
                    THEN DATEADD ( HOUR , -7 , UTC.Field )
                --Otherwise -6 because it'll be after 2am making it Summer daylight time
                ELSE DATEADD ( HOUR , -6 , UTC.Field )
            END
    WHEN MONTH ( UTC.Field ) = 3
        AND ( DATEPART ( WEEKDAY , UTC.Field ) + 7 ) <= DATEPART ( day , UTC.Field ) 
        THEN 
            --According to the date, it's moved onto Summer daylight, but we need to account for the hours leading up to 2am if it's Sunday
            CASE 
                WHEN DATEPART ( WEEKDAY , UTC.Field ) = 1 
                    AND UTC.Field < '9:00'
                    --Before 9am UTC is before 2am Mountain so it's winter Daylight, -7 hours
                    THEN DATEADD ( HOUR , -7 , UTC.Field )
                --Otherwise, it's summer daylight, -6 hours
                ELSE DATEADD ( HOUR , -6 , UTC.Field )
            END
    --When it's November and the weekday is greater than the calendar date, it's still Summer so -6 from the time
    WHEN MONTH ( UTC.Field ) = 11
        AND DATEPART ( WEEKDAY , UTC.Field ) > DATEPART ( DAY , UTC.Field ) 
        THEN DATEADD ( HOUR , -6 , UTC.Field )
    WHEN MONTH ( UTC.Field ) = 11
        AND DATEPART ( WEEKDAY , UTC.Field ) <= DATEPART ( DAY , UTC.Field ) 
            --If the weekday is less than or equal to the calendar day it's Winter daylight but we need to account for the hours leading up to 2am.
            CASE 
                WHEN DATEPART ( WEEKDAY , UTC.Field ) = 1 
                    AND UTC.Field < '8:00'
                    --If it's before 8am UTC and it's Sunday in the logic outlined, then it's still Summer daylight, -6 hours
                    THEN DATEADD ( HOUR , -6 , UTC.Field )
                --Otherwise, adjust for Winter daylight at -7
                ELSE DATEADD ( HOUR , -7 , UTC.Field )
            END
    --If the date doesn't fall into any of the above logic, it's Winter daylight, -7
    ELSE
        DATEADD ( HOUR , -7 , UTC.Field )
END
  • Hi. Just to note, these rules for DST [might soon be changing](https://www.cnn.com/2022/03/15/politics/senate-daylight-saving-time-permanent/index.html). If you're using this function, you'll need to update it if this law is enacted. Preferably, one should not rely on hard-coded rules like that. Modern SQL Server can use [`AT TIME ZONE`](https://learn.microsoft.com/sql/t-sql/queries/at-time-zone-transact-sql) instead. Thanks. – Matt Johnson-Pint Mar 16 '22 at 18:31