cancel
Showing results for 
Search instead for 
Did you mean: 

Year 2038 Problem

Brian12412
Associate II

Does STM currently have a guideline or recommendation(s) for handling the 2038 problem?

1 ACCEPTED SOLUTION

Accepted Solutions

STM32 RTCs store year as last 2 digits. They also have a primitive leap-year calculation that only checks remainder of 4.
So the year will go from 0 to 99 and then wrap (overflow). So a fixed offset of 2000 means the year will fail at 2100.
Also leap years will fail in 2100 as 2100 is seen as a leap year, when it's not.

So they have two year-2100 problems.

I have a fix for both:

  1. Use build year to calculate current year. Will work from build year to buildyear + 99, then it will overflow to buildyear. So the program will fail in 100 years (in 2125) instead of 75 years (2100).
  2. if year in RTC is 00 and date is 29 feb, then jump to march 1st. This fixes leap years until 2399 since 2100, 2200 and 2300 are not leap years. Will fail in 2400 instead of 2100.

run this before processing rtc dates:

 

 

HAL_RTC_GetTime(&hrtc, &systemTimeTemp, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &systemDateTemp, RTC_FORMAT_BIN); // Always call GetDate after GetTime (RTC locking)

//leap year correction to allow proper leap year until 2400
if (systemDateTemp.Year == 0 && systemDateTemp.Month == 2 && systemDateTemp.Date == 29)
{
	//skip leap year as year is multiple of 100, but not multiple of 400
	systemDateTemp.Month = 3;
	systemDateTemp.Date = 1;

	__disable_irq();
	SystemTime = systemTimeTemp;
	SystemDate = systemDateTemp;
	__enable_irq();

	HAL_RTC_SetTime(&hrtc, &SystemTime, RTC_FORMAT_BIN);
	HAL_RTC_SetDate(&hrtc, &SystemDate, RTC_FORMAT_BIN);
}
else
{
	__disable_irq();
	SystemTime = systemTimeTemp;
	SystemDate = systemDateTemp;
	__enable_irq();
}

 

(Note that this only works if the routine is run every 24 hours. So if the device is not turned on when it thinks there is a leap year (monday march 1st 2100) it won't work. You would need some algorithm to flag if it skipped the leap day that year. You could probably use some spare RTC memory for this. So if it is after march 1st and this flag has not been set, then you should add 1 day to the date and set that flag. Clear the flag if the date is before feb 29 or if it is not 2100, 2200 or 2300. An alternative algorithm would keep the date in the calendar invalid and add an offset of 1 day to a copy.)

 

Use this to convert RTC year to absolute year:

 

 

#define DATE_TO_YEAR(d) (d[7] == '?' ? 1900 : \
                         (((d[7] - '0') * 1000 ) + (d[8] - '0') * 100 + (d[9] - '0') * 10 + d[10] -'0'))

#define BUILD_YEAR DATE_TO_YEAR(__DATE__)

// allow operation beyond 2099 (up to BUILD_YEAR + 99)
uint16_t yearAbs = systemDateCopy.Year + (BUILD_YEAR/100)*100;
if (yearAbs < BUILD_YEAR)
{
  yearAbs += 100;
}

 

 

If you want the program to work longer you need to periodically update the firmware to update the build date. Or you store the year in eeprom every year. RTC battery will fail before 2100. But you can always replace the battery and set the time again. It would be nice if products made now won't be automatically obsolete in 2100.

 

Kudo posts if you have the same problem and kudo replies if the solution works.
Click "Accept as Solution" if a reply solved your problem. If no solution was posted please answer with your own.

View solution in original post

8 REPLIES 8
tjaekel
Lead

What is it?

You mean the year 2038 problem - as @repaint suggested ?

Where do you see that being a specific issue for the STM32 ?

AScha.3
Chief III

You ask about a problem with systems which measure Unix time .

But here is not a Unix system, but just a controller with RTC , that has only years 10 + 1 in BCD : 2025 is only "25" in RTC :

AScha3_0-1740742377026.png

So its up to you, how you handle the full date in your software :

