使用 perl 在数据流中将 UNIX 纪元时间转换为 ISO8601
convert UNIX epoch time to ISO8601 in a stream of data with perl
我正在使用 API 和 curl --write-out '\n%{http_code}\t=%{time_total}s\n'
,它提供有关字段的日期信息 UNIX 纪元时间 而不是 ISO8601,这让人很难理解是怎么回事。
示例输入:
{"message":"Domains list","list":[{"domain":"example.org","created":"1443042000","regtill":"1632430800"}]}
200 =0.126406s
{"list":[{"d":"abc","c":"1443042000"},{"d":"xyz","c":"1000000000"}]}
200 =0.126406s
有没有办法在这个数据流中找到任何看起来像 UNIX 纪元时间的东西(例如,代表最近的时间(引号中的任何 10 位数字都应该这样做(1000000000
是 2001-09-09
根据 env TZ=GMT-3 date -r1000000000 +%Y-%m-%dT%H%M%S%z
和 BSD date(1)
))),并将其全部转换为类似 ISO8601 的日期,在 perl
或 BSD awk
没有任何广泛的依赖关系来进行转换?
期望的输出(有或没有时区偏移):
{"message":"Domains list","list":[
{"domain":"example.org","created":"2015-09-24T000000+0300","regtill":"2021-09-24T000000+0300"}]}
200 =0.126406s
{"list":[
{"d":"abc","c":"2015-09-24T000000+0300"},
{"d":"xyz","c":"2001-09-09T044640+0300"}]}
200 =0.126406s
使用日期命令。这个命令:
date -d '@1443042000'
产生这个答案:
Wed 23 Sep 2015 03:00:00 PM MDT
您可以使用 jq,这是一个很好的操作工具 JSON。
jq '
( .. | select(type == "string") | select(try tonumber | . > 1000000000) ) |=
( tonumber | todateiso8601 )
'
在标准输入上读取您的 JSON 的 Perl 脚本,因此它可以将 curl
输出传送到它:
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;
use experimental qw/postderef/;
use Time::Piece;
# Install through your OS package manager if provided or favorite
# CPAN client. Or use a different JSON module you like better; perl
# has quite a few available. Worst case if you can't install any
# extra modules is to use core JSON::PP
use JSON::MaybeXS;
my $json = JSON::MaybeXS->new->utf8;
my $rawdata = do { local $/; <STDIN> };
my $data = $json->decode($rawdata);
for my $domain ($data->{list}->@*) {
$domain->{created} = localtime($domain->{created})->datetime;
$domain->{regtill} = localtime($domain->{regtill})->datetime;
}
say $json->encode($data);
示例:
$ ./convtimes < input.json
{"message":"Domains list","list":[{"created":"2015-09-23T14:00:00","regtill":"2021-09-23T14:00:00","domain":"example.org"}]}
simbabque 在评论中提供了迄今为止最好的片段:
$ perl -MTime::Piece -pe 's/(\d{10,})/{localtime()->datetime}/ge' <<<'{"message":"Domains list","list":[{"domain":"example.org","created":"1443042000","regtill":"1632430800"}]}'
- you can pipe your curl through this. –
simbabque 1 hour ago
我进一步改编如下:
- 修改正则表达式以使用正向后视和前视,对于
\w":"
(单词、引号、冒号、引号)和 "\W{2}
(两次引用,而不是单词),(?<=\w":")
和 (?="\W{2})
,分别将 UNIX 纪元时间匹配为某个关键字的 JSON 值,并在随机其他数据流入脚本时避免任何可能的误报;
- 将 UNIX 纪元时间限制为 10 位数字,这应该涵盖 2001 年到 2286 年之间的日期时间段:
env TZ=GMT perl -MTime::Piece -e 'print localtime(1000000000)->datetime, "Z/", localtime(9999999999)->datetime, "Z\n"'
2001-09-09T01:46:40Z/2286-11-20T17:46:39Z
- 使用一些额外的正则表达式为域数组的每个条目插入换行符;
最简单的片段:
curl ... \
| perl -MTime::Piece -pe's#(\d{10})#localtime()->datetime#ge'
仅针对 opening/closing 引号添加 lookbehind/lookahead:
curl ... \
| perl -MTime::Piece -pe's#(?<=")(\d{10})(?=")#localtime()->datetime#ge'
指定输入数据的时区,更严格 JSON-特定 lookbehind/lookahead,并为列表中的每个域插入换行符,以使其更易于用户阅读:
curl ... \
| env TZ=GMT-3 perl -MTime::Piece -p \
-e 's#(?<=":\[|["\d]},)(?={")#\n\t#g;' \
-e 's#(?<=\w":")(\d{10})(?="\W{2})#localtime()->datetime#ge;'
整体转换最终解决方案的示例测试 运行 -- 适用于任何数据流,忽略任何不是 JSON 的内容,永远不会在无效 [=64] 上给出任何错误=]:
% printf '{"list":[{"d":"abc","c":"1443042000"},{"d":"xyz","c":"1000000000"}]}\n' \
| env TZ=GMT-3 perl -MTime::Piece -p \
-e 's#(?<=":\[|["\d]},)(?={")#\n\t#g;' \
-e 's#(?<=\w":")(\d{10})(?="\W{2})#localtime()->datetime#ge;'
{"list":[
{"d":"abc","c":"2015-09-24T00:00:00"},
{"d":"xyz","c":"2001-09-09T04:46:40"}]}
%
此解决方案比其他假定输入有效 JSON 的解决方案更灵活,因为它也可用于使用单个 curl
命令进行更多操作的情况而不是单个请求,这不是有效的 JSON,因为 JSON 只能有一个根元素。它也适用于 curl --write-out
(例如,curl -w '\n%{http_code}\t=%{time_total}s\n'
),同样,JSON 也是无效的。它也更灵活,因为它甚至不假设输入数据是任何特定格式——任何在 \w":"
和 "\W{2}
之间的 10 位数字都将自动从 UNIX Epoch time 到 ISO8601 完全按照问题中的指定。
这个 awk
脚本不是很好,但它确实产生了预期的输出(可能有一些缺陷?)。
#!/usr/bin/env awk -f
BEGIN {
FS=":|,"; OFS=":"; RS="["
} NR==1 {
$NF="["
} NR>1 {
gsub(/"/,"");
=strftime("%Y-%m-%dT%H%M%S%z",);
=strftime("%Y-%m-%dT%H%M%S%z}]}",);
gsub(/:/,"\":\"");
sub(/{/,"{\"");
sub(/}/,"}\"")
} 1
或作为一个班轮
awk 'BEGIN {FS=":|,"; OFS=":"; RS="["} NR==1 {$NF="["} NR>1 {gsub(/"/,""); =strftime("%Y-%m-%dT%H%M%S%z",);=strftime("%Y-%m-%dT%H%M%S%z}]}",); gsub(/:/,"\":\""); sub(/{/,"{\""); sub(/}/,"}\"")}1' input_file
输出
{"message":"Domains list":"list":[
{"domain":"example.org":"created":"2015-09-23T220000+0100":"regtill":"2021-09-23T220000+0100}"]}
我正在使用 API 和 curl --write-out '\n%{http_code}\t=%{time_total}s\n'
,它提供有关字段的日期信息 UNIX 纪元时间 而不是 ISO8601,这让人很难理解是怎么回事。
示例输入:
{"message":"Domains list","list":[{"domain":"example.org","created":"1443042000","regtill":"1632430800"}]}
200 =0.126406s
{"list":[{"d":"abc","c":"1443042000"},{"d":"xyz","c":"1000000000"}]}
200 =0.126406s
有没有办法在这个数据流中找到任何看起来像 UNIX 纪元时间的东西(例如,代表最近的时间(引号中的任何 10 位数字都应该这样做(1000000000
是 2001-09-09
根据 env TZ=GMT-3 date -r1000000000 +%Y-%m-%dT%H%M%S%z
和 BSD date(1)
))),并将其全部转换为类似 ISO8601 的日期,在 perl
或 BSD awk
没有任何广泛的依赖关系来进行转换?
期望的输出(有或没有时区偏移):
{"message":"Domains list","list":[
{"domain":"example.org","created":"2015-09-24T000000+0300","regtill":"2021-09-24T000000+0300"}]}
200 =0.126406s
{"list":[
{"d":"abc","c":"2015-09-24T000000+0300"},
{"d":"xyz","c":"2001-09-09T044640+0300"}]}
200 =0.126406s
使用日期命令。这个命令:
date -d '@1443042000'
产生这个答案:
Wed 23 Sep 2015 03:00:00 PM MDT
您可以使用 jq,这是一个很好的操作工具 JSON。
jq '
( .. | select(type == "string") | select(try tonumber | . > 1000000000) ) |=
( tonumber | todateiso8601 )
'
在标准输入上读取您的 JSON 的 Perl 脚本,因此它可以将 curl
输出传送到它:
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;
use experimental qw/postderef/;
use Time::Piece;
# Install through your OS package manager if provided or favorite
# CPAN client. Or use a different JSON module you like better; perl
# has quite a few available. Worst case if you can't install any
# extra modules is to use core JSON::PP
use JSON::MaybeXS;
my $json = JSON::MaybeXS->new->utf8;
my $rawdata = do { local $/; <STDIN> };
my $data = $json->decode($rawdata);
for my $domain ($data->{list}->@*) {
$domain->{created} = localtime($domain->{created})->datetime;
$domain->{regtill} = localtime($domain->{regtill})->datetime;
}
say $json->encode($data);
示例:
$ ./convtimes < input.json
{"message":"Domains list","list":[{"created":"2015-09-23T14:00:00","regtill":"2021-09-23T14:00:00","domain":"example.org"}]}
simbabque 在评论中提供了迄今为止最好的片段:
$ perl -MTime::Piece -pe 's/(\d{10,})/{localtime()->datetime}/ge' <<<'{"message":"Domains list","list":[{"domain":"example.org","created":"1443042000","regtill":"1632430800"}]}'
- you can pipe your curl through this. – simbabque 1 hour ago
我进一步改编如下:
- 修改正则表达式以使用正向后视和前视,对于
\w":"
(单词、引号、冒号、引号)和"\W{2}
(两次引用,而不是单词),(?<=\w":")
和(?="\W{2})
,分别将 UNIX 纪元时间匹配为某个关键字的 JSON 值,并在随机其他数据流入脚本时避免任何可能的误报; - 将 UNIX 纪元时间限制为 10 位数字,这应该涵盖 2001 年到 2286 年之间的日期时间段:
env TZ=GMT perl -MTime::Piece -e 'print localtime(1000000000)->datetime, "Z/", localtime(9999999999)->datetime, "Z\n"'
2001-09-09T01:46:40Z/2286-11-20T17:46:39Z
- 使用一些额外的正则表达式为域数组的每个条目插入换行符;
最简单的片段:
curl ... \
| perl -MTime::Piece -pe's#(\d{10})#localtime()->datetime#ge'
仅针对 opening/closing 引号添加 lookbehind/lookahead:
curl ... \
| perl -MTime::Piece -pe's#(?<=")(\d{10})(?=")#localtime()->datetime#ge'
指定输入数据的时区,更严格 JSON-特定 lookbehind/lookahead,并为列表中的每个域插入换行符,以使其更易于用户阅读:
curl ... \
| env TZ=GMT-3 perl -MTime::Piece -p \
-e 's#(?<=":\[|["\d]},)(?={")#\n\t#g;' \
-e 's#(?<=\w":")(\d{10})(?="\W{2})#localtime()->datetime#ge;'
整体转换最终解决方案的示例测试 运行 -- 适用于任何数据流,忽略任何不是 JSON 的内容,永远不会在无效 [=64] 上给出任何错误=]:
% printf '{"list":[{"d":"abc","c":"1443042000"},{"d":"xyz","c":"1000000000"}]}\n' \
| env TZ=GMT-3 perl -MTime::Piece -p \
-e 's#(?<=":\[|["\d]},)(?={")#\n\t#g;' \
-e 's#(?<=\w":")(\d{10})(?="\W{2})#localtime()->datetime#ge;'
{"list":[
{"d":"abc","c":"2015-09-24T00:00:00"},
{"d":"xyz","c":"2001-09-09T04:46:40"}]}
%
此解决方案比其他假定输入有效 JSON 的解决方案更灵活,因为它也可用于使用单个 curl
命令进行更多操作的情况而不是单个请求,这不是有效的 JSON,因为 JSON 只能有一个根元素。它也适用于 curl --write-out
(例如,curl -w '\n%{http_code}\t=%{time_total}s\n'
),同样,JSON 也是无效的。它也更灵活,因为它甚至不假设输入数据是任何特定格式——任何在 \w":"
和 "\W{2}
之间的 10 位数字都将自动从 UNIX Epoch time 到 ISO8601 完全按照问题中的指定。
这个 awk
脚本不是很好,但它确实产生了预期的输出(可能有一些缺陷?)。
#!/usr/bin/env awk -f
BEGIN {
FS=":|,"; OFS=":"; RS="["
} NR==1 {
$NF="["
} NR>1 {
gsub(/"/,"");
=strftime("%Y-%m-%dT%H%M%S%z",);
=strftime("%Y-%m-%dT%H%M%S%z}]}",);
gsub(/:/,"\":\"");
sub(/{/,"{\"");
sub(/}/,"}\"")
} 1
或作为一个班轮
awk 'BEGIN {FS=":|,"; OFS=":"; RS="["} NR==1 {$NF="["} NR>1 {gsub(/"/,""); =strftime("%Y-%m-%dT%H%M%S%z",);=strftime("%Y-%m-%dT%H%M%S%z}]}",); gsub(/:/,"\":\""); sub(/{/,"{\""); sub(/}/,"}\"")}1' input_file
输出
{"message":"Domains list":"list":[
{"domain":"example.org":"created":"2015-09-23T220000+0100":"regtill":"2021-09-23T220000+0100}"]}