【问题标题】:convert UNIX epoch time to ISO8601 in a stream of data with perl使用 perl 在数据流中将 UNIX 纪元时间转换为 ISO8601
【发布时间】:2021-09-16 14:57:36
【问题描述】:

我正在使用带有 curl --write-out '\n%{http_code}\t=%{time_total}s\n' 的 API,它提供有关 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 位数字都应该这样做(10000000002001-09-09 根据@ 987654327@ 和 BSD date(1)))),然后使用 perl 或 BSD awk 中的小型 shell 脚本 sn-p 将其全部转换为类似 ISO8601 的日期,而不需要任何广泛的依赖项来进行转换?

期望的输出(有或没有时区偏移):

{"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

【问题讨论】:

  • 是的,基本上任何有 10 位数字并用引号括起来的数字。
  • 你试过什么?你有什么具体问题吗?另外,这里有两个问题:如何找到 UNIX 纪元时间,以及如何转换它们。第二个已经被问过很多次了。 (第一个是“如何找到一个低于 42 亿的 10 位数字?”,这可能之前已经被问过)
  • $ perl -MTime::Piece -pe 's/(\d{10,})/{localtime($1)->datetime}/ge' <<<'{"message":"Domains list","list":[{"domain":"example.org","created":"1443042000","regtill":"1632430800"}]}' - 你可以通过这个管道你的curl
  • @simbabque,完美,谢谢!正是我想要的!无需安装任何额外的工具或软件包即可工作!您可以将此作为答案发布吗?我会接受的。
  • 我把这段代码放在 cmets 中作为可以做什么的指针。当我尝试这样做时,问题已经结束。我没有声称它会使 JSON 真正起作用,但我将问题理解为“我不关心 JSON,我只想读取值”。 @ikegami 当然是对的,它根本不是一个生产就绪的解决方案。但我并没有声称会。

标签: perl unix-timestamp iso8601


【解决方案1】:

您可以使用 jq,这是一个处理 JSON 的好工具。

jq '
   ( .. | select(type == "string") | select(try tonumber | . > 1000000000) ) |=
      ( tonumber | todateiso8601 )
'

Demo

【讨论】:

  • 这需要安装一个额外的工具; jq 不包含在基本 OpenBSD 系统中,但 perl 和 BSD awk 包含。
  • 相反,perljq 可以安装在 Windows 上,但不能安装在 awk 上。
  • awk 可以安装在 Windows 上就好了,例如见gnuwin32.sourceforge.net/packages/gawk.htm
  • 不错。我知道您可以将它安装在 Windows 上的 unix 仿真环境中(例如 Cygwin 和 MSYS),但这是我第一次听说端口。抱歉,我应该检查一下。
  • 我自己从不使用它,除非在这样的 Unix 仿真环境中,所以虽然我知道它存在于 Windows 上,但我很惊讶那个网站上的 gawk 是这么旧的版本(从 2008 年开始!)。我怀疑某处有更新的版本,但我不知道在哪里,我懒得去寻找它。
【解决方案2】:

Perl 脚本在标准输入上读取您的 JSON,以便将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"}]}

【讨论】:

  • 该问题未指定输入是有效的 JSON。这似乎也不起作用,因为并非所有版本的 perl 都具有实验性?
【解决方案3】:

这个awk 脚本不是很好,但它确实产生了预期的输出(可能有一些缺陷?)。

#!/usr/bin/env awk -f

BEGIN {
    FS=":|,"; OFS=":"; RS="["
} NR==1 {
    $NF="["
}  NR>1 {
    gsub(/"/,"");
    $4=strftime("%Y-%m-%dT%H%M%S%z",$4);
    $6=strftime("%Y-%m-%dT%H%M%S%z}]}",$6);
    gsub(/:/,"\":\"");
    sub(/{/,"{\"");
    sub(/}/,"}\"")
} 1

或者作为一个班轮

awk 'BEGIN {FS=":|,"; OFS=":"; RS="["} NR==1 {$NF="["}  NR>1 {gsub(/"/,""); $4=strftime("%Y-%m-%dT%H%M%S%z",$4);$6=strftime("%Y-%m-%dT%H%M%S%z}]}",$6); gsub(/:/,"\":\""); sub(/{/,"{\""); sub(/}/,"}\"")}1' input_file

输出

{"message":"Domains list":"list":[
{"domain":"example.org":"created":"2015-09-23T220000+0100":"regtill":"2021-09-23T220000+0100}"]}

【讨论】:

  • 谢谢!它看起来不像在 BSD awk 中工作,虽然(给出一个错误),但似乎在 gawk 中工作。但是,它似乎依赖于 UNIX 纪元时间元素在 JSON 中的特定定位,因此,它确实适用于原始示例输入,但在一般情况下不起作用——不是谷歌友好的解决方案,但可能成为某人的起点。
【解决方案4】:

simbabque 在评论中提供了迄今为止最好的 sn-p:

$ perl -MTime::Piece -pe 's/(\d{10,})/{localtime($1)-&gt;datetime}/ge' &lt;&lt;&lt;'{"message":"Domains list","list":[{"domain":"example.org","created":"1443042000","regtill":"1632430800"}]}' - 你可以通过这个管道传递你的卷曲。 – simbabque 1 小时前


我进一步改编如下:

  • 修改了正则表达式以使用正向后向和前向,对于\w":"(单词、引号、冒号、引号)和"\W{2}(引号,而不是单词两次),分别使用(?&lt;=\w":")(?="\W{2}),仅将 UNIX 纪元时间匹配为某个关键字的 JSON 值,并避免在随机其他数据流入脚本时出现任何可能的误报;
  • 将 UNIX 纪元时间限制为 10 位,这应该涵盖 2001 到 2286 之间的日期时间段:
    • env TZ=GMT perl -MTime::Piece -e 'print localtime(1000000000)-&gt;datetime, "Z/", localtime(9999999999)-&gt;datetime, "Z\n"'
    • 2001-09-09T01:46:40Z/2286-11-20T17:46:39Z
  • 使用一些额外的正则表达式为域数组的每个条目插入换行符;

最简单的sn-p:

curl ... \ 
  | perl -MTime::Piece -pe's#(\d{10})#localtime($1)->datetime#ge'

仅为开始/结束引号符号添加后向/前瞻:

curl ... \ 
  | perl -MTime::Piece -pe's#(?<=")(\d{10})(?=")#localtime($1)->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($1)->datetime#ge;'

整体转换最终解决方案的示例测试运行——适用于任何数据流,忽略任何不是 JSON 的内容,绝不会在无效 JSON 上出现任何错误:

% 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($1)->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 纪元时间转换 em> 到 ISO8601 完全符合问题中的规定。

【讨论】:

  • 这对于许多输入模式仍然失败。此外,该解决方案也不能用于任何有夏令时的时区。
  • 我建议清除混乱。它甚至导致你同意不好的事情。紧随其后的是初始代码块不好的一长串原因。这些都不需要在那里。
  • Re "我不明白你为什么一直坚持认为我选择的解决方案是错误的,而实际上它更适合所面临的问题!",我实际上说不行。并举例说明。
  • @ikegami 为什么你不赞成真正解决问题的完全有效的解决方案?此解决方案不会遇到您之前为我发现最有用的原始 sn-p 确定的任何问题;并且世界上大部分地区也不使用 DST,因此,TZ=GMT-3 的 DST 歧义也没有问题。总而言之,这仍然是完全符合这个问题的标题要求的最佳解决方案!不,我绝对不同意最初的代码块不好——它仍然是整个问题中最有用的评论/提示!
  • @ikegami 至于从答案中删除“原因列表”和“初始代码块不好”,我对这样的要求感到非常惊讶,因为我认为 StackOverflow 的全部目的不仅提供有用且可重用的代码 sn-ps,而且还准确解释了它们的工作原理。这个解决方案正是这样做的。您自己的解决方案没有任何解释;并且也不适用于“数据流”。请再读一遍问题的标题,不要再把 JSON 塞进人们的喉咙里了。这个问题很清楚,足以让其他用户提供有效的确切代码。
【解决方案5】:

使用日期命令。这个命令:
date -d '@1443042000'
产生这个答案:
Wed 23 Sep 2015 03:00:00 PM MDT

【讨论】:

  • 在 BSD 和 macOS 上正确的命令是 date -r1443042000 +%Y-%m-%dT%H%M%S%z,但它没有什么帮助,因为它不能在文本流上自动化。
猜你喜欢
  • 2017-08-13
  • 2017-12-20
  • 2014-01-25
  • 2015-12-22
  • 1970-01-01
  • 2012-01-30
  • 1970-01-01
  • 2015-07-08
  • 1970-01-01
相关资源
最近更新 更多