2016-08-17 4 views
0

У меня есть запрос, который выполняет некоторые преобразования часового пояса. В качестве примера, я хочу, чтобы преобразовать из EST времени GMT:Oracle - Как правильно * конвертировать из EST/EDT в GMT?

За время EDT из 3/13/2016 @2:00 AM (сразу после EST-> EDT переключающий) я должен получить время по Гринвичу 3/13/2016 7:00:00 AM (verified here на дату-/13/2016 @ 2am). Вместо этого я получаю 3/13/2016 6:00:00 AM использовать этот запрос:

SELECT NEW_TIME(TO_DATE('2016/3/13 02:00:00 AM', 
       'YYYY/MM/DD HH:MI:SS AM'),'EDT','GMT') 
FROM DUAL; 

За время EST из 3/13/2016 @1:00AM (за одну секунду до перехода) я получаю то, что, как представляется, правильный результат:

SELECT NEW_TIME(TO_DATE('2016/3/13 01:59:59 AM', 
         'YYYY/MM/DD HH:MI:SS AM'),'EST','GMT') 
FROM DUAL; 

Результат:

3/13/2016 6:59:59 AM 

Что я здесь делаю неправильно? Я прочитал документацию Oracle по NEW_TIME и попытался переключить EDT/EST с GMT (основанный на одном примере, который у них есть в нижней части страницы), но это дает мне еще более странные результаты.

+0

На самом деле, я точно вижу, что здесь происходит, я не могу поверить, что мне потребовалось много времени, чтобы понять это. 2:00 утра 13/13/2016 НЕ СУЩЕСТВУЕТ в EPT! Вы бы никогда не увидели 2 часа на ваших часах в этот день! Мы просто пропустим до 3 утра ... (Человек, которому я ненавижу летнее время!) –

+1

Вы должны использовать полный идентификатор места, 'America/New_York' –

+0

Не похоже, что это проблема для вас здесь, но убедитесь, что что вы обновляетесь в патчах часовых поясов базы данных, если работаете с данными часового пояса, особенно если у вас есть данные из часовых поясов по всему миру, а не только несколько часовых поясов США. Многие производственные базы данных не обновляются со всеми морщинами о таких вещах, как, когда DST начинается в разных местах. –

ответ

2

Вы не должны использовать старую функцию моды NEW_TIME, используйте FROM_TZ и Datetime Expressions, как рекомендовано Oracle.

Одна проблема времени часовых поясов EST или EDTне имеют любое летнее! EST всегда означает (скорее всего) UTC-05: 00 независимо от того, лето зимы. EDT всегда означает (скорее всего) UTC-04: 00 независимо от того, лето зимы.

Помимо отсутствующего летнего поддержку, EST или EDT неоднозначны, увидеть этот список:

SELECT tzabbrev, TZ_OFFSET(tzname), tzname 
FROM V$TIMEZONE_NAMES 
WHERE tzname IN ('EST', 'EDT') OR tzabbrev IN ('EST', 'EDT') 
ORDER BY 1,2; 

TZABBREV TZ_OFFSET(TZNAME) TZNAME 
=============================================== 
EDT -04:00 America/Detroit 
EDT -04:00 US/Eastern 
EDT -04:00 US/East-Indiana 
EDT -04:00 EST5EDT 
EDT -04:00 Canada/Eastern 
EDT -04:00 America/Toronto 
EDT -04:00 America/Thunder_Bay 
EDT -04:00 America/Santo_Domingo 
EDT -04:00 America/Pangnirtung 
EDT -04:00 America/Nipigon 
EDT -04:00 America/New_York 
EDT -04:00 America/Nassau 
EDT -04:00 America/Montreal 
EDT -04:00 America/Louisville 
EDT -04:00 America/Kentucky/Monticello 
EDT -04:00 America/Kentucky/Louisville 
EDT -04:00 America/Iqaluit 
EDT -04:00 America/Indianapolis 
EDT -04:00 America/Indiana/Winamac 
EDT -04:00 America/Indiana/Vincennes 
EDT -04:00 America/Indiana/Vevay 
EDT -04:00 America/Indiana/Petersburg 
EDT -04:00 America/Indiana/Marengo 
EDT -04:00 US/Michigan 
EDT -04:00 America/Fort_Wayne 
EDT -04:00 America/Grand_Turk 
EDT -04:00 America/Indiana/Indianapolis 
EDT -05:00 America/Jamaica 
EDT -05:00 America/Indiana/Tell_City 
EDT -05:00 Jamaica 
EDT -05:00 America/Cancun 
EDT -05:00 America/Port-au-Prince 
EST +09:30 Australia/Broken_Hill 
EST +09:30 Australia/Yancowinna 
EST +10:00 Australia/Canberra 
EST +10:00 Australia/Brisbane 
EST +10:00 Australia/ACT 
EST +10:00 Australia/Currie 
EST +10:00 Australia/Hobart 
EST +10:00 Australia/Lindeman 
EST +10:00 Australia/Melbourne 
EST +10:00 Australia/NSW 
EST +10:00 Australia/Queensland 
EST +10:00 Australia/Tasmania 
EST +10:00 Australia/Sydney 
EST +10:00 Australia/Victoria 
EST +10:30 Australia/Lord_Howe 
EST +10:30 Australia/LHI 
EST +11:00 Antarctica/Macquarie 
EST -03:00 America/Moncton 
EST -04:00 America/Antigua 
EST -04:00 America/Detroit 
EST -04:00 America/Fort_Wayne 
EST -04:00 America/Grand_Turk 
EST -04:00 America/Indiana/Indianapolis 
EST -04:00 America/Indiana/Marengo 
EST -04:00 America/Indiana/Petersburg 
EST -04:00 America/Indiana/Vevay 
EST -04:00 America/Indiana/Vincennes 
EST -04:00 America/Indiana/Winamac 
EST -04:00 America/Indianapolis 
EST -04:00 America/Iqaluit 
EST -04:00 America/Kentucky/Louisville 
EST -04:00 America/Kentucky/Monticello 
EST -04:00 America/Louisville 
EST -04:00 America/Montreal 
EST -04:00 America/Nassau 
EST -04:00 America/New_York 
EST -04:00 America/Nipigon 
EST -04:00 America/Pangnirtung 
EST -04:00 America/Santo_Domingo 
EST -04:00 America/Thunder_Bay 
EST -04:00 America/Toronto 
EST -04:00 Canada/Eastern 
EST -04:00 EST5EDT 
EST -04:00 US/East-Indiana 
EST -04:00 US/Eastern 
EST -04:00 US/Michigan 
EST -05:00 US/Central 
EST -05:00 Jamaica 
EST -05:00 America/Cancun 
EST -05:00 America/Cayman 
EST -05:00 America/Chicago 
EST -05:00 America/Coral_Harbour 
EST -05:00 America/Indiana/Knox 
EST -05:00 America/Indiana/Tell_City 
EST -05:00 America/Jamaica 
EST -05:00 America/Knox_IN 
EST -05:00 America/Atikokan 
EST -05:00 America/Menominee 
EST -05:00 America/Merida 
EST -05:00 America/Panama 
EST -05:00 America/Port-au-Prince 
EST -05:00 America/Rankin_Inlet 
EST -05:00 America/Resolute 
EST -05:00 CST 
EST -05:00 EST 
EST -05:00 US/Indiana-Starke 
EST -06:00 America/Managua 
EST -06:00 America/Cambridge_Bay 

