0

In my application running on Java 8, I have a Date object. The timezone for this object depends on the client's location.

At one particular point, I need to convert this Date to GMT so that it could be comparable to a value that I access from the DB.

I tried SimpleDateFormat and ZonedDateTime, but there's a pain-point. These API's provide me GMT time in String values, which is perfectly fine. However, once I parse it and assign it to a Date object, it is back to my local timezone!

For Ex:

public class TimeZoneDemo {
    public static void main(String[] args) throws ParseException {
        Date istDate = Calendar.getInstance().getTime();

        ZonedDateTime gmtTime = istDate.toInstant().atZone(ZoneId.of("GMT"));
        System.out.println(gmtTime);

        Date gmtDate = Date.from(gmtTime.toInstant());
        System.out.println(gmtDate);
    }
}

In the code above, gmtTime displays the right value relevant to the GMT timezone, but gmtDate (the Date object) prints value in the local time zone.

P.S.: My ultimate goal is to have a the GMT value in a java.sql.TimeStamp object.

How could this be achieved?

UPDATE 1: After going through the comments & replies I understand that the Date object just holds a long value containing milliseconds. BUT, my expectation here is that when this line is executed:

Date gmtDate = Date.from(gmtTime.toInstant());

Whatever time the object gmtTime contains, I need to capture just that time in a TimeStamp or Date object. The motive is to then be able to make comparison with a value persisted in the database. I'm passing the date as parameter to my SQL query.

Can someone pls help me understand how can this be achieved?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Curious Coder
  • 646
  • 8
  • 18
  • 3
    "However, once I parse it and assign it to a Date object, it is back to my local timezone!" Not really. Date objects aren't "in" any time zone. They just represent instants of time. See https://codeblog.jonskeet.uk/2017/04/23/all-about-java-util-date/ – Jon Skeet Sep 05 '19 at 15:13
  • 1
    Always, and even more so when using Java 8 (or later), avoid the `Date` and `Timestamp` classes. They are poorly designed and long outdated. Use [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Sep 05 '19 at 17:26
  • *comparison with a value persisted in the database* — as in is equal or is before or after? Would you prefer to compare in SQL or in Java? I’m sure we can help. – Ole V.V. Sep 05 '19 at 17:28
  • 2
    No need to ever use `java.sql.TimeStamp` again. Replaced by [`OffsetDateTime`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/OffsetDateTime.html) in JDBC 4.2 and later. `myPreparedStatement.setObject( … , OffsetDateTime.now( ZoneOffset.UTC ) ) ;` and `OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;`. Likewise, never use `java.util.Date` nor `java.util.Calendar` ever again. Supplanted by *java.time* when JSR 310 was adopted years ago. – Basil Bourque Sep 05 '19 at 19:35
  • @OleV.V.: I'm passing the date as parameter to my SQL query – Curious Coder Sep 06 '19 at 04:52

3 Answers3

2

First of all Date class is part of the old outdated (no pun intended) infrastructure. If it is at all possible get rid of it and just use java.time package. But if you must work with Date then your problem with time zone is not a problem. your line System.out.println(gmtDate); only prints it with your local time zone, since the system assumes that it is the best option. But regardless of that Date holds a particular moment in time in milliseconds since January 1, 1970, 00:00:00 GMT. Class Date has methods compareTo(), after() and before() that allow you to compare 2 Dates. Also Date has method getTime() that returns you the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this date. So you can compare the long values. But again, The best option is to switch to java.time package and classes Instant and ZonedDateTime (and others) have methods compareTo(), isAfter() and isBefore().

Michael Gantman
  • 7,315
  • 2
  • 19
  • 36
2

java.time and JDBC 4.2

The answer is in @BasilBourque’s comment: use OffsetDateTime.

    PreparedStatement yourPreparedStatement = yourDatabaseConnection.prepareStatement(
            "select smth from your_table where your_time_stamp_col < ?;");
    OffsetDateTime gmtTime = OffsetDateTime.now(ZoneOffset.UTC);
    yourPreparedStatement.setObject(1, gmtTime);

This requires a JDBC 4.2 compliant JDBC driver, I think that about all of us are using that by now. Which is good because it allows us to bypass java.sql.Timestamp and the other date-time types in java.sql. They are all poorly designed and long outdated.

As others have said, neither of the outdated classes Date and Timestamp have any time zone or offset from UTC/GMT.

Some related questions

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • 2
    This is likely only to work if a) your driver supports `OffsetDateTime` and b) you use the `TIMESTAMP WITH TIME ZONE` data type – Philippe Marschall Sep 06 '19 at 15:43
  • @PhilippeMarschall Thank you, you are correct. A JDBC 4.2 compliant driver *does* support `OffsetDateTime` by definition. The database datatype ought to be `timestamp with time zone`. If it’s a mere `timestamp`(without time zone), one needs to use `LocalDateTime` on the Java side instead. Everything else is still the same. – Ole V.V. Sep 07 '19 at 07:49
