1

I have an issue trying to get the exact GPS time. Looks like the issue is related to command mktime used in the code. To get the correct GPS time I have added -2 hrs in UTC.

But the purpose is to have a code to calculate the GPS time automatically and don't use manually changes at UTC-2 as an example

Input file GMT time ( > 3 hrs in local time )

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

When I run the code below, got 1 hour less :

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

result output GPS TIME ( < 1 hr )

1210922327
1210922443
1210922480

When I run the code below, got exactly the correct GPS time.

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

Result output GPS TIME ( exactly time: OUTPUT DESIRED)

1210925927
1210926043
1210926080

Thanks in advance

kvantour
  • 25,269
  • 4
  • 47
  • 72
OXXO
  • 724
  • 5
  • 12
  • What are you trying to achieve? `mktime` expects an input of the form "YYYY MM DD hh mm ss". So everything is space delimited and there are no special characters such as "-" or "/" or "T" or "." or ":" – kvantour May 23 '18 at 12:14
  • Hi kvantor in the input file is the information for mktime ( 18/05/21 08:18:29 ) – OXXO May 23 '18 at 12:16
  • Hi OXXO, as I indicated in the previous comment, `mktime` cannot interpret such format, you need to convert it from `18/05/21 08:18:29` into `2018 05 21 08 18 29` only then you will receive the correct seconds since `1970-01-01T00:00:00`. You can do something like `d=$0; gsub(/[/:]/," ",d); mktime("20"d);` – kvantour May 23 '18 at 12:18
  • kvantor, it works the only issue i have is the problem of one hr less, that i believe is because mktime. – OXXO May 23 '18 at 12:20
  • In december 2017 it works perfectly, but now it have the issue of < 1 hr?. – OXXO May 23 '18 at 12:24
  • 1
     Daylight saving time? – kvantour May 23 '18 at 12:25
  • how it affects ? how to avoid it? – OXXO May 23 '18 at 12:27
  • 1
    _[**`mktime(datespec [, utc-flag ])`**](https://www.gnu.org/software/gawk/manual/gawk.html#Time-Functions) :: `datespec` is of the form `"YYYY MM DD HH MM SS [DST]"` If the `DST` daylight-savings flag is positive, the time is assumed to be daylight savings time; if zero, the time is assumed to be standard time; and if **negative (the default)**, `mktime()` attempts to determine whether daylight savings time is in effect for the specified time._ So I suggest to add an extra `0` behind it. – kvantour May 23 '18 at 12:31
  • Eveything depends on your systems timezone (`TZ`) probably in December, your timezone was set to `UTC-3` and now it is `UTC-2` that is why you have the hour difference. If your times represent pure `UTC` times, then you should indicate this clearly to `mktime` or do `export TZ="UTC"` before issuing `awk` – kvantour May 23 '18 at 12:44
  • Kvantor, kindly can you explain how to do export TZ=UTC where it will located in the code.. Tks – OXXO May 23 '18 at 13:18

1 Answers1

3

What you are encountering is mostly related to the time-zone settings of your system. Due to Daylight Saving Time, your time-zone shifted one hour which causes the discrepancy. A very nice post of detecting DST, can be found here. Copying its examples, we can show that for the time-zone TZ=Europe/Stockholm, the time-zone changes to summer time depending on the date :

$ 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

and therefore it will for sure have an effect on the epoch time which is given from 1970-01-01T00:00:00 UTC. With zdump we see when DST comes into affect :

$ 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

and this is seen in the epoch time as :

$ 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’

As you see, for TZ=Europe/Stockholm, the time 2018-03-25T02:00:00 does not exist and the other two are only 1 sec apart.


Summary, what does this all mean: it essentially means that your system automatically compensates for DST, unless your TZ is UTC. And this play a role on all date related command such as systime(), date or even Awk's mktime().


Can we avoid DST compensation with awk: Since the OP wants the GPS-time, i.e. the total seconds since 1980-01-06T00:00:00, you essentially subtract two times. So if both are computed in the same TZ without DST correction, you always get the correct result. There are 2 ways to do this :

  • execute your command in a specific timezone: By forcing the system to work in a single TZ (such as UTC, UTC+2, ...), there will be no DST correction. For the OP's question, the TZ of interest is 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
    

    or from awk 4.20 onwards you can tell mktime() to assume that the date is in UTC by using the UTC flag. (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
    

    both result in the following output.

    1210925927
    1210926043
    1210926080
    

    In both cases, you don't need to worry about your system-timezone and all the mumbo-jumbo related to daylight savings time.

  • disable DST correction with mktime: When adding a DST entry to the datespec part of mktime, you can tell the system to alwyas work in DST or not or let the system figure it out by himself. The latter is what you do not want. datespec is a string of the form YYYY MM DD HH MM SS [DST]. And this brings it down too:

    $ 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
    

Documentation for mktime awk 4.2.0 onwards:

mktime(datespec [, utc-flag ]) Turn datespec into a timestamp in the same form as is returned by systime(). It is similar to the function of the same name in ISO C. The argument, datespec, is a string of the form "YYYY MM DD HH MM SS [DST]". The string consists of six or seven numbers representing, respectively, the full year including century, the month from 1 to 12, the day of the month from 1 to 31, the hour of the day from 0 to 23, the minute from 0 to 59, the second from 0 to 60,55 and an optional daylight-savings flag.

The values of these numbers need not be within the ranges specified; for example, an hour of -1 means 1 hour before midnight. The origin-zero Gregorian calendar is assumed, with year 0 preceding year 1 and year -1 preceding year 0. If utc-flag is present and is either non-zero or non-null, the time is assumed to be in the UTC time zone; otherwise, the time is assumed to be in the local time zone. If the DST daylight-savings flag is positive, the time is assumed to be daylight savings time; if zero, the time is assumed to be standard time; and if negative (the default), mktime() attempts to determine whether daylight savings time is in effect for the specified time.

If datespec does not contain enough elements or if the resulting time is out of range, mktime() returns -1.

kvantour
  • 25,269
  • 4
  • 47
  • 72
  • Kvantour, amazing explanation. tks a lot for this, I have modified in my output the results as I noticed one extra digit now the results matches with the output from your code. Many tks again for the extensive information and excellent code. – OXXO May 23 '18 at 17:16
  • @OXXO I have rewritten the post, believing that the solution is in there. I hope this helps. Please remove some of the comments here as they do not contribute to the post anymore and all issues you presented have been resolved. – kvantour May 24 '18 at 09:56
  • Hi Kvantour, it is a very good explanation about the Daylight Saving Time, and how it affects to the mktime functions, really appreciate you share of this information, will be very useful for other users. I use the option to force the system to work in a single TZ, it works perfectly now. Thanks again for very useful post. – OXXO May 24 '18 at 11:04