Skip to content

Commit 5f32531

Browse files
committed
Add support for parsing time zones in DateFormatters and enhance Iso8601Parser
We can now parse ISO date-time such as : 2029-05-15T17:14:56.123456789-08:00[America/Los_Angeles] or 2031-12-03T10:15:30.123456789+01:00[Europe/Paris] or with "short-id" (like `NST` for `Pacific/Auckland`) 2025-06-26T12:01:48.211+12:00[NST]
1 parent bf2a283 commit 5f32531

File tree

3 files changed

+40
-0
lines changed

3 files changed

+40
-0
lines changed

docs/changelog/130054.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 130054
2+
summary: Add support for parsing ISO date time with zone-id (RFC 9557)
3+
area: Mapping
4+
type: enhancement
5+
issues: []

server/src/main/java/org/elasticsearch/common/time/Iso8601Parser.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,10 @@ private ZoneId parseZoneId(CharSequence str, int pos) {
477477
if (minutes == null || minutes > 59) return null;
478478
if (len == pos) return ofHoursMinutesSeconds(hours, minutes, 0, positive);
479479

480+
if (str.charAt(pos) == '[' && str.charAt(len - 1) == ']') {
481+
return parseRawZoneId(str, pos);
482+
}
483+
480484
// either both dividers have a colon, or neither do
481485
if ((str.charAt(pos) == ':') != hasColon) return null;
482486
if (hasColon) {
@@ -487,10 +491,32 @@ private ZoneId parseZoneId(CharSequence str, int pos) {
487491
if (seconds == null || seconds > 59) return null;
488492
if (len == pos) return ofHoursMinutesSeconds(hours, minutes, seconds, positive);
489493

494+
if (str.charAt(pos) == '[' && str.charAt(len - 1) == ']') {
495+
return parseRawZoneId(str, pos);
496+
}
497+
490498
// there's some text left over...
491499
return null;
492500
}
493501

502+
/**
503+
* Parses a raw zone id, which is a string of the form [zoneId] (eg [Europe/Paris]).
504+
*
505+
* @param str The string to parse
506+
* @param pos The position in the string where the zone id starts (the first character after the opening [)
507+
* @return The parsed zone id, or {@code null} if the string is not a valid zone id.
508+
*/
509+
private ZoneId parseRawZoneId(CharSequence str, int pos) {
510+
try {
511+
String zoneId = str.subSequence(pos + 1, str.length() - 1).toString();
512+
// Try to resolve short zone ids to offsets.
513+
zoneId = ZoneId.SHORT_IDS.getOrDefault(zoneId, zoneId);
514+
return ZoneId.of(zoneId);
515+
} catch (DateTimeException e) {
516+
return null;
517+
}
518+
}
519+
494520
/*
495521
* ZoneOffset.ofTotalSeconds has a ConcurrentHashMap cache of offsets. This is fine,
496522
* but it does mean there's an expensive map lookup every time we call ofTotalSeconds.

server/src/test/java/org/elasticsearch/common/time/DateFormattersTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,15 @@ public void testIso8601Parsing() {
608608
formatter.format(formatter.parse("2018-05-15T17:14:56,123456789+01:00"));
609609
}
610610

611+
public void testParsingDateTimeWithZoneId() {
612+
DateFormatter formatter = DateFormatters.forPattern("strict_date_optional_time");
613+
formatter.format(formatter.parse("2018-05-15T17:14:56-08:00[America/Los_Angeles]"));
614+
formatter.format(formatter.parse("2029-05-15T17:14:56.123456789-08:00[America/Los_Angeles]"));
615+
formatter.format(formatter.parse("2031-12-03T10:15:30.123456789+01:00:00[Europe/Paris]"));
616+
formatter.format(formatter.parse("2031-12-03T10:15:30.123456789+01:00:00[PST]"));
617+
formatter.format(formatter.parse("2031-12-03T10:15:30.123456789+01:00:00[EST]"));
618+
}
619+
611620
public void testRoundupFormatterWithEpochDates() {
612621
assertRoundupFormatter("epoch_millis", "1234567890", 1234567890L);
613622
// also check nanos of the epoch_millis formatter if it is rounded up to the nano second

0 commit comments

Comments
 (0)