Get Timezone from GPS coordinates in standalone design

I'd like to make a standalone design that provides local time based solely on the GPS coordinates received from a NEO-6M. The design would use an ESP32, additional memory, basic display, etc.

In my research, I've found plenty of designs that require an internet link to compare the GPS coords to a database and provide the local time. I'd like to do this without the internet location.

If possible, would like it to be fairly accurate, so not leaning toward comparing to a list of cities. Would prefer to compare to a boundary database. Just some thoughts.

I know this is possible. My question to the arduino community of experts is has this been done already?

Thanks ahead of time!

1 Like

I think it depends on how general (worldwide) you need your design to be - some of those timezone maps are awfully . . . knobbly.

TheMemberFormerlyKnownAsAWOL:
I think it depends on how general (worldwide) you need your design to be - some of those timezone maps are awfully . . . knobbly.

Agreed. Was thinking of first doing this for North America and see how it goes. With today's large capacity I2C memory chips, it might not be that difficult to do.

Here are the NMEA sentnces:

//Serial.println(gps.location.lat(), 6); // Latitude in degrees (double)
//Serial.println(gps.location.lng(), 6); // Longitude in degrees (double)
//Serial.print(gps.location.rawLat().negative ? "-" : "+");
//Serial.println(gps.loc2ation.rawLat().deg); // Raw latitude in whole degrees
//Serial.println(gps.location.rawLat().billionths);// ... and billionths (u16/u32)
//Serial.print(gps.location.rawLng().negative ? "-" : "+");
//Serial.println(gps.location.rawLng().deg); // Raw longitude in whole degrees
//Serial.println(gps.location.rawLng().billionths);// ... and billionths (u16/u32)
//Serial.println(gps.date.value()); // Raw date in DDMMYY format (u32)
//Serial.println(gps.date.year()); // Year (2000+) (u16)
//Serial.println(gps.date.month()); // Month (1-12) (u8)
//Serial.println(gps.date.day()); // Day (1-31) (u8)
//Serial.println(gps.time.value()); // Raw time in HHMMSSCC format (u32)
//Serial.println(gps.time.hour()); // Hour (0-23) (u8)
//Serial.println(gps.time.minute()); // Minute (0-59) (u8)
//Serial.println(gps.time.second()); // Second (0-59) (u8)
//Serial.println(gps.time.centisecond()); // 100ths of a second (0-99) (u8)
//Serial.println(gps.speed.value()); // Raw speed in 100ths of a knot (i32)
//Serial.println(gps.speed.knots()); // Speed in knots (double)
//Serial.println(gps.speed.mph()); // Speed in miles per hour (double)
//Serial.println(gps.speed.mps()); // Speed in meters per second (double)
//Serial.println(gps.speed.kmph()); // Speed in kilometers per hour (double)
//Serial.println(gps.course.value()); // Raw course in 100ths of a degree (i32)
//Serial.println(gps.course.deg()); // Course in degrees (double)
//Serial.println(gps.altitude.value()); // Raw altitude in centimeters (i32)
//Serial.println(gps.altitude.meters()); // Altitude in meters (double)
//Serial.println(gps.altitude.miles()); // Altitude in miles (double)
//Serial.println(gps.altitude.kilometers()); // Altitude in kilometers (double)
//Serial.println(gps.altitude.feet()); // Altitude in feet (double)
//Serial.println(gps.satellites.value()); // Number of satellites in use (u32)
//Serial.println(gps.hdop.value()); // Horizontal Dim. of Precision (100ths-i32)

Drop the //Serial.print. Just pick what You need.

Railroader:
Here are the NMEA sentnces:

//Serial.println(gps.location.lat(), 6); // Latitude in degrees (double)
//Serial.println(gps.location.lng(), 6); // Longitude in degrees (double)
//Serial.print(gps.location.rawLat().negative ? "-" : "+");
//Serial.println(gps.loc2ation.rawLat().deg); // Raw latitude in whole degrees
//Serial.println(gps.location.rawLat().billionths);// ... and billionths (u16/u32)
//Serial.print(gps.location.rawLng().negative ? "-" : "+");
//Serial.println(gps.location.rawLng().deg); // Raw longitude in whole degrees
//Serial.println(gps.location.rawLng().billionths);// ... and billionths (u16/u32)
//Serial.println(gps.date.value()); // Raw date in DDMMYY format (u32)
//Serial.println(gps.date.year()); // Year (2000+) (u16)
//Serial.println(gps.date.month()); // Month (1-12) (u8)
//Serial.println(gps.date.day()); // Day (1-31) (u8)
//Serial.println(gps.time.value()); // Raw time in HHMMSSCC format (u32)
//Serial.println(gps.time.hour()); // Hour (0-23) (u8)
//Serial.println(gps.time.minute()); // Minute (0-59) (u8)
//Serial.println(gps.time.second()); // Second (0-59) (u8)
//Serial.println(gps.time.centisecond()); // 100ths of a second (0-99) (u8)
//Serial.println(gps.speed.value()); // Raw speed in 100ths of a knot (i32)
//Serial.println(gps.speed.knots()); // Speed in knots (double)
//Serial.println(gps.speed.mph()); // Speed in miles per hour (double)
//Serial.println(gps.speed.mps()); // Speed in meters per second (double)
//Serial.println(gps.speed.kmph()); // Speed in kilometers per hour (double)
//Serial.println(gps.course.value()); // Raw course in 100ths of a degree (i32)
//Serial.println(gps.course.deg()); // Course in degrees (double)
//Serial.println(gps.altitude.value()); // Raw altitude in centimeters (i32)
//Serial.println(gps.altitude.meters()); // Altitude in meters (double)
//Serial.println(gps.altitude.miles()); // Altitude in miles (double)
//Serial.println(gps.altitude.kilometers()); // Altitude in kilometers (double)
//Serial.println(gps.altitude.feet()); // Altitude in feet (double)
//Serial.println(gps.satellites.value()); // Number of satellites in use (u32)
//Serial.println(gps.hdop.value()); // Horizontal Dim. of Precision (100ths-i32)

Drop the //Serial.print. Just pick what You need.

While the GPS can provide UTC time, it does not know the geopolitical boundaries for timezones and thus can't provide the 'local time'. What I'm seeking to do is COMPARE the GPS coords to a timezone boundary database so the local time can then be determined.

There is a NMEA sentence GPZDA "Date and Time" which gives the local time zone.

$GPZDA
Date & Time

UTC, day, month, year, and local time zone.

$--ZDA,hhmmss.ss,xx,xx,xxxx,xx,xx
hhmmss.ss = UTC
xx = Day, 01 to 31
xx = Month, 01 to 12
xxxx = Year
xx = Local zone description, 00 to +/- 13 hours
xx = Local zone minutes description (same sign as hours)

cattledog:
There is a NMEA sentence GPZDA "Date and Time" which gives the local time zone.

I thought this required already knowing the UTC Offset for where the GPS receiver is located? From my research, I'm trying to have the GPS coords automatically determine this offset in a standalone design (no wifi/bt connections).

How is this 'local time' calculated by the NMEA definitions?

I came across this: timezone-boundary-builder. However, it doesn't appear to directly support an Arduino setup using an ESP32.

Curious if anyone has gone down this path?

cattledog:
There is a NMEA sentence GPZDA "Date and Time" which gives the local time zone.

There is such a sentence for devices that support it.

Standard Ublox GPSs do not support it and put out 0s for the timezone fields.

srnet:
There is such a sentence for devices that support it.

Standard Ublox GPSs do not support it and put out 0s for the timezone fields.

srnet:
There is such a sentence for devices that support it.

Standard Ublox GPSs do not support it and put out 0s for the timezone fields.

I believe if you set the OFFSET, then it will provide this info. Even if true, I want to make a design that AUTOMATICALLY determines this Offset.

Bwanna:
I believe if you set the OFFSET, then it will provide this info.

Not accoding to the datasheet.

You are correct. I have not been able to find any receivers which implement the local time fields of GPZDA.

Check out this Time Zone Boundary Builder

There links to different lookup libraries where you can enter lat/lon and get a timezone.

You might be able to convert one of them to run on the ESP32.

EDIT: Look like you found your way to the boundary builder before I posted.

cattledog:
You are correct. I have not been able to find any receivers which implement the local time fields of GPZDA.

Check out this Time Zone Boundary Builder

GitHub - evansiroky/timezone-boundary-builder: A tool to extract data from Open Street Map (OSM) to build the boundaries of the world's timezones.

There links to different lookup libraries where you can enter lat/lon and get a timezone.

You might be able to convert one of them to run on the ESP32.

EDIT: Look like you found your way to the boundary builder before I posted.

This seems like the best approach out there. Not sure how to convert it to something that can run on ESP32 using Arduino IDE.

Without using the Internet, you would have to store a ridiculous amount of data points defining the time zone boundaries just in the U.S.

If you have a cellular modem in the project, you can pull the local ‘cell’ time with AT+CCLK

How accurate are you needing this to be? Extreme accuracy, within a few meters of a time zone boundary, would require a huge database with a lot of high-precision location points defining the boundary. Time zone boundaries are not simple straight lines.

You would also need to maintain an updated database for the local time, since the implementation of daylight savings time is subject to political forces, and occasionally a location will even move from one time zone to another.

SteveMann:
Without using the Internet, you would have to store a ridiculous amount of data points defining the time zone boundaries just in the U.S.

I appreciate that info. That's what I'm realizing.

lastchancename:
If you have a cellular modem in the project, you can pull the local ‘cell’ time with AT+CCLK

I started with this idea, booty didn't want to pay for a cell account just to get accurate local time.

david_2018:
How accurate are you needing this to be? Extreme accuracy, within a few meters of a time zone boundary, would require a huge database with a lot of high-precision location points defining the boundary. Time zone boundaries are not simple straight lines.

You would also need to maintain an updated database for the local time, since the implementation of daylight savings time is subject to political forces, and occasionally a location will even move from one time zone to another.

Not looking for huge accuracy, just enough to make it practical traveling across the country. U. S.

You could do a terrible first approximation by dividing the lower 48 into the four zones and choosing a longitude for the boundaries between them.

Then divide each zone into several parts defined by lat and long. Keep subdividing until you're satisfied with the accuracy, bored, or out of PROGMEM to store your data in.