Merge pull request #12581 from cbuescher/fix/12531

Fix setting timezone on default DateTime formatter
This commit is contained in:
Christoph Büscher 2015-08-10 15:05:39 +02:00
commit a269b96799
5 changed files with 64 additions and 38 deletions

View File

@ -64,6 +64,9 @@ public abstract class TimeZoneRounding extends Rounding {
} }
public Builder timeZone(DateTimeZone timeZone) { public Builder timeZone(DateTimeZone timeZone) {
if (timeZone == null) {
throw new IllegalArgumentException("Setting null as timezone is not supported");
}
this.timeZone = timeZone; this.timeZone = timeZone;
return this; return this;
} }

View File

@ -33,10 +33,7 @@ import org.elasticsearch.search.aggregations.AggregatorFactory;
import org.elasticsearch.search.aggregations.support.ValueType; import org.elasticsearch.search.aggregations.support.ValueType;
import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; import org.elasticsearch.search.aggregations.support.ValuesSourceConfig;
import org.elasticsearch.search.aggregations.support.ValuesSourceParser; import org.elasticsearch.search.aggregations.support.ValuesSourceParser;
import org.elasticsearch.search.aggregations.support.format.ValueFormatter.DateTime;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import org.joda.time.DateTimeZone;
import java.io.IOException; import java.io.IOException;
/** /**
@ -45,7 +42,6 @@ import java.io.IOException;
public class DateHistogramParser implements Aggregator.Parser { public class DateHistogramParser implements Aggregator.Parser {
static final ParseField EXTENDED_BOUNDS = new ParseField("extended_bounds"); static final ParseField EXTENDED_BOUNDS = new ParseField("extended_bounds");
static final ParseField TIME_ZONE = new ParseField("time_zone");
static final ParseField OFFSET = new ParseField("offset"); static final ParseField OFFSET = new ParseField("offset");
static final ParseField INTERVAL = new ParseField("interval"); static final ParseField INTERVAL = new ParseField("interval");
@ -83,6 +79,7 @@ public class DateHistogramParser implements Aggregator.Parser {
ValuesSourceParser vsParser = ValuesSourceParser.numeric(aggregationName, InternalDateHistogram.TYPE, context) ValuesSourceParser vsParser = ValuesSourceParser.numeric(aggregationName, InternalDateHistogram.TYPE, context)
.targetValueType(ValueType.DATE) .targetValueType(ValueType.DATE)
.formattable(true) .formattable(true)
.timezoneAware(true)
.build(); .build();
boolean keyed = false; boolean keyed = false;
@ -90,7 +87,6 @@ public class DateHistogramParser implements Aggregator.Parser {
ExtendedBounds extendedBounds = null; ExtendedBounds extendedBounds = null;
InternalOrder order = (InternalOrder) Histogram.Order.KEY_ASC; InternalOrder order = (InternalOrder) Histogram.Order.KEY_ASC;
String interval = null; String interval = null;
DateTimeZone timeZone = DateTimeZone.UTC;
long offset = 0; long offset = 0;
XContentParser.Token token; XContentParser.Token token;
@ -101,9 +97,7 @@ public class DateHistogramParser implements Aggregator.Parser {
} else if (vsParser.token(currentFieldName, token, parser)) { } else if (vsParser.token(currentFieldName, token, parser)) {
continue; continue;
} else if (token == XContentParser.Token.VALUE_STRING) { } else if (token == XContentParser.Token.VALUE_STRING) {
if (context.parseFieldMatcher().match(currentFieldName, TIME_ZONE)) { if (context.parseFieldMatcher().match(currentFieldName, OFFSET)) {
timeZone = DateTimeZone.forID(parser.text());
} else if (context.parseFieldMatcher().match(currentFieldName, OFFSET)) {
offset = parseOffset(parser.text()); offset = parseOffset(parser.text());
} else if (context.parseFieldMatcher().match(currentFieldName, INTERVAL)) { } else if (context.parseFieldMatcher().match(currentFieldName, INTERVAL)) {
interval = parser.text(); interval = parser.text();
@ -121,8 +115,6 @@ public class DateHistogramParser implements Aggregator.Parser {
} else if (token == XContentParser.Token.VALUE_NUMBER) { } else if (token == XContentParser.Token.VALUE_NUMBER) {
if ("min_doc_count".equals(currentFieldName) || "minDocCount".equals(currentFieldName)) { if ("min_doc_count".equals(currentFieldName) || "minDocCount".equals(currentFieldName)) {
minDocCount = parser.longValue(); minDocCount = parser.longValue();
} else if ("time_zone".equals(currentFieldName) || "timeZone".equals(currentFieldName)) {
timeZone = DateTimeZone.forOffsetHours(parser.intValue());
} else { } else {
throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: ["
+ currentFieldName + "].", parser.getTokenLocation()); + currentFieldName + "].", parser.getTokenLocation());
@ -193,13 +185,10 @@ public class DateHistogramParser implements Aggregator.Parser {
} }
Rounding rounding = tzRoundingBuilder Rounding rounding = tzRoundingBuilder
.timeZone(timeZone) .timeZone(vsParser.input().timezone())
.offset(offset).build(); .offset(offset).build();
ValuesSourceConfig config = vsParser.config(); ValuesSourceConfig config = vsParser.config();
if (config.formatter()!=null) {
((DateTime) config.formatter()).setTimeZone(timeZone);
}
return new HistogramAggregator.Factory(aggregationName, config, rounding, order, keyed, minDocCount, extendedBounds, return new HistogramAggregator.Factory(aggregationName, config, rounding, order, keyed, minDocCount, extendedBounds,
new InternalDateHistogram.Factory()); new InternalDateHistogram.Factory());

View File

@ -20,6 +20,7 @@
package org.elasticsearch.search.aggregations.support; package org.elasticsearch.search.aggregations.support;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.fielddata.IndexGeoPointFieldData; import org.elasticsearch.index.fielddata.IndexGeoPointFieldData;
@ -39,6 +40,7 @@ import org.elasticsearch.search.SearchParseException;
import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.InternalAggregation;
import org.elasticsearch.search.aggregations.support.format.ValueFormat; import org.elasticsearch.search.aggregations.support.format.ValueFormat;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import org.joda.time.DateTimeZone;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
@ -50,6 +52,8 @@ import static com.google.common.collect.Maps.newHashMap;
*/ */
public class ValuesSourceParser<VS extends ValuesSource> { public class ValuesSourceParser<VS extends ValuesSource> {
static final ParseField TIME_ZONE = new ParseField("time_zone");
public static Builder any(String aggName, InternalAggregation.Type aggType, SearchContext context) { public static Builder any(String aggName, InternalAggregation.Type aggType, SearchContext context) {
return new Builder<>(aggName, aggType, context, ValuesSource.class); return new Builder<>(aggName, aggType, context, ValuesSource.class);
} }
@ -66,14 +70,19 @@ public class ValuesSourceParser<VS extends ValuesSource> {
return new Builder<>(aggName, aggType, context, ValuesSource.GeoPoint.class).targetValueType(ValueType.GEOPOINT).scriptable(false); return new Builder<>(aggName, aggType, context, ValuesSource.GeoPoint.class).targetValueType(ValueType.GEOPOINT).scriptable(false);
} }
private static class Input { public static class Input {
String field = null; private String field = null;
Script script = null; private Script script = null;
@Deprecated @Deprecated
Map<String, Object> params = null; // TODO Remove in 3.0 private Map<String, Object> params = null; // TODO Remove in 3.0
ValueType valueType = null; private ValueType valueType = null;
String format = null; private String format = null;
Object missing = null; private Object missing = null;
private DateTimeZone timezone = DateTimeZone.UTC;
public DateTimeZone timezone() {
return this.timezone;
}
} }
private final String aggName; private final String aggName;
@ -83,6 +92,7 @@ public class ValuesSourceParser<VS extends ValuesSource> {
private boolean scriptable = true; private boolean scriptable = true;
private boolean formattable = false; private boolean formattable = false;
private boolean timezoneAware = false;
private ValueType targetValueType = null; private ValueType targetValueType = null;
private ScriptParameterParser scriptParameterParser = new ScriptParameterParser(); private ScriptParameterParser scriptParameterParser = new ScriptParameterParser();
@ -105,6 +115,8 @@ public class ValuesSourceParser<VS extends ValuesSource> {
input.field = parser.text(); input.field = parser.text();
} else if (formattable && "format".equals(currentFieldName)) { } else if (formattable && "format".equals(currentFieldName)) {
input.format = parser.text(); input.format = parser.text();
} else if (timezoneAware && context.parseFieldMatcher().match(currentFieldName, TIME_ZONE)) {
input.timezone = DateTimeZone.forID(parser.text());
} else if (scriptable) { } else if (scriptable) {
if ("value_type".equals(currentFieldName) || "valueType".equals(currentFieldName)) { if ("value_type".equals(currentFieldName) || "valueType".equals(currentFieldName)) {
input.valueType = ValueType.resolveForScript(parser.text()); input.valueType = ValueType.resolveForScript(parser.text());
@ -123,6 +135,14 @@ public class ValuesSourceParser<VS extends ValuesSource> {
} }
return true; return true;
} }
if (token == XContentParser.Token.VALUE_NUMBER) {
if (timezoneAware && context.parseFieldMatcher().match(currentFieldName, TIME_ZONE)) {
input.timezone = DateTimeZone.forOffsetHours(parser.intValue());
} else {
return false;
}
return true;
}
if (scriptable && token == XContentParser.Token.START_OBJECT) { if (scriptable && token == XContentParser.Token.START_OBJECT) {
if (context.parseFieldMatcher().match(currentFieldName, ScriptField.SCRIPT)) { if (context.parseFieldMatcher().match(currentFieldName, ScriptField.SCRIPT)) {
input.script = Script.parse(parser, context.parseFieldMatcher()); input.script = Script.parse(parser, context.parseFieldMatcher());
@ -203,7 +223,7 @@ public class ValuesSourceParser<VS extends ValuesSource> {
config.fieldContext = new FieldContext(input.field, indexFieldData, fieldType); config.fieldContext = new FieldContext(input.field, indexFieldData, fieldType);
config.missing = input.missing; config.missing = input.missing;
config.script = createScript(); config.script = createScript();
config.format = resolveFormat(input.format, fieldType); config.format = resolveFormat(input.format, input.timezone, fieldType);
return config; return config;
} }
@ -222,9 +242,9 @@ public class ValuesSourceParser<VS extends ValuesSource> {
return valueFormat; return valueFormat;
} }
private static ValueFormat resolveFormat(@Nullable String format, MappedFieldType fieldType) { private static ValueFormat resolveFormat(@Nullable String format, @Nullable DateTimeZone timezone, MappedFieldType fieldType) {
if (fieldType instanceof DateFieldMapper.DateFieldType) { if (fieldType instanceof DateFieldMapper.DateFieldType) {
return format != null ? ValueFormat.DateTime.format(format) : ValueFormat.DateTime.mapper((DateFieldMapper.DateFieldType) fieldType); return format != null ? ValueFormat.DateTime.format(format, timezone) : ValueFormat.DateTime.mapper((DateFieldMapper.DateFieldType) fieldType, timezone);
} }
if (fieldType instanceof IpFieldMapper.IpFieldType) { if (fieldType instanceof IpFieldMapper.IpFieldType) {
return ValueFormat.IPv4; return ValueFormat.IPv4;
@ -238,6 +258,10 @@ public class ValuesSourceParser<VS extends ValuesSource> {
return ValueFormat.RAW; return ValueFormat.RAW;
} }
public Input input() {
return this.input;
}
public static class Builder<VS extends ValuesSource> { public static class Builder<VS extends ValuesSource> {
private final ValuesSourceParser<VS> parser; private final ValuesSourceParser<VS> parser;
@ -256,6 +280,11 @@ public class ValuesSourceParser<VS extends ValuesSource> {
return this; return this;
} }
public Builder<VS> timezoneAware(boolean timezoneAware) {
parser.timezoneAware = timezoneAware;
return this;
}
public Builder<VS> targetValueType(ValueType valueType) { public Builder<VS> targetValueType(ValueType valueType) {
parser.targetValueType = valueType; parser.targetValueType = valueType;
return this; return this;

View File

@ -20,6 +20,7 @@
package org.elasticsearch.search.aggregations.support.format; package org.elasticsearch.search.aggregations.support.format;
import org.elasticsearch.index.mapper.core.DateFieldMapper; import org.elasticsearch.index.mapper.core.DateFieldMapper;
import org.joda.time.DateTimeZone;
/** /**
* *
@ -67,12 +68,12 @@ public class ValueFormat {
public static final DateTime DEFAULT = new DateTime(DateFieldMapper.Defaults.DATE_TIME_FORMATTER.format(), ValueFormatter.DateTime.DEFAULT, ValueParser.DateMath.DEFAULT); public static final DateTime DEFAULT = new DateTime(DateFieldMapper.Defaults.DATE_TIME_FORMATTER.format(), ValueFormatter.DateTime.DEFAULT, ValueParser.DateMath.DEFAULT);
public static DateTime format(String format) { public static DateTime format(String format, DateTimeZone timezone) {
return new DateTime(format, new ValueFormatter.DateTime(format), new ValueParser.DateMath(format)); return new DateTime(format, new ValueFormatter.DateTime(format, timezone), new ValueParser.DateMath(format));
} }
public static DateTime mapper(DateFieldMapper.DateFieldType fieldType) { public static DateTime mapper(DateFieldMapper.DateFieldType fieldType, DateTimeZone timezone) {
return new DateTime(fieldType.dateTimeFormatter().format(), ValueFormatter.DateTime.mapper(fieldType), ValueParser.DateMath.mapper(fieldType)); return new DateTime(fieldType.dateTimeFormatter().format(), ValueFormatter.DateTime.mapper(fieldType, timezone), ValueParser.DateMath.mapper(fieldType));
} }
public DateTime(String pattern, ValueFormatter formatter, ValueParser parser) { public DateTime(String pattern, ValueFormatter formatter, ValueParser parser) {
@ -81,7 +82,7 @@ public class ValueFormat {
@Override @Override
public DateTime create(String pattern) { public DateTime create(String pattern) {
return format(pattern); return format(pattern, DateTimeZone.UTC);
} }
} }

View File

@ -33,7 +33,6 @@ import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone;
/** /**
* A strategy for formatting time represented as millis long value to string * A strategy for formatting time represented as millis long value to string
@ -61,7 +60,6 @@ public interface ValueFormatter extends Streamable {
String format(long value); String format(long value);
/** /**
* The
* @param value double The double value to format. * @param value double The double value to format.
* @return The formatted value as string * @return The formatted value as string
*/ */
@ -104,8 +102,8 @@ public interface ValueFormatter extends Streamable {
public static final ValueFormatter DEFAULT = new ValueFormatter.DateTime(DateFieldMapper.Defaults.DATE_TIME_FORMATTER); public static final ValueFormatter DEFAULT = new ValueFormatter.DateTime(DateFieldMapper.Defaults.DATE_TIME_FORMATTER);
private DateTimeZone timeZone = DateTimeZone.UTC; private DateTimeZone timeZone = DateTimeZone.UTC;
public static DateTime mapper(DateFieldMapper.DateFieldType fieldType) { public static DateTime mapper(DateFieldMapper.DateFieldType fieldType, DateTimeZone timezone) {
return new DateTime(fieldType.dateTimeFormatter()); return new DateTime(fieldType.dateTimeFormatter(), timezone);
} }
static final byte ID = 2; static final byte ID = 2;
@ -122,15 +120,21 @@ public interface ValueFormatter extends Streamable {
this.formatter = formatter; this.formatter = formatter;
} }
public DateTime(String format, DateTimeZone timezone) {
this.formatter = Joda.forPattern(format);
this.timeZone = timezone != null ? timezone : DateTimeZone.UTC;
}
public DateTime(FormatDateTimeFormatter formatter, DateTimeZone timezone) {
this.formatter = formatter;
this.timeZone = timezone != null ? timezone : DateTimeZone.UTC;
}
@Override @Override
public String format(long time) { public String format(long time) {
return formatter.printer().withZone(timeZone).print(time); return formatter.printer().withZone(timeZone).print(time);
} }
public void setTimeZone(DateTimeZone timeZone) {
this.timeZone = timeZone;
}
@Override @Override
public String format(double value) { public String format(double value) {
return format((long) value); return format((long) value);
@ -264,7 +268,7 @@ public interface ValueFormatter extends Streamable {
} }
} }
static class BooleanFormatter implements ValueFormatter { static class BooleanFormatter implements ValueFormatter {
static final byte ID = 10; static final byte ID = 10;