From f774bfcdf3d629b92d4cbb6a57504c5782e8019e Mon Sep 17 00:00:00 2001 From: Joey Marianer Date: Wed, 31 Mar 2021 21:33:18 -0700 Subject: [PATCH] Use a regex to parse ISO8601 dates --- src/builtin.jq | 38 ++++++++++++++++++++++++++++++++++---- tests/man.test | 20 ++++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/builtin.jq b/src/builtin.jq index ee78017609..c221209f5b 100644 --- a/src/builtin.jq +++ b/src/builtin.jq @@ -53,10 +53,6 @@ def _flatten($x): reduce .[] as $i ([]; if $i | type == "array" and $x != 0 then def flatten($x): if $x < 0 then error("flatten depth must not be negative") else _flatten($x) end; def flatten: _flatten(-1); def range($x): range(0;$x); -def fromdateiso8601: strptime("%Y-%m-%dT%H:%M:%SZ")|mktime; -def todateiso8601: strftime("%Y-%m-%dT%H:%M:%SZ"); -def fromdate: fromdateiso8601; -def todate: todateiso8601; def match(re; mode): _match_impl(re; mode; false)|.[]; def match($val): ($val|type) as $vt | if $vt == "string" then match($val; null) elif $vt == "array" and ($val | length) > 1 then match($val[0]; $val[1]) @@ -78,6 +74,40 @@ def scan(re): then [ .captures | .[] | .string ] else .string end ; +def fromdateiso8601: + capture + ( "^(?[0-9]{4})" + + "-(?[0-9]{2})" + + "-(?[0-9]{2})" + + "T(?[0-9]{2})" + + ":(?[0-9]{2})" + + ":(?[0-9]{2})" + + "(?\\.[0-9]+)?" # Support optional subsecond precision + + "(Z|" # Support Zulu or offset + + "(?[-+])" + + "(?[0-9]{2})" + + ":?" + + "(?[0-9]{2})" + + ")$") + + # Subseconds are optional, and so is the offset if Zulu time is specified + | .subseconds //= 0 + | .offset_hours //= 0 + | .offset_minutes //= 0 + | .offset_sign //= "+" # string math ftw + + | .offset_sign += "1" # string math ftw + + | (.year, .month, .day, .hour, .minute, .second, + .subseconds, .offset_sign, .offset_hours, .offset_minutes) |= tonumber + | .offset = (.offset_hours * 3600 + .offset_minutes * 60) + * .offset_sign * -1 # the Earth rotates eastward + + | ([.year, .month - 1, .day, .hour, .minute, .second, 0, 0] | mktime) + + .offset + .subseconds; +def todateiso8601: strftime("%Y-%m-%dT%H:%M:%SZ"); +def fromdate: fromdateiso8601; +def todate: todateiso8601; # # If input is an array, then emit a stream of successive subarrays of length n (or less), # and similarly for strings. diff --git a/tests/man.test b/tests/man.test index 2a49effe6c..b9d7f7efbe 100644 --- a/tests/man.test +++ b/tests/man.test @@ -643,6 +643,22 @@ fromdate "2015-03-05T23:51:47Z" 1425599507 +fromdate +"2015-03-05T23:51:47+00:00" +1425599507 + +fromdate +"2015-03-05T23:51:47+01:00" +1425595907 + +fromdate +"2015-03-05T23:51:47-01:00" +1425603107 + +fromdate +"2015-03-05T23:51:47.123456Z" +1425599507.123456 + strptime("%Y-%m-%dT%H:%M:%SZ") "2015-03-05T23:51:47Z" [2015,2,5,23,51,47,4,63] @@ -651,6 +667,10 @@ strptime("%Y-%m-%dT%H:%M:%SZ")|mktime "2015-03-05T23:51:47Z" 1425599507 +fromdateiso8601 +"2015-03-05T23:51:47Z" +1425599507 + .[] == 1 [1, 1.0, "1", "banana"] true