Как разобрать строку YYYYMMDD_HHMMSSZ в Java 8 - PullRequest
0 голосов
/ 01 июня 2018

Мне нужно проанализировать строку даты и времени UTC, например, 20180531_132001Z, в объект даты и времени Java 8.Как мне сделать это, используя новые библиотеки даты и времени Java 8?Большинство примеров, которые я вижу, относится к LocalDateTime, например:

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss'Z'");
    LocalDateTime localDateTime = LocalDateTime.parse("20180531_132001Z", formatter);
    System.out.println(localDateTime);
    System.out.println(localDateTime.atOffset(ZoneOffset.UTC));

Код выводит:

2018-05-31T13:20:01
2018-05-31T13:20:01Z
  • Считается ли это местным временем или временем UTC?Строковое значение, которое я анализирую, основано на UTC, поэтому мне интересно, нужно ли мне делать что-то еще, прежде чем сохранять в базе данных.
  • Если первое, как мне преобразовать это в дату и время UTC?

В конечном итоге мне нужно сохранить это в таблице базы данных SQL Server (тип столбца [datetime2](7), используя [Spring] JDBC.

Обновление: На основекомментарии и ответы, я думаю, что мой вопрос не очень хорошо продуман. Другими словами, если я получу входную строку и проанализирую ее без учета какой-либо зоны или смещения, я получу объект LocalDateTime.этот объект и преобразовать инкапсулированное значение в дату и время UTC?

Ответы [ 5 ]

0 голосов
/ 02 июня 2018

tl; dr

myPreparedStatement.setObject(   // Pass java.time objects directly to database, as of JDBC 4.2.
    … ,                          // Indicate which placeholder in your SQL statement text.
    OffsetDateTime.parse(        // Parse input string as a `OffsetDateTime` as it indicates an offset-from-UTC but not a time zone.
        "20180531_132001Z" ,     // Define a formatting pattern to match your particular input.
        DateTimeFormatter.ofPattern( "uuuuMMdd_HHmmssX" )  // TIP: When exchanging date-time values as text, use use standard ISO 8601 formats rather than inventing your own.
    )                            // Returns a `OffsetDateTime` object.
    .toInstant()                 // Returns a `Instant` object, always in UTC by definition.
)

Подробности

В других ответах есть некоторая полезная информация, но все они содержат некоторую дезинформацию, которую я пытался исправить, публикуя комментарии.

Самое главное, ваш код использует неправильный класс Java и неверный тип данных базы данных для данного ввода.

Ниже приведено объяснение вместе с полным примером кода с использованием современного java.time классы с JDBC 4.2 или более поздней версии.

Z = UTC

DateTimeFormatter.ofPattern ("yyyyMMdd_HHmmss'Z '")

Никогда не ставьте одинарные кавычки вокруг важных частей вашего ввода, как вы сделали здесь с Z.Это Z означает UTC и произносится как «зулу».Он говорит нам, что текст даты и времени суток следует интерпретировать как использование часового времени UTC, а не, скажем, America/Montreal или Pacific/Auckland часовых поясов.

не используйте LocalDateTime для таких входов. В этом классе отсутствует понятие часового пояса или смещения от UTC.Таким образом, этот класс не представляет момент и является не точкой на временной шкале.LocalDateTime представляет набор потенциальных моментов в диапазоне около 26-27 часов (во всех часовых поясах).Используйте LocalDateTime, когда вы имеете в виду любые или все часовые пояса, а не одну конкретную зону / смещение.Напротив, Z говорит нам, что этот ввод использует время по часам UTC.

Синтаксический анализ

Определение шаблона форматирования для соответствия всем важным частям вашей входной строки.

String input = "20180531_132001Z" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuuMMdd_HHmmssX" ) ;

Кстати, когда это возможно, используйте стандартные форматы ISO 8601 , а не пользовательский формат, как показано в вашем вопросе.Эти форматы разработаны так, чтобы их было легко анализировать на машине и легко читать людям в разных культурах, устраняя при этом неоднозначность.

Анализировать как OffsetDateTime, потому что ваш ввод показывает смещение от UTC (ноль часов).Смещение от UTC - это просто количество часов и минут, ни больше, ни меньше.

Используйте класс ZonedDateTime только в том случае, если во входной строке указан часовой пояс .Часовой пояс имеет имя Contintent/Region, например Africa/Tunis.Зона представляет историю прошлых, настоящих и будущих изменений в смещении, используемом людьми определенного региона.

OffsetDateTime odt = OffsetDateTime.parse( input , f ) ;

odt.toString (): 2018-05-31T13: 20: 01Z

База данных

Чтобы сообщить об этом моменте базе данных, используя JDBC 4.2 и выше, мы можем напрямую передать java.time object.

myPreparedStatement.setObject( … , odt ) ;

Если ваш драйвер JDBC не принимает OffsetDateTime, извлеките более простой класс Instant.Instant всегда в UTC, по определению.

Instant instant = odt.toInstant() ;
myPreparedStatement.setObject( … , instant ) ;

И поиск.

Instant instant = myResultSet.getObject( … , Instant.class ) ;

Осторожно - неверный тип данных в вашей базе данных

Я не пользователь MS SQL Server, но согласно этодокументация , тип данных столбца DATETIME2 не подходит для вашего ввода.Этот тип данных, кажется, эквивалентен стандартному типу SQL DATETIME WITHOUT TIME ZONE.Такой тип никогда не должен использоваться при записи определенного момента в истории.

В отсутствие какой-либо концепции часового пояса или смещения от UTC этот тип столбца следует использовать только для трех ситуаций:

  • Зона или смещение неизвестно .Это плохо.Это неверные данные.Аналогично наличию цены / стоимости без знания валюты.Вы должны отвергать такие данные, а не хранить их.
  • Намерение «везде» , как в каждом часовом поясе.Например, корпоративная политика, которая гласит: «Все наши фабрики будут работать на обед в 12:30» означает, что фабрика в Дели сломается за несколько часов до фабрики в Дюссельдорфе, которая ломается за несколько часов до фабрики в Детройте.
  • A speНамечается важный момент в будущем, но мы боимся политиков переопределить часовой пояс .Правительства меняют правила своих часовых поясов с удивительной частотой и с удивительно небольшим предупреждением (даже [без предупреждения] [10]).Поэтому, если вы хотите записаться на прием в 3 часа дня на определенную дату, и вы действительно имеете в виду 3 часа дня, независимо от какого-либо сумасшедшего решения, которое правительство может принять за это время, сохраните LocalDateTime.Чтобы распечатать отчет или отобразить календарь, динамически примените часовой пояс (ZoneId), чтобы сгенерировать определенный момент (ZonedDateTime или Instant).Это нужно делать на лету, а не сохранять значение.

Поскольку ваш ввод - это определенный момент, определенная точка на временной шкале, вы должны хранить его в базе данных, используя столбецтип сродни стандартному SQL-типу TIMESTAMP WITH TIME ZONE.


О java.time

Среда java.time встроена в Java 8 и более поздние версии.Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar, & SimpleDateFormat.

Проект Joda-Time , теперь в режиме обслуживания , рекомендует перейти на классы java.time .

Чтобы узнать больше, см. Oracle Tutorial .И поиск переполнения стека для многих примеров и объяснений.Спецификация: JSR 310 .

Вы можете обмениваться java.time объектами напрямую с вашей базой данных.Используйте драйвер JDBC , совместимый с JDBC 4.2 или более поздней версии.Нет необходимости в строках, нет необходимости в java.sql.* классах.

Где получить классы java.time?

  • Java SE 8 , Java SE 9 , Java SE 10, а позже
    • Встроенный.
    • Часть стандартного Java API с встроенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и Java SE 7
    • Большая часть функций java.time перенесена на Java 6 & 7 в ThreeTen-Backport .
  • Android
    • Более поздние версии реализации связки Android классов java.time.
    • Для более ранних версий Android (<26) проект <a href="https://github.com/JakeWharton/ThreeTenABP" rel="nofollow noreferrer"> ThreeTenABP адаптируется ThreeTen-Backport (упомянуто выше).См. Как использовать ThreeTenABP… .

ThreeTen-Extra Проект расширяет java.time дополнительными классами.Этот проект является полигоном для возможных будущих дополнений к java.time.Здесь вы можете найти некоторые полезные классы, такие как Interval, YearWeek, YearQuarter и more .

0 голосов
/ 01 июня 2018

LocalDateTime может вводить в заблуждение.Он не представляет вашу местную дату / время, он представляет a местную дату / время.Он не несет никакой информации о часовом поясе вообще.То есть, он просто говорит, например, «это 13:20».Здесь не сказано , где , это 13:20.Вы должны интерпретировать часть , где .

. Из-за этого LocalDateTime обычно не очень полезен для переноса меток времени, он полезен только в ситуациях, когда часовой пояс зависит от некоторого контекста.. 1

При работе с метками времени лучше использовать ZonedDateTime или OffsetDateTime.Они содержат дату, время и смещение.

Таким образом, localDateTime.atOffset(ZoneOffset.UTC) фактически вернет экземпляр OffsetDateTime, интерпретируя localDateTime как время UTC.

Можно утверждать, что вы можетеизбегайте интерпретации части , анализируя в первую очередь информацию о часовом поясе (даже если она всегда Z):

    String example = "20180531_132001Z";
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmssX");
    OffsetDateTime dateTime = OffsetDateTime.parse(example, formatter);
    System.out.println(dateTime); // look ma, no hardcoded UTC

Выведет:

2018-05-31T13:20:01Z

Дополнительным преимуществом является то, что ваш код автоматически поддерживает часовые пояса (например, "20180531_132001+05").

Драйвер, совместимый с JDBC 4.2, может иметь возможность напрямую обращаться к типам java.time, вызывая setObject.

Для более старых драйверов JDBC вы можете преобразовать dateTime в java.sql.Timestamp или java.util.Date:

java.sql.Timestamp.from(dateTime.toInstant());
java.util.Date.from(dateTime.toInstant());

1 Почти всегда есть некоторые context , в котором работает LocalDateTime.Например, « Рейс KL1302 прибывает в аэропорт Х завтра в 13: 20 ».Здесь контекст « завтра в 13: 20 » - это местное время в аэропорту X;это можно определить, посмотрев на часовой пояс X.

0 голосов
/ 01 июня 2018

Может быть, это может помочь вам.

public static void main(String... strings) {
   OffsetDateTime utc = OffsetDateTime.now(ZoneOffset.UTC);
   System.out.println(utc.toString());
   DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy mm dd  hh:mm a");
   System.out.println(utc.format(format));
}
0 голосов
/ 01 июня 2018

Хотя вы, безусловно, можете использовать LocalDateTime и отформатировать его, чтобы он выглядел как зонированное время даты с использованием смещения, было бы проще использовать объект, предназначенный для хранения часового пояса.

ZonedDateTime zonedDateTime = ZonedDateTime.parse("20180531_132001Z", DateTimeFormatter.ofPattern("yyyMMdd_HHmmssX"));

Это дает вам возможностьиспользовать Instant для преобразования в метку времени SQL или любой другой формат без необходимости жесткого кодирования часового пояса, особенно если часовой пояс добавляется в будущем или изменяется.

java.sql.Timestamp timestamp = new java.sql.Timestamp(zonedDateTime.toInstant().toEpochMilli());

Вы можете просмотреть момент времени и меткусравните его с toString, которая должна быть привязана к вашему часовому поясу, и instant.toString, которая привязана к UTC.

System.out.print(timestamp + " " + timestamp.toInstant().toString());
0 голосов
/ 01 июня 2018

это должно сделать трюк для разбора строки в LocalDateTime:

    String example = "20180531_132001Z";
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmssX");
    ZonedDateTime dateTime = ZonedDateTime.parse(example, formatter);

Обратите внимание, что код запускается в режиме реального времени в IdeOne.com.

dateTime.toString (): 2018-05-31T13: 20: 01Z

    Timestamp timestamp = Timestamp.from(dateTime.toInstant());

Отметка времени затем сохраняется в дБ

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