本文档主要介绍使用函数进行日期时间处理的一些常见场景和最佳方案示例。

场景1:数据类型转换

LOG DSL语法中的日期时间处理主要涉及三种数据类型:Unix时间戳、日期时间字符串和日期时间对象。它们的相互转换方式如下图:
转化方式
子场景1:日期时间对象和Unix时间戳的相互转换。
  • 日期时间对象转为Unix时间戳。
    • dt_parsetimestamp智能转换函数,可以将日期时间对象或日期时间字符串转化为Unix时间戳。
    • dt_totimestamp只支持将日期时间对象转化为Unix时间戳。
  • Unix时间戳转为日期时间对象。
    • dt_parse智能转换函数,可以将Unix时间戳或日期时间字符串转化为日期时间对象。
    • dt_fromtimestamp只支持将Unix时间戳转化为日期时间对象。
子场景2:日期时间对象和日期时间字符串的相互转换。
  • 日期时间对象转为日期时间字符串。
    • dt_str智能转换函数,可以将日期时间对象、Unix时间戳和日期时间字符串转化为指定格式的日期时间字符串。
    • dt_strftime只支持将日期时间对象转化为日期时间字符串。
  • 日期时间字符串转为日期时间对象。
    • dt_parse智能转换函数,可以将日期时间字符串或Unix时间戳转化为日期时间对象。
    • dt_strptime只支持将日期时间字符串转化为日期时间对象。
子场景3:日期时间字符串和Unix时间戳的相互转换。
  • 日期时间字符串转为Unix时间戳。

    dt_parsetimestamp智能转换函数,可以将日期时间字符串或日期时间对象转化为Unix时间戳。

  • Unix时间戳转为日期时间字符串。
    • dt_str智能转换函数,可以将Unix时间戳、日期时间对象和日期时间字符串转化为指定格式的日期时间字符串。
    • dt_strftimestamp只支持将Unix时间戳转化为日期时间字符串。

子场景4:dt_parse等转换函数的应用场景。

