This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

2038 time_t issue - looking for options

Dear community, as already mentioned here https://devzone.nordicsemi.com/f/nordic-q-a/30622/nrf_cal_set_time-setting-issue-for-2100-year/121167 Im wondering how do you deal with the 2038 year problem in your applications. We are supplying sensor loggers and our products are usually used for very long time after installed (20 years is not unheard of) - as we have recently switched to NRF52 modules and are using the time.h from SES (5.32A) the time_t is defined as long, which will eventually hit us with the Y2038 problem. Since we are using the time.h library heavily - especially the tm struct and localtime_r() I was wondering whether there is a reasonably good way to fix this. 

One of the options I was thinking of is to actually offset the time_t value by 50 years, so I will be interpreting the timestamp as seconds since 2020, not since 1970, but Im not sure this will work well with interpreting the leap years and other specialties of time like different time offsets done in the past?

Is there a good solution here to pursue?

Thanks, 
Marcel

  • Hello,

    Looking at the calendar example from nordicplayground on Github (which FYI is not an officially tested example, but published because it is nice to have for some users, like yourself).

    Examining the calendar and time around January 19th, year 2038:

    Uncalibrated time:	01/19/38 - 03:13:59
    Uncalibrated time:	01/19/38 - 03:14:00
    Uncalibrated time:	01/19/38 - 03:14:01
    Uncalibrated time:	01/19/38 - 03:14:02
    Uncalibrated time:	01/19/38 - 03:14:03
    Uncalibrated time:	01/19/38 - 03:14:04
    Uncalibrated time:	01/19/38 - 03:14:05
    Uncalibrated time:	01/19/38 - 03:14:06
    Uncalibrated time:	01/19/38 - 03:14:07
    Uncalibrated time:	12/13/01 - 20:45:52
    Uncalibrated time:	12/13/01 - 20:45:53
    Uncalibrated time:	12/13/01 - 20:45:54
    Uncalibrated time:	12/13/01 - 20:45:55
    Uncalibrated time:	12/13/01 - 20:45:56
    Uncalibrated time:	12/13/01 - 20:45:57
    Uncalibrated time:	12/13/01 - 20:45:58
    Uncalibrated time:	12/13/01 - 20:45:59
    Uncalibrated time:	12/13/01 - 20:46:00
    Uncalibrated time:	12/13/01 - 20:46:01
    Uncalibrated time:	12/13/01 - 20:46:02
    Uncalibrated time:	12/13/01 - 20:46:03

    So as we see, the calendar breaks at january 19th 2038, 03:14:07 is the last valid time. After that, the clock rolls around to year 1901, December 13th.

    Now, this example consists of two main modules. The local RTC (Real Time Counter, not to be mixed with RTC - Real Time Clock) which is updated every time interval "interval" set in:

    nrf_cal_set_callback(void (*callback)(void), uint32_t interval)

    This sets up an interrupt callback which increments the local time every interval. 

    Then there is the functionality that translates this into a day and time using the C-function mktime and localtime(), both from time.h.

    Exactly how this is implemented I am not sure. It is not written by Nordic Semiconductor. 

    Perhaps the safest way to work around this issue is to write the time.h functionality yourself in a custom module. Pseudo:

    void CAL_RTC_IRQHandler(void)
    {
        if(CAL_RTC->EVENTS_COMPARE[0])
        {
            CAL_RTC->EVENTS_COMPARE[0] = 0;
            
            CAL_RTC->TASKS_CLEAR = 1;
            
            //m_time += m_rtc_increment;
            // Assume m_rtc_increment < 60 seconds !
            
            if (time_struct.seconds+m_rtc_increment >= 60)
            {
                time_struct.minutes += 1;
                if (time_struct.minutes >= 60)
                {
                    time_struct.hours += 1;
                    if (time_struct.hours >= 24)
                    {
                        time_struct.day += 1;
                        ...
                    }
                }
            }
            
            time_struct.seconds = (m_time+m_rtc_increment)%60
            
            
            if(cal_event_callback) cal_event_callback();
        }
    }

    I realize when writing this that it becomes messy when you need to consider number of days in months, and leap years, but it is doable. This time library doesn't consider time zones, so you would have to add that either way, if necessary. 

    Best regards,

    Edvin

  • Yeah, so I thought about this and it seems to me that if I offset the RTCounter by 48 years I can actually still use the time.h in the current implementation to get an extra 26 years on top of 2038 - until 2066. 

    The reasoning behind this is this:

    epoch time starts 1.1.1970 (epoch 0), leap year before that is 1968 so offsetting 1970 by 48 years will give us the 2018 (leap year 2016 + 2 years as with 1968 + 2 years). So the time.h calendar will show 2002, but in reality this will be 2050. The calendar should otherwise match just fine as its adjusted for leap years.

    The correctness check is the epoch time counter - from Python the epoch time for 1.1.2066 is 3029529600, which is double of that of 1.1.2018 (1514764800).

    Any additional thoughts here? Really interested in any feedback.

  • Since the rules about leap years are quite easy within 2050 (not including the any years dividable by 100 or 400), then I believe this approach would work. The only thing I am not sure of is how leap seconds will affect this, but I am not sure whether this is that relevant in this case, since you need to set the time either way.

    As far as I can tell, it looks to be ok. You of course try to run through it. Decreasing the CAL_RTC->CC[0] will increase the calendar tickspeed, e.g. in nrf_cal_set_callback().

    By default it is set to CAL_RTC->CC[0] = interval * 8, because it is 8 ticks pr second. It needs to be an integer (not decimal), so you can set it to interval (not * 8), which means that you can get up to 8 calendar seconds per real time second. If you want it to tick even faster, you can decrease the CAL_RTC->PRESCALER set in nrf_cal_init()

    0xFFF = 4095.

    The tickrate of the RTC is defined by the prescaler as described in this description:

    https://infocenter.nordicsemi.com/topic/ps_nrf52840/rtc.html?cp=4_0_0_5_21_1#concept_iwc_1mj_sr

    So 0xFFF would mean 32768Hz / 4096 = 8Hz.

    To be honest, I don't think that leap seconds would be taken into account here. Perhaps they are usually added or subtracted to the Unix time, instead of adding them to the calculations for the current time.

    BR,

    Edvin

Related