I'm trying to understand how to save a file's modification date I received as a java.nio.file.attribute.FileTime
into a column in PostgreSQL which is a timestamp
.
Reading the PostgreSQL page relating to Java 8 dates & times:https://jdbc.postgresql.org/documentation/head/8-date-time.html
I can just pinpoint this: Note that ZonedDateTime, Instant and OffsetTime / TIME [ WITHOUT TIMEZONE ] are not supported.
In the matching table (see link above) PostgreSQL advices TIMESTAMP [ WITHOUT TIMEZONE ] LocalDateTime
but some people discourage you to use this Java class, eg http://blog.schauderhaft.de/2018/03/14/dont-use-localdatetime/
And by the way, FileTime
can only be converted into either a long
(millisecongs) or an Instant
(see Java doc).
So what am I supposed to do? I'm lost.
You asked:
how to save a file's modification date I received as a
java.nio.file.attribute.FileTime
into a column in PostgreSQL
Use the types TIMESTAMP WITH TIME ZONE
and OffsetDateTime
in JDBC 4.2 or later to store the moment represented by a FileTime
object.
myPreparedStatement
.setObject(
… , // Specify number of placeholder `?` in your SQL code.
myFileTime // Your `java.nio.file.attribute.FileTime` object.
.toInstant() // Convert to a `java.time.Instant` object.
.atOffset( // JDBC 4.2 oddly does not require support for `Instant`, so simply convert to an `OffsetDateTime` object. Same moment, both in UTC, so no value added except to match JDBC spec.
ZoneOffset.UTC // We must specify *some* offset, so we might as well use an offset of zero hours-minutes-seconds. The constant `ZoneOffset.UTC` represents just that offset.
) // Returns an `OffsetDateTime` object we can use with JDBC 4.2 compliant drivers.
);
I can just pinpoint this: Note that ZonedDateTime, Instant and OffsetTime / TIME [ WITHOUT TIMEZONE ] are not supported.
That text is not a Postgres issue, it is a JDBC 4.2 issue. Let me explain.
The three classes Instant
, OffsetDateTime
, and ZonedDateTime
all represent a moment, a specific point on the timeline. Representing a moment requires the context of an offset-from-UTC (a number of hours-minutes-seconds from UTC) or a time zone (a history of past, present, and future changes to the offset used by the people of a particular region).
Instant
is always in UTC (an offset of zero hours-minutes-seconds). OffsetDateTime
is a date, a time-of-day, and an offset. ZonedDateTime
is a date, a time-of-day, and a time zone. Logically, all three of these map to a TIMESTAMP WITH TIME ZONE
column in Postgres. Postgres takes any time zone or offset info to adjust into UTC, stores the UTC value, and then discards any provided zone/offset.
So you would think the JDBC spec would require support for all three. But inexplicably, the JDBC team chose to require support only for OffsetDateTime
. That was an unfortunate decision as the other two types are more commonly used. At any rate, you can easily convert. Look to the to…
, from…
, at…
, and with…
methods.
Instant instant = Instant.now() ;
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ; // Effectively the same thing as an `Instant`, a moment as seen in UTC.
myPreparedStatement.setObject( … , odt ) ;
A particular JDBC driver may support Instant
and/or ZonedDateTime
as well as OffsetDateTime
. But use OffsetDateTime
alone if your intention is to write portable code to be used with various drivers.
java.nio.file.attribute.FileTime
You said:
'm trying to understand how to save a file's modification date I received as a java.nio.file.attribute.FileTime into a column in PostgreSQL which is a timestamp.
Check the type of your database column.
TIMESTAMP WITH TIME ZONE
.TIMESTAMP WITHOUT TIME ZONE
cannot represent a moment, so it cannot store the value of your FileTime
object.As you mentioned, the java.nio.file.attribute.FileTime
class added a toInstant
method in Java 8 and later. Just what we need to store this moment in the database via JDBC 4.2 or later.
Same kind of code as seen above:
Instant instant = myFileTime.toInstant() ;
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ; // Effectively the same thing as an `Instant`, a moment as seen in UTC.
myPreparedStatement.setObject( … , odt ) ;
Or shorter, but not necessarily better:
myPreparedStatement.setObject( … , myFileTime.toInstant().atOffset( ZoneOffset.UTC ) ) ;
Retrieval.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
If you need an Instant
, convert.
Instant instant = odt.toInstant() ;
You said:
In the matching table (see link above) PostgreSQL advices TIMESTAMP [ WITHOUT TIMEZONE ] LocalDateTime
Those two types, TIMESTAMP WITHOUT TIMEZONE
and LocalDateTime
purposely lack any concept of time zone or offset-from-UTC. As such they cannot represent a moment. So they cannot store your FileTime
value as value is a specific point on the timeline.
If you want to see a moment through the wall-clock time of a particular time zone, apply a ZoneId
to get a ZonedDateTime
object. Same moment, same point on the timeline, different wall-clock time & date.
Instant instant = myFileTime.toInstant() ;
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Generate text to represent that value. We can ask java.time to automatically localize such text.
Locale locale = Locale.CANADA_FRENCH ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( locale ) ;
String output = zdt.format( f ) ;
See this code run live at IdeOne.com . Note how the date and hour differ yet represent the same moment.
instant.toString(): 2020-12-18T00:37:55.704644Z
output: jeudi 17 décembre 2020 à 19 h 37 min 55 s heure normale de l'Est
You said:
So what am I supposed to do? I'm lost.
Date-time handling is surprisingly confusing. Our intuitive understanding and quotidian habits are not helpful and actually are counter-productive as programmers doing this work.
The main concept to get very clear is: A moment versus Not a moment .
Another key concept is that programmers and sys-admins should think of UTC (an offset of zero hours-minutes-seconds) as the one true time. All time zones are but mere variations. While on the job, forget about your local parochial time; keep a clock on your desk set to UTC. Converting back and forth between parochial time and UTC will drive a person batty.
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
.
To learn more, see the Oracle Tutorial . And search Stack Overflow for many examples and explanations. Specification is JSR 310 .
The Joda-Time project, now in maintenance mode , advises migration to the java.time classes.
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. Hibernate 5 & JPA 2.2 support java.time .
Where to obtain the java.time classes?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.