夏令时影响mktime的结果

时间:2018-05-23 12:00:30

标签: awk timezone dst

我有一个问题,试图获得准确的GPS时间。看起来该问题与代码中使用的命令mktime有关。 为了获得正确的GPS时间,我在UTC中增加了-2小时。

但目的是让代码自动计算GPS时间,并且不要在UTC-2上手动更改为例

输入文件GMT时间(当地时间> 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 TIME(确切时间:OUTPUT DESIRED)

1210925927
1210926043
1210926080

提前致谢

1 个答案:

答案 0 :(得分:3)

您遇到的问题主要与系统的时区设置有关。由于Daylight Saving Time,您的时区会移动一小时,从而导致出现差异。可以找到一个检测DST的非常好的帖子here。复制其示例,我们可以显示,对于时区 TZ = Europe / Stockholm ,时区会根据日期更改为夏令时:

$ 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 = Europe / Stockholm ,时间 2018-03-25T02:00:00 不存在,其他两个只有1秒开。

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

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

  • 在特定时区执行您的命令: 通过强制系统在单个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
    

    在这两种情况下,您都不必担心您的系统时区以及与夏令时相关的所有mumbo-jumbo。

  • 使用mktime 停用DST更正:向DST的{​​{1}}部分添加datespec条目时,您可以告诉alwy的系统是否在DST中工作或让系统自己解决。后者是你做想要的。 mktimedatespec形式的字符串。这也让它失望了:

    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 awk 4.2.0以后的文档:

  

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

     

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

     

如果mktime()未包含足够的元素,或者结果时间超出范围,则datespec会返回mktime()