Преобразовать столбец Datetime из UTC в местное время в операторе выбора

208

Я делаю несколько запросов на выборку SQL и хочу преобразовать свой столбец даты и времени в формате UTC в местное время, чтобы оно отображалось как местное время в результатах моего запроса. Обратите внимание, я не собираюсь делать это преобразование с помощью кода, а скорее, когда я делаю ручные и случайные запросы SQL к моим базам данных.

Nugs
источник
Этот вопрос кажется похожим на ваши обстоятельства? stackoverflow.com/questions/3404646/…
Taryn East
Возможный дубликат TSQL: Как перевести местное время в UTC? (SQL Server 2008)
Восстановите Монику. Пожалуйста,

Ответы:

322

Вы можете сделать это следующим образом на SQL Server 2008 или более поздней версии:

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

Вы также можете сделать менее многословно:

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

Что бы вы ни делали, не используйте -для вычитания дат, потому что операция не является атомарной, и вы будете иногда получать неопределенные результаты из-за состязаний между системным временем и временем, проверяемым в разное время (т. Е. Неатомно) ,

Обратите внимание, что этот ответ не принимает во внимание DST. Если вы хотите включить корректировку DST, пожалуйста, посмотрите также следующий вопрос:

Как создать функции начала и окончания перехода на летнее время в SQL Server

Михаил Гольдштейн
источник
38
Есть ли способ сделать эту учетную запись для дневного света?
Стив
15
Я не вижу здесь название часового пояса, поэтому это неверно. Это действительно плохая идея предположить, что вы можете преобразовать в местное время, выполняя арифметику
JonnyRaa
7
@MichaelGoldshteyn Если у вас есть смещение часового пояса, вы все еще не знаете, в каком логическом часовом поясе находится время. Часовые пояса являются социальными вещами и могут быть изменены правительствами в разных точках. Смещение для часового пояса может быть различным в разные моменты времени / истории (летнее время является обычным). Вы компенсируете время на текущее смещение от UTC ... еще один момент заключается в том, что это даст часовой пояс сервера, а не клиента, что может стать дополнительной проблемой, если вы размещаете хостинг в другой стране.
JonnyRaa
4
@Niloofar Простой ответ: вы этого не делаете и не должны. Дата и время всегда должны храниться в UTC (предпочтительно на основе часов сервера базы данных, а не часов веб-сервера, сервера приложений и определенно не часов клиента). Отображение даты и времени является функцией для слоя пользовательского интерфейса и отвечает за преобразование даты и времени в нужный вам формат (включая часовой пояс, если это необходимо).
Роберт Макки
11
Для любого, кто использует sql azure, этот подход не будет работать, потому что все функции даты / времени возвращают UTC, поэтому сравнение GETDATE () с GETUTCDATE () не даст вам ничего, с чем вы работаете, и ваш результат такой же, как вы начали.
Брайан Суровец
59

Я не нашел ни одного из этих примеров полезным для получения даты и времени, хранящихся как UTC, с датой и временем в указанном часовом поясе (НЕ часовом поясе сервера, поскольку базы данных SQL Azure работают как UTC). Вот как я справился с этим. Это не элегантно, но просто и дает правильный ответ без ведения других таблиц:

select CONVERT(datetime, SWITCHOFFSET(dateTimeField, DATEPART(TZOFFSET, 
dateTimeField AT TIME ZONE 'Eastern Standard Time')))
Эйден Каскела
источник
4
Работает только на 2016 год и использует системный реестр. Но отличное решение для Azure.
Дэн Канди
2
Это работает для лазурного времени, хранящегося в UTC, спасибо, ребята
Null
3
Вот список строк, которые вы можете использовать для часовых поясов: stackoverflow.com/a/7908482/631277
Мэтт Кемп
1
При использовании SQL 2016 и AT TIME ZONEсинтаксиса, рассмотрит stackoverflow.com/a/44941536/112764 - вы можете цепи несколько переходов вместе просто конкатенации нескольких at time zone <blah>сек.
NateJ
3
Это тоже немного странно, но некоторые очень простые тесты выглядят так, как будто это тоже может сработать - dateTimeField AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time'просто цепочка AT TIME ZONEутверждений. (@NateJ упомянул об этом выше, я вижу сейчас)
Дэвид Мохундро
22

