如何使用jq从JSON字符串中选择日期范围?

时间:2016-10-24 03:26:36

标签: json macos shell jq

我有一个像这样的JSON字符串(MacOS):

[{
    "id": 3624,
    "created_at": "2016-10-21T20:51:16.000+08:00",
  },
  {
     "id": 3625,
    "created_at": "2016-10-22T08:09:16.000+08:00",
  },
 {
     "id": 3626,
    "created_at": "2016-10-23T09:19:55.000+08:00",
  }]

我想从“2016-10-21”到“2016-10-22”选择“created_at”; 我想得到这样的结果:

[{
    "id": 3624,
    "created_at": "2016-10-21T20:51:16.000+08:00",
  },
  {
     "id": 3625,
    "created_at": "2016-10-22T08:09:16.000+08:00",
  }]

有人能指出我正确的方向吗?

问题已解决。 现在,我使用此代码来选择日期,我希望它对其他人有用:

jq --arg s '2016-10-26T18:16' --arg e '2016-10-27T20:24' '[($s, $e) | strptime("%Y-%m-%dT%H:%M") | mktime] as $r
  | map(select(
        (.updated_at[:19] | strptime("%Y-%m-%dT%H:%M:%S") | mktime) as $d
          | $d >= $r[0] and $d <= $r[1]))' <<< "$requestJson"

2 个答案:

答案 0 :(得分:4)

对于更强大的解决方案,最好解析日期以获取其组件并比较这些组件。最接近的是使用strptime/1来解析返回其组件数组的日期。然后比较组件以检查它是否在范围内。

strptime返回的数组是组件:

year (%Y)
month (%m)
date (%d)
hours (%H)
minutes (%M)
seconds (%S)
day of week (%w)
day of year (%j)

由于您只是比较日期,因此比较应仅查看前3个组件。

$ jq --arg s '2016-10-21' --arg e '2016-10-22' '
[($s, $e) | strptime("%Y-%m-%d")[0:3]] as $r
  | map(select(
        (.created_at[:19] | strptime("%Y-%m-%dT%H:%M:%S")[0:3]) as $d
          | $d >= $r[0] and $d <= $r[1]
    ))
' input.json

由于您在Mac上运行,我希望您的构建中可以使用这些方法。您可能必须调整日期格式以使其按预期工作。正如你在评论中看到的那样,我们不得不按摩它以使其有效。

答案 1 :(得分:1)

按要求使用命令行JSON解析器jq

注意:Jeff Mercado's helpful answer演示了许多优秀的高级jq技术,但针对手头的具体问题,我相信基于 text 的方法答案要简单得多,同时还要足够灵活。

#!/usr/bin/env bash

# Create variable with sample input.
IFS= read -r -d '' json <<'EOF'
[
  {
    "id": 3624,
    "created_at": "2016-10-21T20:51:16.000+08:00"
  },
  {
     "id": 3625,
    "created_at": "2016-10-22T08:09:16.000+08:00"
  },
  {
     "id": 3626,
    "created_at": "2016-10-23T09:19:55.000+08:00"
  }
]
EOF

# Use `jq` to select the objects in the array whose .created_at
# property value falls between "2016-10-21:T20:51" and "2016-10-22T08:09"
# and return them as an array (effectively a sub-array of the input).
# (To solve the problem as originally stated, simply pass "2016-10-21" 
#  and "2016-10-22" instead.)
jq --arg s '2016-10-21T20:51' --arg e '2016-10-22T08:09' '
  map(select(.created_at | . >= $s and . <= $e + "z"))
' <<<"$json"
  • 参数--arg s '2016-10-21T20:51'--arg e '2016-10-22T08:09'分别定义变量$s(日期开始+时间范围)和$e(日期结束+时间范围),在jq脚本中使用。

  • 函数map()将包含的表达式应用于输入数组的所有元素,并将结果作为数组输出。

  • 函数select()接受一个过滤表达式:每个输入对象都是根据所包含的表达式计算的,只有当表达式求值为“truthy”值时才会传递输入对象。

  • 表达式.created_at | . >= $s and . <= $e + "z"访问每个输入对象的created_at属性,并将其值发送到比较表达式,该表达式执行 lexical 比较,由于格式化日期+时间字符串 - 相当于按时间顺序比较。

    • 请注意附加到范围端点的尾随"z",以确保它匹配前缀匹配端点的JSON字符串中的所有日期+时间字符串;例如,端点2016-10-22T08:09应与2016-10-22T08:09:01以及2016-10-22T08:59匹配。

    • 这种词汇方法允许您根据需要从头开始指定任意数量的组件,以缩小或扩大日期范围;例如--arg s '2016-10-01' --arg e '2016-10-31'将匹配2016年10月整个月的所有条目。