Каков наиболее рекомендуемый способ хранения времени в PostgreSQL с использованием Java? - PullRequest
46 голосов
/ 08 июля 2011

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

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

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

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

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

Ответы [ 3 ]

74 голосов
/ 08 июля 2011

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

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

Мой рецепт для каждого случая:


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

(Не позволяйте 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 * 1111)*: временная метка вместе с часовым поясом, которая неявно также включает в себя полную гражданскую информацию о дате и времени (плюс часовой пояс), тогда вам не повезло: PostgreSQL не имеет тип данных для этого (ни другие базы данных, насколько мне известно)).Вы должны разработать свое собственное хранилище, возможно, в паре полей: это могут быть два вышеуказанных типа (сильно избыточные, но эффективные для извлечения и вычисления) или один из них плюс смещение времени (вы теряете информацию о часовом поясе, некоторые вычисления становятсятрудно, а некоторые невозможно), или один из них плюс часовой пояс (как строка; некоторые вычисления могут быть очень дорогими). ​​

3 голосов
/ 26 сентября 2015

java.time

Это не красиво, но это то, что мне помогло с экземпляром ZonedDateTime, использующим новый фреймворк java.time в Java 8и позже ( Учебник ):

ZonedDateTime receivedTimestamp = some_ts_value;
Timestamp ts = new Timestamp(receivedTimestamp.toInstant().toEpochMilli());
ps.setTimestamp(
   1, 
   ts, 
   Calendar.getInstance(TimeZone.getTimeZone(receivedTimestamp.getZone()))
); 
2 голосов
/ 08 июля 2011

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

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