The first mappings to be specified in JDBC used the JDBC-specific classes
java.sql.Date
, java.sql.Time
, and java.sql.Timestamp
, all of which
are based on java.util.Date
(but only as an implementation detail; they
should always be treated as their own types and not as instances of
java.util.Date
).
PL/Java function parameters and returns can be declared in Java to have those
types, objects of those types can be passed to PreparedStatement.setObject
,
ResultSet.updateObject
, and SQLOutput.writeObject
methods, as well as to
the methods that are specific to those types. The JDBC getObject
and
readObject
methods that do not take a Class<?>
parameter will return
objects of those types when retrieving PostgreSQL date or time values.
Those classes have never been a good representation for PostgreSQL date/time
values, because they are based on java.util.Date
, which implies knowledge of
a time zone, even when they are used to represent PostgreSQL values with no time
zone at all. For all of these conversions but one, PL/Java must do time zone
computations, with the one exception being, unintuitively, timestamp with time zone
. The conversions of non-zoned values involve a hidden dependency on the
PostgreSQL session's current setting of TimeZone
, which can vary from session
to session at the connecting client's preference.
There are known issues of long standing in PL/Java's conversions to and from these types, detailed in issue #200. While these particular issues are expected to be fixed in a future PL/Java release, the Java 8 / JDBC 4.2 mappings described next are the strongly-recommended alternative to the legacy mappings, avoiding these issues entirely.
Java 8 introduced the much improved set of date/time classes in the java.time
package specified by JSR 310. JDBC 4.2 (the version in Java 8)
allows those as alternate Java class mappings of the SQL types date
,
time
(with and without timezone), and timestamp
(with/without timezone).
These new types are a much better fit to the corresponding PostgreSQL types than
the original JDBC java.sql
Date
/Time
/Timestamp
classes.
To avoid a breaking change, JDBC 4.2 does not modify what any of the
pre-existing JDBC API does by default. The getDate
, getTime
, and
getTimestamp
methods on a ResultSet
still return the same java.sql
types,
and so does getObject
in the form that does not specify a class. Instead, the
update takes advantage of the general purpose ResultSet.getObject
methods that
take a Class<?>
parameter (added in JDBC 4.1), and likewise the
SQLInput.readObject
method with a Class<?>
parameter (overlooked in 4.1 but
added in 4.2), so a caller can request a java.time
class by passing the right
Class
:
PostgreSQL type | Pass to getObject /readObject |
---|---|
date |
java.time.LocalDate.class |
time without time zone |
java.time.LocalTime.class |
time with time zone |
java.time.OffsetTime.class |
timestamp without time zone |
java.time.LocalDateTime.class |
timestamp with time zone |
java.time.OffsetDateTime.class |
The java.time
types can also be used as parameter and return types of PL/Java
functions without special effort (the generated function declarations will make
the right conversions happen), and passed to the setter methods of prepared
statements, writable result sets (for triggers or composite-returning
functions), and SQLOutput
for UDTs.
Conversions to and from these types never involve the PostgreSQL session time zone, which can vary from session to session. Any code developed for PL/Java and Java 8 or newer is strongly encouraged to use these types for date/time manipulations, for their much better fit to the PostgreSQL types.
PostgreSQL accepts 24:00:00.000000 as a valid time, while a day for
LocalTime
or OffsetTime
maxes out at the preceding nanosecond. That is
still a distinguishable value (as the PostgreSQL resolution is only to
microseconds), so the PostgreSQL 24 value is bidirectionally mapped to that.
When a time with time zone
is mapped to a java.time.OffsetTime
, the Java
value will have a zone offset equal to the one assigned to the value in
PostgreSQL, and so in the reverse direction.
When a timestamp with time zone
is mapped to a java.time.OffsetDateTime
,
the Java value will always have a zone offset of zero (UTC). When an
OffsetDateTime
created in Java is mapped to a PostgreSQL
timestamp with time zone
, if its offset is not zero, the value adjusted to UTC
is used.
These different behaviors accurately reflect how PostgreSQL treats the two types differently.
PostgreSQL allows date
and timestamp
(with or without time zone) values of
infinity
and -infinity
.
There is no such notion in the corresponding Java classes (the original JDBC
ones or the JDBC 4.2 / JSR 310 ones), but PL/Java will map those PostgreSQL
values repeatably to certain values of the Java classes, and will map Java
objects with those exact values back to PostgreSQL infinity
or -infinity
on the return trip. Java code that needs to recognize those values could do
an initial query returning infinity
and -infinity
and save the resulting
Java values to compare others against. It must compare with equals()
; it
cannot assume that the mapping will produce the very same Java objects
repeatedly, but only objects with equal values.
When dates and timestamps are mapped to the java.time
classes,
the mapping will have
the useful property that -infinity
really is earlier than other
PostgreSQL-representable values, and infinity
really is later. That does not
hold under the old java.sql.Timestamp
mapping, where both values will be
distant from the present but not further specified.
integer_datetimes
In PostgreSQL builds with integer_datetimes
as off
(a configuration that is
non-default since PostgreSQL 8.4, and impossible since PG 10), an error results
if a timestamp being converted to Java has either infinite value. As uses of
infinite timestamps are probably rare and the configuration is long out of use,
there is no plan to lift this limitation unless an issue is opened to address a
practical need.