Try это так, то ваше время будет автоматически преобразовано правильно, независимо от ваших сезонов:

SELECT 
    FROM_TZ(TO_TIMESTAMP('2016/January/01 12:00:00 AM', 'YYYY/MONTH/DD HH:MI:SS AM'), 'America/New_York') AT TIME ZONE 'UTC', 
    FROM_TZ(TO_TIMESTAMP('2016/July/01 12:00:00 AM', 'YYYY/MONTH/DD HH:MI:SS AM'), 'America/New_York') AT TIME ZONE 'UTC' 
FROM dual; 
+0

В большинстве случаев я бы рекомендовал людям использовать ваш ответ, но мне было предложено использовать немного другое решение (мой ответ). Если бы это было полностью для меня, я бы использовал функцию 'FROM_TZ()' в соответствии с вашей рекомендацией. Спасибо! –

-1

Я закончил использование:

SELECT SYSDATE+(SUBSTR(TO_CHAR(SYSTIMESTAMP, 'TZR'),2,2))/24 
FROM DUAL; 

, где (SUBSTR(TO_CHAR(SYSTIMESTAMP, 'TZR'),2,2)) предоставляет текущее смещение от utc. Я согласен с тем, что FROM_TZ, вероятно, более точен во многих краевых случаях, которые существуют с часовым поясом, но мы действительно искали что-то, что быстро вычислялось при встраивании этого в представление.

Для правильного преобразования между зонами, я просто добавил ток смещения в любой момент это было то, что мне нужно, чтобы преобразовать:

DECLARE 
l_convert_me_to_utc DATE := TO_DATE('9/12/2016 1:00:00 PM','MM/DD/YYYY HH12:MI:SS AM'); 
l_converted DATE; 
BEGIN 
    l_converted:=(l_convert_me_to_utc+(SUBSTR(TO_CHAR(SYSTIMESTAMP, 'TZR'),2,2))/24); 
    DBMS_OUTPUT.put_line ('UTC TIME IS: '||TO_CHAR(l_converted, 'MM/DD/YYYY HH12:MI:SS AM'));  
END; 

UTC TIME IS: 09/12/2016 05:00:00 PM 

Который имеет смысл, так как сейчас я нахожусь в Нью-Йорке TZ в EDT с - 4 смещение от UTC

EDIT: В соответствии с приведенными ниже замечаниями, это действительно работает только в том случае, если вы конвертируете то, что ваша ОС SYSDATE сейчас находится в UTC. Если это какой-то другой часовой пояс или какое-то время в прошлом или будущем, это не работает. Моя последняя логика была более сложной, чем то, что вы видите выше. Для моего приложения и с более сложной логикой (проверка таблицы со всеми датами переключения TZ с настоящего момента до 2100), это действительно работает.Просто не принимайте то, что у меня есть, и предполагайте, что он преобразует все в UTC отлично, как есть.

+0

Нет - извините. Вы не можете предположить, что * текущий * смещение является правильным смещением для момента времени, с которым вы работаете. Это является ведущим источником ошибок данных, когда речь идет о преобразованиях часового пояса. См. «Часовой пояс! = Смещение» в [wiki часового пояса] (http://stackoverflow.com/tags/timezone/info). Вы должны использовать 'America/New_York', чтобы правильно представлять все состояния и переходы в Восточном часовом поясе в США. Учтите, что хотя текущее смещение в NY составляет -4, если дата, которую вы конвертировали, была в декабре (например), то правильное смещение было бы -5. –

+0

(Кроме того, вы сказали +4, что будет применяться в некоторых частях России и на Ближнем Востоке. Вы имеете в виду -4) –

+0

Помните, что 'SYSTIMESTAMP' возвращает время в часовом поясе часового пояса операционной системы базы данных. Майские времена меняются два раза в год из-за дневного светового времени! (По крайней мере, я видел это поведение на многих серверах). Рассмотрим также функцию [SYS_EXTRACT_UTC] (http://docs.oracle.com/cd/E11882_01/server.112/e41084/functions186.htm#SQLRF06119), чтобы получить время в UTC. –