UTC
Думайте, работайте и храните данные в UTC , а не в любом часовом поясе. Думайте о UTC как о одном истинном времени , а все остальные часовые пояса - просто вариации. Поэтому при кодировании забудьте все о своем часовом поясе. Делайте свою бизнес-логику, ведение журнала, хранение данных и обмен данными в UTC. Я предлагаю каждому программисту держать на столе вторые часы, настроенные на UTC.
java.time
Современный способ - это java.time классы.
Упомянутый проект Joda-Time послужил источником вдохновения для классов java.time, и в настоящее время проект находится в режиме обслуживания, и команда рекомендует перейти на классы java.time.
Инфраструктура java.time встроена в Java 8 и более поздние версии. Эти классы заменяют проблемные старые устаревшие классы даты и времени, такие как java.util.Date
, .Calendar
, & java.text.SimpleDateFormat
.
Чтобы узнать больше, см. Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .
Где получить классы java.time?
- Java SE 8 и SE 9 и более поздние
- Встроенный.
- Часть стандартного Java API со встроенной реализацией.
- Java 9 добавляет некоторые незначительные функции и исправления.
- Java SE 6 и SE 7
- Большая часть функциональности java.time перенесена на Java 6 и 7 в ThreeTen-Backport .
- Android
Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Здесь вы можете найти некоторые полезные классы, такие как Interval
, YearWeek
, YearQuarter
и more .
ISO 8601
При сериализации значения даты и времени в тексте используйте стандарт ISO 8601 .
Например, дата-время в UTC равна 2016-10-17T01:24:35Z
, где Z
- это сокращение от Zulu
и означает UTC. Для других смещение от UTC в конце появляется смещение часов и минут, например 2016-01-23T12:34:56+05:30
. Классы java.time расширяют этот стандартный формат, добавляя имя часового пояса (если известно) в квадратных скобках, например 2016-01-23T12:34:56+05:30[Asia/Kolkata]
.
Стандарт также имеет много других удобных форматов, включая длительности , интервалы , порядковые номера и год-неделя .
База данных
Для хранения базы данных используйте типы даты-времени для значений даты-времени, такие как стандартные типы данных SQL , которые в основном DATE
, TIME
и TIMESTAMP WITH TIME ZONE
.
Пусть ваш JDBC-драйвер сделает тяжелую работу. Драйвер обрабатывает мельчайшие подробности о посредничестве и адаптации между внутренними компонентами того, как Java обрабатывает данные, и как ваша база данных обрабатывает данные на своей стороне. Но обязательно попрактикуйтесь с примерами данных, чтобы узнать поведение вашего драйвера и вашей базы данных. Стандарт SQL очень мало определяет обработку времени и даты, поэтому поведение, как ни удивительно, сильно различается.
Если вы используете драйвер JDBC, совместимый с JDBC 4.2 и более поздними версиями, вы можете извлекать и сохранять типы java.time напрямую с помощью методов ResultSet::getObject
и PreparedStatement::setObject
.
Instant instant = myResultSet.getObject( … );
myPreparedStatement.setObject( … , instant );
Для более старых драйверов вам нужно будет вернуться к конвертации через типы java.sql. Ищите новые методы преобразования, добавленные к старым классам. Например, java.sql.Timestamp.toInstant()
.
Instant instant = myResultSet.getTimestamp( … ).toInstant();
myPreparedStatement.setObject( … , java.sql.Timestamp.from( instant ) );
Используйте типы java.sql как можно более кратко. Это плохо спроектированный хак, такой как java.sql.Date
, маскирующийся под значение только для даты, но на самом деле как подкласс java.util.Date
для него действительно установлено время дня в 00:00:00
в UTC. И, о, вы должны игнорировать тот факт, что наследство говорит класс док. Гадкий беспорядок.
Пример кода
Получить текущий момент в UTC.
Instant instant = Instant.now();
Хранение и извлечение этого Instant
объекта в / из базы данных показано выше.
Чтобы сгенерировать строку ISO 8601, просто наберите toString
. Все классы java.time по умолчанию используют форматы ISO 8601 для анализа и генерации строк с различными значениями даты и времени.
String output = instant.toString();
Настройте любое смещение от UTC, применив ZoneOffset
, чтобы получить OffsetDateTime
. Вызовите toString
, чтобы сгенерировать строку в формате ISO 8601.
ZoneOffset offset = ZoneOffset.ofHoursMinutes( 5 , 30 );
OffsetDateTime odt = instant.atOffset( offset );
Часовой пояс - это смещение плюс набор правил для обработки аномалий, таких как Летнее время (DST) . Когда вам нужно увидеть тот же момент через объектив собственного региона времени настенного времени , примените часовой пояс (ZoneId
), чтобы получить ZonedDateTime
объект.
Укажите собственное имя часового пояса в формате continent/region
. Никогда не используйте 3-4-буквенное сокращение, такое как EST
или IST
, поскольку они не являются истинными часовыми поясами, не стандартизированы и даже не уникальны (!).
ZoneId z = ZoneId.of( "Asia/Kolkata" );
ZonedDateTime zdt = instant.atZone( z );
Идя в другом направлении, вы можете извлечь Instant
из OffsetDateTime
или ZonedDateTime
, вызвав toInstant
.
Instant instant = zdt.toInstant();
Форматирование
Для представления пользователю в виде строк в форматах, отличных от ISO 8601, поиск переполнения стека для использования класса DateTimeFormatter
.
Хотя вы можете указать собственный формат, обычно лучше всего разрешить java.time автоматически локализоваться. Для локализации укажите:
FormatStyle
для определения длины или сокращения строки.
Locale
для определения (а) человеческого языка для перевода названия дня, названия месяца и т. Д. И (б) культурных норм, решающих вопросы сокращения, капитализации, пунктуации, и такие.
Пример:
Locale l = Locale.CANADA_FRENCH ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( l );
String output = zdt.format( f );
Преобразование
Лучше всего по возможности избегать устаревших типов даты и времени. Но если вы работаете со старым кодом, который еще не обновлен для типов java.time, вы можете конвертировать в / из типов java.time. Подробности см. В вопросе, Преобразовать java.util.Date в тип «java.time»? .
Использовать объекты
Используйте объекты, а не простые кодированные примитивы и простые строки. Например:
- Не используйте 1-7 для обозначения дня недели, используйте перечисление
DayOfWeek
, например DayOfWeek.TUESDAY
.
- Вместо того, чтобы передавать строку как дату, передайте вокруг
LocalDate
объекты.
- Вместо того, чтобы передавать пару целых чисел в течение года и месяца, передайте вокруг
YearMonth
объектов.
- Вместо 1-12 в течение месяца используйте гораздо более читаемое перечисление
Month
, например Month.JANUARY
.
Использование таких объектов делает ваш код более самодокументируемым, обеспечивает допустимые значения и обеспечивает безопасность типов .