0

You misunderstand the semantics of date time classes. java.util.Date is a specific point in time, an instant, it has no time zone associated with it. However if you have a time zone you can ask the time zone for the time of that java.util.Date.

java.sql.TimeStamp is the equivalent of java.time.LocalDateTime. It is not an instant and has no time zone associated with it.

Philippe Marschall
  • 4,452
  • 1
  • 34
  • 52
  • Thanks for the explanation, could you pls refer to the updated title and 'Update 1' in the question above and help me with some pointers to accomplish it? – Curious Coder Sep 05 '19 at 17:23
  • **Incorrect on `Date`**. The `java.util.Date` class represents a moment in UTC defined as a count of milliseconds since 1970-01-01T00:00Z where the `Z` means UTC (an offset of zero hours-minutes-seconds). Without the context of UTC or some such reference, you cannot represent a moment. – Basil Bourque Sep 05 '19 at 19:30
  • **Incorrect on [`java.sql.Timestamp`](https://docs.oracle.com/en/java/javase/11/docs/api/java.sql/java/sql/Timestamp.html).** That class is actually a subclass of `java.util.Date`, awkwardly adding on a fractional second in nanoseconds while still carrying the milliseconds of `Date`. As `Timestamp` *is* a `Date`, it too represents a moment in UTC. It is most certainly ***not*** equivalent to `LocalDateTime`, which purposely lacks the concepts of time zone or offset-from-UTC. See: [*What's the difference between Instant and LocalDateTime?*](https://stackoverflow.com/q/32437550/642706) – Basil Bourque Sep 05 '19 at 19:33
  • `java.sql.Timestamp` was replaced by [`OffsetDateTime`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/OffsetDateTime.html) in JDBC 4.2 and later (not `LocalDateTime` as stated incorrectly on this Answer). See [my comment](https://stackoverflow.com/questions/57808424/for-a-given-date-object-capture-its-value-relevant-to-gmt-timezone#comment102055294_57808424) on Question for example code. – Basil Bourque Sep 05 '19 at 19:41
  • @BasilBourque not really no, `java.sql.Timestamp` is a subclass but not to a subtype, therefore not a `java.util.Date`. `java.sql.Timestamp`, unlike java.util.Date is in JVM time zone. You can check it out for yourself, the value of Timestamp.valueOf("2019-09-06 17:47:11").getTime() depends on the JVM time zone. Also note the `java.time.LocalDateTime` conversion methods on `java.sql.Timestamp` as they are equivalent. – Philippe Marschall Sep 06 '19 at 15:50
  • That two such knowledgeable people can disagree about what a `java.sql.Timestamp` is at all, should send a clear message: **Stay far away from that poorly designed class.** – Ole V.V. Sep 08 '19 at 06:07
  • @BasilBourque as for java.util.Date and time zone UTC, I defer to John Skeet https://codeblog.jonskeet.uk/2017/04/23/all-about-java-util-date/ – Philippe Marschall Sep 08 '19 at 15:39