Как я могу преобразовать строку UTC в LocalDateTime с настройкой DST? - PullRequest
2 голосов
/ 25 октября 2019

Я получаю строку метки времени UTC от внешнего API, и мне нужно сохранить ее как LocalDateTime. Другими словами, если временная метка находится в пределах периода, когда летнее время активно, ее следует настроить на летнее время (обычно на час).

Я анализирую входящую строку в 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
  }

Как мне это сделать? Я не хочу прибегать к чему-то уродливому, например, повторному анализу Instant.toString().

Ответы [ 2 ]

5 голосов
/ 25 октября 2019

Проблема в том, как вы конвертируете входные данные в ZonedDateTime значения

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

Здесь вы говорите: "возьмите версию OffsetDateTime с локальной датой и временем и притворитесь, что это былона самом деле локальное значение в данном часовом поясе ". Таким образом, вы получите «10:00 по местному времени в часовом поясе», а не «10:00 UTC, преобразованные в местный часовой пояс».

Вы писали, что «На данный момент время летнего времени корректно настроено» -но это не так. Вы начали со значения «2019-09-11T10: 00: 00.000 + 0000», но при печати Instant печатается «2019-09-11T09: 00: 00Z». 10:00 UTC и 9:00 UTC - это не одно и то же мгновение.

Вместо этого вы должны преобразовать OffsetDateTime в Instant - это то, что вы действительно проанализировали - и затем положить , что в соответствующем часовом поясе:

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

Или используйте OffsetDateTime.atZoneSameInstant

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

Обратите внимание, что тогда нет смысла переходить от этого назад кмгновенно, чтобы получить LocalDateTime - просто используйте 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();

Если вам не нужен 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();
3 голосов
/ 25 октября 2019

Когда вы делаете odtSummer.toLocalDateTime(), вы отбрасываете тот факт, что введенная дата / время является UTC, поэтому вы сразу же потеряли информацию.

Вместо этого конвертируйте OffsetDateTime вZonedDateTime нужный часовой пояс, позвонив по номеру atZoneSameInstant(zoneId).

Затем получите LocalDateTime, позвонив по номеру toLocalDateTime().

К вашему сведению: Используйте ZoneId.systemDefault(), чтобы получить часовой пояс по умолчанию, а не TimeZone.getDefault().toZoneId().

final DateTimeFormatter OFFSET_FORMAT = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSXX");
final ZoneId zoneId = ZoneId.of("America/New_York"); // or ZoneId.systemDefault()

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); // 2019-09-11T10:00Z
OffsetDateTime odtWinter = OffsetDateTime.parse(winterTime, OFFSET_FORMAT); // 2019-12-11T10:00Z

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

System.out.println("zdtSummer = " + zdtSummer);  // 2019-09-11T06:00-04:00[America/New_York]
System.out.println("zdtWinter = " + zdtWinter);  // 2019-12-11T05:00-05:00[America/New_York]

LocalDateTime ldtSummer = zdtSummer.toLocalDateTime();
LocalDateTime ldtWinter = zdtWinter.toLocalDateTime();

System.out.println("ldtSummer = " + ldtSummer);  // 2019-09-11T06:00
System.out.println("ldtWinter = " + ldtWinter);  // 2019-12-11T05:00
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...