Если вы используете местное время, скажем, Eastern Standard Timeи хотите преобразовать его в UTC, тогда в Azure SQL и SQL Server 2016 и более поздних версиях вы можете выполнить:

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

Полный список названий часовых поясов можно найти с помощью:

SELECT * FROM sys.time_zone_info 

И да, часовые пояса имеют плохое название - хотя это так Eastern Standard Time, летнее время учитывается.

Мэтт Фреар
источник
2
Часовые
пояса
21

Если вам нужно преобразование, отличное от местоположения вашего сервера, вот функция, которая позволяет вам передавать стандартное смещение и учетные записи для летнего времени США:

-- =============================================
-- 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
Рон Смит
источник
2
Рон Смит: Я знаю, что это старая статья, но мне было любопытно, что обозначают жестко закодированные «0314» и «1107» при получении диапазона летнего времени. Похоже, это жестко закодированные дни, которые меняются из-за DTS. Зачем вам это жестко кодировать, если это будет расчетная дата, потому что дни меняются в зависимости от того, где в календаре выпадает второе воскресенье марта и первое воскресенье ноября. Трудно закодированные дни сделали бы этот код принципиально ошибочным.
Майк
2
Хороший вопрос :) Это максимальные даты, когда может произойти второе воскресенье марта и первое воскресенье ноября. Следующие строки устанавливают переменные на фактическую дату.
Рон Смит
8

Использование новых возможностей SQL Server 2016:

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

Но процедура clr работает в 5 раз быстрее: '- (

Обратите внимание, что смещение для одной временной зоны может изменяться на зимнее или летнее время. Например

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'

полученные результаты:

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

Вы не можете просто добавить постоянное смещение.

Павел Самойленко
источник
5

Если включение CLR в вашей базе данных является опцией, а также использование часового пояса сервера SQL, его можно легко написать в .Net.

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());
    }
}

Вводится значение даты и времени в формате UTC, и выводится локальное значение даты и времени относительно сервера. Нулевые значения возвращают ноль.

JGates
источник
5

Не существует простого способа сделать это правильным и общим способом.

Прежде всего следует понимать, что смещение зависит от рассматриваемой даты, часового пояса и летнего времени. GetDate()-GetUTCDateтолько дает вам смещение сегодня в TZ сервера, что не имеет значения.

Я видел только два рабочих решения, и я много искал.

1) Пользовательская функция SQL с парой таблиц базовых данных, таких как часовые пояса и правила летнего времени на TZ. Работает, но не очень элегантно. Я не могу опубликовать его, так как у меня нет кода.

РЕДАКТИРОВАТЬ: Вот пример этого метода https://gist.github.com/drumsta/16b79cee6bc195cd89c8

2) Добавьте сборку .net к БД, .Net может сделать это очень легко. Это работает очень хорошо, но недостатком является то, что вам нужно настроить несколько параметров на уровне сервера, и конфигурация легко нарушается, например, если вы восстанавливаете базу данных. Я использую этот метод, но не могу опубликовать его, так как у меня нет кода.

vikjon0
источник
4

Ни один из них не работал для меня, но это ниже работало на 100%. Надеюсь, что это может помочь другим, пытающимся преобразовать это, как я.

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
Майк
источник
1
Это должен быть принятый ответ. Единственное, что я хотел бы изменить, - это имя, поскольку оно подразумевает, что оно относится к времени EST, тогда как на самом деле оно относится к местному времени, а StandardOffset передается в качестве параметра.
Грег Гам
Согласитесь, лучший ответ ... но вместо того, чтобы передавать смещение в качестве параметра, я добавил это в тело функции:declare @StandardOffset int = datediff (hh, GETUTCDATE(), GETDATE())
Том Варфилд
В продолжение моего предыдущего предложения - если вы рассчитываете @StandardOffset, вам не нужно вносить поправку на летнее время.
Том Уорфилд
3

