Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ enum DateFormat {
Iso8601 {
@Override
Function<String, ZonedDateTime> getFunction(String format, ZoneId timezone, Locale locale) {
return (date) -> DateFormatters.from(DateFormatter.forPattern("strict_date_time").parse(date)).withZoneSameInstant(timezone);
return (date) -> {
ZonedDateTime zonedDateTime = DateFormatters.from(DateFormatter.forPattern("strict_date_time").parse(date));
return timezone == null ? zonedDateTime : zonedDateTime.withZoneSameInstant(timezone);
};
}
},
Unix {
Expand Down Expand Up @@ -88,9 +91,12 @@ Function<String, ZonedDateTime> getFunction(String format, ZoneId zoneId, Locale
}

int year = LocalDate.now(ZoneOffset.UTC).getYear();
DateFormatter formatter = DateFormatter.forPattern(format)
.withLocale(locale)
.withZone(zoneId);
DateFormatter dateFormatter = DateFormatter.forPattern(format)
.withLocale(locale);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose locale cannot be null?
the if condition in JavaDateFormatter is reversed compared to JodaDateFormatter which might throw NPE?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed, cannot be null, fallback is done in the date processor configuration

if (zoneId != null) {
dateFormatter = dateFormatter.withZone(zoneId);
}
final DateFormatter formatter = dateFormatter;
return text -> {
TemporalAccessor accessor = formatter.parse(text);
// if there is no year, we fall back to the current one and
Expand All @@ -103,7 +109,15 @@ Function<String, ZonedDateTime> getFunction(String format, ZoneId zoneId, Locale
}
}

accessor = newTime.withZoneSameLocal(zoneId);
if (zoneId != null) {
accessor = newTime.withZoneSameLocal(zoneId);
} else {
accessor = newTime;
}
}

if (zoneId == null) {
return DateFormatters.from(accessor).withZoneSameInstant(ZoneOffset.UTC);
}

return DateFormatters.from(accessor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import org.elasticsearch.script.TemplateScript;

import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -43,7 +42,7 @@ public final class DateProcessor extends AbstractProcessor {

public static final String TYPE = "date";
static final String DEFAULT_TARGET_FIELD = "@timestamp";
public static final DateFormatter FORMATTER = DateFormatter.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
private static final DateFormatter FORMATTER = DateFormatter.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");

private final TemplateScript.Factory timezone;
private final TemplateScript.Factory locale;
Expand All @@ -68,7 +67,7 @@ public final class DateProcessor extends AbstractProcessor {
}

private ZoneId newDateTimeZone(Map<String, Object> params) {
return timezone == null ? ZoneOffset.UTC : ZoneId.of(timezone.newInstance(params).execute());
return timezone == null ? null : ZoneId.of(timezone.newInstance(params).execute());
}

private Locale newLocale(Map<String, Object> params) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package org.elasticsearch.ingest.common;

import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.common.time.DateUtils;
import org.elasticsearch.test.ESTestCase;

Expand All @@ -43,6 +44,14 @@ public void testParseJava() {
equalTo("11 24 01:29:01"));
}

public void testParseJavaWithTimeZone() {
Function<String, ZonedDateTime> javaFunction = DateFormat.Java.getFunction("yyyy-MM-dd'T'HH:mm:ss.SSSZZ",
null, Locale.ROOT);
ZonedDateTime datetime = javaFunction.apply("2018-02-05T13:44:56.657+0100");
String expectedDateTime = DateFormatter.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withZone(ZoneOffset.UTC).format(datetime);
assertThat(expectedDateTime, is("2018-02-05T12:44:56.657Z"));
}

public void testParseJavaDefaultYear() {
String format = randomFrom("8dd/MM", "dd/MM");
ZoneId timezone = DateUtils.of("Europe/Amsterdam");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -97,6 +98,18 @@ public void testJavaPatternMultipleFormats() {
}
}

public void testJavaPatternNoTimezone() {
DateProcessor dateProcessor = new DateProcessor(randomAlphaOfLength(10),
null, null,
"date_as_string", Arrays.asList("yyyy dd MM HH:mm:ss XXX"), "date_as_date");

Map<String, Object> document = new HashMap<>();
document.put("date_as_string", "2010 12 06 00:00:00 -02:00");
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document);
dateProcessor.execute(ingestDocument);
assertThat(ingestDocument.getFieldValue("date_as_date", String.class), equalTo("2010-06-12T02:00:00.000Z"));
}

public void testInvalidJavaPattern() {
try {
DateProcessor processor = new DateProcessor(randomAlphaOfLength(10),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,58 @@ teardown:
id: 1
- match: { _source.date_source_field: "12/06/2010" }
- match: { _source.date_target_field: "2010-06-12T00:00:00.000+02:00" }

---
"Test date processor with no timezone configured":

- do:
ingest.put_pipeline:
id: "my_pipeline"
# sample formats from beats, featuring mongodb, icinga, apache
body: >
{
"description": "_description",
"processors": [
{
"date" : {
"field" : "date_source_1",
"target_field" : "date_target_1",
"formats" : ["yyyy-MM-dd'T'HH:mm:ss.SSSZZ" ]
}
},
{
"date" : {
"field" : "date_source_2",
"target_field" : "date_target_2",
"formats" : ["yyyy-MM-dd HH:mm:ss Z" ]
}
},
{
"date" : {
"field" : "date_source_3",
"target_field" : "date_target_3",
"formats" : [ "dd/MMM/yyyy:H:m:s Z" ]
}
}
]
}
- match: { acknowledged: true }

- do:
index:
index: test
id: 1
pipeline: "my_pipeline"
body: { date_source_1: "2018-02-05T13:44:56.657+0100", date_source_2: "2017-04-04 13:43:09 +0200", date_source_3: "10/Aug/2018:09:45:56 +0200" }

- do:
get:
index: test
id: 1
- match: { _source.date_source_1: "2018-02-05T13:44:56.657+0100" }
- match: { _source.date_target_1: "2018-02-05T12:44:56.657Z" }
- match: { _source.date_source_2: "2017-04-04 13:43:09 +0200" }
- match: { _source.date_target_2: "2017-04-04T11:43:09.000Z" }
- match: { _source.date_source_3: "10/Aug/2018:09:45:56 +0200" }
- match: { _source.date_target_3: "2018-08-10T07:45:56.000Z" }