【问题标题】:jq fromdate "does not match format "%Y-%m-%dT%H:%M:%SZ"jq fromdate "不匹配格式 "%Y-%m-%dT%H:%M:%SZ"
【发布时间】:2021-06-29 14:25:50
【问题描述】:

我在下面有一个示例 json 输出,LastAccessedDate 值是 AWS 在运行 CLI 命令时输出的值,因此我无法控制日期的格式。

{
  "MyList": [
    {
      "Name": "MyName1",
      "LastAccessedDate": "2021-06-29T02:00:00+02:00"
    }
  ]
    
}

当尝试运行 jq 命令以使用 fromdate 进行选择时,如下所示:

cat output.json | jq '.[] | .[] | select ( .LastAccessedDate | fromdate > "2021-01-01T02:00:00+02:00")'

然后我收到错误消息: jq: error (at <stdin>:8): date "2021-06-29T02:00:00+02:00" does not match format "%Y-%m-%dT%H:%M:%SZ"

有什么办法可以让我使用 jq 过滤输出吗? 甚至希望能解释一下可能出现的问题,以便我理解未来的用例。

【问题讨论】:

  • jq 尚不支持使用时区(本机)解析时间字符串。即带有 +/- 偏移量。你需要自己写
  • 顺便说一句,注意最好避免cat output.json | jq ...——而不是开始单独的/bin/cat执行并从附加到该程序输出的FIFO中读取jq,使用jq ... <output.json所以jq 可以直接从文件中读取,而不需要从从文件中读取的单独的可执行文件 的输出中读取。特别是对于 jq,在实践中并没有那么大的差异,但是还有其他标准工具的性能差异很大。
  • (例如,cat file | wc -c 是 O(n),因为它需要读取 file 的每个字节才能知道文件有多大;但 wc -c <file 是 O(1),因为无论文件大小,它都可以调用seek() 跳转到末尾,调用tell() 获取当前位置)。
  • strptime("%FT%T%z") 可能是一个选项,但它需要像 +0200 这样的时区,而不是 +02:00
  • 但是,假设时间戳在相同的时区和相同的格式,你可以按字典顺序比较它们。

标签: json jq timezone-offset timestamp-with-timezone


【解决方案1】:

一种方法是使用自定义函数去除时区部分并将日期字符串格式化为jq 可以解析的兼容格式。

请注意,这在您的参考字符串和源字符串位于相同时区偏移时有效。

jq --arg ref "2021-01-01T02:00:00+02:00" '
  def c(str): str | (split("+")[0] + "Z") | fromdate ; 
  .MyList | map(select( c(.LastAccessedDate) > c($ref) ))' json

来自 jq 手册

fromdate 内置函数解析日期时间字符串。目前fromdate 只支持ISO 8601 datetime字符串,未来会尝试解析更多格式的datetime字符串。

【讨论】:

  • 请注意,c 生成的时间戳与其输入不同。这很好,两个参数最初都在同一个时区,但否则会导致问题。
  • @chepner:是的,添加了一条注释
  • 手册的措辞也很不幸,因为Z 的要求意味着jq 仅解析有效ISO 8601 日期时间的子集
  • 1) 2021-01-01T02:00:00+02:00 是有效的 ISO 8601 时间戳。 (也是有效的 RFC 3339,它比 ISO 8601 限制得多。) 2) 您的解决方案仅在所有时间戳具有相同偏移量的情况下才有效,它可能围绕 DST 更改。
【解决方案2】:

这是一个 jq 函数,用于将具有时区偏移的某些 ISO 8601 时间戳转换为自纪元开始以来的秒数,从而便于比较具有不同偏移的时间戳。

# Convert a timestamp with a possibly empty timezone offset to seconds since the Epoch.
# Input should be a string of the form 
# yyyy-mm-ddThh:mm:ss or yyyy-mm-ddThh:mm:ss<OFFSET>
# where <OFFSET> is Z, or has the form `+hh:mm` or `-hh:mm`.
# If no timezone offset is explicitly given, it is taken to be Z.
def datetime_to_seconds:
  if test("[-+]")
  then capture("(?<datetime>^.*T[0-9:]+)(?<s>[-+])(?<hh>[0-9]+):?(?<mm>[0-9]*)")
  | (.datetime +"Z" | fromdateiso8601) as $seconds
  | (if .s == "+" then -1 else 1 end) as $plusminus
  | ([.hh,.mm] | map(tonumber) |.[0] *= 60 | add * 60 * $plusminus) as $offset
  | ($seconds + $offset)
  else . + (if test("Z") then "" else "Z" end) | fromdateiso8601
  end;

注意,偏移量中+-的解释符合示例中封装的原则:

以下时间均指同一时刻:“18:30Z”、“22:30+04”、“1130−0700”和“15:00−03:30”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-20
    • 2020-04-03
    • 2016-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多