Вот версия, которая учитывает переход на летнее время, смещение UTC и не привязана к определенному году.

---------------------------------------------------------------------------------------------------
--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

Я обнаружил, что одноразовый способ слишком медленный, когда данных много. Поэтому я сделал это, присоединившись к табличной функции, которая позволила бы рассчитать разницу в часах. В основном это сегменты даты и времени со смещением часа. Год будет 4 ряда. Так что табличная функция

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

вернул бы эту таблицу:

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

Это учитывает летнее время. Пример его использования приведен ниже, а полный пост в блоге здесь .

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
источник
По-видимому, он не учитывает DST правильно. Не уверен, если вы предполагаете, что существуют только США и правила DST никогда не меняются.
vikjon0
@ vikjon0 да, проект, для которого я построил, имел только часовые пояса США.
JBrooks
2
 declare @mydate2 datetime
 set @mydate2=Getdate()
 select @mydate2 as mydate,
 dateadd(minute, datediff(minute,getdate(),@mydate2),getutcdate())
Looking_for_answers
источник
1

Ответ Рона содержит ошибку. Используется 2:00 по местному времени, когда требуется эквивалент UTC. У меня недостаточно очков репутации, чтобы прокомментировать ответ Рона, поэтому исправленная версия появится ниже:

-- =============================================
-- 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
jlspublic
источник
Это особенность, а не ошибка :). Большая часть Соединенных Штатов начинает переход на летнее время в 2:00. En.wikipedia.org/wiki/Daylight_saving_time
Рон Смит,
@RonSmith Да, в 2:00 по местному времени, которое мы должны преобразовать в UTC, чтобы определить, находится ли указанное время UTC в диапазоне летнего времени.
Jlspublic
1

Отметка времени UNIX - это просто количество секунд между определенной датой и эпохой Unix,

SELECT DATEDIFF (SECOND, {d '1970-01-01'}, GETDATE ()) // Это вернет метку времени UNIX на сервере SQL

Вы можете создать функцию для локального преобразования даты и времени в UIX UTC, используя функцию смещения по стране в метку времени Unix на сервере SQL.

Васантлал В.А.
источник
1

Это просто. Попробуйте это для Azure SQL Server:

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

Для локального сервера SQL:

SELECT CONVERT(datetime2, SWITCHOFFSET(CONVERT(datetimeoffset, gETDATE()), DATENAME(TzOffset, gETDATE() AT TIME ZONE 'Eastern Standard Time'))) FROM YourTable
lijuthomas
источник
1
Что происходит с этим в летнее время (так как часовой пояс определенно говорит «Восточное стандартное время»)?
Mark
1

Для пользователей Azure SQL и @@Version> = SQL Server 2016 ниже приведено описание простой функции 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

Для типов значений, которые @LocalTimeZoneмогут принять, пожалуйста, перейдите по этой ссылке или Перейти кKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones

рейнджер
источник
0

В качестве предупреждения - если вы собираетесь использовать следующее (обратите внимание на миллисекунды вместо минут):

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

Имейте в виду, что часть DATEDIFF не всегда возвращает один и тот же номер. Так что не используйте его для сравнения DateTimes до миллисекунд.

Sasquatch
источник
0

Я обнаружил, что эта функция быстрее, чем другие решения, использующие отдельную таблицу или циклы. Это просто базовое утверждение. Учитывая, что все месяцы между апрелем и октябрем имеют смещение на -4 часа (восточное время), нам просто нужно добавить еще несколько строк случаев для крайних дней. В противном случае смещение составляет -5 часов.

