garmin: add a _third_ time offset field

So FIT files seem to have many many different ways to describe time
offsets.  And I'm not talking about the overall Garmin time offset of
631065600, which is the conversion from the "Unix Epoch" (Jan 1, 1970)
to the "Garmin Epoch" (Dec 31, 1989).

No, I'm talking just about "device time" to "local time" to "UTC"
conversions.

The DEVICE_SETTINGS message has two different fields for time offsets:
there's a "UTC offset" (presumably this is the timezone the device is
set to), and a "time offset" which we actually use to transform the
recorded time of the dive into the local time that we report.

But the Suunto FIT export doesn't seem to use either of those, and
instead Nick Clark points outthe Suunto FAQ:

 "Timestamp fields are deliberately defined as UTC time so that they may
  be conveniently displayed in the local time if so desired.

  In some instances it is useful to know the UTC offset when the file
  was generated (possibly different from when it is decoded). This can
  be accomplished by logging a single message containing both a
  local_timestamp and a timestamp field. This will establish the UTC
  offset of the file.

  Presently these fields are predefined for activity and monitoring
  messages"

so to get the actual local time, instead of getting it from the
DEVICE_SETTINGS message, we now have to parse the ACTIVITY message, and
take the difference between the regular timestamp and the
"local_timestamp" field.

The great thing about standards is that there are so many to choose
from.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Linus Torvalds 2023-07-16 11:17:32 -07:00
parent dc8f32609e
commit e81eca685a

View File

@ -65,6 +65,7 @@ struct garmin_sensor {
struct record_data {
unsigned int pending;
unsigned int time;
unsigned int timestamp;
// RECORD_DECO
int stop_time;
@ -492,6 +493,7 @@ struct field_desc {
// Convert to "standard epoch time" by adding 631065600.
DECLARE_FIELD(ANY, timestamp, UINT32)
{
garmin->record_data.timestamp = data;
if (garmin->callback) {
dc_sample_value_t sample = {0};
@ -652,7 +654,11 @@ DECLARE_FIELD(ACTIVITY, num_sessions, UINT16) { }
DECLARE_FIELD(ACTIVITY, type, ENUM) { }
DECLARE_FIELD(ACTIVITY, event, ENUM) { }
DECLARE_FIELD(ACTIVITY, event_type, ENUM) { }
DECLARE_FIELD(ACTIVITY, local_timestamp, UINT32) { }
DECLARE_FIELD(ACTIVITY, local_timestamp, UINT32)
{
int time_offset = data - garmin->record_data.timestamp;
garmin->dive.time_offset = time_offset;
}
DECLARE_FIELD(ACTIVITY, event_group, UINT8) { }
// SPORT
@ -1693,10 +1699,22 @@ garmin_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
garmin_parser_t *garmin = (garmin_parser_t *) abstract;
dc_ticks_t time = 631065600 + (dc_ticks_t) garmin->dive.time;
int timezone = DC_TIMEZONE_NONE;
// Show local time (time_offset)
dc_datetime_gmtime(datetime, time + garmin->dive.time_offset);
datetime->timezone = DC_TIMEZONE_NONE;
/* See if we might have a valid timezone offset */
if (garmin->dive.time_offset || garmin->dive.utc_offset) {
int offset = garmin->dive.time_offset - garmin->dive.utc_offset;
/* 15-minute (900-second) offsets are real */
if ((offset % 900) == 0 &&
offset >= -12*60*60 &&
offset <= 14*60*60)
timezone = offset;
}
datetime->timezone = timezone;
return DC_STATUS_SUCCESS;
}