TSQL: Как преобразовать местное время в UTC? (SQL Server 2008)

Мы имеем дело с приложением, которое должно обрабатывать глобальные данные о времени из разных часовых поясов и настройки перехода на летнее время. Идея состоит в том, чтобы хранить все в формате UTC внутри и конвертировать только туда и обратно для локализованных пользовательских интерфейсов. Предлагает ли SQL Server какие-либо механизмы для работы с переводами с учетом времени, страны и часового пояса?

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

Есть указатели?


person BuschnicK    schedule 30.07.2009    source источник
comment
У меня есть mssql-сервер, связанный с mysql-сервером. Интересно, можно ли запускать mysql CONVERT_TZ (time, srczone, dstzone) по запросам :-) Странно, что эта функция отсутствует; он встроен в Linux.   -  person Leif Neland    schedule 27.08.2015
comment
@BuschnicK См. Мой ответ ниже. На самом деле, я думаю, вы можете принять это, даже если другим легче найти.   -  person Piotr Owsiak    schedule 28.10.2016
comment
Короткий ответ: до SQL Server 2016 встроенного способа сделать это не существует, поэтому в более ранних версиях потребуется специальный код.   -  person lehiester    schedule 10.09.2018
comment
Средний ответ: все функции смещения времени до SQL Server 2016 работали только с абсолютными смещениями, без поддержки переменных смещений, которые происходят в большинстве часовых поясов из-за перехода на летнее время. Запросы не имеют никакого способа получить доступ к смещениям времени, за исключением того, что является текущим смещением местного времени сервера, что бесполезно для попытки автоматизировать преобразование.   -  person lehiester    schedule 10.09.2018


Ответы (11)


Прошло 7 лет, и ... на самом деле есть эта новая функция SQL Server 2016, которая делает именно то, что вам нужно.
Она называется AT TIME ZONE и преобразует дату в указанный часовой пояс с учетом DST (летнее время) изменения.
Подробнее здесь: https://msdn.microsoft.com/en-us/library/mt612795.aspx

person Piotr Owsiak    schedule 28.10.2016
comment
Я больше не работаю над этим - ни в этом проекте, ни в SQL Server, ни в той же компании, ни даже в той же стране ;-) Так что это мне не поможет, но я буду голосовать за людей, которые найдут этот вопрос в настоящее время. - person BuschnicK; 04.12.2016
comment
@BuschnicK да, я полагаю, но я пришел сюда в поисках решения той же проблемы, что и у вас, поэтому я решил опубликовать ответ сейчас, когда есть реальное решение: D - person Piotr Owsiak; 20.02.2017
comment
Вопрос для SQL Server 2008. Обновите вопрос, если вы принимаете этот ответ. Спасибо - person Robert; 10.04.2017
comment
Как вы используете это для преобразования в UTC, который не является часовым поясом? - person xr280xr; 08.08.2017
comment
Чтобы преобразовать в UTC, вы можете сделать «В ЧАСОВОЙ ПОЯС» UTC. - person Krzyserious; 21.03.2018
comment
@Piotr Owsiak: Да, но предполагается, что время ввода - UTC, что бессмысленно, если вы хотите преобразовать местное время в UTC ... Итак, прошло 7 лет, а они все еще не думают, что стоит обрабатывать это должным образом. .. - person Stefan Steiger; 01.03.2019

Это работает для дат, которые в настоящее время имеют такое же смещение в формате UTC, что и хост SQL Server; он не учитывает изменения летнего времени. Замените YOUR_DATE местной датой для преобразования.

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

