[PATCH] rtems: Fix sp2038 test.

Chris Johns chrisj at rtems.org
Sun Apr 27 08:17:19 UTC 2014


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 743e1ec..eb01f0f 100644
--- a/cpukit/rtems/src/clockgettod.c
+++ b/cpukit/rtems/src/clockgettod.c
@@ -22,13 +22,74 @@
 #include <rtems/score/todimpl.h>
 #include <rtems/config.h>
 
+#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 9fac38a..e5a3906 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();
 
-- 
1.9.0




More information about the devel mailing list