tswapnil
tswapnil

Reputation: 55

IST mapped to wrong ZoneId in java.time library

I am trying to parse a ZonedDateTime from a String of the format (yyyy-MM-dd HH:mm z). The input String (2019-08-29 00:00 IST) generates a UTC timestamp.

Debugging led me to a point where the ZoneId for IST was mapped to Atlantic/Reykjavik which doesn't make sense. It should be mapped Asia.

timestamp = ZonedDateTime.parse(timeInput, DATE_WITH_TIMEZONE_FORMATTER)
            .toInstant().toEpochMilli()

where

DateTimeFormatter DATE_WITH_TIMEZONE_FORMATTER = DateTimeFormatter
      .ofPattern(DATE_TIME_WITH_TIMEZONE_PATTERN).withChronology(IsoChronology.INSTANCE);

Am I missing something here ?

Upvotes: 5

Views: 1410

Answers (3)

Arvind Kumar Avinash
Arvind Kumar Avinash

Reputation: 79425

Avoid using the three-letter time zone ID. Given below is an extract from as old as Java 6 documentation:

Three-letter time zone IDs

For compatibility with JDK 1.1.x, some other three-letter time zone IDs (such as "PST", "CTT", "AST") are also supported. However, their use is deprecated because the same abbreviation is often used for multiple time zones (for example, "CST" could be U.S. "Central Standard Time" and "China Standard Time"), and the Java platform can then only recognize one of them.

Corresponding to the Indian Standard Time which has a time offset of UTC+05:30, you can build a custom formatter using .appendZoneText(TextStyle.SHORT, Set.of(ZoneId.of("Asia/Kolkata"))) with DateTimeFormatterBuilder as shown below:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
        .appendPattern("uuuu-MM-dd HH:mm")
        .appendLiteral(' ')
        .appendZoneText(TextStyle.SHORT, Set.of(ZoneId.of("Asia/Kolkata")))
        .toFormatter(Locale.ENGLISH);

Now, let's use this custom formatter:

Instant instant = ZonedDateTime.parse("2019-08-29 00:00 IST", formatter).toInstant();
System.out.println(instant);
System.out.println(instant.toEpochMilli());

Output:

2019-08-28T18:30:00Z
1567017000000

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

Upvotes: 2

Anonymous
Anonymous

Reputation: 86323

First, if there’s any way you can avoid it, don’t rely on three letter time zone abbreviations. They are ambiguous, probably more often than not. When you say that IST should be mapped to Asia, it still leaves the choice between Asia/Tel_Aviv and Asia/Kolkata open (plus the aliases for those two, Asia/Jerusalem and Asia/Calcutta). In other places in the world IST may mean Irish Summer Time and apparently also Iceland Standard Time (or something like it; it certainly makes sense). It’s not the first time I have seen IST recognized as Atlantic/Reykjavik.

If you can’t avoid having to parse IST, control the interpretation through the two-arg appendZoneText method of a DateTimeFormatterBuilder. It accepts a set of preferred zones:

DateTimeFormatter DATE_WITH_TIMEZONE_FORMATTER = new DateTimeFormatterBuilder()
        .appendPattern("yyyy-MM-dd HH:mm ")
        .appendZoneText(TextStyle.SHORT, Collections.singleton(ZoneId.of("Asia/Kolkata")))
        .toFormatter(Locale.ENGLISH);

Substitute your preferred preferred zone where I put Asia/Kolkata. The rest shouldn’t cause trouble:

    String timeInput = "2019-08-29 00:00 IST";
    ZonedDateTime zdt = ZonedDateTime.parse(timeInput, DATE_WITH_TIMEZONE_FORMATTER);
    System.out.println("Parsed ZonedDateTime: "+ zdt);
    long timestamp = zdt
            .toInstant().toEpochMilli();
    System.out.println("timestamp: " + timestamp);

Output:

Parsed ZonedDateTime: 2019-08-29T00:00+05:30[Asia/Kolkata]
timestamp: 1567017000000

Link: Time Zone Abbreviations – Worldwide List (you will notice that IST comes three times in the list and that many other abbreviations are ambiguous too).

Upvotes: 3

Jim Garrison
Jim Garrison

Reputation: 86774

I do not know why you're getting Iceland, even in the master TZDB IST appears only in reference to Irish Standard Time (and prior to 1968 erroneously Irish Summer Time), Israel Standard Time and India Standard Time. The appearance of Iceland may be an error in Java's timezone database.

After further investigation I have found that the problem seems to occur only if the current Locale's language is set to some non-null value. If you create a Locale without a specified language you get Asia/Kolkata, but if the language is present (any language) it returns Atlantic/Reykjavik. This is highly likely to be a bug in Java's implementation.

    String input = "2019-08-29 00:00 IST";
    Locale loc = new Locale.Builder().setRegion("US").build(); // Note no language
    System.out.println(loc.toString());
    DateTimeFormatter DATE_WITH_TIMEZONE_FORMATTER = 
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z").withLocale(loc);
    ZonedDateTime zdt = ZonedDateTime.parse(input, DATE_WITH_TIMEZONE_FORMATTER);
    System.out.println(zdt);

This produces

_US
2019-08-29T00:00+05:30[Asia/Kolkata]

But changing

    Locale loc = new Locale.Builder().setLanguage("ta").build();

produces

ta
2019-08-29T00:00Z[Atlantic/Reykjavik]

Regardless, the bare timezone IST is ambiguous out of context. To avoid confusion, if you want IST to always be Asia/Kolkata you may have to modify the incoming data prior to parsing.

Upvotes: 2

Related Questions