如何通过DST调整将UTC字符串转换为LocalDateTime?

Chr*_*ris 2 java datetime

我从外部API收到UTC时间戳字符串,我需要将其存储为LocalDateTime。换句话说,如果时间戳在启用夏令时的时段内,则应将其调整为DST(通常是一个小时)。

我将传入的字符串解析为OffsetDateTime,然后将其转换为ZonedDateTime,然后转换为Instant。此时,DST时间已正确调整。但是,当我LocalDateTime从创建时Instant,它会丢失调整。

  @Test
  public void testDates() {
    final DateTimeFormatter OFFSET_FORMAT = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSXX");
    final ZoneId zoneId = TimeZone.getDefault().toZoneId();

    final String summerTime = "2019-09-11T10:00:00.000+0000";
    final String winterTime = "2019-12-11T10:00:00.000+0000";

    OffsetDateTime odtSummer = OffsetDateTime.parse(summerTime, OFFSET_FORMAT);
    OffsetDateTime odtWinter = OffsetDateTime.parse(winterTime, OFFSET_FORMAT);

    ZonedDateTime zdtSummer = odtSummer.toLocalDateTime().atZone(zoneId);
    ZonedDateTime zdtWinter = odtWinter.toLocalDateTime().atZone(zoneId);

    Instant instSummer = zdtSummer.toInstant();
    Instant instWinter = zdtWinter.toInstant();

    System.out.println("instSummer = " + instSummer);  // instSummer = 2019-09-11T09:00:00Z
    System.out.println("instWinter = " + instWinter);  // instWinter = 2019-12-11T10:00:00Z

    LocalDateTime ldtSummer = LocalDateTime.ofInstant(instSummer, zoneId);
    LocalDateTime ldtWinter = LocalDateTime.ofInstant(instWinter, zoneId);

    System.out.println("ldtSummer = " + ldtSummer);  // ldtSummer = 2019-09-11T10:00
    System.out.println("ldtWinter = " + ldtWinter);  // ldtWinter = 2019-12-11T10:00
  }
Run Code Online (Sandbox Code Playgroud)

我应该怎么做?我不想诉诸诸如重新解析之类的丑陋的东西Instant.toString()

Jon*_*eet 5

问题是您将输入转换为ZonedDateTime值的方式

ZonedDateTime zdtSummer = odtSummer.toLocalDateTime().atZone(zoneId);
ZonedDateTime zdtWinter = odtWinter.toLocalDateTime().atZone(zoneId);
Run Code Online (Sandbox Code Playgroud)

在这里,您说的是“获取的本地日期时间版本OffsetDateTime,并假装实际上是给定时区中的本地值”。因此,您最终得到的是“时区当地时间上午10点”,而不是“转换为本地时区的上午10点UTC”。

您写道:“此时,DST时间已正确调整”,但事实并非如此。您开始的值是“ 2019-09-11T10:00:00.000 + 0000”,但是当您打印时,Instant它的打印内容是“ 2019-09-11T09:00:00Z”。UTC上午10点和UTC上午9点不是同一时刻。

相反,您应该将转换OffsetDateTimeInstant-,因为这就是您真正解析的内容-,然后将放在相关的时区中:

 ZonedDateTime zdtSummer = odtSummer.toInstant().atZone(zoneId);
 ZonedDateTime zdtWinter = odtWinter.toInstant().atZone(zoneId);
Run Code Online (Sandbox Code Playgroud)

或使用 OffsetDateTime.atZoneSameInstant

 ZonedDateTime zdtSummer = odtSummer.atZoneSameInstant(zoneId);
 ZonedDateTime zdtWinter = odtSummer.atZoneSameInstant(zoneId);
Run Code Online (Sandbox Code Playgroud)

请注意,从那回溯到瞬间以获得LocalDateTime-just use是没有意义的toLocalDateTime。如果需要所有相关类型,请使用以下适当的代码:

OffsetDateTime odtSummer = OffsetDateTime.parse(summerTime, OFFSET_FORMAT);
OffsetDateTime odtWinter = OffsetDateTime.parse(winterTime, OFFSET_FORMAT);

Instant instSummer = odtSummer.toInstant();
Instant instWinter = odtWinter.toInstant();

ZonedDateTime zdtSummer = instSummer.atZone(zoneId);
ZonedDateTime zdtWinter = instWinter.atZone(zoneId);

LocalDateTime ldtSummer = zdtSummer.toLocalDateTime();
LocalDateTime ldtWinter = zdtWinter.toLocalDateTime();
Run Code Online (Sandbox Code Playgroud)

如果您不需要Instant,只需:

OffsetDateTime odtSummer = OffsetDateTime.parse(summerTime, OFFSET_FORMAT);
OffsetDateTime odtWinter = OffsetDateTime.parse(winterTime, OFFSET_FORMAT);

ZonedDateTime zdtSummer = odtSummer.atZoneSameInstant(zoneId);
ZonedDateTime zdtWinter = odtWinter.atZoneSameInstant(zoneId);

LocalDateTime ldtSummer = zdtSummer.toLocalDateTime();
LocalDateTime ldtWinter = zdtWinter.toLocalDateTime();
Run Code Online (Sandbox Code Playgroud)