Elasticsearch 时区问题 彻底搞懂

2024-09-05 14:58:51 浏览数 (2)

概述

  • es中date类型字段, 底层写入转换规则:
    • 如果写入的时间字段没有时区偏移量标识,elasticsearch 就会默认它为UTC时间,即0时区时间,并且转为(epoch time millisecond)毫秒值保存
  • es中的date类型字段有映射属性format
    • format 用来指定该字段时间日期的格式
    • 未指定时默认为"format": "strict_date_optional_time||epoch_millis", 其中strict_date_optional_time格式对应的java中的时间日期格式为: yyyy-MM-dd'T'HH:mm:ss.SSSZyyyy-MM-dd。这个也是最常见的时间日期格式, 其中时区偏移量标识为Z
    • Z在某些日期格式下, 在具体的某一日期值后面出现时表示该时间为格林威治时,就是本初子午线所在地的时间
      • 本初子午线, 即0度经线,亦称格林威治子午线或格林尼治子午线
  • 所以写入es的时候需要带上时区偏移量, 以这种形式写入, 例如:
代码语言:txt复制
"time":"2024-09-04T12:03:10.158 0800"
  • 在kibana的discover中去查看数据时 , kibana会根据浏览器时区给创时间字段再加上时区偏移量的值
  • 案例
    • 比如我有这样一条记录, 可以看到这个文档中时间字段值为"@timestamp" : "2024-08-02T11:38:53.953Z", 这里Z就是指定的0时区
  • 在discover界面去过滤出这条记录, kibana就会给时间字段加8h如下
  • elasticsearch由java语言编写,里面使用的java中的时间日期格式类如下 DateTimeFormatter (Java Platform SE 8 ) (oracle.com)
    • 所有字母“A”到“Z”和“a”到“z”都保留为模式字母。定义了以下模式字母
  • 其中关于时区的有以下几个字母

不同字母表示时区的用法

以下列举了几种不同字母表示时区的用法, 演示为主, 代码执行时最好将案例时间2024-05-18换成您这边执行的当天日期,这样比较容易在discover中去查看, 如果您这边用默认format, 直接看总结就行

时区用大写V表示

  • 时区可以是zone-id,例如America/Los_Angeles,Asia/Shanghai等,也可以是Z以及 00:00的时区偏移量的形式
代码语言:txt复制
# 时区用V表示时,需要用两个大V,我这里时区用|隔开下,原版打算用[]包裹,但是[]应该也是保留内容
DELETE date_format_time_zone_big_v_test
PUT date_format_time_zone_big_v_test
{
  "mappings": {
    "properties": {
      "@timestamp":{
        "type": "date",
        "format": "yyyy-MM-dd'T'HH:mm:ss|VV"
      }
    }
  }
}

PUT date_format_time_zone_big_v_test/_bulk?refresh
{"create":{}}
{"@timestamp": "2024-05-18T16:00:00|Z","message": "UTC时间16点整"}
{"create":{}}
{"@timestamp": "2024-05-19T00:00:00| 08:00","message": "东八区时间0点整"}
{"create":{}}
{"@timestamp": "2024-05-19T00:00:00|Asia/Shanghai","message": "上海时区0点整"}
  • 有哪些可用的zone-id,可以在这里找到 https://blog.csdn.net/weixin_34392435/article/details/86028719 https://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html 最好可以直接到源码中去找, 我这里条件有限,仅部分演示
  • 在discover中创建索引模式, 可以看到表示的是同一个时间

时区用小写z表示

  • 时区用小写z表示时
  • 时区需要写zone-name
  • 下面整理了部分zone-name到对应zone-id的对应关系 https://24time.hnymr.com/abbr
  • UTC是标准时间参照,像GMT(格林威治时间)、ET(美国东部时间)、PST(太平洋时间)、CST(北京时间)等等都是具体的时区时间。
  • GMT能和UTC直接转换,仅仅是因为碰巧GMT是0时区时间,数值上刚好和UTC是相等的(不需要精确到秒的情况下,二者可以视为相等),看起来一样,但是概念含义上请务必区分开来哈。
  • 测试如下
