简体   繁体   中英

How to save a java.nio.file.attribute.FileTime into a PostgreSQL's timestamp column?

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.

tl;dr

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. 
);

Java(传统和现代)和标准 SQL 中的日期时间类型表

A moment

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.

Storing java.nio.file.attribute.FileTime

Column type

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.

  • Your column must be of type TIMESTAMP WITH TIME ZONE .
  • The other type TIMESTAMP WITHOUT TIME ZONE cannot represent a moment, so it cannot store the value of your FileTime object.

Java type

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.

Adjusting into a time zone

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

Date-time handling is tricky

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 .

  • The completion of a sale transaction, a record being created in the database, or a delivery being dropped at your front door — these are all moments.
  • Saying that Christmas this year starts on December 25th, 2020 this year is not a moment, as the beginning of that day varies around the world coming earlier in the east and later in the west. Santa starts deliveries in the Pacific islands, and flies westward as the calendar flips to a new day successively hour-by-hour.
  • Setting a dental appointment in six months that should start at 3 PM regardless of politicians changing that region's offset-from-UTC — that is 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.


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 .

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM