Aggregations: Format bucket key_as_string in `date_histogram` according to `time_zone`

Change bucket key_as_string to reflect `time_zone` parameter. Currently `time_zone`
shifts bucket boundaries to other time zone, but keys are displayed in UTC, so e.g.
daily buckets in "+01:00" time zone have key_as_string like "2014-01-01T23:00:00Z". With this
change the default is to format this dates according to the local time zone, so the
above bucket key would be "2014-01-02T00:00:00+01:00".

Closes #9710
Closes #9744
This commit is contained in:
Christoph Büscher 2015-02-17 17:23:10 +01:00
parent 4125e406c7
commit 6d41601b4e
3 changed files with 42 additions and 20 deletions

View File

@ -31,7 +31,9 @@ import org.elasticsearch.search.SearchParseException;
import org.elasticsearch.search.aggregations.Aggregator;
import org.elasticsearch.search.aggregations.AggregatorFactory;
import org.elasticsearch.search.aggregations.support.ValueType;
import org.elasticsearch.search.aggregations.support.ValuesSourceConfig;
import org.elasticsearch.search.aggregations.support.ValuesSourceParser;
import org.elasticsearch.search.aggregations.support.format.ValueFormatter.DateTime;
import org.elasticsearch.search.internal.SearchContext;
import org.joda.time.DateTimeZone;
@ -185,7 +187,11 @@ public class DateHistogramParser implements Aggregator.Parser {
.timeZone(timeZone)
.offset(offset).build();
return new HistogramAggregator.Factory(aggregationName, vsParser.config(), rounding, order, keyed, minDocCount, extendedBounds,
ValuesSourceConfig config = vsParser.config();
if (config.formatter()!=null) {
((DateTime) config.formatter()).setTimeZone(timeZone);
}
return new HistogramAggregator.Factory(aggregationName, config, rounding, order, keyed, minDocCount, extendedBounds,
new InternalDateHistogram.Factory());
}

View File

@ -28,12 +28,14 @@ import org.elasticsearch.common.joda.FormatDateTimeFormatter;
import org.elasticsearch.common.joda.Joda;
import org.elasticsearch.index.mapper.core.DateFieldMapper;
import org.elasticsearch.index.mapper.ip.IpFieldMapper;
import org.joda.time.DateTimeZone;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.TimeZone;
/**
* A strategy for formatting time represented as millis long value to string
@ -101,6 +103,7 @@ public interface ValueFormatter extends Streamable {
public static class DateTime implements ValueFormatter {
public static final ValueFormatter DEFAULT = new ValueFormatter.DateTime(DateFieldMapper.Defaults.DATE_TIME_FORMATTER);
private DateTimeZone timeZone = DateTimeZone.UTC;
public static DateTime mapper(DateFieldMapper mapper) {
return new DateTime(mapper.dateTimeFormatter());
@ -122,9 +125,13 @@ public interface ValueFormatter extends Streamable {
@Override
public String format(long time) {
return formatter.printer().print(time);
return formatter.printer().withZone(timeZone).print(time);
}
public void setTimeZone(DateTimeZone timeZone) {
this.timeZone = timeZone;
}
public String format(double value) {
return format((long) value);
}
@ -137,11 +144,13 @@ public interface ValueFormatter extends Streamable {
@Override
public void readFrom(StreamInput in) throws IOException {
formatter = Joda.forPattern(in.readString());
timeZone = DateTimeZone.forID(in.readString());
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(formatter.format());
out.writeString(timeZone.getID());
}
}

View File

@ -35,6 +35,7 @@ import org.hamcrest.Matchers;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.tz.DateTimeZoneBuilder;
import org.junit.After;
import org.junit.Test;
@ -123,7 +124,11 @@ public class DateHistogramTests extends ElasticsearchIntegrationTest {
}
private static String getBucketKeyAsString(DateTime key) {
return Joda.forPattern(DateFieldMapper.Defaults.DATE_TIME_FORMATTER.format()).printer().print(key);
return getBucketKeyAsString(key, DateTimeZone.UTC);
}
private static String getBucketKeyAsString(DateTime key, DateTimeZone tz) {
return Joda.forPattern(DateFieldMapper.Defaults.DATE_TIME_FORMATTER.format()).printer().withZone(tz).print(key);
}
@Test
@ -167,7 +172,7 @@ public class DateHistogramTests extends ElasticsearchIntegrationTest {
SearchResponse response = client().prepareSearch("idx")
.addAggregation(dateHistogram("histo").field("date").interval(DateHistogramInterval.DAY).timeZone("+01:00")).execute()
.actionGet();
DateTimeZone tz = DateTimeZone.forID("+01:00");
assertSearchResponse(response);
Histogram histo = response.getAggregations().get("histo");
@ -179,43 +184,43 @@ public class DateHistogramTests extends ElasticsearchIntegrationTest {
DateTime key = new DateTime(2012, 1, 1, 23, 0, DateTimeZone.UTC);
Histogram.Bucket bucket = buckets.get(0);
assertThat(bucket, notNullValue());
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key)));
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key, tz)));
assertThat(((DateTime) bucket.getKey()), equalTo(key));
assertThat(bucket.getDocCount(), equalTo(1l));
key = new DateTime(2012, 2, 1, 23, 0, DateTimeZone.UTC);
bucket = buckets.get(1);
assertThat(bucket, notNullValue());
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key)));
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key, tz)));
assertThat(((DateTime) bucket.getKey()), equalTo(key));
assertThat(bucket.getDocCount(), equalTo(1l));
key = new DateTime(2012, 2, 14, 23, 0, DateTimeZone.UTC);
bucket = buckets.get(2);
assertThat(bucket, notNullValue());
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key)));
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key, tz)));
assertThat(((DateTime) bucket.getKey()), equalTo(key));
assertThat(bucket.getDocCount(), equalTo(1l));
key = new DateTime(2012, 3, 1, 23, 0, DateTimeZone.UTC);
bucket = buckets.get(3);
assertThat(bucket, notNullValue());
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key)));
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key, tz)));
assertThat(((DateTime) bucket.getKey()), equalTo(key));
assertThat(bucket.getDocCount(), equalTo(1l));
key = new DateTime(2012, 3, 14, 23, 0, DateTimeZone.UTC);
bucket = buckets.get(4);
assertThat(bucket, notNullValue());
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key)));
assertThat(((DateTime) bucket.getKey()), equalTo(key.withZone(DateTimeZone.UTC)));
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key, tz)));
assertThat(((DateTime) bucket.getKey()), equalTo(key));
assertThat(bucket.getDocCount(), equalTo(1l));
key = new DateTime(2012, 3, 22, 23, 0, DateTimeZone.UTC);
bucket = buckets.get(5);
assertThat(bucket, notNullValue());
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key)));
assertThat(((DateTime) bucket.getKey()), equalTo(key.withZone(DateTimeZone.UTC)));
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key, tz)));
assertThat(((DateTime) bucket.getKey()), equalTo(key));
assertThat(bucket.getDocCount(), equalTo(1l));
}
@ -1058,7 +1063,7 @@ public class DateHistogramTests extends ElasticsearchIntegrationTest {
.field("date")
.timeZone("-02:00")
.interval(DateHistogramInterval.DAY)
.format("yyyy-MM-dd:hh-mm-ss"))
.format("yyyy-MM-dd:HH-mm-ssZZ"))
.execute().actionGet();
assertThat(response.getHits().getTotalHits(), equalTo(5l));
@ -1069,12 +1074,12 @@ public class DateHistogramTests extends ElasticsearchIntegrationTest {
Histogram.Bucket bucket = buckets.get(0);
assertThat(bucket, notNullValue());
assertThat(bucket.getKeyAsString(), equalTo("2014-03-10:02-00-00"));
assertThat(bucket.getKeyAsString(), equalTo("2014-03-10:00-00-00-02:00"));
assertThat(bucket.getDocCount(), equalTo(2l));
bucket = buckets.get(1);
assertThat(bucket, notNullValue());
assertThat(bucket.getKeyAsString(), equalTo("2014-03-11:02-00-00"));
assertThat(bucket.getKeyAsString(), equalTo("2014-03-11:00-00-00-02:00"));
assertThat(bucket.getDocCount(), equalTo(3l));
}
@ -1233,6 +1238,8 @@ public class DateHistogramTests extends ElasticsearchIntegrationTest {
.execute().actionGet();
assertSearchResponse(response);
DateTimeZone tz = DateTimeZone.forID("+01:00");
Histogram histo = response.getAggregations().get("histo");
assertThat(histo, notNullValue());
@ -1243,21 +1250,21 @@ public class DateHistogramTests extends ElasticsearchIntegrationTest {
DateTime key = new DateTime(2011, 12, 31, 23, 0, DateTimeZone.UTC);
Histogram.Bucket bucket = buckets.get(0);
assertThat(bucket, notNullValue());
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key)));
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key, tz)));
assertThat(((DateTime) bucket.getKey()), equalTo(key));
assertThat(bucket.getDocCount(), equalTo(1l));
key = new DateTime(2012, 1, 31, 23, 0, DateTimeZone.UTC);
bucket = buckets.get(1);
assertThat(bucket, notNullValue());
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key)));
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key, tz)));
assertThat(((DateTime) bucket.getKey()), equalTo(key));
assertThat(bucket.getDocCount(), equalTo(2l));
key = new DateTime(2012, 2, 29, 23, 0, DateTimeZone.UTC);
bucket = buckets.get(2);
assertThat(bucket, notNullValue());
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key)));
assertThat(bucket.getKeyAsString(), equalTo(getBucketKeyAsString(key, tz)));
assertThat(((DateTime) bucket.getKey()), equalTo(key));
assertThat(bucket.getDocCount(), equalTo(3l));
}
@ -1273,7 +1280,7 @@ public class DateHistogramTests extends ElasticsearchIntegrationTest {
assertSearchResponse(response);
Histogram histo = response.getAggregations().get("histo");
assertThat(histo.getBuckets().size(), equalTo(1));
assertThat(histo.getBuckets().get(0).getKeyAsString(), equalTo("2013-12-31T22:00:00.000Z"));
assertThat(histo.getBuckets().get(0).getKeyAsString(), equalTo("2014-01-01T00:00:00.000+02:00"));
}
/**