2

I am having two different results from these two blocks even though the input date/time to parse is the same

public class DateTimeFormatterUtilsTest 
{
    private static final String ISO_DATETIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";;
    private static final String ISO_DATETIME_TO_PARSE = "2007-12-03T10:15:30.000Z";
    private static final long TARGET_EPOCH_TIME = 1196676930000L;

    @Test
    public void testDateTimeFormatterUtils()  
    {
        ZoneId targetZoneid = TimeUtils.getZoneId(TIMEZONE.PST);    
        
        DateTimeFormatter formatter =  DateTimeFormatter.ISO_INSTANT.withZone(targetZoneid);
        long epochTime = parseDateTime(ISO_DATETIME_TO_PARSE, formatter);       
        assertTrue(epochTime == TARGET_EPOCH_TIME);
        
        
        // specify custom pattern       
        DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern(ISO_DATETIME_PATTERN).withZone(targetZoneid);
        epochTime = parseDateTime(ISO_DATETIME_TO_PARSE, formatter1);
        assertTrue(epochTime == TARGET_EPOCH_TIME);

    }
    
    public long parseDateTime(final String dateTimeString, DateTimeFormatter formatter)
    {
        ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateTimeString, formatter);
        System.out.println("parsed zoned date time" + zonedDateTime);
        
        Instant instant = zonedDateTime.toInstant();
        long epochTime = instant.toEpochMilli();
        System.out.println("Epoch time for" + ISO_DATETIME_TO_PARSE + "is " + epochTime);
        return epochTime;
    }   
}

When I am using DateTimeFormatter.ISO_INSTANT I get the correct epoch time which is 1196676930000, however, when I am usin the .ofPattern method to create the DateTimeFormatter I am getting 1196705730000. Not sure why?

As you can see, the difference is 28 800 000 milliseconds or exactly 8 hours.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Related and similar: [How do I get an Instant object with the timestamp 48962-08-06T23:16:59.000Z in Java](https://stackoverflow.com/questions/63843844/how-do-i-get-an-instant-object-with-the-timestamp-48962-08-06t231659-000z-in-j) – Ole V.V. Oct 20 '22 at 20:14

1 Answers1

1

Never put quote marks around the Z in a date-time formatting pattern.

Z means +00:00

The Z is a standard abbreviation for an offset of zero. Pronounced “Zulu” per aviation/military convention.

Yuor quotes treat the Z as meaningless string literal, preventing semantic interpretation. The Z carries vital information, meaning “an offset from UTC of zero hours-minutes-seconds”. But your 'Z' parsing pattern ignores that info.

This:

"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"

… should be:

"yyyy-MM-dd'T'HH:mm:ss.SSSZ"

By ignoring that offset, the JVM’s current default time zone is applied implicitly when you parsed as a ZonedDateTime. Hence your correct but unexpected results.

Instant, not ZonedDateTime

Your input has no indication of time zone. So ZonedDateTime is not called for here.

Instead, parse as an Instant.

Instant.parse( "2007-12-03T10:15:30.000Z" ) 

If you want to see that moment through the lens of a particular time zone, apply a ZoneId to get a ZonedDateTime. Same moment, different wall-clock time.

ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

Terminology

Quick review of terms:

  • UTC is the temporal prime meridian. In the old days this was the time kept at the Royal Observatory in Greenwich, London.
  • An offset is merely a number of hours-minutes-seconds ahead or behind UTC.
  • A time zone is a named history of the past, present, and future changes to the offset used by the people of a particular region as decided by their politicians.
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • one question though does not this time "2007-12-03T10:15:30.000Z" mean it is in UTC time zone (because it has Z in it?) – blue mountain Oct 18 '22 at 21:04
  • @bluemountain Yes, the `Z` means an offset from UTC of zero. But UTC is not really a time zone. UTC is the temporal prime meridian that defines the offsets used by time zones. – Basil Bourque Oct 18 '22 at 21:09
  • @user16320675 See the terminology section I added. – Basil Bourque Oct 18 '22 at 21:17
  • @user16320675 Yes. And as the point in time defining the offsets of time zones, UTC is a temporal prime meridian, the point at which all other time is kept. (Well, much time is kept, but not all. Other meridians exist, such as a point in Paris used by some French protocols.) In brief, UTC is the new GMT. – Basil Bourque Oct 18 '22 at 21:58
  • @user16320675 No, UTC is not a line drawn on the globe. You keep missing my use of the adjective *temporal*, a "temporal prime meridian". Programmers writing business apps really don't care about atomic clocks, leap seconds, navigation, and the BIPM. That's my audience here, not engineers doing rocket science with GPS/Galileo systems. – Basil Bourque Oct 18 '22 at 22:46
  • "yyyy-MM-dd'T'HH:mm:ss.SSSZ" did not work, however when I try "yyyy-MM-dd'T'HH:mm:ss.SSSX" it was able to recognize the current zone of the input and was able to parse the date time 2007-12-03T10:15:30.000Z. thank you all for the help. – blue mountain Oct 19 '22 at 19:30
  • 1
    the main learning is NOT to use 'Z' while parsing date time, try to use the letters from this table (like yyyy) https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/format/DateTimeFormatter.html to really preserve the zone information while parsing. – blue mountain Oct 19 '22 at 19:35