Подтвердить что ты не робот

Каков наиболее рекомендуемый способ хранения времени в PostgreSQL с помощью Java?

Я храню две даты в базе данных PostgreSQL. Во-первых, это данные посещения веб-страницы, а вторая дата - дата последней модификации веб-страницы (это становится длинной).

У меня есть некоторые сомнения, что является лучшей стратегией для хранения этих значений.

Мне нужен только день/месяц/год и час: секунды, и это будет только для статистических предложений.

Итак, некоторые сомнения:

  • лучше хранить дольше и конвертировать при восстановлении информации или хранить в формате данных выше?
  • лучше всего установить дату посещения программного обеспечения или вставить в базу данных?
  • в Java, как лучшие классы обрабатывают даты?
4b9b3361

Ответ 1

Любая стратегия хранения данных о времени и дате в PostgreSQL должна, IMO, опираться на эти два момента:

  • Ваше решение никогда не должно зависеть от настроек часового пояса сервера или клиента.
  • В настоящее время PostgreSQL (как и большинство баз данных) не имеет типа данных для хранения полной даты и времени с часовым поясом. Итак, вам нужно выбрать тип данных Instant или LocalDateTime.

Мой рецепт следует.


Если вы хотите записать физический момент, когда произошло определенное событие (истинная "timestamp", обычно какое-то событие создания/изменения/удаления), тогда используйте:

(Не позволяйте специфическим типам данных PostgreSQL WITH TIMEZONE/WITHOUT TIMEZONE сбить вас с толку: ни один из них на самом деле не хранит часовой пояс)

Немного шаблонного кода: ниже предполагается, что ps является PreparedStatement, rs a ResultSet и tzUTC является статическим Calendar объектом, соответствующим часовому поясу UTC.

public static final Calendar tzUTC = Calendar.getInstance(TimeZone.getTimeZone("UTC"));  

Запишите Instant в базу данных TIMESTAMPTZ:

Instant instant = ...;
Timestamp ts = instant != null ? new Timestamp(instant.toEpochMilli()) : null;
ps.setTimestamp(col, ts, tzUTC);   // column is TIMESTAMPTZ!

Считать Instant из базы данных TIMESTAMPTZ:

Timestamp ts = rs.getTimestamp(col,tzUTC); // column is TIMESTAMPTZ
Instant inst = ts !=null ? Instant.ofEpochMilli(ts.getTime()) : null;

Это безопасно работает, если ваш тип PG TIMESTAMPTZ (в этом случае calendarUTC не действует в этом коде; но всегда рекомендуется не зависеть от часовых поясов по умолчанию). "Безопасно" означает, что результат не будет зависеть от часового пояса сервера или базы данных или информации о часовых поясах: операция полностью обратима, и что бы ни случилось с настройками часовых поясов, вы всегда получите тот же самый "момент времени" "Вы изначально были на стороне Java.


Если вместо временной метки (мгновенного на физической временной шкале) вы имеете дело с "гражданским" локальным датой-временем (то есть набором полей {year-month-day hour:min:sec(:msecs)}), вы будете использовать :

Считайте LocalDateTime из базы данных TIMESTAMP:

Timestamp ts = rs.getTimestamp(col, tzUTC); //
LocalDateTime localDt = null;
if( ts != null ) 
    localDt =  LocalDateTime.ofInstant(Instant.ofEpochMilli(ts.getTime()), ZoneOffset.UTC);

Запишите LocalDateTime в базу данных TIMESTAMP:

  Timestamp ts = null;
  if( localDt != null)    
      ts = new Timestamp(localDt.toInstant(ZoneOffset.UTC).toEpochMilli()), tzUTC);
  ps.setTimestamp(colNum,ts, tzUTC); 

Опять же, эта стратегия безопасна, и вы можете спокойно спать: если вы сохранили 2011-10-30 23:59:30, вы получите эти точные поля (час = 23, минута = 59... и т.д.) всегда, независимо от того, что - даже если завтра меняется часовой пояс вашего сервера (или клиента) Postgresql, или JVM, или часовой пояс вашей ОС, или если ваша страна изменяет свои правила перехода на летнее время и т.д.


Добавлено: Если вы хотите (это кажется естественным требованием) сохранить полную спецификацию даты и времени (a ZonedDatetime: метка времени вместе с часовым поясом, который неявно также включает в себя полную гражданскую информацию о дате и времени - плюс часовой пояс))... тогда у меня для вас плохие новости: у PostgreSQL для этого нет типа данных (насколько мне известно, ни других баз данных). Вы должны разработать свое собственное хранилище, возможно, в паре полей: это могут быть два вышеуказанных типа (сильно избыточные, но эффективные для извлечения и вычисления) или один из них плюс смещение времени (вы теряете информацию о часовом поясе, некоторые вычисления становятся трудно, а некоторые невозможно), или один из них плюс часовой пояс (в виде строки; некоторые вычисления могут быть чрезвычайно дорогостоящими).

Ответ 2

java.time

Это не очень, но это то, что сработало для меня с ZonedDateTime с использованием нового java.time в Java 8 и более поздних версиях (Tutorial):

ZonedDateTime receivedTimestamp = some_ts_value;
Timestamp ts = new Timestamp(receivedTimestamp.toInstant().toEpochMilli());
ps.setTimestamp(
   1, 
   ts, 
   Calendar.getInstance(TimeZone.getTimeZone(receivedTimestamp.getZone()))
); 

Ответ 3

Используйте java.util.Date в своем приложении Java и timestamp with time zone в своей базе данных.