ttlappalainen / NMEA0183

Library for handling NMEA0183 messages
69 stars 44 forks source link

timezone error in tNMEA0183Msg:makeTime(..) #23

Open lsoltero opened 3 years ago

lsoltero commented 3 years ago

on a linux box working with RMC

input: $GPRMC,203253.00,A,2642.08438,N,07859.51125,W,0.065,,061220,,,A*67

return from if ( !NMEA0183ParseRMC_nc(NMEA0183Msg, GPSTime, Latitude, Longitude, COG, SOG, DaysSince1970, Variation, &DateTime)) { return; }

RMC: 06/12/2020 20:32:53, 26.701406, -78.991854, 0.000000, 0.065000, 18603, 0.000000, 1607304773

timezone set on my machine is EDT (GMT-4).

the issue is that DateTime == 1607304773 corresponds to Monday, December 7, 2020 1:32:53 AM

and not 20:32:53 06/12/20

the problem is that makeTime is defined as static inline time_t makeTime(tmElements_t &TimeElements) { return mktime(&TimeElements); } which uses mktime which according to the docs "The mktime() function converts a broken-down time structure, expressed as local time, to calendar time representation. "

so it uses timezone. since GPS time is always in UTC it seems that the correct definition for makeTime should be

static inline time_t makeTime(tmElements_t &TimeElements) { return mktime(&TimeElements) - timezone; }

i.e. subtract out the timezone to get back to GMT.

lsoltero commented 3 years ago

or better yet...

static inline time_t makeTime(tmElements_t &TimeElements) { return timegm(&TimeElements); }

lsoltero commented 3 years ago

a quick grep of the code shows localtime and mktime in other places. Might be a good to review the code for timezone issues.

ttlappalainen commented 3 years ago

I have to check - it is not that simple. In early days Arduino tim support was, what it was. It is important that any change does not cause incompamtibility to early versions so that it would suddently change behaviour in existing installations.

ttlappalainen commented 3 years ago

Arduino environment does not have timezone so that can not be used. I could bring timezone to library by with:

int32_t tNMEA0183Msg::GetTimeZone() {
  tmElements_t tme;
  SetYear(tme,1970);
  SetMonth(tme,1);
  SetDay(tme,2);
  SetHour(tme,0);
  SetMin(tme,0);
  SetSec(tme,0);

  return (int32_t )makeTime(tme)-SECS_PER_DAY;
}

If mktime implementation would have timezone in future also in Arduino, this should then work. Could you test that? As addition there could be compiler definition

if defined(linux)||defined(__linux)||defined(linux)

return timezone;

else

...