夏令时会影响 MKtime 的结果



我在尝试获取确切的GPS时间时遇到问题。看起来问题与代码中使用的命令mktime有关。 为了获得正确的 GPS 时间,我在 UTC 中添加了 -2 小时。

但目的是有一个代码来自动计算 GPS 时间,而不是以 UTC-2 的手动更改为例

输入文件格林尼治标准时间(当地时间> 3 小时(

18/05/21 08:18:29
18/05/21 08:20:25
18/05/21 08:21:02

当我运行下面的代码时,少了 1 小时:

gawk -F'[/: ]' -v ts=$(date -d'01/06/1980' +%s)  
-v lap=18 '{$1="" d=sprintf(20$0); 
print mktime(d)+lap-ts }'  file

结果输出GPS时间(<1小时(

1210922327
1210922443
1210922480

当我运行下面的代码时,得到了正确的GPS时间。

gawk -F'[/: ]' -v ts=$(TZ=UTC-2 date -d'1/6/1980 0:00' +%s)  
-v lap=18 '{$1="" d=sprintf(20$0); 
print mktime(d)+lap-ts }' file

结果输出GPS时间(确切时间:所需的输出(

1210925927
1210926043
1210926080

提前致谢

您遇到的主要与系统的时区设置有关。由于夏令时,您的时区偏移了一小时,从而导致差异。一个非常好的检测DST的帖子,可以在这里找到。复制其示例,我们可以证明,对于时区TZ=欧洲/斯德哥尔摩,时区根据日期更改为夏令时:

$ TZ=Europe/Stockholm date +%Z # CET or CEST depending of when its run
$ TZ=Europe/Stockholm date --date=20170101 +%Z # CET
$ TZ=Europe/Stockholm date --date=20170601 +%Z # CEST
$ TZ=CET date --date=20170101 +%Z # CET
$ TZ=CET date --date=20170601 +%Z # CEST, note that its auto adjusted to CEST

因此,它肯定会对1970-01-01T00:00:00 UTC给出的纪元时间产生影响。通过zdump我们看到 DST 何时生效:

$ zdump -v /usr/share/zoneinfo/Europe/Stockholm | grep 2018
/usr/share/zoneinfo/Europe/Stockholm  Sun Mar 25 00:59:59 2018 UTC = Sun Mar 25 01:59:59 2018 CET isdst=0 gmtoff=3600
/usr/share/zoneinfo/Europe/Stockholm  Sun Mar 25 01:00:00 2018 UTC = Sun Mar 25 03:00:00 2018 CEST isdst=1 gmtoff=7200
/usr/share/zoneinfo/Europe/Stockholm  Sun Oct 28 00:59:59 2018 UTC = Sun Oct 28 02:59:59 2018 CEST isdst=1 gmtoff=7200
/usr/share/zoneinfo/Europe/Stockholm  Sun Oct 28 01:00:00 2018 UTC = Sun Oct 28 02:00:00 2018 CET isdst=0 gmtoff=3600

这在纪元时间中被视为:

$ TZ=Europe/Stockholm date -d "2018-03-25 01:59:59" +%s
1521939599
$ TZ=Europe/Stockholm date -d "2018-03-25 03:00:00" +%s
1521939600
$ TZ=Europe/Stockholm date -d "2018-03-25 02:00:00" +%s
date: invalid date ‘2018-03-25 02:00:00’

如您所见,对于TZ=欧洲/斯德哥尔摩,时间2018-03-25T02:00:00不存在,其他两个时间仅相隔 1 秒。


总结,这一切意味着什么:它本质上意味着您的系统会自动补偿 DST,除非您的 TZ 是 UTC。这在所有与日期相关的命令中发挥作用,例如systime()date甚至 Awk 的mktime().


我们可以避免 DST 补偿吗awk由于 OP 需要 GPS 时间,即自 1980-01-06T00:00:00 以来的总秒数,因此您基本上减去两次。因此,如果两者都在同一个 TZ 中计算而没有 DST 校正,则始终会得到正确的结果。有两种方法可以做到这一点:

  • 在特定时区执行命令: 通过强制系统在单个 TZ(例如 UTC、UTC+2 等(中工作,将不会进行 DST 更正。对于OP的问题,感兴趣的TZ是UTC。

    $ TZ="UTC" awk 'BEGIN { ts  = mktime("1980 01 06 00 00 00") }
    { datespec="20"$0; gsub(/[/:]/," ",datespec);
    print mktime(datespec) + lap - ts
    }' lap=18 file
    

    或者从awk 4.20开始,您可以使用 UTC 标志告诉mktime()假定日期采用 UTC。(mktime(datespec [, utc-flag ])(

    $ awk 'BEGIN { ts  = mktime("1980 01 06 00 00 00",1) }
    { datespec="20"$0; gsub(/[/:]/," ",datespec);
    print mktime(datespec,1) + lap - ts
    }' lap=18 file
    

    两者都会导致以下输出。

    1210925927
    1210926043
    1210926080
    

    在这两种情况下,您都无需担心系统时区以及与夏令时相关的所有杂乱无章

  • 使用mktime禁用 DST 更正:将DST条目添加到mktimedatespec部分时,您可以告诉系统是否始终在 DST 中工作,或者让系统自己解决。后者是你想要的。datespec是形式为YYYY MM DD HH MM SS [DST]的字符串。这也降低了它:

    $ awk 'BEGIN { ts  = mktime("1980 01 06 00 00 00 0") }
    { datespec="20"$0; gsub(/[/:]/," ",datespec);
    print mktime(datespec" 0") + lap - ts
    }' lap=18 file
    

mktimeawk 4.2.0 及更高版本的文档:

mktime(datespec [, utc-flag ])datespec转换为时间戳,其形式与systime()返回的形式相同。它类似于ISO C中同名的功能。参数datespec是形式为"YYYY MM DD HH MM SS [DST]"的字符串。该字符串由六个或七个数字组成,分别表示包括世纪在内的全年、从 1 到 12 的月份、从 1 到 31 的月份、从 0 到 23 的一天中的小时、从 0 到 59 的分钟、从 0 到 60,55 的第二个数字和一个可选的夏令时标志。

这些数字的值不必在指定的范围内;例如,-1 小时表示午夜前 1 小时。假设原点为零公历,年份 0 在年份 1 之前,年份 -1 在年份 0 之前。如果utc-flag存在且为非零或非空,则假定时间采用 UTC 时区;否则,假定时间采用本地时区。如果DST夏令时标志为正数,则假定时间为夏令时;如果为零,则假定时间为标准时间;如果为负(默认值(,则mktime()尝试确定夏令时是否在指定时间内有效。

如果datespec不包含足够的元素或生成的时间超出范围,则mktime()返回-1

相关内容

  • 没有找到相关文章

最新更新