Ответ 1
Типы "Local…" специально не имеют понятия о часовом поясе. Таким образом, они не представляют момент на временной шкале. LocalDateTime
представляет неопределенный диапазон возможных моментов, но не имеет реального значения до назначения смещения или часового пояса. Это означает применение ZoneId
для получения ZonedDateTime
.
Например, чтобы сказать, что Рождество в этом году начинается в первый момент 25 декабря, мы говорим:
LocalDateTime ldt = LocalDateTime.of( 2017 , 12 , 25 , 0 , 0 , 0 , 0 );
Но этот полуночный удар случается раньше на востоке, чем на западе.
Вот почему отдел логистики эльфов наметил маршрут Санты, начинающийся в Кирибати в Тихом океане, самом раннем часовом поясе в мире на 14 часов впереди UTC. После доставки они направляют Санту на запад в такие места, как Новая Зеландия, на полночь позже. Затем в Азию для их полуночи позже. Затем Индия и т.д. Через несколько часов достигают Европы в полночь, а еще несколько часов спустя - на восточном побережье Северной Америки в полночь. Все эти места испытывали один и тот же LocalDateTime
в разные моменты, каждая доставка была представлена отдельным объектом ZonedDateTime
.
Так...
- Если вы хотите записать концепцию Рождества, начинающуюся после полуночи 25-го числа, используйте
LocalDateTime
и запишите в столбец базы данных типTIMESTAMP WITHOUT TIME ZONE
. - Если вы хотите записать точный момент каждой доставки, которую делает Санта, используйте
ZonedDateTime
и запишите в столбец базы данных типTIMESTAMP WITH TIME ZONE
.
Об этом втором пункте помните, что почти каждая система баз данных будет использовать информацию о зоне, чтобы установить дату и время в формате UTC и сохранить это значение UTC. Некоторые также сохраняют информацию о зоне, но некоторые, такие как Postgres, отбрасывают информацию о зоне после ее использования для настройки в UTC. Таким образом, "с часовым поясом" является чем-то неправильным, на самом деле означает "с уважением к часовому поясу". Если вы хотите запомнить эту исходную зону, вам может потребоваться сохранить ее название в отдельном столбце рядом с ней.
Другая причина использовать типы Local…
- для будущих встреч. Политики любят часто менять свой часовой пояс своей юрисдикции. Они любят переходить на летнее время (DST). Люблю менять даты своих визитов DST. Они любят отказаться от принятия DST. Они любят переопределять свои часовые пояса, меняя границы. Они любят пересматривать свое смещение от UTC иногда на 15 минут. И они редко дают предварительное уведомление, делая такие изменения с всего лишь месяц или два предупреждения.
Таким образом, чтобы назначить медицинский осмотр на следующий год или через шесть месяцев, определение часового пояса не может быть предсказано. Поэтому, если вы хотите назначить встречу в 9:00, вам следует использовать LocalTime
или LocalDateTime
, записанные в столбце базы данных типа TIMESTAMP WITHOUT TIME ZONE
. В противном случае назначение в 9 часов утра, если оно зонировано, где перенос DST откладывается, может отображаться как 8 часов утра или 10 часов утра.
При создании прогнозируемого расписания вы можете применить часовой пояс (ZoneId
) к этим "локальным" (незонированным) значениям для создания объектов ZonedDateTime
. Но не полагайтесь на тех, кто слишком далек во времени, когда политики могут разрушить их значение, изменив зону (зоны).
Совет. Эти частые изменения летнего времени и часовых поясов означают, что вы должны регулярно обновлять базу данных tzdata своего часового пояса. Существует tzdata в вашей операционной системе, вашей JVM и, возможно, в вашей системе баз данных, такой как Postgres. Все три должны часто обновляться. Иногда зоны меняются быстрее, чем запланированные циклы обновления этих продуктов, таких как Турция в прошлом году, решившая остаться на летнее время с уведомлением всего за несколько недель. Так что иногда вам может понадобиться обновить эти файлы tzdata вручную. Oracle предоставляет инструмент для обновления tzdata их реализаций Java.
Общая лучшая практика обработки точных моментов - отслеживать их в UTC. Применяйте часовой пояс только там, где это необходимо, например, при представлении пользователю, который ожидает увидеть значения в своем собственном приходском часовом поясе. В java.time класс Instant
представляет момент на временной шкале. В UTC с разрешением наносекунд.
Instant instant = Instant.now() ; // Current moment on the timeline in UTC.
ZonedDateTime zdt = instant.atZone( z ) ; // Assign a time zone to view the same moment through the lens of a particular regions wall-clock time.
Instant instant = zdt.toInstant(); // revert back to UTC, stripping away the time zone. But still the same moment in the timeline.
Кстати, драйверы, соответствующие JDBC 4.2 и более поздним версиям, могут напрямую работать с типами java.time через:
PreparedStatement::setObject
ResultSet::getObject
Как ни странно, спецификация JDBC 4.2 не требует поддержки двух наиболее распространенных типов java.time: Instant
& ZonedDateTime
. Спецификация требует поддержки OffsetDateTime
. Таким образом, вы можете легко конвертировать туда-сюда.
Избегайте старых устаревших типов данных, таких как java.util.Date
и java.sql.Timestamp
, когда это возможно. Они плохо спроектированы, запутаны и несовершенны.
Поймите, что все эти четыре являются моментом на временной шкале в UTC:
- Современные
java.time.Instant
java.time.OffsetDateTime
с назначенным смещениемZoneOffset.UTC
- Наследие
java.util.Date
java.sql.Timestamp
Если вы хотите использовать значение только для даты без времени суток и без часового пояса, используйте java.time.LocalDate
. Этот класс вытесняет java.sql.Date
.
Что касается конкретных баз данных, имейте в виду, что стандарт SQL едва затрагивает тему типов даты и времени и их обработки. Кроме того, различные базы данных сильно различаются, и я действительно имею в виду, что они поддерживают функции даты и времени. У некоторых практически нет поддержки. Некоторые смешивают стандартные типы SQL с проприетарными типами, которые либо предшествуют стандартным типам, либо предназначены как альтернативы стандартным типам. Кроме того, драйверы JDBC отличаются по своему поведению маршалингом значений даты и времени в/из базы данных. Обязательно изучите документацию и практикуйтесь, практикуйтесь, практикуйтесь.