【问题标题】:Invalid ID for ZoneOffsetZoneOffset 的 ID 无效
【发布时间】:2016-08-25 03:48:07
【问题描述】:

我正在尝试从java.sql.timestamp 转换为OffsetDateTime,以便我可以在我的rest api 中返回ISO8601 标准字符串。我正在使用此代码将 timestamp 转换为 OffsetDateTime

public static OffsetDateTime sqlTimetampeToOffsetDateTime(Timestamp ts, String timeZone)
{
    if (ts == null)
    {
        return null;
    }

    Calendar cal = Calendar.getInstance();
    cal.setTime(ts);
    ZoneOffset offset = ZoneOffset.of(timeZone);
    return OffsetDateTime.of(
            cal.get(Calendar.YEAR),
            cal.get(Calendar.MONTH)+1,
            cal.get(Calendar.DAY_OF_MONTH),
            cal.get(Calendar.HOUR_OF_DAY),
            cal.get(Calendar.MINUTE),
            cal.get(Calendar.SECOND),
            cal.get(Calendar.MILLISECOND)*1000000,
            offset);
}

但是,代码在 ZoneOffset offset = ZoneOffset.of(timezone) 处失败,值 Europe/Copenhagen

我使用以下代码打印所有时区的列表,我确实在该列表中看到了Europe/Copenhagen

    Set<String> allZones = ZoneId.getAvailableZoneIds();
    LocalDateTime dt = LocalDateTime.now();

    List<String> zoneList = new ArrayList<String>(allZones);
    Collections.sort(zoneList);

    for (String s : zoneList) {
        ZoneId zone = ZoneId.of(s);
        ZonedDateTime zdt = dt.atZone(zone);
        ZoneOffset offset = zdt.getOffset();
        int secondsOfHour = offset.getTotalSeconds() % (60 * 60);
        String out = String.format("%35s %10s%n", zone, offset);
        System.out.printf(out);
    }

现在我不明白发生了什么。我如何将java.sql.timestamp 转换为ISO8601 字符串(我不在乎是否必须使用OffsetDateTime。我不想使用任何第三方库

http://pastebin.com/eHJKWpAv

【问题讨论】:

  • 您的数据库中的数据类型是什么?带时区的时间戳?还有什么?
  • 在数据库中我有时间戳,例如2016-05-23 15:00:00.0,我还有另一列有时区,例如Europe/Copenhagen。这就是为什么我将 timezone 作为单独的字符串传递。
  • 表达式 ZoneOffset.of("Europe/Copenhagen") 必须失败,因为该区域 id 不仅仅是一个偏移量(包含诸如夏令时规则之类的附加信息)。在这种情况下,您必须首先构造一个ZonedDateTime,然后从中派生一个OffsetDateTimeInstant

标签: mysql date java-8 timestamp datetimeoffset


【解决方案1】:

ZoneOffset 仅在处理特定时间点时才有意义。在欧洲/伦敦,我们目前根据一年中的时间使用 BST 或 GMT。然而,100 年前(给予或接受),欧洲/伦敦没有 BST。 ZoneOffset.of() 仅从内部缓存中检索区域偏移量,该缓存仅在调用 ZoneOffset.ofTotalSeconds() 时填充。这没有很好的记录。但是,存在一个简单的解决方案:

ZoneId.of("Europe/London").getRules().getOffset(Instant.now());

返回当前欧洲/伦敦的正确 ZoneOffset(例如今天)

【讨论】:

    【解决方案2】:

    如果您有 ZoneId,那么使用 Instant 类来执行此操作非常简单:

    Timestamp t = new Timestamp(System.currentTimeMillis());
    
    ZoneId zone = ZoneId.of("Europe/Copenhagen");
    
    OffsetDateTime offsetDateTime = ZonedDateTime
        .ofInstant(Instant.ofEpochMilli(t.getTime()), zone)
        .toOffsetDateTime();
    

    【讨论】:

      【解决方案3】:

      我确实设法转换了它,但我不确定这是否是正确的方法。这是我给正在寻找答案的其他人的代码

      public static OffsetDateTime sqlTimetampeToOffsetDateTime(Timestamp ts, String timeZone)
      {
          if (ts == null)
          {
              return null;
          }
      
          ZoneId zoneId = ZoneId.of(timeZone);
      
          Calendar cal = Calendar.getInstance();
          cal.setTime(ts);
          ZonedDateTime zdt = ZonedDateTime.of(
                  cal.get(Calendar.YEAR),
                  cal.get(Calendar.MONTH) + 1,
                  cal.get(Calendar.DAY_OF_MONTH),
                  cal.get(Calendar.HOUR_OF_DAY),
                  cal.get(Calendar.MINUTE),
                  cal.get(Calendar.SECOND),
                  cal.get(Calendar.MILLISECOND) * 1000000,
                  zoneId);
          return zdt.toOffsetDateTime();
      }
      

      timeZone 的形式为 Europe/Copenhagen ...您可以在问题中发布的我的 pastebin url 中看到 Java8 支持的完整列表

      【讨论】:

      • 我认为return ts.toInstant().atZone(ZoneId.of(timeZone)).toOffsetDateTime(); 就足够了。
      • 您的代码正在为结果时间添加偏移量。只是想把它写在这里,这样如果其他人在阅读,他就会知道其中的区别。
      • 好的,那就是ts.toLocalDateTime().atZone(ZoneId.of(timeZone)).toOffsetDateTime();
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多