Java 8 localdatetime против даты - PullRequest
0 голосов
/ 17 мая 2018

У меня есть строка 2017-07-31T01:01:00-07:00, и я пытаюсь проанализировать ее до настоящего времени и в часовом поясе CST.Я получаю разные результаты, когда я анализирую эту строку, используя Date и Java 8 ZonedDateTime.Я не понимаю, почему это происходит и что я делаю неправильно.

    String dateStr = "2017-07-31T01:01:00-07:00";
    LocalDateTime time = null;
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss-hh");

    String[] dateArray = dateStr.split("-");
    String[] timeZones = TimeZone
            .getAvailableIDs(TimeZone.getTimeZone("GMT-" + dateArray[dateArray.length - 1]).getRawOffset());
    format.setTimeZone(TimeZone.getTimeZone(timeZones[0]));
    Date dateObj = null;
    try {
        dateObj = format.parse(dateStr);
    } catch (ParseException e) {
        e.printStackTrace();
    }
    time = dateObj.toInstant().atZone(TimeZone.getTimeZone("CST").toZoneId()).toLocalDateTime();
    ZonedDateTime time2 = ZonedDateTime.parse(dateStr).toInstant().atZone(TimeZone.getTimeZone("CST").toZoneId());
    System.out.println(time);
    System.out.println(time2.toLocalDateTime());

Ответы [ 2 ]

0 голосов
/ 17 мая 2018

Я не уверен в точном поведении TimeZone.getAvailableIDs (это не совсем хорошо задокументировано). Когда я запускаю ваш код на моей Java 9.0.4, первым идентификатором, который он возвращает, является America / Boise. В Бойсе, штат Айдахо, США, используется стандартное зимнее время в зимний период (UTC-07: 00) и летнее время в горах (UTC-06: 00). Вы установили format, чтобы использовать это для анализа. Ваш формат анализирует 01 как часы дня (прописные буквы HH, от 00 до 23) и 07 как часы внутри AM или PM (строчные буквы hh, от 01 до 12). Так что здесь конфликт. Похоже, победит первый, я не знаю почему (не всегда настаиваю на понимании SimpleDateFormat). Поскольку 31 июля в летнее время (DST), вы получаете время 01: 01-06: 00, равное 02:01 Североамериканское центральное летнее время (-05: 00). Что неверно.

ZonedDateTime.parse правильно анализирует вашу строку. TimeZone.getTimeZone("CST").toZoneId() интерпретирует CST как Америку / Чикаго (что, строго говоря, неверно, поскольку Чикаго использует CST только для незначительной части года). Так что в time2 вы получите 2017-07-31T03:01-05:00[America/Chicago], что правильно.

Для того, что я думаю, вы пытались получить, я рекомендую:

    ZonedDateTime dateTime = OffsetDateTime.parse(dateStr)
            .atZoneSameInstant(ZoneId.of("America/Chicago"));
    System.out.println(dateTime.toLocalDateTime());

Выход совпадает с вашим вторым выходом:

2017-07-31T03: 01

Если вы сочтете это более подходящим для вашей ситуации, вместо Америки / Чикаго вы можете рассмотреть, например, Америку / Баия_Бандерас, Америку / Индиану / Нокс, Америку / Индиану / Телл-Сити, Америку / Матаморос, Америку / Меномини или Америку / Виннипег , Не полагайтесь на трех- или четырехбуквенное сокращение часового пояса. CST не является истинным часовым поясом, поскольку он используется только в течение некоторого года, и он неоднозначен, он может относиться к центральному стандартному времени Австралии, центральному стандарту Северной и Центральной Америки, стандартному времени Китая или стандартному времени Кубы. И нет никакой гарантии, какую Java вам даст.

0 голосов
/ 17 мая 2018

Вы не должны анализировать смещение часового пояса самостоятельно. Просто используйте шаблон X:

new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX")

Вы не должны использовать часовой пояс CST, поскольку это неоднозначно (Центральное стандартное время, Стандартное время Китая, Стандартное время Кубы). Используйте America/Chicago (я полагаю, вы это имели в виду).

Итак, чтобы проанализировать строку даты со старым и новым API:

String dateStr = "2017-07-31T01:01:00-07:00";

// Using Date
SimpleDateFormat parseFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
Date date = parseFormat.parse(dateStr);

SimpleDateFormat printFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm");
printFormat.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
System.out.println(printFormat.format(date));

// Using ZonedDateTime
ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateStr);
zonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of("America/Chicago"));
System.out.println(zonedDateTime.toLocalDateTime());

выход

2017-07-31T03:01
2017-07-31T03:01

Если вы хотите увидеть часовой пояс, вы можете сделать это:

String dateStr = "2017-07-31T01:01:00-07:00";

// Using Date
SimpleDateFormat parseFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
Date date = parseFormat.parse(dateStr);

SimpleDateFormat printFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm XXX z");
printFormat.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
System.out.println(printFormat.format(date));

// Using ZonedDateTime
ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateStr);

DateTimeFormatter printFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm XXX z")
                                                    .withZone(ZoneId.of("America/Chicago"));
System.out.println(zonedDateTime.format(printFormatter));

выход

2017-07-31T03:01 -05:00 CDT
2017-07-31T03:01 -05:00 CDT

Обратите внимание, как первый пример изменяет часовой пояс ZonedDateTime, преобразует его в LocalDateTime, затем печатает его без форматера, тогда как во втором примере DateTimeFormatter настроен на форматирование значения в определенном часовом поясе, похоже на то, как SimpleDateFormat делает это. Просто разные способы достижения одного и того же результата.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...