if using a uint_16t to store the "full" year, you will get a problem in the year 65536 - if your device still running and in use.  How long you expect it working and be in use ?

If you feel a post has answered your question, please click "Accept as Solution".

Depends a lot on the robustness of  design. Many RTC and calendars assume 2100 is a leap year.

Seen too many with a 1901 to 2099 limit, not that it's likely to be an issue, but people get lazy, and fewer are maintaining and understanding systems.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

STM32 RTCs store year as last 2 digits. They also have a primitive leap-year calculation that only checks remainder of 4.
So the year will go from 0 to 99 and then wrap (overflow). So a fixed offset of 2000 means the year will fail at 2100.
Also leap years will fail in 2100 as 2100 is seen as a leap year, when it's not.

So they have two year-2100 problems.

I have a fix for both:

  1. Use build year to calculate current year. Will work from build year to buildyear + 99, then it will overflow to buildyear. So the program will fail in 100 years (in 2125) instead of 75 years (2100).
  2. if year in RTC is 00 and date is 29 feb, then jump to march 1st. This fixes leap years until 2399 since 2100, 2200 and 2300 are not leap years. Will fail in 2400 instead of 2100.

run this before processing rtc dates:

 

 

HAL_RTC_GetTime(&hrtc, &systemTimeTemp, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &systemDateTemp, RTC_FORMAT_BIN); // Always call GetDate after GetTime (RTC locking)

//leap year correction to allow proper leap year until 2400
if (systemDateTemp.Year == 0 && systemDateTemp.Month == 2 && systemDateTemp.Date == 29)
{
	//skip leap year as year is multiple of 100, but not multiple of 400
	systemDateTemp.Month = 3;
	systemDateTemp.Date = 1;

	__disable_irq();
	SystemTime = systemTimeTemp;
	SystemDate = systemDateTemp;
	__enable_irq();

	HAL_RTC_SetTime(&hrtc, &SystemTime, RTC_FORMAT_BIN);
	HAL_RTC_SetDate(&hrtc, &SystemDate, RTC_FORMAT_BIN);
}
else
{
	__disable_irq();
	SystemTime = systemTimeTemp;
	SystemDate = systemDateTemp;
	__enable_irq();
}

 

(Note that this only works if the routine is run every 24 hours. So if the device is not turned on when it thinks there is a leap year (monday march 1st 2100) it won't work. You would need some algorithm to flag if it skipped the leap day that year. You could probably use some spare RTC memory for this. So if it is after march 1st and this flag has not been set, then you should add 1 day to the date and set that flag. Clear the flag if the date is before feb 29 or if it is not 2100, 2200 or 2300. An alternative algorithm would keep the date in the calendar invalid and add an offset of 1 day to a copy.)

 

Use this to convert RTC year to absolute year:

 

 

#define DATE_TO_YEAR(d) (d[7] == '?' ? 1900 : \
                         (((d[7] - '0') * 1000 ) + (d[8] - '0') * 100 + (d[9] - '0') * 10 + d[10] -'0'))

#define BUILD_YEAR DATE_TO_YEAR(__DATE__)

// allow operation beyond 2099 (up to BUILD_YEAR + 99)
uint16_t yearAbs = systemDateCopy.Year + (BUILD_YEAR/100)*100;
if (yearAbs < BUILD_YEAR)
{
  yearAbs += 100;
}

 

 

If you want the program to work longer you need to periodically update the firmware to update the build date. Or you store the year in eeprom every year. RTC battery will fail before 2100. But you can always replace the battery and set the time again. It would be nice if products made now won't be automatically obsolete in 2100.

 

Kudo posts if you have the same problem and kudo replies if the solution works.
Click "Accept as Solution" if a reply solved your problem. If no solution was posted please answer with your own.
Brian12412
Associate II

Thank you for the responses.  A lot of useful information that answers my original question.  I thought perhaps STM would have some core guidelines for this sort thing and therefore wanted to ask about it.

If you are using UNIX-epoch time, as used by functions prototyped in <time.h>, either by converting from calendar-style time or using the natively binary feature of RTCv1 or some incarnations of RTCv3 - and you use gcc with newlib, you may want to check the version of newlib you are using.

JW