日期转换未按预期进行

日期转换未按预期进行

我有一个包含以下格式记录的文件:

D20220327,S2927,977,1

D20220328,S2927,977,1

D20220329,S2927,977,1

D20220330,S2927,977,1

D20220331,S2927,977,1

D20220401,S2927,977,1

D20220402,S2927,977,1

D20220403,S2927,977,1

D20220404,S2927,977,1

然而,在应用转换将这些时间移回过去 7 天后,它对于 3 月 28 日至 4 月 3 日的日期不起作用,但相同的代码逻辑在 3 月 27 日和 4 月 4 日运行良好。我不明白为什么这仅在一周内不起作用。这是输出

D20220320,S2927,977,1 -- correct

D20220320,S2927,977,1 -- incorrect 

D20220321,S2927,977,1 -- incorrect

D20220322,S2927,977,1 -- incorrect

D20220323,S2927,977,1 -- incorrect

D20220324,S2927,977,1 -- incorrect

D20220325,S2927,977,1 -- incorrect

D20220326,S2927,977,1 -- incorrect

D20220328,S2927,977,1 -- correct

这里使用的逻辑是:

    BEGIN {
        OFS = FS = ","
}

{
        t = mktime(sprintf("%4d %.2d %.2d 00 00 00",
                substr($1,2,4),
                substr($1,6,2),
                substr($1,8,2)));

        $1 = substr($1,1,1) strftime("%Y%m%d", t - 7*24*60*60)

        print
}

答案1

您的计算是在当地时间完成的,并且您会受到 3 月 27 日夏令时切换的影响。

要使用 UTC 时间进行计算(Unix 时间戳不是本地时间),请使用最新版本的 GNU awk,请确保将额外的参数1作为最后一个参数传递给mktime()

t = mktime(sprintf("%4d %.2d %.2d 00 00 00",
        substr($1,2,4),
        substr($1,6,2),
        substr($1,8,2)), 1);

这是 GNU版本 4.2.0+awk中提供的GNU扩展。awk

作为替代方案,您可以避免使用午夜 (UTC) 左右的时间作为一天中的参考时间:

t = mktime(sprintf("%4d %.2d %.2d 12 00 00",
        substr($1,2,4),
        substr($1,6,2),
        substr($1,8,2)));

这将使它能够在较旧的 GNUawk实现和任何其他awk具有所需功能的实现中工作。

另一种选择是让脚本以更改的本地时区运行:

TZ=UTC awk -f script.awk inputfile

这会将TZ环境变量设置UTC为执行脚本,从而更改相关函数awk使用的时区。mktime()

答案2

使用 Raku(以前称为 Perl_6)

raku -pe 's/^ D <( (\d**4)(\d**2)(\d**2) )> \, /{ "$0-$1-$2".Date.earlier(:7days).Str.subst("-", :g); }/;'

输入示例(删除空行):

D20220327,S2927,977,1
D20220328,S2927,977,1
D20220329,S2927,977,1
D20220330,S2927,977,1
D20220331,S2927,977,1
D20220401,S2927,977,1
D20220402,S2927,977,1
D20220403,S2927,977,1
D20220404,S2927,977,1

示例输出:

D20220320,S2927,977,1
D20220321,S2927,977,1
D20220322,S2927,977,1
D20220323,S2927,977,1
D20220324,S2927,977,1
D20220325,S2927,977,1
D20220326,S2927,977,1
D20220327,S2927,977,1
D20220328,S2927,977,1

简而言之,Raku 的逐行(自动打印)-pe标志与熟悉的运算符结合使用s///。数字被捕获到匹配变量$0$1、 和 中,并$2带有括号,并且捕获标记<( … )>用于删除匹配的所有其他元素。

在替换中,Raku 在{ … }块内执行代码。 、和捕获用适当的破折号 ( ) 进行字符串化,并且该字符串被识别为一个对象,可以在该对象上调用该方法$0。[注意:一些用户可能会发现书写是一种更熟悉的语法——两种形式都有效]。一旦对象被设置回 7 天,它就会被符号化并用于删除返回中的破折号 ( )。$1$2-Dateearlier(:7days)earlier(days => 7)DateStrsubst-

https://docs.raku.org/routine/Date
https://docs.raku.org/routine/Dateish
https://raku.org

相关内容