Какова наилучшая практика для манипулирования и хранения дат в Java? - PullRequest
26 голосов
/ 30 января 2010

Какова наилучшая практика для манипулирования и хранения дат, например, использовать GregorianCalendar в корпоративном Java-приложении?

В поисках отзывов, и я объединю любые отличные ответы в передовой опыт, который могут использовать другие. Спасибо!

Ответы [ 5 ]

25 голосов
/ 30 января 2010

Лучшая практика - это, как правило, точно НЕ , чтобы думать в терминах объектов с тяжелыми датами, но сохранять момент времени. Обычно это делается путем сохранения значения, которое не страдает ни от угловых случаев, ни от потенциальных проблем синтаксического анализа. Для этого люди обычно сохраняют количество миллисекунд (или секунд), прошедших с фиксированной точки, которую мы называем эпохой (1970-01-01). Это очень распространено, и любой Java API всегда позволяет вам преобразовывать любую дату в / из времени, выраженного в мс с начала эпохи.

Это для хранения. Вы также можете сохранить, например, предпочтительный часовой пояс пользователя, если есть такая необходимость.

Теперь такая дата в миллисекундах, как:

System.out.println( System.currentTimeMillis() );
1264875453

не очень полезно, когда отображается конечному пользователю, это как должное.

Именно поэтому вы используете, например, пример времени Joda, чтобы преобразовать его в какой-то удобный для пользователя формат перед его отображением конечному пользователю.

Вы просили наилучшую практику, вот мое мнение: хранение объектов «дата» в БД вместо времени в миллисекундах с использованием чисел с плавающей запятой для представления денежных сумм.

Обычно это огромный запах кода.

Так что время Joda в Java - это способ манипулировать датой, да. Но может ли Йода пойти в магазин даты? точно НЕ .

13 голосов
/ 30 января 2010

Йода это путь. Почему?

  1. имеет гораздо более мощный и интуитивно понятный интерфейс, чем стандартный API Date / Time
  2. нет проблем с форматированием даты / времени. java.text.SimpleDateFormat не является потокобезопасным (мало кто знает об этом!)

На каком-то этапе Java Date / Time API будет заменен (JSR-310). Я полагаю, что это будет основано на работе, проделанной теми, кто стоит за Joda, и поэтому вы изучите API, который повлияет на новый стандартный Java API.

11 голосов
/ 30 января 2010

Время Joda (100% совместимость с JDK)

Joda-Time обеспечивает качественную замену для классов даты и времени Java. Конструкция позволяет использовать несколько календарных систем, но при этом предоставляет простой API

.
3 голосов
/ 17 октября 2016

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.

Использование таких объектов делает ваш код более самодокументируемым, обеспечивает допустимые значения и обеспечивает безопасность типов .

0 голосов
/ 30 января 2010

Чтобы начать обсуждение, вот мой опыт:

При создании стандартов для типичного трехуровневого проекта Java Enterprise я обычно рекомендовал бы, чтобы проект использовал GregorianCalendar для манипулирования датами. Причина в том, что GregorianCalendar является стандартом де-факто по сравнению с любым другим экземпляром Календаря, например Юлианский календарь и т. Д. Это признанный календарь в большинстве стран, и он правильно обрабатывает високосные годы и т. Д. Кроме того, я бы рекомендовал приложению сохранять даты в формате UTC, чтобы вы могли легко выполнять вычисления дат, например, находить разницу между даты (например, если бы они хранились в формате EST, вам нужно было бы учитывать время перехода на летнее время). Затем эту дату можно локализовать в любом часовом поясе, который вам нужен, чтобы отображать ее пользователю, например, локализовать ее в EST, если вы являетесь компанией на восточном побережье США и хотите, чтобы ваша информация о времени отображалась в EST.

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