【问题标题】:Oracle - How to *correctly* convert from EST/EDT to GMT?Oracle - 如何*正确*从 EST/EDT 转换为 GMT?
【发布时间】:2016-08-17 19:28:59
【问题描述】:

我有一个查询正在进行一些时区转换。例如,我想将EST 时间转换为GMT

对于3/13/2016 @2:00 AM 的 EDT 时间(紧随 EST->EDT 转换之后),我应该得到 GMT 时间 3/13/2016 7:00:00 AMverified here 日期为 2016 年 3 月 13 日@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;

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

我在这里做错了什么?我已阅读有关 NEW_TIME 的 Oracle 文档,并尝试使用 GMT 切换 EDT/EST(基于页面底部的一个示例),但这给了我更奇怪的结果。

【问题讨论】:

  • 实际上,我看到这里到底发生了什么,我不敢相信我花了这么长时间才弄明白。 2016 年 3 月 13 日凌晨 2:00 在EPT 时间不存在!在这一天,你永远不会在时钟上看到凌晨 2 点!我们只是跳到凌晨 3 点......(我讨厌夏令时!)
  • 您应该使用完全限定的位置标识符America/New_York
  • 这对您来说似乎不是问题,但如果您正在处理时区数据,特别是如果您有来自世界各地时区的数据,而不仅仅是少数美国时区。许多生产数据库都不是最新的,诸如 DST 何时在不同位置开始等问题。

标签: oracle date datetime timezone


【解决方案1】:

你不应该使用旧的时尚功能NEW_TIME,使用Oracle推荐的FROM_TZDatetime Expressions

一个问题是时区ESTEDT 没有任何夏令时! EST 始终表示(最有可能)UTC-05:00,无论冬天是夏天。 EDT 始终表示(很可能)UTC-04:00,无论是冬天还是夏天。

除了缺少夏令时支持之外,ESTEDT 不明确,请参阅此列表:

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

这样试试,不管你的季节如何,你的时间都会自动正确转换:

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;

【讨论】:

  • 在大多数情况下,我建议人们使用您的答案,但我被指示使用一些不同的解决方案(我的答案)。如果完全由我决定,我会按照您的建议使用 FROM_TZ() 函数。谢谢!
【解决方案2】:

我最终使用了:

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

这是有道理的,因为我现在在 EDT 的 NY TZ,与 UTC 有 -4 的偏移

编辑:根据下面的 cmets,这只有在您现在将操作系统 SYSDATE 转换为 UTC 时才有效。如果是其他时区或过去或将来的某个时间,这实际上不起作用。我的最终逻辑比你在上面看到的更复杂。对于我的应用程序和更复杂的逻辑(检查所有 TZ 开关日期从现在到 2100 年的表),这确实有效。只是不要接受我上面的内容并假设它可以完美地将事物转换为 UTC。

【讨论】:

  • 不-抱歉。您不能假设 current 偏移量是您正在使用的时间点的正确偏移量。当涉及到时区转换时,这是数据错误的主要来源。请参阅the timezone tag wiki 中的“时区!= 偏移量”。您需要使用America/New_York 正确表示美国东部时区的所有状态和转换。考虑一下,虽然 NY 的当前偏移量是 -4,但如果您转换的日期是 12 月(例如),那么正确的偏移量应该是 -5。
  • (另外,你说+4,这将适用于俄罗斯和中东的部分地区。你的意思是-4)
  • 请注意,SYSTIMESTAMP 返回的是数据库操作系统时区的时区时间。由于夏令时,每年可能会发生两次变化! (至少我在许多服务器上都看到过这种行为)。还可以考虑使用函数 SYS_EXTRACT_UTC 来获取 UTC 时间。
  • 函数l_convert_me_to_utc+(SUBSTR(TO_CHAR(SYSTIMESTAMP, 'TZR'),2,2))/24仅在数据库操作系统的时区设置为03:00时有效。设置为UTCGMTAmerica/New_York 时会失败。或 ... - 如果您愿意,可以这样做。
猜你喜欢
  • 1970-01-01
  • 2011-07-26
  • 2023-03-20
  • 1970-01-01
  • 2014-06-25
  • 2020-12-13
  • 2020-09-06
  • 2017-08-26
  • 2015-05-14
相关资源
最近更新 更多