使用正确的DST将UTC java.sql.Time转换为java.time.localtime

时间:2018-10-24 10:07:27

标签: java time localtime

我将将从数据库中获取的java.sql.Time(UTC)转换为java.time.LocalTime(GMT + 1 DST)时遇到问题。它总是缺少夏令时。因此,将03:00的时间仅转换为04:00的LocalTime而不是05:00。

//Saved UTC time in DB: 03:00
LocalTime.ofInstant(Instant.ofEpochMilli(sqlTime.getTime()), ZoneId.of("Europe/Berlin"));
=> 04:00 //expected 05:00

我想问题是java.sql.Time使用默认日期1970-01-01保存时间,并且在1970年德国没有DST。但是当然应该显示今天的时间,而不是1970年的时间。

那么我如何获得本示例的正确时间?

2 个答案:

答案 0 :(得分:3)

假设您至少使用JDBC 4.2,则应该能够从结果集中检索LocalTime

    LocalTime timeInUtc = yourResultSet.getObject(yourTimeColumn, LocalTime.class);

然后,您就不必为过时且设计不良的java.sql.Time类而烦恼。当然,您获得的时间仍将是UTC。转换方法如下:

    LocalTime timeInUtc = LocalTime.of(3, 0);

    ZoneId zone = ZoneId.of("Europe/Berlin");
    LocalTime timeInGermany = OffsetDateTime.now(ZoneOffset.UTC)
            .with(timeInUtc)
            .atZoneSameInstant(zone)
            .toLocalTime();

    System.out.println("Zeit heute in Deutschland: " + timeInGermany);

今天运行代码时,我得到了您期望的输出:

  

德国的Zeit heute:05:00

编辑:如果无法避免,请先获取java.sql.Time,然后将其转换为LocalTime。假设Time使用UTC,并且我们不想依靠脆弱的JVM时区设置进行转换,那么您正确地需要使用getTime方法:

    Time sqlTimeInUtc = // Get from database
    LocalTime timeInUtc 
            = LocalTime.MIDNIGHT.plus(sqlTimeInUtc.getTime(), ChronoUnit.MILLIS);

如果您可以将JVM时区设置也设置为UTC,那么以下内容会更好:

    LocalTime timeInUtc = sqlTimeInUtc.toLocalTime();

在这两种情况下,其余均如上所述。

在所有情况下,当您说“应该显示今天的时间”时,您是否想要“今天是UTC”还是“今天是欧洲/柏林时区”都是一个极端的问题。如果时间在凌晨2点至3点之间,而今天是三月的最后一个星期日,则还有一个极端的情况,在那儿,时钟从2点变为3点,以启动德国的夏令时(DST)。请仔细考虑这些极端情况,然后决定您想要什么。

通过这种方式,您的诊断是完全正确的:Time.getTime返回一天中的时间,即1970年1月1日,因此,当您将其输入Instant时,您将在此日期中转换时间日期,即没有夏季时间。

答案 1 :(得分:1)

据我了解,您的问题是:给定UTC时间,请根据当前时间偏移将其转换为本地时间。此时间偏移取决于DST是否有效。

一种可能的方法是使用TimeZone确定当前偏移量:

    TimeZone tz = TimeZone.getTimeZone("Europe/Berlin");
    int timeZoneOffsetMillis = tz.getOffset(new Date().getTime());

现在timeZoneOffsetMillis包含您必须添加到UTC时间以获取本地时间的毫秒数。

您可以这样获得LocalTime

    LocalTime localTime = LocalTime.ofNanoOfDay((sqlTime.getTime() + timeZoneOffsetMillis) * 1000000L);

如果您的时间仅精确到秒而不是十亿分之一秒,则可能要使用LocalTime.ofSecondOfDay