Как правильно обрабатывать TimeZone в SQL Server?

34

Мой локальный сервер разработки находится на Ближнем Востоке, но мой рабочий сервер находится в Великобритании.

Мне нужно показать дату пользователю в его часовом поясе. Например, если пользователь находится в Саудовской Аравии, мне нужно показать время в соответствии с форматом Саудовской Аравии.

Должен ли я создать новую таблицу базы данных с именем TimeZone и сохранить время в формате UTC?

user960567
источник
Эффективное преобразование дат между временем UTC и местным (т. Е. PST) временем в SQL 2005 http://stackoverflow.com/questions/24797/effectively-converting-dates-between-utc-and-local-ie-pst-time-in- sql-2005
Мехди
Это веб-приложение или настольное приложение, или ...? Как это будет реализовано, сильно зависит от механизма доставки клиента, потому что то, что вам нужно, зависит от клиентской стороны.
Джон Зигель
Этот веб, а также родной мобильный. Да этот эффект у разных клиентов разный.
user960567
1
Похоже, ваш вопрос касается не только самой базы данных, но и разработки приложений. Тогда этот вопрос переполнения стека является обязательным для прочтения: переход на летнее время и рекомендации по часовым поясам
Ган

Ответы:

48

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

В любом случае, чтобы начать делать все правильно, важно хранить информацию о часовом поясе со временем . Другими словами, осознание того, что дата / время не 20130407 14:50имеют смысла без (а) включения текущего смещения UTC для часового пояса (примечание 1) или (b) обеспечения того, что вся логика, вставляющая эти значения, сначала преобразуется в определенное фиксированное смещение ( скорее всего 0). Без этих двух данных значения времени несопоставимы , а данные повреждены . ( Кстати, последний метод - игра с огнем (примечание 2) ; не делайте этого.)

В SQL Server 2008+ вы можете сохранять смещение со временем напрямую, используя datetimeoffsetтип данных. (Для полноты, в 2005 году и ранее, я бы добавил второй столбец для хранения текущего значения смещения UTC (в минутах).)

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

Для сети, которая по своей природе является автономной архитектурой, даже с правильно настроенными внутренними данными, она более сложна, потому что вам нужна информация о клиенте, чтобы иметь возможность выполнять преобразование и / или форматирование. Обычно это делается с помощью настроек пользовательских настроек (приложение конвертирует / форматирует вещи перед выводом) или просто показывает вещи с одинаковым фиксированным форматом и смещением часового пояса для всех (что в настоящее время делает платформа Stack Exchange).

Вы можете видеть, как, если внутренние данные не настроены должным образом, очень быстро они станут сложными и хакерскими. Я не рекомендовал бы идти по любому из этих путей, потому что у вас просто будет больше проблем в будущем.

Примечание 1:

Смещение UTC часового пояса не является фиксированным: рассмотрите переход на летнее время, когда смещение UTC зоны изменяется на плюс или минус час. Кроме того, даты перехода на летнее время в зонах регулярно меняются. Таким образом, использование datetimeoffset(или совокупность local timeи UTC offset at that time) приводит к максимальному восстановлению информации.

Заметка 2:

Речь идет об управлении вводом данных. Хотя нет надежного способа проверки входящих значений, лучше применять простой стандарт, который не требует вычислений. Если общедоступный API ожидает тип данных, который включает смещение, это требование будет ясно для вызывающей стороны.

Если это не так, вызывающая сторона должна полагаться на документацию (если они ее читают), или вычисления выполняются неправильно и т. Д. При смещении требуется меньше режимов сбоя / ошибки, в частности для распределенной системы ( или даже просто веб / база данных на отдельных серверах, как здесь).

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

Джон Зигель
источник
3
Насколько я понимаю, смещение не совпадает с часовым поясом ... время, сохраненное в datetimeoffset, теряет информацию, например информацию о летнем времени ... Подробнее: stackoverflow.com/tags/timezone/info
Питер МакЭвой
1
@PeterMcEvoy DST здесь не имеет никакого отношения. datetimeoffsetозначает «это локальное время пользователя / компьютера, когда это произошло» - нам не нужно знать, действовал ли DST или нет, потому что заданное смещение UTC всегда присутствует (встроено в datetimeoffsetзначение).
Дай
12

