Always use UTC as the timezone for postgre timestamp/date columns, which is considered to be the best practice and recommended by Postgre, e.g.
utc_created timestamptz not null
And you java application is normally using LocalDateTime/LocalDate, if your system is not a global operation.
So there needs to be a conversion in data access layer of you java application. According to some documents, Timestamp with Timezone in Postgresql should be mapped to OffsetDateTime in Java, and vice versa:
import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; public class MyTimeZoneHelper { private static final ZoneId MY_TIMEZONE_ID = ZoneId.of("Australia/Melbourne"); public static LocalDateTime toLocalDateTime(OffsetDateTime offsetDateTime) { ZonedDateTime zonedDataTime = offsetDateTime.toZonedDateTime(); return zonedDataTime.withZoneSameInstant(MY_TIMEZONE_ID).toLocalDateTime(); } public static OffsetDateTime toOffsetDateTime(LocalDateTime localDateTime) { ZonedDateTime zonedDateTime = localDateTime.atZone(MY_TIMEZONE_ID); ZonedDateTime utcZonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of("Z")); return utcZonedDateTime.toOffsetDateTime(); } }
And if you are using mybatis, you can have a type handler based on the helper above:
import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.OffsetDateTimeTypeHandler; import org.apache.ibatis.type.TypeHandler; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.time.LocalDateTime; import java.time.OffsetDateTime; /** * Convert the UTC timestamps in the new databases (postgres) to LocalDateTime, and vice versa */ public class MyTimestampWithTimezoneTypeHandler extends BaseTypeHandler<LocalDateTime> implements TypeHandler<LocalDateTime> { private OffsetDateTimeTypeHandler delegate = new OffsetDateTimeTypeHandler(); @Override public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType) throws SQLException { delegate.setNonNullParameter(ps, i, MyTimeZoneHelper.toOffsetDateTime(parameter), jdbcType); } @Override public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException { OffsetDateTime offsetDateTime = delegate.getNullableResult(rs, columnName); return offsetDateTime == null ? null : MyTimeZoneHelper.toLocalDateTime(offsetDateTime); } @Override public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException { OffsetDateTime offsetDateTime = delegate.getNullableResult(rs, columnIndex); return offsetDateTime == null ? null : MyTimeZoneHelper.toLocalDateTime(offsetDateTime); } @Override public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { OffsetDateTime offsetDateTime = delegate.getNullableResult(cs, columnIndex); return offsetDateTime == null ? null : MyTimeZoneHelper.toLocalDateTime(offsetDateTime); } }
<resultMap> ... <result column="utc_created" jdbcType="TIMESTAMP_WITH_TIMEZONE" property="createdWhen" typeHandler="MyTimestampWithTimezoneTypeHandler"/> ... </resultMap>