person shaig    schedule 07.11.2010
comment
Спасибо, это хорошая идея, но она работает только для одного часового пояса - локальной машины. Нам нужно, чтобы он работал для произвольных часовых поясов ... - person BuschnicK; 10.11.2010
comment
умно, но да, берегитесь часовых поясов. - person Krip; 04.04.2011
comment
Это не учитывает летнее время. - person Gabriel McAdams; 30.08.2011
comment
Нет! Разница зависит от точной даты. Это зависит от перехода на летнее время. - person usr; 17.10.2011
comment
В моем случае мне просто нужен был 1 часовой пояс, и это отлично сработало! Спасибо. - person M Thelen; 07.12.2011
comment
Я также использую только один часовой пояс, и все время, которое я конвертирую, - это все сегодня, поэтому мне не нужно беспокоиться о проблемах с летним временем, но я обнаружил, что мне нужно использовать select dateadd (second, (-1 * datiff (second, getutcdate ( ), getdate ())), YOUR_DATE), иначе я был 2 * разницей. - person grahamesd; 09.01.2012
comment
Это хорошо работает, если вы знаете, как вы бегаете сегодня, не исторически, спасибо - person David Adlington; 17.05.2012
comment
Вопрос касался нескольких часовых поясов. Это говорит только об одном - в зависимости от того, в какой TZ находится сервер. - person Rob Farley; 23.03.2013

Хотя некоторые из этих ответов дадут вам приблизительную оценку, вы не можете делать то, что пытаетесь сделать с произвольными датами для SqlServer 2005 и ранее, из-за перехода на летнее время. Использование разницы между текущим локальным и текущим UTC даст мне смещение, которое существует сегодня. Я не нашел способа определить, каким было бы смещение для рассматриваемой даты.

Тем не менее, я знаю, что SqlServer 2008 предоставляет некоторые новые функции даты, которые могут решить эту проблему, но люди, использующие более раннюю версию, должны знать об ограничениях.

Наш подход состоит в том, чтобы сохранять UTC и выполнять преобразование на стороне клиента, где у нас есть больший контроль над точностью преобразования.

person user890155    schedule 14.10.2011

Вот код для преобразования одной зоны DateTime в другую зону 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

Примечание: AT TIME ZONE работает только на SQL Server 2016+, и преимущество состоит в том, что он автоматически учитывает дневной свет при преобразовании в определенный часовой пояс.

person KarthikeyanMlp    schedule 06.07.2017
comment
Мне это нравится, хотя бы по той причине, что он показывает, что вы можете связать несколько AT TIME ZONE вызовов (фраз?) Вместе! Просто элегантно. Ранее я сказал, что stackoverflow.com/a/44579178/112764 отвечает моим потребностям, но это даже лучше. Большая честь. - person NateJ; 19.07.2018
comment
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 Да, вы можете объединить несколько AT TIME ZONE вызовов в цепочку, но От и Куда достаточно для любой конверсии, и больше всего нам нужно - person KarthikeyanMlp; 21.11.2018

Для SQL Server 2016 и новее и базы данных SQL Azure используйте встроенный _ 1_ инструкция.

Для более старых выпусков SQL Server вы можете использовать мой проект Поддержка часовых поясов SQL Server для преобразования между стандартом IANA. часовые пояса, как указано здесь.

UTC в Local выглядит так:

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

Локально для UTC выглядит так:

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

Числовые параметры - это флаг для управления поведением, когда на значения местного времени влияет переход на летнее время. Они подробно описаны в документации по проекту.

person Matt Johnson-Pint    schedule 03.04.2015
comment
Как человек ... этот проект Мэтта великолепен и не требует CLR. Мэтт заслуживает большой похвалы за это +100 - person buckley; 29.04.2016

SQL Server 2008 имеет тип datetimeoffset. Это действительно полезно для такого рода вещей.

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

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

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

Роб

