Oracle时间戳到BST时间的转换



我知道关于时间转换有很多不同的教程,但这篇教程让我非常困惑。我的任务是从Oracle数据库读取UTC日期,并将其转换为BST时间(以更易于阅读的格式)。

事实:

数据库中的字段为DATE类型
  • 当我执行SELECT查询时,它返回2011-07-12 15:26:07结果
  • 我在波兰,所以7月份这里的时区是UTC+2
  • 发生了什么:

    在Java方面,我使用到DB的"经典"JDBC连接。

    当我执行Timestamp timestampDate = resultSet.getTimestamp(COLUMN_NAME)时,我得到了结果。。。但是

    System.out.println(timestampDate)打印到控制台2011-07-12 15:26:07.0(这与我在DB工具中看到的类似。

    System.out.println(timestampDate.getTime());打印到控制台1310477167000(这很奇怪,因为根据我在网上找到的ms to date转换器,它基本上是2011-07-12 13:26:07.0(2小时前,这可能与当天的波兰时区有关)

    当我根据这个代码执行转换时:

    ukDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); ukDateFormatter.setTimeZone(TimeZone.getTimeZone("BST")); return ukDateFormatter.format(timestampDate.getTime());

    我得了2011-07-12 19:26:07,我真的无法解释。

    我也在尝试这个

    GregorianCalendar calendar = new GregorianCalendar(); calendar.setTime(timestampDate); calendar.setTimeZone(TimeZone.getTimeZone("BST")); return ukDateFormatter.format(calendar.getTime());

    结果相同。

    问题

    如何正确地从Oracle数据库中读取"时区不可知"格式的DATE并将其转换为BST?

    以下是在数据库端执行此操作的方法:

    with dates as (select to_date('01/07/2016 10:39:29', 'dd/mm/yyyy hh24:mi:ss') dt from dual union all
    select to_date('01/02/2016 09:18:41', 'dd/mm/yyyy hh24:mi:ss') dt from dual)
    select dt,
    cast(dt AS TIMESTAMP) utc_dt_ts,
    from_tz(cast(dt AS TIMESTAMP), 'UTC') AT time zone 'Europe/London' dt_as_ts_bst,
    cast(from_tz(cast(dt AS TIMESTAMP), 'UTC') AT time zone 'Europe/London' AS DATE) dt_at_bst
    from   dates;
    DT                  UTC_DT_TS                                         DT_AS_TS_BST                                      DT_AT_BST
    ------------------- ------------------------------------------------- ------------------------------------------------- -------------------
    01/07/2016 10:39:29 01-JUL-16 10.39.29.000000                         01-JUL-16 11.39.29.000000 EUROPE/LONDON           01/07/2016 11:39:29
    01/02/2016 09:18:41 01-FEB-16 09.18.41.000000                         01-FEB-16 09.18.41.000000 EUROPE/LONDON           01/02/2016 09:18:41
    

    第四列(dt_at_bst)显示了如何将日期转换为英国夏令时的另一个日期。它首先将日期强制转换为时间戳,然后告诉Oracle将其视为UTC的时间戳,并输出"欧洲/伦敦"地区的时间戳。这样指定区域(而不是通过特定的+01:00时区)意味着生成的时间戳将具有夏令时意识。不建议将该地区指定为三个字母的快捷方式,因为这可能代表多个地区——例如,BST可能是英国夏令时或白令标准时间;两件事都很不一样!

    我假设BST指的是British Summer Time,所以我将要移动到的时间戳的区域指定为Europe/London。如果您需要不同的时区,则需要根据需要对此进行调整。

    我在样本数据中包含了一个冬天和一个夏天的日期,以向您展示将其输入BST的效果——夏天的时间预计会改变,而冬天的时间则不会。

    实际上,它不是关于Oracle的,而是关于Java的。首先:当你使用

    System.out.println(timestampDate)
    

    在输出中,您可以看到已调整到计算机时区的时间。

    当您使用Date(即

    Calendar.getTime()Timestamp.getTime())

    要玩的代码:

    SimpleDateFormat dtFmt = new SimpleDateFormat("HH:mm:ss");
    NumberFormat nFmt = NumberFormat.getIntegerInstance();
    nFmt.setMinimumIntegerDigits(2);
    long currentTimeMs = System.currentTimeMillis();
    GregorianCalendar utcCalendar = new GregorianCalendar(
    TimeZone.getTimeZone("UTC"));
    GregorianCalendar bstCalendar = new GregorianCalendar(
    TimeZone.getTimeZone("Europe/London"));
    GregorianCalendar localCalendar = new GregorianCalendar();
    utcCalendar.setTimeInMillis(currentTimeMs);
    bstCalendar.setTimeInMillis(currentTimeMs);
    localCalendar.setTimeInMillis(currentTimeMs);
    System.out.println("---- milliseconds ----");
    System.out.println("Current ms       : " + currentTimeMs);
    System.out.println("Local Calendar ms: " + localCalendar.getTimeInMillis());
    System.out.println("UTC Calendar   ms: " + utcCalendar.getTimeInMillis());
    System.out.println("BST Calendar   ms: " + bstCalendar.getTimeInMillis());
    System.out.println("---- SimpleFormat Time ----");
    System.out.println("Current Time: "
    + dtFmt.format(new Date(currentTimeMs)));
    System.out.println("Local Time: " + dtFmt.format(localCalendar.getTime()));
    System.out.println("UTC Time  : " + dtFmt.format(utcCalendar.getTime()));
    System.out.println("BST Time  : " + dtFmt.format(bstCalendar.getTime()));
    System.out.println("---- Calendar Zone Time ----");
    System.out.println("Local Zone Time: "
    + nFmt.format(localCalendar.get(Calendar.HOUR_OF_DAY)) + ":"
    + nFmt.format(localCalendar.get(Calendar.MINUTE)) + ":"
    + nFmt.format(localCalendar.get(Calendar.SECOND)));
    System.out.println("UTC Zone Time  : "
    + nFmt.format(utcCalendar.get(Calendar.HOUR_OF_DAY)) + ":"
    + nFmt.format(utcCalendar.get(Calendar.MINUTE)) + ":"
    + nFmt.format(utcCalendar.get(Calendar.SECOND)));
    System.out.println("BST Zone Time  : "
    + nFmt.format(bstCalendar.get(Calendar.HOUR_OF_DAY)) + ":"
    + nFmt.format(bstCalendar.get(Calendar.MINUTE)) + ":"
    + nFmt.format(bstCalendar.get(Calendar.SECOND)));
    }
    

    正如您将看到的,每个日历根据其时区返回时间字段(HOUR_OF_DAY, MINUTE, SECOND),而不是您从Calendar.getTime()打印或格式化的内容

    我做了什么,它似乎对我有用:

    ukDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); ukDateFormatter.setTimeZone(TimeZone.getTimeZone("Europe/London"));

    和执行:

    Timestamp timestampDate = rs.getTimestamp(...); DateTime dateTime = new DateTime(timestampDate).withZoneRetainFields(DateTimeZone.UTC); System.out.println(ukDateFormatter.format(dateTime.getMillis()));

    打印:

    来自输入2011-07-12 15:26:072011-07-12 16:26:07

    为什么会发生在这里

    这里的问题是,rs.getTimestamp(...)隐式地从数据库"原样"返回日期(因为DATE列类型不保留时区),但添加了一些关于本地时区的信息——这是我不想要的。

    最简单的解决方案是使用joda并创建新对象,保留值,但将时区更改为UTC。从这一点来看,用SimpleDateFormat进行转换是非常简单的。

    相关内容

    • 没有找到相关文章

    最新更新