1

In Java 8 I need a way to get the local datetime (GMT+1) from a GMT datetime in ISO 8601 format.

A simple example: Client sends me (Server) this datetime "2020-01-11T23:00:00.000Z" Client sends me this when the user choose the 12 Jan 2020 from the datepicker. Is the 12 Jan for GMT+1 but the day before for GMT.

For the reason above then I know that for me this datetime is not the 11 Jan 2020 but 12 Jan 2020 in GMT+1.

So I need this value "2020-01-12T00:00:00.000"

To be precise I don't need to print this with simpleDateFormat but just covert "2020-01-11T23:00:00.000Z" to "2020-01-12T00:00:00.000" in a java.util.Date class field

Thanks.

HK15
  • 737
  • 1
  • 14
  • 32
  • I recommend you don’t use `SimpleDateFormat` and `java.util.Date`. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. If you are only interested in the date (not the time of day), then use `LocalDate` from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Jan 28 '21 at 16:46
  • As you say yourself 2020-01-11T23:00:00.000Z and 2020-01-12T00:00:00.000+01:00 are the same point in time. A `Date` is just a point in time, so two `Date` objects representing those two times will be identical. There is no conversion to do between them. – Ole V.V. Jan 28 '21 at 16:48
  • Do you know the client time zone or UTC offset? Otherwise you will be at a loss, at least for some input values. – Ole V.V. Jan 28 '21 at 16:49

1 Answers1

3

The problem is that the source system took the pure date value, but added time at midnight, then converted that to UTC, but you want the pure date value in a java.util.Date, which by default prints in your local time zone, i.e. the JVM's default time zone.

So, you have to parse the string, revert the value back to the time zone of the source system, the treat that local time as a time in your own JVM's default time zone.

You can do that like this, showing all the intermediate types:

String sourceStr = "2020-01-11T23:00:00.000Z";
ZoneId sourceTimeZone = ZoneOffset.ofHours(1); // Use real zone of source, e.g. ZoneId.of("Europe/Paris");

// Parse Zulu date string as zoned date/time in source time zone
Instant sourceInstant = Instant.parse(sourceStr);
ZonedDateTime sourceZoned = sourceInstant.atZone(sourceTimeZone);

// Convert to util.Date in local time zone
ZonedDateTime localZoned = sourceZoned.withZoneSameLocal(ZoneId.systemDefault());
Instant localInstant = localZoned.toInstant();
Date localDate = Date.from(localInstant); // <== This is your desired result

// Print value in ISO 8601 format
String localStr = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").format(localDate);
System.out.println(localStr);

Output

2020-01-12T00:00:00.000

The code can of course be merged together:

String input = "2020-01-11T23:00:00.000Z";

Date date = Date.from(Instant.parse(input).atZone(ZoneOffset.ofHours(1))
        .withZoneSameLocal(ZoneId.systemDefault()).toInstant());

System.out.println(date);

Output

Sun Jan 12 00:00:00 EST 2020

As you can see, the date value is correct, even though I'm in the US Eastern time zone.

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • I'm not sure I understand what `withZoneSameLocal` is for. I think `Instant.parse("2020-01-11T23:00:00.000Z").atZone(ZoneId.of("GMT+1")).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)` could already do the trick. Why do we need two `ZonedDateTime`s; is it just for the OP for easier understanding? – akuzminykh Jan 28 '21 at 10:36
  • 1
    @akuzminykh `java.util.Date` stores values in UTC, but parses and prints values in the default time zone of the JVM, aka the "local" time zone, which is the original of the probem in the first place. On the source server (`GMT+1`), the "local" date `2020-01-12` aka `2020-01-12T00:00+01:00`, is stored in a `Date` as `2020-01-11T23:00Z`. On my machine (`US Eastern`), the "local" date `2020-01-12` aka `2020-01-12T00:00-05:00` is stored as `2020-01-12T05:00Z`. We use `withZoneSameLocal` to convert from `2020-01-12T00:00+01:00` to `2020-01-12T00:00-05:00`. – Andreas Jan 28 '21 at 11:01
  • @Andreas thanks for your answer. How the DST is handled in this kind of situation. Is it an issue to deal with or not? – HK15 Jan 28 '21 at 13:26
  • 1
    @Yuri It is, which is why you should use the real time zone of the source system (e.g. `Europe/Paris`), instead of a hard-coded `GMT+1`, as commented in the code. – Andreas Jan 28 '21 at 15:27