person Rob Farley    schedule 30.07.2009
comment
SWITCHOFFSET не учитывает летнее время, поэтому полезен только в некоторых ситуациях. - person robocat; 18.03.2013
comment
Нет. Но вопрос заключался в возможности переключения на любой часовой пояс, который был запрошен. - person Rob Farley; 23.03.2013
comment
Из вопроса разные часовые пояса и настройки летнего времени. Мы тоже ищем решение для местного времени. Ваше предложение не решает проблему перехода на летнее время? - person robocat; 25.03.2013
comment
Каждый раз, когда вам нужно определить часовой пояс на клиенте, а затем получить время возврата базы данных в указанном часовом поясе, очень полезна функция SWITCHOFFSET. - person Rob Farley; 01.04.2013
comment
@RobFarley Определение часового пояса на клиенте и использование SWITCHOFFSET все равно может привести к ошибкам. Вам необходимо знать, применено ли к дате и времени, которое вы конвертируете, летнее время или нет. Простое определение часового пояса и применение текущего смещения к всемирному координированному времени может привести к отключению на час - и это в простом случае, когда все ваши преобразования происходят в одной стране. Не все страны переключаются на летнее время в одни и те же дни. SWITCHOFFSET отлично работает, если вы храните местное время и знаете разницу между исходной и целевой зоной в одной и той же стране. - person JamieSee; 02.03.2015
comment
да. Если вы знаете, что хотите переключиться с GMT-5 на GMT + 930, вы можете сделать это, не меняя порядок времени. Но вы должны знать, что вас интересует 0930, а не 1030. - person Rob Farley; 02.03.2015

Я склоняюсь к использованию DateTimeOffset для всего хранения даты и времени, которое не связано с местным событием (например, встреча / вечеринка и т. Д., С 12 до 15 в музее).

Чтобы получить текущий DTO как 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]

ПРИМЕЧАНИЕ. Я всегда буду использовать UTC при отправке по сети ... JS на стороне клиента может легко добраться до / из местного UTC. См .: new Date().toJSON() ...

Следующий JS будет обрабатывать синтаксический анализ даты UTC / GMT в формате ISO8601 в локальную дату и время.

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;
  };
}
person Tracker1    schedule 21.05.2012
comment
+1 Я тоже перешел на DateTimeOffset. Это позволяет избежать ряда проблем с локальными преобразованиями в формате UTC +. Однако по тем же причинам я рекомендую также отправлять значения со смещением по сети (через JSON). - person user2864740; 07.12.2013
comment
В качестве примечания, я делаю то же, что и вы, точно наоборот. Для DateTimes, привязанных к локальному событию, я храню DateTimeOffset. Для DateTime, не привязанного к локальному событию, я храню DateTime в формате UTC. У первого есть две соответствующие точки данных (когда это по местному времени и какое это местное время), у последнего - только одна (когда это) - person Martijn; 23.01.2014
comment
@Martijn Но часовой пояс не дает вам местоположения, и вы все равно должны хранить его отдельно. - person Tracker1; 05.02.2014
comment
@ tracker1 Это работает только тогда, когда у местоположения есть способ узнать свой часовой пояс, и даже в этом случае преобразовать его очень сложно. - person Martijn; 06.02.2014

Да, в некоторой степени столь же подробно здесь.
Подход, который я использовал (до 2008 г.), заключается в выполнении преобразования в бизнес-логике .NET перед вставкой в ​​БД.

person AdaTheDev    schedule 30.07.2009

Вы можете использовать функцию GETUTCDATE (), чтобы получить дату и время в формате UTC. Возможно, вы можете выбрать разницу между GETUTCDATE () и GETDATE () и использовать эту разницу для корректировки ваших дат по UTC.

Но я согласен с предыдущим сообщением, что намного проще контролировать правильное datetime на бизнес-уровне (например, в .NET).

person Bogdan_Ch    schedule 30.07.2009
comment
Нет! Разница зависит от точной даты. Это зависит от перехода на летнее время. - person usr; 17.10.2011
comment
Не учитывает летнее время. Некоторое время я использовал подобные решения, и это вызывало серьезные проблемы. Вы должны определить, находится ли дата, с которой вы сравниваете, летнее время. - person Jeff Davis; 29.05.2012

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

Возврат (например):

-06:0

Не на 100% положительно, это всегда будет работать.

person Jared Beach    schedule 14.01.2021

Пример использования:

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

Возврат:

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
person Robert Cantor    schedule 07.12.2013