Я разработал комплексное решение для преобразования часовых поясов в SQL Server. См. Поддержка часовых поясов SQL Server на GitHub .

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

По своей концепции он аналогичен решению «T-SQL Toolbox», описанному в ответе adss, но использует более распространенные часовые пояса IANA / Olson / TZDB вместо Microsoft и включает в себя утилиты для хранения данных в новых выпусках TZDB выходи.

Пример использования (для ответа на ОП):

SELECT Tzdb.UtcToLocal(sysutcdatetime(), 'Asia/Riyadh') as CurrentTimeInSaudiArabia

Смотрите readme на GitHub для дополнительных API и подробного объяснения.

Также обратите внимание, что, поскольку это решение на основе UDF, оно не предназначено для производительности в качестве основной цели. Было бы еще лучше, если бы эта функциональность была встроена в SQL Server, подобно тому, как эта CONVERT_TZфункция существует в Oracle и MySQL.

Мэтт Джонсон-Пинт
источник
6

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

Таким образом, сохранение всего внутри UTC времени позволило SQL Server хорошо работать в глобальном масштабе. Это одна из причин, по которой переход на летнее время является проблемой для Windows, поскольку другие продукты MS, такие как Outlook, также сохраняют дату / время внутри системы в формате UTC и создают смещение, которое необходимо исправить.

Я работаю в компании, где у нас есть тысячи серверов (хотя не MS SQL Server, но все виды серверов), разбросанных по всему миру, и если бы мы не специально заставляли все работать по UTC, мы бы сошли с ума очень быстро.

Али Разеги
источник
5

Я разработал и опубликовал проект «T-SQL Toolbox» для codeplex, чтобы помочь всем, кто борется с обработкой даты и времени в SQL Server. Это открытый исходный код и полностью бесплатное использование.

Он предлагает простые функции преобразования даты и времени с использованием простого T-SQL с дополнительными таблицами конфигурации из коробки.

В вашем примере вы можете использовать следующий пример:

SELECT [DateTimeUtil].[UDF_ConvertLocalToLocalByTimezoneIdentifier] (
    'GMT Standard Time', -- the original timezone in which your datetime is stored
    'Middle East Standard Time', -- the target timezone for your user
    '2014-03-30 01:55:00' -- the original datetime you want to convert
)

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

Список всех поддерживаемых часовых поясов можно найти в таблице «DateTimeUtil.Timezone», также предоставленной в базе данных T-SQL Toolbox.

АДА
источник
Такой инструментарий может показаться отличным помощником для разработчика. Однако скалярная функция является черным ящиком для оптимизатора запросов. Это означает, что когда я применяю вашу функцию к столбцу, таблицы конфигурации, о которых вы упоминаете, будут поражены столько раз, сколько строк в наборе результатов (при условии, что я использую функцию только в предложении SELECT). Это не кажется хорошей перспективой с точки зрения производительности. Я действительно не проверял ваш инструментарий, поэтому не могу сказать наверняка, это просто общая проблема, основанная на том, что я знаю.
Андрей М
Андрей, конечно, вы правы с точки зрения производительности. Таблицы запросов UDF предназначены не для массовых операций с данными, а для удобства использования. Тем не менее, они неплохо работают на моей машине примерно до 100.000 записей.
Adss
2
Если вам нужно обработать больше записей в своем сценарии использования, и производительность имеет значение, вам лучше подумать о сохранении предварительно рассчитанных данных. Но даже тогда эти пользовательские функции могут помочь сохранить значения времени данных в требуемых тимзонах.
adss
С моей точки зрения, эта проблема не в производительности, а в том, чтобы просто получить базовую функциональность. Чтобы иметь возможность вернуть точную информацию из запроса, это первое. Разработка некоторой временной таблицы для кеширования и последующего обращения к ней в запросе или к тому, как вы ее обрабатываете, для повышения производительности занимает второе место.
Действие Дан
1

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

20c
источник