summaryrefslogtreecommitdiff
path: root/cpukit/rtems/src/clockgettod.c
blob: f65f5e2f4b1a8381fa1812fce9c3788e84e16976 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/**
 * @file
 *
 * @brief Obtain Current Time of Day (Classic TOD)
 * @ingroup ClassicClock Clocks
 */

/*
 *  COPYRIGHT (c) 1989-2007.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <rtems/rtems/clock.h>
#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
)
{
  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;

  if ( !_TOD_Is_set() )
    return RTEMS_NOT_DEFINED;

  /* Obtain the current time */
  _TOD_Get_timeval( &now );

  /* 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;
}