2

I've been scratching my head trying to understand why the FastDateFormat parser is returning a very incorrect time. The string timestamp I'm trying to convert is in GMT/UTC, and I'm trying to insert it into a Timestamp column in DB2.

Here's the code:

String gmtTimestamp = "2017-03-12 02:38:30.417000000";
FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSSSSSSSS", TimeZone.getTimeZone("GMT"));
java.util.Date d = fdf.parse(gmtTimestamp);
Timestamp ts1 = new Timestamp(d.getTime());
System.out.println(ts1);

The time that's printed is: "2017-03-16 17:28:30.0", 4 days and nearly 15 hours off. What's happening here?

David F
  • 31
  • 4

1 Answers1

1

TL;DR

    String gmtTimestamp = "2017-03-12 02:38:30.417000000";
    DateTimeFormatter dtf 
            = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSSSSSSSS");
    Instant i1 = LocalDateTime.parse(gmtTimestamp, dtf)
            .atOffset(ZoneOffset.UTC)
            .toInstant();
    System.out.println(i1);

This prints

2017-03-12T02:38:30.417Z

The implicit call to Instant.toString() produces date and time in UTC (for our purpose the same as GMT), so you recognize the date and time from your GMT string.

java.time

I recommend you drop the java.sql.Timestamp class. It is long outdated, and today we have so much better in java.time, the modern Java date and time API. The original purpose of Timestamp was to store and retrieve date-time values to and from SQL databases. With a sufficiently new JDBC driver (JDBC 4.2), you can and will want to take advantage of two classes from java.time for that purpose: Instant for a point on the timeline and LocalDateTime for date and time without time zone.

In case you do need a Timestamp (for instance for a legacy API or an older JDBC driver you don’t want to upgrade just now), convert the Instant from above to Timestamp just before handing it to the legacy API or the database:

    Timestamp ts1 = Timestamp.from(i1);
    System.out.println(ts1);

Running in America/Chicago time zone this prints:

2017-03-11 20:38:30.417

Timestamp.toString() grabs the JVM’s time zone setting and outputs the date and time in this time zone (which may be confusing).

What was happening in your code snippet?

FastDateFormat uses SimpleDateFormat format patterns. In SimpleDateFormat and FastDateFormat capital S means milliseconds. so 417000000 was taken as milliseconds (where you intended 417 milliseconds), it is rpughly the same as 4 days 20 hours, which were added to the date-time value up to the seconds. I reproduced your result using a SimpleDateFormat and setting my JVM to America/Chicago time zone. Other time zones that are -5 hours from UTC in March will produce the same result. Since the Timestamp was printed at offset -5:00, the apparent difference in the output was a bit less than the real difference of 4 days 20 hours, “only” 4 days 15 hours.

By contrast, though the modern DateTimeFormatter mostly uses the same format pattern letters, to it capital S means fraction of second, which is why we get 30.417 seconds as expected and desired.

Quotes

All patterns are compatible with SimpleDateFormat (except time zones and some year patterns - see below).

(FastDateFormat documentation)

S Millisecond Number 978

(SimpleDateFormat documentation)

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161