Какова наилучшая практика для управления и хранения дат в Java?

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

Ищем обратную связь, и я объединю любые большие ответы в Лучшую Практику, которую могут использовать другие. Спасибо!

Ответ 1

Joda - это путь. Почему?

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

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

Ответ 2

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

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

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

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

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

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

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

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

Итак, время Джоды в Java - это способ манипулировать датой, да. Но может ли Йода пойти на хранение дат? НЕКОТОК.

Ответ 3

Joda time (100% совместим с JDK)

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

Ответ 4

UTC

Думайте, работайте и храните данные в UTC, а не в любой часовой пояс. Подумайте о UTC как о единственном истинном времени, и все остальные часовые пояса - это просто вариации. Поэтому, кодируя, забудьте все о своем часовом поясе. Сделайте свою бизнес-логику, протоколирование, хранение данных и обмен данными в UTC. Я предлагаю, чтобы каждый программист держал на часах часы, установленные на UTC.

java.time

Современный способ - это java.time классы.

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

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

Чтобы узнать больше, см. Учебник Oracle. И поиск Qaru для многих примеров и объяснений. Спецификация 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 и больше.

ISO 8601

При сериализации значения даты и времени используйте текст стандарт ISO 8601.

Например, дата-время в UTC 2016-10-17T01:24:35Z, где Z меньше для Zulu и означает UTC. Для других offset-from-UTC в конце появляется смещение часов и минут, например 2016-01-23T12:34:56+05:30. Классы java.time расширяют этот стандартный формат, чтобы добавить имя часового пояса (если известно) в квадратных скобках, например 2016-01-23T12:34:56+05:30[Asia/Kolkata].

В стандарте есть много других удобных форматов, в том числе для durations, intervals, ординалы и седьмина.

База данных

Для хранения базы данных используйте типы даты и времени для значений даты, таких как стандартные типы данных 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. И, о, вы должны игнорировать факт того, что наследование говорит класс doc. Уродливый беспорядок.

Пример кода

Получить текущий момент в 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.

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

Ответ 5

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

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