Reputation: 55
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
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
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
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