54

I have an object of ZonedDateTime that is constructed like this

ZonedDateTime z = ZonedDateTime.of(LocalDate.now().atTime(11, 30), ZoneOffset.UTC);

How can I convert it to LocalDateTime at time zone of Switzerland? Expected result should be 16 april 2018 13:30.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Vitalii
  • 10,091
  • 18
  • 83
  • 151
  • 1
    As an aside, pass a `ZoneId` to `LocalDate.now()` too to obtain predictable results. It is never the same date everywhere on the globe. – Ole V.V. Apr 16 '18 at 11:43

5 Answers5

87

How can I convert it to LocalDateTime at time zone of Switzerland?

You can convert the UTC ZonedDateTime into a ZonedDateTime with the time zone of Switzerland, but maintaining the same instant in time, and then get the LocalDateTime out of that, if you need to. I'd be tempted to keep it as a ZonedDateTime unless you need it as a LocalDateTime for some other reason though.

ZonedDateTime utcZoned = ZonedDateTime.of(LocalDate.now().atTime(11, 30), ZoneOffset.UTC);
ZoneId swissZone = ZoneId.of("Europe/Zurich");
ZonedDateTime swissZoned = utcZoned.withZoneSameInstant(swissZone);
LocalDateTime swissLocal = swissZoned.toLocalDateTime();
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
26

It helps to understand the difference between LocalDateTime and ZonedDateTime. What you really want is a ZonedDateTime. If you wanted to remove the timezone from the string representation, you would convert it to a LocalDateTime.

What you're looking for is: ZonedDateTime swissZonedDateTime = withZoneSameInstant(ZoneId.of("Europe/Zurich"));

LocalDateTime - this is basically a glorified string representation of a Date and Time; it is timezone agnostic, which means it does not represent any point in time on a timeline

Instant - this is a millisecond representation of time that has elapsed since EPOCH. This represents a specific instant in time on a timeline

ZonedDateTime - this also represents an instant in time on a timeline, but it represents it as a Date and Time with a TimeZone.

The code below illustrates how to use all 3.

LocalDateTime localDateTime = LocalDateTime.of(2018, 10, 25, 12, 00, 00);  //October 25th at 12:00pm
ZonedDateTime zonedDateTimeInUTC = localDateTime.atZone(ZoneId.of("UTC")); 
ZonedDateTime zonedDateTimeInEST = zonedDateTimeInUTC.withZoneSameInstant(ZoneId.of("America/New_York")); 
    
System.out.println(localDateTime.toString()); // 2018-10-25T12:00
System.out.println(zonedDateTimeInUTC.toString()); // 2018-10-25T12:00Z[UTC]
System.out.println(zonedDateTimeInEST.toString()); // 2018-10-25T08:00-04:00[America/New_York]

If you were to compare the Instant values of both ZonedDateTimes from above, they would be equivalent because they both point to the same instant in time. Somewhere in Greenwich (UTC timezone), it is noon on October 25th; at the same time, in New York, it would be 8 am (America/NewYork timezone).

cs_pupil
  • 2,782
  • 27
  • 39
Stevers
  • 516
  • 5
  • 11
5

Try this out. Substitute US/Central with your timezone.

ZonedDateTime z = ZonedDateTime.of(LocalDate.now().atTime(11, 30), ZoneOffset.UTC);

System.out.println(z.withZoneSameInstant(ZoneId.of("US/Central")));
Ravindra Ranwala
  • 20,744
  • 6
  • 45
  • 63
2

Another option that seems a little more intuitive is to convert the zoned date to an instant, then use LocalDateTime.ofInstant:

ZonedDateTime zdt = ZonedDateTime.of(LocalDate.now().atTime(11, 30), ZoneOffset.UTC);
ZoneId zId = ZoneId.of("US/Central");
LocalDateTime l = LocalDateTime.ofInstant(zdt.toInstant(), zId);

I prefer this to withZoneSameInstant because that solution little more opaque - you're getting a ZonedDateTime that has to be in a particular internal state (the correct time zone). Using ofInstant can be used on any ZonedDateTime in any time zone.

Freak
  • 6,786
  • 5
  • 36
  • 54
Jon Nichols
  • 2,211
  • 1
  • 20
  • 21
2

There are many ways to achieve what you want. Some of them are as follows:

  1. Convert the given ZonedDateTime to Instant and derive LocalDateTime from the Instant.
ZonedDateTime zdtUtc = ZonedDateTime.of(LocalDate.now().atTime(11, 30), ZoneOffset.UTC);

Instant instant = zdtUtc.toInstant();

LocalDateTime ldtSwitzerland = LocalDateTime.ofInstant(instant, ZoneId.of("Europe/Zurich"));

ONLINE DEMO

  1. Use ZonedDateTime#withZoneSameInstant to convert the given ZonedDateTime to a ZonedDateTime in the desired timezone, and then get the LocalDateTime from the ZonedDateTime.
ZonedDateTime zdtUtc = ZonedDateTime.of(LocalDate.now().atTime(11, 30), ZoneOffset.UTC);

ZonedDateTime zdtSwitzerland = zdtUtc.withZoneSameInstant(ZoneId.of("Europe/Zurich"));

LocalDateTime ldtSwitzerland = zdtSwitzerland.toLocalDateTime();

ONLINE DEMO

  1. Convert the given ZonedDateTime to Instant which can be converted to a ZonedDateTime using Instant#atZone , and then get the LocalDateTime from the ZonedDateTime.
ZonedDateTime zdtUtc = ZonedDateTime.of(LocalDate.now().atTime(11, 30), ZoneOffset.UTC);

Instant instant = zdtUtc.toInstant();

ZonedDateTime zdtSwitzerland = instant.atZone(ZoneId.of("Europe/Zurich"));

LocalDateTime ldtSwitzerland = zdtSwitzerland.toLocalDateTime();

ONLINE DEMO

  1. Use DateTimeFormatter to format the given ZonedDateTime into the Date-Time string pertaining to the desired timezone and then derive the LocalDateTime by parsing the obtained Date-Time string. Note: Use this method just for your learning purpose; you should implement the first way (topmost) in your production code.
ZonedDateTime zdtUtc = ZonedDateTime.of(LocalDate.now().atTime(11, 30), ZoneOffset.UTC);

DateTimeFormatter dtfSwitzerland = DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(ZoneId.of("Europe/Zurich"));
String strZdtSwitzerland = dtfSwitzerland.format(zdtUtc);

LocalDateTime ldt = LocalDateTime
        .from(DateTimeFormatter.ISO_LOCAL_DATE_TIME.parse(strZdtSwitzerland, new ParsePosition(0)));

ONLINE DEMO

Learn more about the modern Date-Time API from Trail: Date Time.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110