From eae0863a1cf09d5f2f6f6af5df405dc9b727fac5 Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Sun, 27 Apr 2014 18:11:09 +1000 Subject: rtems: Fix sp2038 test. Avoid using newlib's gmtime_r call which fails with a max signed int. Add an RTEMS specific version for 1/1/1988 to 31/12/2100. Update sp2038 to test every day from 1/1/1988 to 31/12/2100. Only days need be tested as the code splits the seconds based on days. --- cpukit/rtems/src/clockgettod.c | 107 ++++++++++++++++++++++++++++++++++----- testsuites/sptests/sp2038/init.c | 37 ++++++++++++++ 2 files changed, 130 insertions(+), 14 deletions(-) diff --git a/cpukit/rtems/src/clockgettod.c b/cpukit/rtems/src/clockgettod.c index 743e1ecb42..eb01f0ffdc 100644 --- a/cpukit/rtems/src/clockgettod.c +++ b/cpukit/rtems/src/clockgettod.c @@ -22,13 +22,74 @@ #include #include +#define RTEMS_SECS_PER_MINUTE (60UL) +#define RTEMS_MINUTE_PER_HOUR (60UL) +#define RTEMS_SECS_PER_HOUR (RTEMS_SECS_PER_MINUTE * RTEMS_MINUTE_PER_HOUR) +#define RTEMS_HOURS_PER_DAY (24UL) +#define RTEMS_SECS_PER_DAY (RTEMS_SECS_PER_HOUR * RTEMS_HOURS_PER_DAY) +#define RTEMS_DAYS_PER_YEAR (365UL) +#define RTEMS_YEAR_BASE (1970UL) + +extern const uint16_t _TOD_Days_to_date[2][13]; + +static bool _Leap_year( + uint32_t year +) +{ + return (((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0); +} + +static uint32_t _Leap_years_before( + uint32_t year +) +{ + year -= 1; + return (year / 4) - (year / 100) + (year / 400); +} + +static uint32_t _Leap_years_between( + uint32_t from, uint32_t to +) +{ + return _Leap_years_before( to ) - _Leap_years_before( from + 1 ); +} + +static uint32_t _Year_day_as_month( + uint32_t year, uint32_t *day +) +{ + const uint16_t* days_to_date; + uint32_t month = 0; + + if ( _Leap_year( year ) ) + days_to_date = _TOD_Days_to_date[1]; + else + days_to_date = _TOD_Days_to_date[0]; + + days_to_date += 2; + + while (month < 11) { + if (*day < *days_to_date) + break; + ++month; + ++days_to_date; + } + + *day -= *(days_to_date - 1); + + return month; +} + rtems_status_code rtems_clock_get_tod( rtems_time_of_day *time_buffer ) { - rtems_time_of_day *tmbuf = time_buffer; - struct tm time; struct timeval now; + uint32_t days; + uint32_t day_secs; + uint32_t year; + uint32_t year_days; + uint32_t leap_years; if ( !time_buffer ) return RTEMS_INVALID_ADDRESS; @@ -39,18 +100,36 @@ rtems_status_code rtems_clock_get_tod( /* Obtain the current time */ _TOD_Get_timeval( &now ); - /* Split it into a closer format */ - gmtime_r( &now.tv_sec, &time ); - - /* Now adjust it to the RTEMS format */ - tmbuf->year = time.tm_year + 1900; - tmbuf->month = time.tm_mon + 1; - tmbuf->day = time.tm_mday; - tmbuf->hour = time.tm_hour; - tmbuf->minute = time.tm_min; - tmbuf->second = time.tm_sec; - tmbuf->ticks = now.tv_usec / - rtems_configuration_get_microseconds_per_tick(); + /* How many days and how many seconds in the day ? */ + days = now.tv_sec / RTEMS_SECS_PER_DAY; + day_secs = now.tv_sec % RTEMS_SECS_PER_DAY; + + /* How many non-leap year years ? */ + year = ( days / RTEMS_DAYS_PER_YEAR ) + RTEMS_YEAR_BASE; + + /* Determine the number of leap years. */ + leap_years = _Leap_years_between( RTEMS_YEAR_BASE, year ); + + /* Adjust the remaining number of days based on the leap years. */ + year_days = ( days - leap_years ) % RTEMS_DAYS_PER_YEAR; + + /* Adjust the year and days in the year if in the leap year overflow. */ + if ( leap_years > ( days % RTEMS_DAYS_PER_YEAR ) ) { + year -= 1; + if ( _Leap_year( year ) ) { + year_days += 1; + } + } + + time_buffer->year = year; + time_buffer->month = _Year_day_as_month( year, &year_days ) + 1; + time_buffer->day = year_days + 1; + time_buffer->hour = day_secs / RTEMS_SECS_PER_HOUR; + time_buffer->minute = day_secs % RTEMS_SECS_PER_HOUR; + time_buffer->second = time_buffer->minute % RTEMS_SECS_PER_MINUTE; + time_buffer->minute = time_buffer->minute / RTEMS_SECS_PER_MINUTE; + time_buffer->ticks = now.tv_usec / + rtems_configuration_get_microseconds_per_tick( ); return RTEMS_SUCCESSFUL; } diff --git a/testsuites/sptests/sp2038/init.c b/testsuites/sptests/sp2038/init.c index 9fac38a23c..e5a3906ee6 100644 --- a/testsuites/sptests/sp2038/init.c +++ b/testsuites/sptests/sp2038/init.c @@ -283,12 +283,49 @@ static void test_leap_year(void) rtems_test_assert(test_status == false); } +static bool test_year_is_leap_year(uint32_t year) +{ + return (((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0); +} + +static void test_every_day(void) +{ + rtems_time_of_day every_day = { + .year = 1970, + .month = 1, + .day = 1, + .hour = 0, + .minute = 1, + .second = 2 + }; + const int days_per_month[2][12] = { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + }; + rtems_status_code sc = RTEMS_SUCCESSFUL; + rtems_time_of_day now; + + for (every_day.year = 1988; every_day.year <= 2100; ++every_day.year) { + int leap_year = test_year_is_leap_year(every_day.year) ? 1 : 0; + for (every_day.month = 1; every_day.month <= 12; ++every_day.month) { + int days = days_per_month[leap_year][every_day.month - 1]; + for (every_day.day = 1; every_day.day <= days; ++every_day.day) { + sc = rtems_clock_set(&every_day); + ASSERT_SC(sc); + sc = rtems_clock_get_tod(&now); + ASSERT_SC(sc); + rtems_test_assert(memcmp(&now, &every_day, sizeof(now)) == 0); + } + } + } +} rtems_task Init(rtems_task_argument argument) { TEST_BEGIN(); test_tod_to_seconds(); + test_every_day(); test_problem_year(); test_leap_year(); -- cgit v1.2.3