RTEMS Time Representation Limits (or RTEMS End of Time)

The purpose of this topic is to discuss RTEMS time representations across the multiple supported APIs, the limits of those representations, and efforts underway to improve this. Help is appreciated checking the math and ensuring that this is explained well for users and RTEMS maintainers. If a data type is not covered here or the limits calculated incorrectly, please help correct it.

Given that it has multiple APIs, RTEMS has multiple representations of the date and time or Time of Day (e.g. TOD). This is also known as calendar time and POSIX refers to this as CLOCK_REALTIME. Each representation has a limited range based on the primitive data types used. The point of this topic is to highlight activities which expand the current limits, give a heads up on upcoming changes, and get some help computing the new limits.

RTEMS and the Year 2100

RTEMS development started in 1988. Initial leap year calculations could take advantage of the year 2000 being a leap year and simply do a modulo 4. But this breaks in the year 2100 since it is not a leap year. Because of this algorithmic limitation, dates based 2099 are not currently supported in RTEMS.

Merge Request 339 has changes to remove this limitation.

Date and Time Data Types

There are multiple POSIX data types used to represent the TOD:

  • POSIX time_t which is seconds since the POSIX epoch of January 1 1970
  • POSIX struct timespec defined in time.h which includes seconds and nanoseconds since the POSIX epoch of January 1 1970
  • POSIX struct timeva defined in sys/time.h which includes seconds and microseconds since the POSIX epoch of January 1 1970
  • POSIX struct tm defined in time.h which represents the TOD broken into components like month, day, year, etc.

Additionally, the RTEMS Classic API has the follow types to represent time;

  • Classic API rtems_time_of_day which is used by rtems_clock_get_tod() and other services
  • Classic API rtems_interval to represent seconds since the RTEMS epoch of January 1 1988. It is also used to specify delays and timeouts in clock ticks.

RTEMS supports the following time representations from BSD:

  • struct bintime is defined in this man page
  • sbintime_t is from the FreeBSD kernel to represent time with high precision. It is a “shrinked bintime” that fits into a standard int64_t .

POSIX time_t Limits

The Year 2038 Problem is when the 32-bit representation of seconds since the POSIX epoch overflows. It specific programming terms, it is when time_t overflows if is is a 32-bit signed type. RTEMS has had 64-bit time_t since the 5 release series. Per Google,

A signed 64-bit time_t value, which represents the number of seconds since the Unix Epoch (January 1, 1970, 00:00:00 UTC), has a theoretical maximum limit of 9,223,372,036,854,775,807 seconds.
This maximum value corresponds to a date in the year 292,277,026,596 AD, or approximately 292 billion years in the future, which is over twenty times the estimated age of the universe.

In practical terms, this is not a problem.

POSIX struct timespec

The POSIX definition of struct timespec is:

time_t  tv_sec    Seconds. 
long    tv_nsec   Nanoseconds.

With tv_sec defined as a time_t this has the 64-bit time_t limit.

POSIX struct timeval

The POSIX definition of struct timeval is:

time_t         tv_sec      Seconds. 
suseconds_t    tv_usec     Microseconds. 

With tv_sec defined as a time_t this has the 64-bit time_t limit.

The suseconds_t defined in sys/types.h per POSIX. It is signed and has the following requirement:

The type suseconds_t shall be a signed integer type capable of storing values at least in the range [-1, 1000000].

POSIX struct tm Limits

POSIX defines struct tm as having the following fields:

int    tm_sec   Seconds [0,60]. 
int    tm_min   Minutes [0,59]. 
int    tm_hour  Hour [0,23]. 
int    tm_mday  Day of month [1,31]. 
int    tm_mon   Month of year [0,11]. 
int    tm_year  Years since 1900. 
int    tm_wday  Day of week [0,6] (Sunday =0). 
int    tm_yday  Day of year [0,365]. 
int    tm_isdst Daylight Savings flag.

The field of interest is tm_year which is defined as a signed int. Without knowing the target architecture, this could be 16, 32, or 64 bits in size. Since RTEMS currently has no 16 bit targets, it is either 32 or 64 bits. Assuming it is 32-bits, this yields 2,147,483,647 since 1900 or the year 2,147,485,547 AD.

This is far from the 64-bit time_t end of time but very far in the future.

But being conservative, the C99 standard defines the minimum one can expect for INT_MAX as +32767 (e.g. 2^15 − 1). That sets a lower value for “last year” as 32767 - 1 - 1900 or 32576.

This could be the basis for an “end of time” error check for RTEMS. But there is another issue lurking – the fact that a year is not precisely 365 days. It is actually 365.2422 days (365 days, 5 hours, 48 minutes, 46 seconds). Leap years occur based on an assumption of 365.25 days. Given enough time, the error builds up. Per this:

around the year 4813 (year 1582 + 3231 years), a leap day will have to be omitted to position the calendar date of March 21st again close to the time of the primal equinox .

Without knowing when and how this error will be corrected for, the RTEMS “end of time” needs to be before 4813.

I think it would be fun to have an interesting number as the last year and have proposed 4096 which is a power of two. Or we could be boring and use 4099 which is exactly 2000 years from 2099 which is the limit we are trying to address.

BSD struct bintime

The struct bintime is defined in this man page as follows:

struct bintime {
  time_t   sec;
  uint64_t   frac;
};

With sec defined as a time_t this has the 64-bit time_t limit.

BSD sbinime_t

sbintime_t is from the FreeBSD kernel to represent time with high precision. It is a “shrinked bintime” that fits into a standard int64_t .

It is used in the RTEMS Classic API as an alternative representation of CLOCK_MONOTONIC by the rtems_clock_get_monotonic_sbintime().

It is not used to represent date and time.

Classic API rtems_time_of_day

The pertinent field in this structure is year but not an offset as it was in struct tm. The rtems_time_of_day structure is defined in rtems/rtems/types.h as follows:

uint32_t year;

The RTEMS Epoch is January 1, 1988 but that is not a factor here. The limit is 4,294,967,295 which is well beyond what the signed int in struct tm can represent.

Classic API rtems_interval

In all versions of RTEMS including the upcoming 7.1, rtems_interval is defined as an unsigned 32-bit integer. This type is used in multiple places:

  • It is the return value from rtems_clock_get_seconds_since_epoch(). This results in a limit lower than the 64-bit time_t.
  • It is used for all delays and timeouts in the Classic API which are measured in clock ticks. This results in the longest delay or timeout being (2^32) milliseconds or 49.7102696 days assuming a 1 millisecond clock tick. Since some systems are now using 250 microsecond ticks, this brings the maximum delay or timeout on those systems to 12.4275674 days.

It is anticipated that rtems_interval change to an unsigned 64-bit value in RTEMS 8.1. But there is no commitment to that yet and investigation will need to be performed to evaluate the impact.

1 Like