代码语言:txt复制
# 时区用z表示即zone-name, 与具体时间用|隔开
DELETE date_format_time_zone_small_z_test
PUT date_format_time_zone_small_z_test
{
  "mappings": {
    "properties": {
      "@timestamp":{
        "type": "date",
        "format": "yyyy-MM-dd'T'HH:mm:ss|z"
      }
    }
  }
}
# UTC和GMT是同一个时区,AWST是澳大利亚时区,因为CST不支持中国标准时的缩写也是古巴或者中部标准时的缩写,所以这边用其他东8区的时区来代替
PUT date_format_time_zone_small_z_test/_bulk?refresh
{"create":{}}
{"@timestamp": "2024-05-20T16:00:00|UTC","timestamp": "2024-05-20T16:00:00","message": "UTC时间16点整"}
{"create":{}}
{"@timestamp": "2024-05-20T16:00:00|GMT","timestamp": "2024-05-20T16:00:00","message": "GMT时间16点整"}
{"create":{}}
{"@timestamp": "2024-05-21T00:00:00|AWST","timestamp": "2024-05-21T00:00:00","message": "东八区/澳大利亚时区0点整"}
  • 到discover中可以看到,是同一时间点的

时区用大写O表示

  • localized zone-offset = zone-name offset
    • 例如: GMT 8; GMT 08:00; UTC-08:00;
    • 但是这里测试发现只有GMT可以用,其他时区缩写都解析不了
  • offset格式为 H或者 H:MM
  • 测试如下
代码语言:txt复制
DELETE date_format_time_zone_big_o_test
PUT date_format_time_zone_big_o_test
{
  "mappings": {
    "properties": {
      "@timestamp":{
        "type": "date",
        "format": "yyyy-MM-dd'T'HH:mm:ss|O"
      },
      "#timestamp":{
        "type": "keyword"
      }
    }
  }
}

# 这里测试了, 好像只有GMT可以用, 其他都解析不出来
PUT date_format_time_zone_big_o_test/_bulk?refresh
{"create":{}}
{"@timestamp": "2024-05-27T16:00:00|GMT 8","#timestamp": "2024-05-27T16:00:00|GMT 8","message": "比GMT时区快8小时的时区的16点,即北京时间16点"}
{"create":{}}
{"@timestamp": "2024-05-27T16:00:00|GMT 08:00","#timestamp": "2024-05-27T16:00:00|GMT 08:00","message": "比GMT时区快8小时的时区的16点,即北京时间16点"}
{"create":{}}
{"@timestamp": "2024-05-27T08:00:00|GMT 0","#timestamp": "2024-05-27T08:00:00|GMT 0","message": "GMT时区的8点"}
  • 到discover中可以看到,是同一时间点的

时区用大写X表示

  • zone-offset 'Z' for zero
  • 即相较于0时区的时间偏移量
  • 示例值:Z; -08; -0830; -08:30; -083015; -08:30:15;
  • 测试如下, 测试发现好像部分格式还是不支持不支持带冒号:以及最多支持4位数字
代码语言:txt复制
DELETE date_format_time_zone_big_x_test
PUT date_format_time_zone_big_x_test
{
  "mappings": {
    "properties": {
      "@timestamp": {
        "type": "date",
        "format": "yyyy-MM-dd'T'HH:mm:ss|X"
      },
      "#timestamp": {
        "type": "keyword"
      }
    }
  }
}

# 案例上支持很多形式, 但是实测只有这几种可以用
PUT date_format_time_zone_big_x_test/_bulk?refresh
{"create":{}}
{"@timestamp": "2024-05-27T08:00:00|Z","#timestamp": "2024-05-27T08:00:00|Z","message": "0时区8点"}
{"create":{}}
{"@timestamp": "2024-05-27T16:00:00| 08","#timestamp": "2024-05-27T16:00:00| 08","message": "相较于0时区多8小时的所在地区的16点,即上海时间16点"}
{"create":{}}
{"@timestamp": "2024-05-27T16:00:00| 0800","#timestamp": "2024-05-27T16:00:00| 0800","message": "相较于0时区多8小时的所在地区的16点,即上海时间16点"}
  • 到discover中可以看到,是同一时间点的