Это характерно для преобразования из UTC в восточное время, но при необходимости могут быть добавлены дополнительные функции часового пояса.

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
Джейсон Грин
источник
0

Это должно быть в состоянии получить время сервера с DST

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

sysdatetimeoffset учитывает DST

select [InputTime] = @dt
       , [LocalTime2] = dateadd(mi, datediff(mi, sysdatetimeoffset(),getdate()), @dt) 
Diam
источник
0

Первая функция: настроена для итальянского часового пояса (+1, +2), переключение дат: последнее воскресенье марта и октябрь, возвращает разницу между текущим часовым поясом и датой и временем в качестве параметра.

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

Код является:

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

Тогда функция, которая конвертирует дату

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
источник
0

- получить стандартное индийское время от UTC

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

    RETURN dateadd(minute,330,@UTCDate)

END
GO
Меградж Свами
источник
0

Это можно сделать без функции. Приведенный ниже код преобразует время UTC в горное время с учетом перехода на летнее время. Отрегулируйте все числа -6 и -7 в соответствии с вашим часовым поясом (т.е. для EST вы должны настроить соответственно на -4 и -5)

--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
Алекс Фенек
источник
0

Вы должны переформатировать строку, а также преобразовать в правильное время. В этом случае мне нужно было время Зулу.

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
источник
0

Лучший способ для Oracle:

С жестко закодированной датой:

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

С именами столбцов и таблиц:

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
ЛПП
источник
0

У меня есть код для выполнения UTC в локальное и локальное время в UTC, что позволяет преобразование с использованием кода, подобного этому

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

и

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

Функции могут поддерживать все или подмножество часовых поясов в IANA / TZDB, как это предусмотрено NodaTime - см. Полный список на https://nodatime.org/TimeZones.

Помните, что мой вариант использования означает, что мне нужно только «текущее» окно, позволяющее преобразовать время в диапазоне примерно +/- 5 лет. Это означает, что метод, который я использовал, вероятно, не подходит вам, если вам нужен очень широкий период времени, из-за способа, которым он генерирует код для каждого интервала часового пояса в данном диапазоне дат.

Проект находится на GitHub: https://github.com/elliveny/SQLServerTimeConversion

Это генерирует код функции SQL согласно этому примеру

Elliveny
источник
0

Ну, если вы храните данные в виде даты UTC в базе данных, вы можете сделать что-то простое, как

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

это было всегда локально с точки зрения сервера, и вы не возитесь с AT TIME ZONE 'your time zone name', если ваша база данных будет перемещена в другой часовой пояс, например, при установке клиента, жесткий кодированный часовой пояс может вас укусить.

Уолтер Веховен
источник
0

В postgres это работает очень хорошо .. Скажите серверу время, в которое сэкономлено время, 'utc', а затем попросите его преобразовать в определенный часовой пояс, в данном случае 'Brazil / East'

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

Получить полный список часовых поясов с помощью следующего выбора;

select * from pg_timezone_names;

Подробности здесь.

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

HHarada
источник
-1

Вот более простой, который принимает DST для учета

CREATE FUNCTION [dbo].[UtcToLocal] 
(
    @p_utcDatetime DATETIME 
)
RETURNS DATETIME
AS
BEGIN
    RETURN DATEADD(MINUTE, DATEDIFF(MINUTE, GETUTCDATE(), @p_utcDatetime), GETDATE())
END
Мортен Зольвберг
источник
6
Это на самом деле не принимает во внимание DST. Просто попробуйте SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, GETUTCDATE(), '20150101'), GETDATE()). В настоящее время я нахожусь в CEST (UTC + 2), но DST не будет действовать в новогодний день, поэтому правильный ответ для меня будет 1 января 2015 года 01:00. Ваш ответ, как и принятый ответ, возвращается 1 января 2015 года 02:00.