Skip to content

Commit 52e4e73

Browse files
committed
Fix ingest timezone parsing (elastic#63876)
this commit aligns timezone parsing logic with DateFormat.Iso8601. Timezone can be provided on a pattern and as ingest parameter. When no timezone is provided on a pattern it is defaulted to UTC or an ingest parameter (if provided). When timezone is not present in a pattern, but an ingest timezone parameter is provided - a date should be parsed as if it was in that timezone. Example: pattern "uuuu-MM-dd'T'HH:mm", text "2020-01-01T01:00", timezone: -01:00 should return 2020-01-01T01:00:00-01:00 If the pattern has a timezone and a timezone parameter is provided - a date should parsed with a timezone from input, and then "recalculated" to an ingest parameter Example "uuuu-MM-dd'T'HH:mm XXX", text "2020-01-01T01:00 -02:00", timezone -01:00 should return 2020-01-01T02:00:00-01:00 relates elastic#38407 relates elastic#51215 closes elastic#63458
1 parent 256d5c0 commit 52e4e73

File tree

2 files changed

+43
-11
lines changed

2 files changed

+43
-11
lines changed

modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DateFormat.java

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,10 @@ private long parseMillis(String date) {
7878

7979
@Override
8080
Function<String, ZonedDateTime> getFunction(String format, ZoneId zoneId, Locale locale) {
81-
boolean isUtc = ZoneOffset.UTC.equals(zoneId);
8281

8382
DateFormatter dateFormatter = DateFormatter.forPattern(format)
8483
.withLocale(locale);
85-
// if UTC zone is set here, the time zone specified in the format will be ignored, leading to wrong dates
86-
if (isUtc == false) {
87-
dateFormatter = dateFormatter.withZone(zoneId);
88-
}
84+
8985
final DateFormatter formatter = dateFormatter;
9086
return text -> {
9187
TemporalAccessor accessor = formatter.parse(text);
@@ -105,11 +101,9 @@ Function<String, ZonedDateTime> getFunction(String format, ZoneId zoneId, Locale
105101
accessor = newTime.withZoneSameLocal(zoneId);
106102
}
107103

108-
if (isUtc) {
109-
return DateFormatters.from(accessor, locale).withZoneSameInstant(ZoneOffset.UTC);
110-
} else {
111-
return DateFormatters.from(accessor, locale);
112-
}
104+
return DateFormatters.from(accessor, locale, zoneId)
105+
.withZoneSameInstant(zoneId);
106+
113107
};
114108
}
115109
};

modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/DateFormatTests.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,52 @@ public void testParseWeekBasedWithLocale() {
8585
assumeFalse("won't work in jdk8 " +
8686
"because SPI mechanism is not looking at classpath - needs ISOCalendarDataProvider in jre's ext/libs",
8787
JavaVersion.current().equals(JavaVersion.parse("8")));
88-
String format = randomFrom("YYYY-ww");
88+
String format = "YYYY-ww";
8989
ZoneId timezone = DateUtils.of("Europe/Amsterdam");
9090
Function<String, ZonedDateTime> javaFunction = DateFormat.Java.getFunction(format, timezone, Locale.US);
9191
ZonedDateTime dateTime = javaFunction.apply("2020-33");
9292
//33rd week of 2020 starts on 9th August 2020 as per US locale
9393
assertThat(dateTime, equalTo(ZonedDateTime.of(2020,8,9,0,0,0,0,timezone)));
9494
}
9595

96+
public void testNoTimezoneOnPatternAndOverride() {
97+
{
98+
String format = "yyyy-MM-dd'T'HH:mm";
99+
ZoneId timezone = ZoneId.of("UTC");
100+
Function<String, ZonedDateTime> javaFunction = DateFormat.Java.getFunction(format, timezone, Locale.ROOT);
101+
// this means that hour will be 01:00 at UTC as timezone was not on a pattern, but provided as an ingest param
102+
ZonedDateTime dateTime = javaFunction.apply("2020-01-01T01:00");
103+
assertThat(dateTime, equalTo(ZonedDateTime.of(2020, 01, 01, 01, 0, 0, 0, timezone)));
104+
}
105+
{
106+
String format = "yyyy-MM-dd'T'HH:mm";
107+
ZoneId timezone = ZoneId.of("-01:00");
108+
Function<String, ZonedDateTime> javaFunction = DateFormat.Java.getFunction(format, timezone, Locale.ROOT);
109+
// this means that hour will be 01:00 at -01:00 as timezone was not on a pattern, but provided as an ingest param
110+
ZonedDateTime dateTime = javaFunction.apply("2020-01-01T01:00");
111+
assertThat(dateTime, equalTo(ZonedDateTime.of(2020, 01, 01, 01, 0, 0, 0, timezone)));
112+
}
113+
}
114+
115+
public void testTimezoneOnAPatternAndNonUTCOverride() {
116+
String format = "yyyy-MM-dd'T'HH:mm XXX";
117+
ZoneId timezone = ZoneId.of("-01:00");
118+
Function<String, ZonedDateTime> javaFunction = DateFormat.Java.getFunction(format, timezone, Locale.ROOT);
119+
// this means that hour will be 01:00 at -02:00 as timezone on a pattern. Converted to -01:00 as requested on ingest param
120+
121+
ZonedDateTime dateTime = javaFunction.apply("2020-01-01T01:00 -02:00");
122+
assertThat(dateTime, equalTo(ZonedDateTime.of(2020, 01, 01, 02, 0, 0, 0, timezone)));
123+
}
124+
125+
public void testDefaultHourDefaultedToTimezoneOverride() {
126+
String format = "yyyy-MM-dd";
127+
ZoneId timezone = ZoneId.of("-01:00");
128+
Function<String, ZonedDateTime> javaFunction = DateFormat.Java.getFunction(format, timezone, Locale.ROOT);
129+
// this means that hour will be 00:00 (default) at -01:00 as timezone was not on a pattern, but -01:00 was an ingest param
130+
ZonedDateTime dateTime = javaFunction.apply("2020-01-01");
131+
assertThat(dateTime, equalTo(ZonedDateTime.of(2020, 01, 01, 0, 0, 0, 0, timezone)));
132+
}
133+
96134
public void testParseUnixMs() {
97135
assertThat(DateFormat.UnixMs.getFunction(null, ZoneOffset.UTC, null).apply("1000500").toInstant().toEpochMilli(),
98136
equalTo(1000500L));

0 commit comments

Comments
 (0)