时区用小写x表示

  • 相较于0时区的偏移量
  • 0000; -08; -0830; -08:30; -083015; -08:30:15;
  • 测试如下, 实际还是最多只支持4位时区偏移量
代码语言:txt复制
DELETE date_format_time_zone_small_x_test
PUT date_format_time_zone_small_x_test
{
  "mappings": {
    "properties": {
      "@timestamp": {
        "type": "date",
        "format": "yyyy-MM-dd'T'HH:mm:ss|x"
      },
      "#timestamp": {
        "type": "keyword"
      }
    }
  }
}

PUT date_format_time_zone_small_x_test/_bulk?refresh
{"create":{}}
{"@timestamp": "2024-05-27T16:00:00| 08","#timestamp": "2024-05-27T16:00:00| 08","message": "相较于0时区多8小时的所在地区的16点,即上海时间16点"}
{"create":{}}
{"@timestamp": "2024-05-27T16:00:00| 0800","#timestamp": "2024-05-27T16:00:00| 0800","message": "相较于0时区多8小时的所在地区的16点,即上海时间16点"}
{"create":{}}
{"@timestamp": "2024-05-27T16:00:00| 08:00","#timestamp": "2024-05-27T16:00:00| 08:00","message": "相较于0时区多8小时的所在地区的16点,即上海时间16点"}
  • 到discover中可以看到,是同一时间点的

时区用大写Z表示

  • 相较于0时区的偏移量
  • 0000; -0800; -08:00;
  • 相比x只支持4位偏移量,2位8位都不支持,冒号:也不支持
  • 测试如下
代码语言:txt复制
DELETE date_format_time_zone_big_z_test
PUT date_format_time_zone_big_z_test
{
  "mappings": {
    "properties": {
      "@timestamp": {
        "type": "date",
        "format": "yyyy-MM-dd'T'HH:mm:ss|Z"
      },
      "#timestamp": {
        "type": "keyword"
      }
    }
  }
}

PUT date_format_time_zone_big_z_test/_bulk?refresh
{"create":{}}
{"@timestamp": "2024-05-27T08:00:00| 0000","#timestamp": "2024-05-27T16:00:00| 0000","message": "0时区8点"}
{"create":{}}
{"@timestamp": "2024-05-27T16:00:00| 0800","#timestamp": "2024-05-27T16:00:00| 0800","message": "相较于0时区多8小时的所在地区的16点,即上海时间16点"}
  • 到discover中可以看到,是同一时间点的

总结

  • 不标注时区就默认0时区
  • 标注时区,最终也会转换为0时区的毫秒值存储
  • date类型默认format为strict_date_optional_time||epoch_millis
  • strict_date_optional_time为日期必须至少包含年份和时间(由 T 分隔)的日期字符串。示例: yyyy-MM-dd'T'HH:mm:ss.SSSZyyyy-MM-dd
代码语言:txt复制
DELETE date_format_strict_date_optional_time_test
PUT date_format_strict_date_optional_time_test
{
  "mappings": {
    "properties": {
      "@timestamp": {
        "type": "date",
        "format": "strict_date_optional_time"
      },
      "#timestamp": {
        "type": "date",
        "format": "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
      }
    }
  }
}

PUT date_format_strict_date_optional_time_test/_bulk?refresh
{"create":{}}
{"@timestamp": "2024-05-27T08:00:00.000 0000","#timestamp": "2024-05-27T08:00:00.00 0000","message": "0时区8点"}

其他说明:

date类型有其他字段映射参数locale, 这个表示的是对应地区的语言, 比如将星期六理解为Saturday, 用来解析时间字段文本的,和时区无关

文章由牛朝阳本人撰写, 如转载或改编请标注来源

文档内容勘误, 请评论区留言, 欢迎大佬们协助改进指正

0 人点赞