Elasticsearch Date Histogram 时区问题

前言:项目中使用 Elasticsearch (以下简称 ES)获取数据,在用 Date Histogram 做聚合查询涉及到时区问题.特此记录一下.

ES中对时间类型字段,是统一采用 UTC 时间记录,详见此链接关于UTC和GMT时间辨析

整个地球分为二十四时区,每个时区都有自己的本地时间。在国际无线电通信场合,为了统一起见,使用一个统一的时间,称为通用协调时(UTC,Universal Time Coordinated)。
UTC与GMT(格林尼治平均时, Greenwich MeanTime)一样,都与英国伦敦的本地时相同。
本地时间转化为UTC,公式: UTC + 时区差 = 本地时间


本例中用的是 ES 版本为 2.4.4,
开发中采用 ES JavaAPI 方式操作.点此查看官方2.4 Java API


本测试案例中有一个索引下 字段为 @timestamp 存储的值的格式为 2017-05-24T12:56:28.000+08:00 .说明该条记录时间是 2017年5月24号12点56分(28秒),这个地方的本地时领先UTC八个小时.
对我们而言就是本地这个时间点.

本例采用以下代码进行聚合测试

// 聚合查询配置
AggregationBuilder dateAggs =
                AggregationBuilders
                        .dateHistogram("dateAggs") // 别名
                        .field("@timestamp")  // 指定聚合哪个时间字段
                        .interval(DateHistogramInterval.DAY) // 按天聚合
                        .minDocCount(0L) // 默认为0
                        .order(Histogram.Order.KEY_ASC) // 按时间正序
                        .timeZone("+08:00") // 指定时区
                        .subAggregation( // 子聚合
                                AggregationBuilders
                                        .sum("sumAggs")
                                        .field("tx_count")
                        );

查询 2017-05-16 00:00:00 到 2017-05-21 00:00:00 每天的交易总数

  1. 如果不加 .timeZone("+08:00") // 指定时区,结果为

    dateKey count
    2017-05-15T00:00:00.000Z XXX
    2017-05-16T00:00:00.000Z XXX
    2017-05-17T00:00:00.000Z XXX
    2017-05-18T00:00:00.000Z XXX
    2017-05-19T00:00:00.000Z XXX
    2017-05-20T00:00:00.000Z XXX

    这个结果集明显就是有误的,这个会与之后的对比

  2. .timeZone("+08:00") // 指定时区,结果为

    dateKey count
    2017-05-15T16:00:00.000Z SSS
    2017-05-16T16:00:00.000Z SSS
    2017-05-17T16:00:00.000Z SSS
    2017-05-18T16:00:00.000Z SSS
    2017-05-19T16:00:00.000Z SSS

    注意: 此时看到结果集的时间字段有 16时,说明 ES 按时间聚合之后 返回的时间值是按UTC计算 ,也就是比本地时间少 8 个小时,最后在遍历聚合结果的时候再对时间字段加上8个小时即可.最后会附上处理 UTC 格式的方法.

ES 中提供了对返回的时间进行时区处理,步骤如下:

SearchResponse response = ...;
DateHistogram timeHistogram = response.getAggregations().get("dateAggs");
// For each entry
for (DateHistogram.Bucket timeHistogramEntry : timeHistogram.getBuckets()) {
    DateTime keyAsDate = timeHistogramEntry.getKeyAsDate();
    // 将返回的时间转化为本地时间
    Date dateKey = keyAsDate.toDateTime(DateTimeZone.forTimeZone(TimeZone.getTimeZone("GMT+8")).toDate();
    String dateKey = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(dateKey);
}

总结:
ES 做时间聚合查询的时候,是按 UTC 去查,并且最后返回的结果也为 UTC,所以我们在用Java API查询的时候指定 timeZone 会是比较好的解决方式.最后对时间结果格式化即可.
而Kibana上查询并不存在此问题,当然,肯定也是作了处理的.

另附,格式UTC时间方法:

时间格式: yyyy-MM-dd'T'HH:mm:ss.SSSXXX
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
String str = dateFormat.format(date);
System.out.println(str); // 2017-05-16T10:10:36.602+08:00

String dateStr = "2017-05-15T16:00:00.000Z";
Date newDate = dateFormat.parse(dateStr);
System.out.println(newDate); // Tue May 16 00:00:00 GMT+08:00 2017

最后,希望通过这篇博文能给你带来帮助,感谢你的阅读.

你可能感兴趣的:(elasticsearch,elasticsearch)