1

Could somebody please help with getting UTC-converted Java timestamp of current local time? The main goal is to get current date and time, convert into UTC Timestamp and then store in Ignite cache as a Timestamp yyyy-MM-dd hh:mm:ss[.nnnnnnnnn].

My attempt was Timestamp.from(Instant.now()). However, it still considers my local timezone +03:00. I am getting '2020-02-20 10:57:56' as a result instead of desirable '2020-02-20 07:57:56'.

How can I get UTC-converted Timestamp?

Mark Rotteveel
  • 82,132
  • 136
  • 114
  • 158
kempel
  • 47
  • 4
  • 2
    https://stackoverflow.com/questions/43259722/java-date-and-timestamp-from-instance-of-zoneddatetime-utc Have a look at this link for information regarding UTC timestamps. – b3p0 Feb 20 '20 at 08:13
  • 1
    I recommend you don’t use `Timestamp`. That class is poorly designed and long outdated. Instead your modern database driver or JPA implementation should be able to accept an `OffsetDateTime` in UTC or if needs be, a `LocalDateTime`, both from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Feb 22 '20 at 11:29

3 Answers3

3

You can do it like this :

LocalDateTime localDateTime = Instant.now().atOffset(ZoneOffset.UTC).toLocalDateTime();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
    System.out.println(localDateTime.format(formatter));
Vipin Sharma
  • 107
  • 1
  • 8
  • 1
    HH (24h) i.o. hh (12h) but fine. Almost a toString (where a T instead of a space). – Joop Eggen Feb 20 '20 at 08:43
  • completely agree, just formatted it to the format as expected in the question. – Vipin Sharma Feb 20 '20 at 09:00
  • 1
    Thanks a lot for suggestions! However it returns a String typed value whilst Timestamp is required for storing it as a date&time in Apache Ignite cache. – kempel Feb 20 '20 at 09:07
  • Timestamp.valueOf(localDateTime.format(formatter)) will work in your case. – Vipin Sharma Feb 20 '20 at 09:17
  • Thanks. The above one worked even without ```.format(formatter)```. Just ```Timestamp.valueOf(Instant.now().atOffset(ZoneOffset.UTC).toLocalDateTime())``` returns ```'2020-02-20 10:24:28'``` in UTC. Thanks again! – kempel Feb 20 '20 at 10:26
1

Don’t use Timestamp

You most probably don’t need a Timestamp. Which is good because the Timestamp class is poorly designed, indeed a true hack on top of the already poorly designed Date class. Both classes are also long outdated. Instead nearly 6 years ago we got java.time, the modern Java date and time API. Since JDBC 4.2 this works with your JDBC driver too, and also with your modern JPA implementation.

Use OffsetDateTime

For a timestamp the recommended datatype in your database is timestamp with time zone. In this case in Java use an OffsetDateTime with an offset of zero (that is, UTC). For example:

    OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
    System.out.println(now);

    PreparedStatement statement = yourDatabaseConnection
            .prepareStatement("insert into your_table (tswtz) values (?);");
    statement.setObject(1, now);
    int rowsInserted = statement.executeUpdate();

Example output from the System.out.println() just now:

2020-02-22T13:04:06.320Z

Or use LocalDateTime if your database timestamp is without time zone

From your question I get the impression that the datatype in your database is timestamp without time zone. It’s only the second best option, but you can pass a LocalDateTime to it.

    LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);

The rest is the same as before. Example output:

2020-02-22T13:05:08.776

If you do need an old-fashioned java.sql.Timestamp

You asked for a Timestamp in UTC. A Timestamp is always in UTC. More precisely, it’s a point in time independent of time zone, so converting it into a different time zone does not make sense. Internally it’s implemented as a count of milliseconds and nanoseconds since the epoch. The epoch is defined as the first moment of 1970 in UTC.

The Timestamp class is a confusing class though. One thing that might have confused you is when you print it, thereby implicitly calling its toString method. The toString method uses the default time zone of the JVM for rendering the string, so prints the time in your local time zone. Confusing. If your datatype in SQL is timestamp without time zone, your JDBC driver most probably interprets the Timestamp in your time zone for the conversion into an SQL timestamp. Which in your case is incorrect since your database uses UTC (a recommended practice). I can think of three possible solutions:

  1. Some database engines allow you to set a time zone on the session. I haven’t got any experience with it myself, it’s something I have read; but it may force the correct conversion from your Java Timestamp to your SQL timestamp in UTC to be performed.
  2. You may make an incorrect conversion in Java to compensate for the opposite incorrect conversion being performed between Java and SQL. It’s a hack, not something that I would want to have in my code. I present it as a last resort.

        LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
        Timestamp ts = Timestamp.valueOf(now);
        System.out.println(ts);
    

    2020-02-22 13:05:08.776

    You notice that it only appears to agree with the UTC time above. It‘s the same result you get from the answer by Vipin Sharma except (1) my code is simpler and (2) you’re getting a higher precision, fraction of second is included.

  3. Have you database generate the current timestamp in UTC instead of generating it in Java.

Links

Ole V.V.
  • 65,573
  • 11
  • 96
  • 117
  • Many thanks for greatly detailed answer! This definitely makes sense to me. However it contradicts a bit to my needs. Actually I am working with Apache Ignite data persistence mechanism (which is not SQL itself). Plus the logic of the system is that setter of the 'database-worker' class gets `LocalDateTime.now()` (various timezones users are expected) value and it needs to be stored as a Timestamp in Ignite's cache. Will think of your suggestions and try to integrate it in my work. Thanks! – kempel Feb 24 '20 at 13:42
  • Thanks for reporting back. Sorry I don’t know Apache Ignite. Sounds like you’ve got yourself an interesting challenge. – Ole V.V. Feb 24 '20 at 14:01
0

Despite what the Ignite docs say you can pass in a 24hr time. The docs says yyyy-MM-dd hh:mm:ss[.nnnnnnnnn] so you may be tempted in your code to use this to format your dates but this will lead to times after midday being wrong. Instead, format your dates with yyyy-MM-dd HH:mm:ss[.nnnnnnnnn].

Notice the upper case HH. If you're using ZonedDateTime or Joda's DateTime when you call now with UTC now(UTC) and then toString("yyyy-MM-dd HH:mm:ss") will store the correct time in UTC.

zcourts
  • 4,213
  • 4
  • 43
  • 68