Search code examples
cembeddedlocaltime

localtime() is off by about 460,000 years on RTL8710


I'm using an RTL8710 (running rustl8710, but that's irrelevant as far as this question goes, since all code I'm talking about is C code). The RTL8710 has proprietary libraries for some parts of the system, including localtime.

However, localtime seems to be off by about 460,000 years when using it like this (taken from the included NTP implementation):

long current_sec = 1543067026;
struct tm current_tm = *(localtime(&current_sec));

This returns the following struct:

{tm_sec = 51, 
 tm_min = 36, 
 tm_hour = 15, 
 tm_mday = 25, 
 tm_mon = 8, 
 tm_year = -458682, 
 tm_wday = 2, 
 tm_yday = 267, 
 tm_isdst = -1515870811}

Obviously that's incorrect, it should be something close to 24 Nov 2018 13:52:04 GMT. It's not random though, because the time always seems to differ by around 2 hours and some minutes.

The only other noteworthy thing here is that the code uses the default system headers time.h and string.h (which would be from Ubuntu 18.10, 64bit in my case).

I have no idea what stdlib library RealTek is really using, but judging by debugging info (newlib/libc/time/lcltime.c), I'd guess that it's a (modified?) version of newlib.


Solution

  • Code is using the wrong type which leads to undefined behavior.1

    // long current_sec = 1543067026;
    time_t current_sec = 1543067026;
    struct tm current_tm = *(localtime(&current_sec));
    

    Better code would test the localtime() return value before de-referencing it.

    struct tm *p = localtime(&current_sec);
    if (p) {
      struct tm current_tm = *p;
      ...
    

    1 The wrong type width is evidenced by following.

    int main() {
      long current_sec = 1543067026;
      struct tm current_tm = { .tm_sec = 51, .tm_min = 36, .tm_hour = 15, //
          .tm_mday = 25, .tm_mon = 8, .tm_year = -458682, //
          .tm_wday = 2, .tm_yday = 267, .tm_isdst = -1515870811 };
    
      time_t t = mktime(&current_tm);
      printf("%16lx\n%16llx\n", current_sec, (unsigned long long) t);
    }
    

    Output

            5bf95592
    fffff2d55bf9a9f3
            ^^^^
    

    Many bits lines up. I suspect the upper bit difference was due to junk bits being referenced by local_time() and lower bit difference due to extreme calculation, OP non-synchronized posting of code and purported output, or a difference between my and OP's standard library. Although time_t is zone-less, my and OP's timezone difference contributes to locatime()/mktime() differences.