我在尝试获取确切的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
条目添加到mktime
的datespec
部分时,您可以告诉系统是否始终在 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
mktime
awk 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
。