12

When I try to convert OffsetDateTime to LocalDateTime from java.time, I expect the resulting LocalDateTime to be updated with the local time zone. So, If I have an OffsetDateTime of 2011-12-03T10:00:00Z, and my local timezone is UTC+2, I expect the LocalDateTime to be 2011-12-03T12:00:00, but I get instead 2011-12-03T10:00:00. I'm converting it with the method toLocalDateTime() that OffsetDateTime has. It seems that it only truncates the date, removing the offset part, without adjusting the time.

So I'm trying to figure out a way to get a LocalDateTime that represents the local date time taking into account the zone offset. Following the example, I would like to get 2011-12-03T12:00:00

user1116714
  • 315
  • 2
  • 3
  • 12
  • It's a bit unclear about what you mean by "the local time zone". Keep in mind that you are working with _offset_ date times, not _zoned_ date times, so I'll assume you mean "the local offset". However, that is ambiguous. Do you mean the local offset at the instant `2011-12-03T10:00:00Z`, or do you mean the local offset _now_? Note that local offsets can change. – Sweeper Jun 10 '21 at 09:58
  • *"It seems that it only truncates the date, removing the offset part, without adjusting the time"* .. that's correct and that's what the method description says (https://docs.oracle.com/javase/8/docs/api/java/time/OffsetDateTime.html#toLocalDateTime--). Also, the "local" part in `LocalDateTime` doesn't mean it bothers about your actual local timezone, it just means that it represents a time that is valid for any current local timezone/offset, whatever that might be. – Tom Jun 10 '21 at 10:04

6 Answers6

16

LocalDateTime would give you the time of a wall clock of your OffsetDateTime. That's 10:00

You need to first convert to a ZonedDatedTime in your time zone

Like this

OffsetDateTime off = OffsetDateTime.of(2011,12,3,10,00,0,0, ZoneOffset.UTC);
ZonedDateTime zoned = off.atZoneSameInstant(ZoneId.of("Europe/Athens"));
LocalDateTime athensWallTime = zoned.toLocalDateTime();
System.out.println(athensWallTime);
David Lilljegren
  • 1,799
  • 16
  • 19
8

I think what you are looking for is OffsetDateTime.atZoneSameInstant:

OffsetDateTime.parse("2011-12-03T10:00:00Z")
    .atZoneSameInstant(ZoneId.systemDefault())
    .toLocalDateTime()
Adam Millerchip
  • 20,844
  • 5
  • 51
  • 74
4

OffsetDateTime#withOffsetSameInstant

If you need to convert an object of OffsetDateTime to an OffsetDateTime object with a different ZoneOffset, you can do so by using OffsetDateTime#withOffsetSameInstant.

Demo:

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;

public class Main {
    public static void main(String[] args) {
        OffsetDateTime odt = OffsetDateTime.parse("2011-12-03T10:00:00Z");

        OffsetDateTime odtWithOffsetTwoHours = odt.withOffsetSameInstant(ZoneOffset.of("+02:00"));
        System.out.println(odtWithOffsetTwoHours);

        LocalDateTime ldt = odtWithOffsetTwoHours.toLocalDateTime();
        System.out.println(ldt);
    }
}

Output:

2011-12-03T12:00+02:00
2011-12-03T12:00

ONLINE DEMO

I suggest you keep using OffsetDateTime because LocalDateTime, as the name suggests, throws away the useful timezone information. Nevertheless, LocalDateTime is useful in some scenarios as mentioned on this page.

If you are dealing with JDBC, check this answer and this answer.

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

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

The answer marked as correct put me in the right direction to make it a little bit more production oriented. Also, I need this for Kotlin, so considering it is retro-compatible, here it goes.

For me, what worked was using withOffsetSameInstant. This method requires the offset, and I think the key is to obtain it from the system to provide the user with the visualization in its own time.

The offset can be obtained like this:

val offset = OffsetDateTime.now().offset

And then we proceed to parse the date-time text to OffsetDateTime:

val dateTime = "2023-01-20T01:00:00+00:00"

val offsetDateTime =  OffsetDateTime.parse(dateTime).withOffsetSameInstant(offset)

Then we can obtain the local date-time like this:

val localDateTime = offsetDateTime.toLocalDateTime()
println(localDateTime)

Let's say that my offset is minus 3 (-03:00). Then that should print:

2023-01-19T22:00

This, for me, seems ok because it was day 20th at 1:00 am, so with minus 3, it should be the day 19th.

A small clarification regarding using a formatter. For the format above, you don't need to provide a DateTimeFormatter because the method parse with a single argument; assumes it is ISO, and the format above is ISO (DateTimeFormatter.ISO_OFFSET_DATE_TIME).

This is how you can create a formatter in case you need it:

val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX")
cutiko
  • 9,887
  • 3
  • 45
  • 59
-1

I think the most concise solution is

LocalDateTime.ofInstant(Instant.now(), ZoneOffset.UTC)
Semyon Evgrafov
  • 106
  • 1
  • 6
-2

Here is some code you can try :

public static void main(String[] args) {
        OffsetDateTime offsetDT1 = OffsetDateTime.now();
        System.out.println("OffsetDateTime1: " + offsetDT1);
 
        OffsetDateTime offsetDT2 = OffsetDateTime.now(Clock.systemUTC());
        System.out.println("OffsetDateTime2: " + offsetDT2);
        
        OffsetDateTime offsetDT3 = OffsetDateTime.now(ZoneId.of("Asia/Jakarta"));
        System.out.println("OffsetDateTime3: " + offsetDT3);
        
        OffsetDateTime offsetDT4 = OffsetDateTime.of(1980, 4, 9, 20, 15, 45, 345875000, ZoneOffset.of("+07:00"));
        System.out.println("OffsetDateTime4: " + offsetDT4);
        
        OffsetDateTime offsetDT5 = OffsetDateTime.of(LocalDate.now(), LocalTime.of(15, 50, 25), ZoneOffset.of("+07:00"));
        System.out.println("OffsetDateTime5: " + offsetDT5);
        
        OffsetDateTime offsetDT6 = OffsetDateTime.of(LocalDateTime.now(), ZoneOffset.of("+07:00"));
        System.out.println("OffsetDateTime6: " + offsetDT6);
        
        OffsetDateTime offsetDT7 = OffsetDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
        System.out.println("OffsetDateTime7: " + offsetDT7);
        
        OffsetDateTime offsetDT8 = OffsetDateTime.parse("2019-08-31T15:20:30+08:00");
        System.out.println("OffsetDateTime8: " + offsetDT8);
        
        OffsetDateTime offsetDT9 = OffsetDateTime.parse("1980-04-09T08:20:45+07:00", DateTimeFormatter.ISO_OFFSET_DATE_TIME);
        System.out.println("OffsetDateTime9: " + offsetDT9);

Output

OffsetDateTime1: 2019-08-31T23:49:05.629+08:00
OffsetDateTime2: 2019-08-31T15:49:05.630Z
OffsetDateTime3: 2019-08-31T22:49:05.630+07:00
OffsetDateTime4: 1980-04-09T20:15:45.345875+07:00
OffsetDateTime5: 2019-08-31T15:50:25+07:00   
OffsetDateTime6: 2019-08-31T23:49:05.631+07:00
OffsetDateTime7: 2019-08-31T23:49:05.631+08:00
OffsetDateTime8: 2019-08-31T15:20:30+08:00
OffsetDateTime9: 1980-04-09T08:20:45+07:00

You can take a look on this website for more explaination or browse javadoc wbsite.

stic-lab
  • 433
  • 7
  • 16
  • 1
    Which part of all that code is supposed to be an answer to the question? – Andreas Jun 10 '21 at 10:55
  • According to the output I given, you can choose one function you want to use. – stic-lab Jun 10 '21 at 10:58
  • And which of those *changes* `2011-12-03T10:00:00Z` (type `OffsetDateTime`) to `2011-12-03T12:00:00` (type `LocalDateTime`)? There is no `LocalDateTime` in this answer, and there is no conversion of a `OffsetDateTime` for changing the time from 10 to 12, so that would be: None of them. – Andreas Jun 10 '21 at 12:44
  • For #7, `OffsetDateTime.ofInstant(Instant.now(), ZoneId.systemDefault())` is the long way of saying `OffsetDateTime.now()`. – Andreas Jun 10 '21 at 12:47