上述的转化过程中,大多数转化都有两种方式,一种使用智能转换函数,另一种使用该转换的专用函数。以dt_parse为代表的智能转换函数可以接收Unix时间戳、日期时间对象以及日期时间字符串等不同类型的参数,实现智能转换。例如:
  • 原始日志1
    time1: 1562741899
    time2: 2019-07-10 06:58:19
  • LOG DSL编排
    e_set("time3", dt_parse(v("time1"), tz="Asia/Shanghai"))
    e_set("time4", dt_parse(v("time2"), tz="Asia/Shanghai")
  • 加工结果
    time1: 1562741899
    time2: 2019-07-10 06:58:19
    time3: 2019-07-10 06:58:19+08:00
    time4: 2019-07-10 06:58:19+08:00
但是有些场景,智能转换函数无法满足用户的需求。如对于用户自定义的特殊日期格式,dt_parse等智能转换函数无法自动解析日志,此时需要使用dt_strptime来进行指定格式的解析。
  • 原始日志2
    time1: 2019-07-10 06:58:19
    time2: 2019/07/10 06-58-19
  • LOG DSL编排
    e_set("time3", dt_parsetimestamp(v("time1")))
    e_set("time4", dt_parsetimestamp(dt_strptime(v("time2"), fmt="%Y/%m/%d %H-%M-%S")))
  • 加工结果
    time1: 2019-07-10 06:58:19
    time2: 2019/07/10 06-58-19
    time3: 1562741899
    time4: 1562741899
说明
  • 智能转换函数可以对不同类型的参数进行自动转换,因此默认推荐使用智能转换函数。
  • 对一些特殊的用户自定义日期格式,智能转换函数无法自动解析,需要使用dt_strptime来进行解析。

场景2: 时区的概念

  • 子场景1: 时间字符串的含义。
    LOG DSL语法中的日期时间字符串主要分为两种形式:
    • 带有时区信息的日期时间字符串,如2019-06-02 18:41:26+08:00
    • 不带时区信息的日期时间字符串,如2019-06-02 10:41:26
    带有时区信息的日期时间字符串通过在日期时间后添加额外的时差信息来表达时区:
    • 2019-06-02 18:41:26+08:00表示该时间是东8区时区下的2019-06-02 18:41:26
    • 2019-06-02 18:41:26-07:00表示该时间是西7区时区下的2019-06-02 18:41:26
    对不带时区信息的日期时间字符串,时区信息不同,该时间字符串表达的时间也不同。如果没有传递时区参数,默认为UTC时区下的时间。以2019-06-02 18:41:26为例:
    • 默认情况下是UTC时区下的时间,即该时间字符串表达的时间是UTC时区下的时间,等同于2019-06-02 18:41:26+00:00
    • 如果是东8区,该时间字符串表达的时间是东8区下的时间,等同于2019-06-02 18:41:26+08:00
  • 子场景2: 将日期时间转化为Unix时间戳。
    • 不带时区信息

      对于不带时区信息的日期时间字符串如2019-06-02 18:41:26,将日期时间转化为Unix时间戳,需要指定该日期时间的时区,不同的时区转化得到的Unix时间戳的值不一样。

      原始日志
      { 'time': '2019-06-02 18:41:26'}
      LOG DSL编排
      e_set("Shanghai_timestamp", dt_parsetimestamp(v("time"), tz="Asia/Shanghai"))
      e_set("Los_Angeles_timestamp", dt_parsetimestamp(v("time"), tz="America/Los_Angeles"))
      e_set("UTC_timestamp", dt_parsetimestamp(v("time")))
      • tz="Asia/Shanghai"表示time字段表示的时间是上海所在时区对应的时间。
      • 如果不指定时区,默认将给定日期时间当做UTC时区下的日期时间。
      • 时区参数tz=时区字符串中所有可选时区字符串请参见时区列表
      加工结果
      {
        'Shanghai_timestamp': '1559472086',
        'Los_Angeles_timestamp': '1559526086',
        'UTC_timestamp': '1559500886'
      }
    • 带有时区信息

      对带时区信息的日期时间字符串如2019-06-02 18:41:26+08:00,则无须指定时区参数。

      原始日志
      { 'China_time': '2019-06-02 18:41:26+08:00',
        'America_time': '2019-06-02 3:41:26-07:00',
        'UTC_time': '2019-06-02 10:41:26+00:00'
      }
      LOG DSL编排
      e_set("timestamp1", dt_parsetimestamp(v("China_time")))
      e_set("timestamp2", dt_parsetimestamp(v("America_time")))
      e_set("timestamp3", dt_parsetimestamp(v("UTC_time")))
      加工结果
      {
        "timestamp1": "1559472086",
        "timestamp2": "1559472086",
        "timestamp3": "1559472086"
      }
  • 子场景3: 不同时区下的日期时间相互转换。
    • 不带时区信息

      对于不带时区信息的日期时间字符串2019-06-02 18:41:26,可以通过Unix时间戳,实现不同时区下的日期时间的相互转换。

      加工需求:将洛杉矶时区下的日期时间转换为上海时区下的日期时间。

      原始日志
      #已知time字段的值的时间是洛杉矶时间
      {'time': '2019-06-04 2:41:26'}
      LOG DSL编排
      e_set("timestamp", dt_parsetimestamp(v("time"), tz="America/Los_Angeles"))
      e_set("Shanghai_time", dt_parse(v("timestamp"), tz="Asia/Shanghai"))
      加工结果
      {
        'time': '2019-06-04 2:41:26',
        'Shanghai_time': '2019-06-04 17:41:26+08:00'
      }
    • 带有时区信息

      对带有时区信息的日期时间字符串2019-06-02 18:41:26+08:00,可直接通过dt_astimezone实现不同时区下的日期时间的相互转换。

      原始日志
      {'time': '2019-06-04 2:41:26+08:00'}
      LOG DSL编排
      e_set("new_time", dt_astimezone(v("time"), tz="America/Los_Angeles"))
      加工结果
      {
        'time': '2019-06-04 2:41:26+08:00',
        'new_time': '2019-06-03 11:41:26-07:00'
      }

场景3: 日期时间和Unix时间戳的应用场景

LOG DSL编排中涉及到的日期时间主要有两种形式:Unix时间戳和日期时间的字符串或对象。

日期时间:主要是为了便于展示以及提升用户可读性等。

Unix时间戳:从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,它的主要应用场景有:
  • 表示系统时间。
    日志事件中表示日志产生时间的元字段__time__,表示日志接收时间的字段__receieve_time__等,这些字段的值都使用Unix时间戳来表示对应的系统时间。
    __source__:  1.2.3.4
    __tag__:__receive_time__:  1562741899
    __topic__: 
    __time__: 1562731122
  • 时间相关的计算。
    Unix时间戳是从1970年1月1日开始所经过的秒数,因此在很多场景下便于直接进行日期时间相关的计算。
    • 原始日志
      time1: 1562741899
      time2: 1562731122
    • LOG DSL编排
      e_set("time_diff", op_sub(v("time1"), v("time2")))
    • 加工结果
      time1: 1562741899
      time2: 1562731122
      time_diff: 10777

场景4:日期偏移

dt_add函数支持在特定时间粒度上修改(增加、减少、覆盖)日期时间的值。dt_add的参数如下:
dt_add(字段名, dt1=None, dt2=None, year(s)=None, month(s)=None, day(s)=None, hour(s)=None, minute(s)=None, second(s)=None, microsecond(s)=None, weeks(s)=None, weekday=None)
  • 子场景1:year(s)month(s)的含义。

    year(s)month(s)day(s)等参数的后面都带有s,表示这些参数可以有两种形式,即yearyearsmonthmonths等。

    yearyears为例,如果参数传递的是year,表示在年份粒度上覆盖为year参数的值;如果传递的是years,表示在年份粒度上增加years参数的值。

    • 原始日志
      "time1": "2019-06-04 2:41:26"
    • LOG DSL编排1
      e_set("time2", dt_add(v("time1"), year=2018))
    • 加工结果1
      "time1": "2019-06-04 2:41:26"
      "time2": "2018-06-04 02:41:26"
    • LOG DSL编排2
      e_set("time2", dt_add(v("time1"), years=2018))
    • 加工结果2
      "time1": "2019-06-04 2:41:26"
      "time2": "4037-06-04 02:41:26"
  • 子场景2:weekday参数用法。
    weekday参数通常和dt_MOdt_TU等参数一起使用,表示特定星期几的偏移。具体请参见dt_MO
    • 原始日志
      #2019-06-04是周二
      
      "time1": "2019-06-04 2:41:26"
    • LOG DSL编排
      #time1的下一个星期一对应的日期
      e_set("nex_Monday", dt_add(v("time1"), weekday=dt_MO(1)))
      
      #time1的上一个星期二对应的日期
      e_set("previous_Tuesday", dt_add(v("time1"), weekday=dt_TU(op_neg(1))))
      
      #time1的下下一个星期六对应的日期
      e_set("nex_next_Saturday", dt_add(v("time1"), weekday=dt_SA(2)))
      
      #time1的上上一个星期日对应的日期
      e_set("previous_previous_Sunday", dt_add(v("time1"), weekday=dt_SU(op_neg(2))))
    • 加工结果
      "time1": "2019-06-04 2:41:26",
      "next_Monday": "2019-06-10 02:41:26",
      "previous_Tuesday": "2019-06-04 2:41:26",
      "next_next_Saturday": "2019-06-15 02:41:26",
      "previous_previous_Sunday": "2019-05-26 02:41:26"
    说明 如果time1对应的日期是周二,那么它的上一个周二和下一个周二都是time1本身。