Search code examples
javadatetimedatetime-parsingjsr310rfc3339

Zone adjustment not taken into account when parsing RFC 3339 date time


(Migrated from CodeReview)

I am experimenting and trying to understand Java Time better. My code is not working as expected and I want to ask the reason, probably because there is a misunderstandment from my side about JSR-310 time zone handling.

I have a timestamp string to parse with DateTimeFormatter

String text = "2019-08-28T10:39:57+02:00";
DateTimeFormatter ISO_RFC_3339_PARSER = DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.of("Europe/Paris"));

The above value is generated when the server's local time is 10:39 AM in Central Europe. UTC time is 8:39 in the morning because of the summer time zone. You can do your own math to find what time it is in your zone.

The following test works when the time zone of the computer is the same as displayed in the timestamp (+2)

@Test
public void testTimestamp()
{
    LocalDateTime time = ZonedDateTime.from(ISO_RFC_3339_PARSER.parse(text)).toLocalDateTime();

    errorCollector.checkThat(time.getYear(), is(2019));
    errorCollector.checkThat(time.getMonthValue(), is(8));
    errorCollector.checkThat(time.getDayOfMonth(), is(28));
    errorCollector.checkThat(time.getHour(), is(10));
    errorCollector.checkThat(time.getMinute(), is(39));
    errorCollector.checkThat(time.getSecond(), is(57));

}

I have tried to tinker with the input string to better understand how Java Time ajusts time zones. But results are surprisingly wrong!

I tried to change the time zone in the input string repeatedly, so that the test should fail. For example if I change the string to 2019-08-28T10:39:57+08:00 it means it is 2AM in Paris. But the above test code continues to pass when checking for the hour of day. I.e. in the resulting LocalDateTime the time of the day is still 10AM.

Question

Why is my code still returning 10 as local time of day regardless of the fact that I am constantly changing its time zone in the source string?

Then what is the correct way to parse a RFC 3339 string (which represents an instant in the embedded offset) and convert it into a LocalDateTime object with regards of possible zone adjustments? Assume the machine is running in CE[S]T time zone.

Environment

I need to compare a timestamp with computer time and check whether it is not too old. That means if a US time is evaluated in Europe it may not be "too old".


Solution

  • Getting LocalDateTime from ZonedDateTime simply cuts of Time Zone. Its value is of no relevance. What you need to do is to convert your ZonedDateTime from zone +8 to zone +2, that will change the time accordingly. Then you can get the LocalDateTime from your modified ZonedDateTime to get the desired effect. Here is an example that will demonstrate it:

    private static void testDateParsing() {
        ZonedDateTime zdt = ZonedDateTime.parse("2019-08-28T10:39:57+08:00", DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZZZZZ"));
        ZonedDateTime modifiedZdt = zdt.withZoneSameInstant(ZoneId.systemDefault());
        System.out.println(zdt);
        System.out.println(modifiedZdt);
        System.out.println(LocalDateTime.from(zdt));
        System.out.println(LocalDateTime.from(modifiedZdt));
    }
    

    The output is:

    2019-08-28T10:39:57+08:00
    2019-08-28T05:39:57+03:00[Asia/Jerusalem]
    2019-08-28T10:39:57
    2019-08-28T05:39:57
    

    Note that my local time zone is +03 and not +02 as it was for you.