Incompatible types
I have a field in postgres …
timestamp without time zone …
…and…
I have a unix timestamp stored in long in Java
long createdAtTime = data.getcreatedAtTime();
This is a contradiction.
The SQL-standard type TIMESTAMP WITHOUT TIME ZONE
purposely lacks any indicator of time zone or offset-from-UTC. As such, this type does not represent a moment, is not a point on the timeline. This type represents potential moments along a range of about 26-27 hours, the range of time zones around the globe.
If you are trying to track specific moments, use the other SQL-standard type, TIMESTAMP WITH TIME ZONE
. In Postgres, all values of this type are stored in UTC (an offset of zero). If you pass a value indicating some other offset or time zone, Postgres adjusts the value to UTC before storing.
When retrieving a value from a column of type TIMESTAMP WITH TIME ZONE
in Postgres, you are always getting a value in UTC. Unfortunately, some well-intentioned tools or drivers sitting between you and the database may decide the dynamically apply a time zone to the value. While well-intentioned, I consider this quite the anti-feature. This behavior creates the illusion of a time zone stored in the database while in fact Postgres only stores UTC in this type.
Date convertedTime = new Date(createdAtTime*1000L);
The java.util.Date
class is terrible, poorly designed and flawed. Never use this class nor its siblings, Calendar
, SimpleDateFormat
, and such. These are all legacy now, supplanted years ago by the modern java.time classes defined in JSR 310.
Instant
The Instant
class takes over for java.util.Date
. Both classes represent a moment in UTC, though Instant
has a finer resolution of nanoseconds versus milliseconds.
unix timestamp stored in long in Java
If you have a count of whole seconds from the epoch reference of the first moment of 1970 in UTC, 1970-01-01T00:00:00Z, convert to an Instant
.
Instant instant = Instant.ofEpochSecond( 1_539_555_140L ) ;
Tip: Do not make a habit of tracking time as a count-from-epoch. This is ambiguous (different systems use different resolutions and different epoch references), error-prone, and makes debugging/logging treacherous. Use java.time objects and standard ISO 8601 strings for representing date-time values.
Your JDBC driver may be able to accept an Instant
.
myPreparedStatement.setObject( … , instant ) ;
Retrieval:
Instant instant = myResultSet.getObject( … , Instant.class ) ;
OffsetDateTime
If not supporting Instant
, use convert to OffsetDateTime
. Any JDBC 4.2 or later driver is required to support OffsetDateTime
.
OffsetDateTime
represents a date and time-of-day with an offset-from-UTC. In contrast, Instant
is fixed at UTC, serving as a basic building-block class in java.time framework. Also, OffsetDateTime
is more flexible with abilities such as generating strings in various formats versus Instant
using only standard ISO 8601 format.
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;
Retrieval:
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
…or…
Instant instant = myResultSet.getObject( … , OffsetDateTime.class ).toInstant() ;
LocalDateTime
If you are not trying to represent moments, such as database type TIMESTAMP WITHOUT TIME ZONE
, use the LocalDateTime
class.
But if you are thinking use of these types is somehow avoiding the work of using time zones in tracking moments, you are sorely mistaken. This is a “pay now or pay later” situation: Either learn basic date-time concepts and handling practice now, or desperately wrestle with a horrible mess of failed data later.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.*
classes.
Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.