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