Complete
over 5 years ago

By Design.

Corrupt time_offset in a watch app FIT file when time zone is negative.

The problem is in the "time_offset" field in the "device_settings" message in the FIT file generated from a watch app.

The time_offset value is correct with a positive time zone but it is corrupt when the time zone is negative.

To replicate

A. to show it works with a positive time zone:

Create an Activity file from a watch app in a time zone east of the Prime Meridian, i.e. with a positive time zone.

I tested in Sydney (UTC+11).

Decode the FIT file with FitCSVTool.jar

Observe the device_settings time_offset field is correct.

I received the correct value of 36900 (11x60x60)

B. to see the error:

Re run in a time zone west of the prime meridian, i.e. a negative time zone, like USA CA (UTC + 8)

Decode the FIT file with FitCSVTool.jar

Observe the device_settings time_offset field is incorrect.

I expect time_offset,"-28800" (-8x60x60), I receive, time_offset,"4294938496",

It's not a problem with FitCSVTool:

I receive the same incorrect value with my FIT file decoder, so the problem is not within FitCSVTool.

The problem appears to be the way the negative time_offset is being generated.

Work-around:

The work around is to subtract 0x1000000 from the incorrect value.



  • The time_offset value is correct with a positive time zone but it is corrupt when the time zone is negative.

    The time_offset value is used to get the local time from a system time. If you add the time_offset to a system time, you will get the appropriate local time.

    Basically, if you're given a system time, you can add utc_offset and get the UTC time. If given a system time, you can add time_offset to get a local time. For this to work correctly, you need to be sure that the resulting timestamp is truncated to 32 bits.

    In the data that you've posted above, utc_offset=0, so the system time is equivalent to UTC time. With this in mind, consider the case where the system time (current UTC timestamp from https://www.unixtimestamp.com) is 1575919085, and time_offset is 4294938496. If you add these, you get 1575919085 + 4294938496 => 5870857581, but that value is too big to fit into a 32-bits. The truncated 32-bit value is 1575890285 (exactly -28800 from the original value). If I plug this value back into https://www.unixtimestamp.com, I get 12/09/2019 @ 11:18am which is the local time that I started writing all of this up.

    If you want to see the time zone offset, you can look at the time_zone_offset field. It is a signed 8-bit integer that represents the time zone offset from UTC in 15-minute increments. If you were actually recording an activity from California today, I'd expect this to be -32 (-8hr * 60min/hr => -480min, -480min / 15m => -32).

  • Now that we have established it is a bug that is unlikely to get fixed, the simple but messy work-around to selectively ignore the baseTypeNumber for time_offset and treat it as a Type #5 sint32.



  • As I mentioned above, the documentation and the implementations (C/C++/Java/...) all indicate that this is a uint32 (base type 6). I'm not certain that this is a bug, or even if it is a bug that can be fixed without breaking lots of other stuff.

    I can ask around, but the easiest thing to do would be to interpret the bits as a signed integer. I'm pretty sure you're working with PHP, and I'm not sure how do do this there, but in C/C++ you just cast to a signed 32-bit integer and you're done. If you must, you could do something like this:

    uint32_val = /* the input value */;
    sint32_val = 0;
    
    if (uint32_val & 0x80000000) {
        // the sign bit is set, flip all bits and add one to get
        // absolute value, then make it negative
        sint32_val = -1 * (~uint32_val + 1);
    }
    else {
        sint32_val = uint32_val;
    }

  • Profile.xls  also (incorrectly) documents time_offset as uint32:

  • And that would explain why FitCSVTool is also getting it wrong and returning 4294938496 instead of -28800