【问题标题】:SQL to convert TimeZone of TimeStamp column from A to B when system is in TimeZone C当系统处于 TimeZone C 时,SQL 将 TimeStamp 列的 TimeZone 从 A 转换为 B
【发布时间】:2017-03-19 06:23:03
【问题描述】:

Oracle 11g 中的BirthDatabase 表有以下五列:

  1. ID - 非空 - 数字
  2. Name - Varchar2(32)
  3. DOB - 日期
  4. Place - Varchar2(32)
  5. BirthTime - 时间戳(6)

BirthTime 列不支持 TimeZone,但有 UTC 中的 DateTime 数据。

但是,(最重要的)数据库的系统时间戳在Europe\London

这意味着 BirthTime 数据不知道 timezone 并且与系统 TZ 相比位于不同的 TZ。

任务:编写一个 SQL 查询以获取伦敦当地时间的 BirthTime(即 DST 关闭时的 UTC 和 DST 开启时的 UTC+1)

我的做法:

我尝试在 SQL 中使用cast 函数,但没有完全达到预期的输出

select
    ID, Name, DOB, Place,
    BirthTime as orig_BT,
    cast(BirthTime as timestamp with time zone) as BT_withTz, 
    cast(cast(BirthTime as timestamp with time zone) at time zone 'Europe/London' as timestamp) BT_BST,
    cast(cast(BirthTime as timestamp with time zone) at time zone 'UTC' as timestamp) BT_UTC
from
    BirthDatabase

这将返回以下输出:

ID  NAME    DOB         PLACE   ORIG_BT                         BT_WITHTZ                                   BT_BST                          BT_UTC
________________________________________________________________________________________________________________________________________________________________________
1   John    28-OCT-16   Bristol 28-OCT-16 10.48.12.000000000    28-OCT-16 10.48.12.000000000 EUROPE/LONDON  28-OCT-16 10.48.12.000000000    28-OCT-16 09.48.12.000000000
2   Jane    01-NOV-16   London  01-NOV-16 11.48.29.000000000    01-NOV-16 11.48.29.000000000 EUROPE/LONDON  01-NOV-16 11.48.29.000000000    01-NOV-16 11.48.29.000000000

我想要的输出如下:

ID  NAME    DOB         PLACE   ORIG_BT                         BT_DESIRED
____________________________________________________________________________________________
1   John    28-OCT-16   Bristol 28-OCT-16 10.48.12.000000000    28-OCT-16 11.48.12.000000000
2   Jane    01-NOV-16   London  01-NOV-16 11.48.29.000000000    01-NOV-16 11.48.29.000000000

我认为使用上述查询的输出获得所需输出的一种快速而肮脏的方法是修改 SQL 以计算以下内容:

BT_DESRIED = ORIG_BT + (BT_BST - BT_UTC)

我的问题如下:

  1. 上面的BT_DESRIED = ORIG_BT + (BT_BST - BT_UTC)表达式的计算语法是什么

  2. 有没有更优雅的方法来实现这一点?

【问题讨论】:

  • 还有更优雅的方式——Oracle 有大量的时间戳函数可以从一个时区转换到另一个时区。我现在可以看到的最大问题是您想从 PLACE 推断 ORIG_BT 的时区。所以你需要一个帮助表来告诉你每个地方应该使用哪个时区。 (而且会有并发症 - 那个时候在那个地方使用什么时区名称?他们使用 DST THEN 吗?等等)
  • BirthTime\ORIG_BT 不知道 tz,但该列中的数据采用 UTC。 Place不需要翻译。我相信只要函数可以从一个 tz 转换为另一个,DST 应该不会造成任何问题。

标签: sql oracle timezone


【解决方案1】:

您可以使用以下查询:

select
   ID, Name, DOB, Place, BirthTime as orig_BT,
   FROM_TZ(BirthTime, 'UTC') AT TIME ZONE 'Europe/London' AS BT_BST
FROM BirthDatabase;

当您创建 cast(BirthTime as timestamp with time zone) 时,Oracle 会使用您的 SESSIONTIMEZONE 进行转换 - 而不是数据库系统时区。

当你事先执行alter session set time_zone = 'UTC'; 时,cast(BirthTime as timestamp with time zone) at time zone 'Europe/London' 会给出正确的结果。

【讨论】:

  • 这确实是我正在寻找的解决方案。谢谢@Wernfried
【解决方案2】:

我的服务器位于AMERICA/CHICAGO 时区。在下面的示例中,假设给出了时间戳ts(没有时区)。我选择的第二个值是相同的时间戳,附有我的服务器的时区。

如果您想将其视为代表不同的时区,而不是服务器的时区,您可以使用 FROM_TZ() 函数来实现 - 请参阅我选择的第三个表达式。然后您可以使用AT TIME ZONE 修饰符(第四个值)从NEW YORK 时区转换为LONDON 时区。我将值显示在另一个下方以适合窗口(当然在屏幕上它们将在一个很长的行中)。此外,时间戳的格式使用我当前的设置 - 它们在您的机器上可能看起来不同,或者使用带有显式格式模型的 to_char()

select ts, 
       cast(ts as timestamp with time zone)                         as ts_at_server_tz,
       from_tz(ts, 'America/New_York')                              as ts_at_NY_tz,
       from_tz(ts, 'America/New_York') at time zone 'Europe/London' as ts_at_LN_tz
from   ( 
         select to_timestamp('2000-01-15 14:30:00', 'yyyy-mm-dd hh24:mi:ss') as ts 
         from   dual 
       )
;


TS                              
------------------------------- 
15-JAN-00 02.30.00.000000000 PM

TS_AT_SERVER_TZ                        
----------------------------------------------- 
15-JAN-00 02.30.00.000000000 PM AMERICA/CHICAGO 

TS_AT_NY_TZ                            
-------------------------------------- ---------
15-JAN-00 02.30.00.000000000 PM AMERICA/NEW_YORK


TS_AT_LN_TZ
---------------------------------------------
15-JAN-00 07.30.00.000000000 PM EUROPE/LONDON

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    • 2018-12-11
    • 2011-09-29
    • 2016-04-21
    • 2014-05-22
    相关资源
    最近更新 更多