16
16
17
17
#include "mbed_mktime.h"
18
18
19
- /*
20
- * time constants
21
- */
19
+ /* Time constants. */
22
20
#define SECONDS_BY_MINUTES 60
23
21
#define MINUTES_BY_HOUR 60
24
22
#define SECONDS_BY_HOUR (SECONDS_BY_MINUTES * MINUTES_BY_HOUR)
25
23
#define HOURS_BY_DAY 24
26
24
#define SECONDS_BY_DAY (SECONDS_BY_HOUR * HOURS_BY_DAY)
25
+ #define LAST_VALID_YEAR 206
26
+
27
+ /* Macros which will be used to determine if we are within valid range. */
28
+ #define EDGE_TIMESTAMP_FULL_LEAP_YEAR_SUPPORT 3220095 // 7th of February 1970 at 06:28:15
29
+ #define EDGE_TIMESTAMP_4_YEAR_LEAP_YEAR_SUPPORT 3133695 // 6th of February 1970 at 06:28:15
27
30
28
31
/*
29
32
* 2 dimensional array containing the number of seconds elapsed before a given
@@ -63,10 +66,10 @@ static const uint32_t seconds_before_month[2][12] = {
63
66
}
64
67
};
65
68
66
- bool _rtc_is_leap_year (int year ) {
69
+ bool _rtc_is_leap_year (int year , rtc_leap_year_support_t leap_year_support ) {
67
70
/*
68
71
* since in practice, the value manipulated by this algorithm lie in the
69
- * range [70 : 138], the algorith can be reduced to: year % 4.
72
+ * range: [70 : 206] the algorithm can be reduced to: year % 4 with exception for 200 (year 2100 is not leap year) .
70
73
* The algorithm valid over the full range of value is:
71
74
72
75
year = 1900 + year;
@@ -80,86 +83,108 @@ bool _rtc_is_leap_year(int year) {
80
83
return true;
81
84
82
85
*/
86
+ if (leap_year_support == RTC_FULL_LEAP_YEAR_SUPPORT && year == 200 ) {
87
+ return false; // 2100 is not a leap year
88
+ }
89
+
83
90
return (year ) % 4 ? false : true;
84
91
}
85
92
86
- time_t _rtc_mktime (const struct tm * time ) {
87
- // partial check for the upper bound of the range
88
- // normalization might happen at the end of the function
89
- // this solution is faster than checking if the input is after the 19th of
90
- // january 2038 at 03:14:07.
91
- if ((time -> tm_year < 70 ) || (time -> tm_year > 138 )) {
92
- return ((time_t ) - 1 );
93
+ bool _rtc_maketime (const struct tm * time , time_t * seconds , rtc_leap_year_support_t leap_year_support ) {
94
+ if (seconds == NULL || time == NULL ) {
95
+ return false;
96
+ }
97
+
98
+ /* Partial check for the upper bound of the range - check years only. Full check will be performed after the
99
+ * elapsed time since the beginning of the year is calculated.
100
+ */
101
+ if ((time -> tm_year < 70 ) || (time -> tm_year > LAST_VALID_YEAR )) {
102
+ return false;
93
103
}
94
104
95
105
uint32_t result = time -> tm_sec ;
96
106
result += time -> tm_min * SECONDS_BY_MINUTES ;
97
107
result += time -> tm_hour * SECONDS_BY_HOUR ;
98
108
result += (time -> tm_mday - 1 ) * SECONDS_BY_DAY ;
99
- result += seconds_before_month [_rtc_is_leap_year (time -> tm_year )][time -> tm_mon ];
109
+ result += seconds_before_month [_rtc_is_leap_year (time -> tm_year , leap_year_support )][time -> tm_mon ];
110
+
111
+ /* Check if we are within valid range. */
112
+ if (time -> tm_year == LAST_VALID_YEAR ) {
113
+ if ((leap_year_support == RTC_FULL_LEAP_YEAR_SUPPORT && result > EDGE_TIMESTAMP_FULL_LEAP_YEAR_SUPPORT ) ||
114
+ (leap_year_support == RTC_4_YEAR_LEAP_YEAR_SUPPORT && result > EDGE_TIMESTAMP_4_YEAR_LEAP_YEAR_SUPPORT )) {
115
+ return false;
116
+ }
117
+ }
100
118
101
119
if (time -> tm_year > 70 ) {
102
- // valid in the range [70:138]
120
+ /* Valid in the range [70:206]. */
103
121
uint32_t count_of_leap_days = ((time -> tm_year - 1 ) / 4 ) - (70 / 4 );
122
+ if (leap_year_support == RTC_FULL_LEAP_YEAR_SUPPORT ) {
123
+ if (time -> tm_year > 200 ) {
124
+ count_of_leap_days -- ; // 2100 is not a leap year
125
+ }
126
+ }
127
+
104
128
result += (((time -> tm_year - 70 ) * 365 ) + count_of_leap_days ) * SECONDS_BY_DAY ;
105
129
}
106
130
107
- if (result > INT32_MAX ) {
108
- return (time_t ) - 1 ;
109
- }
131
+ * seconds = result ;
110
132
111
- return result ;
133
+ return true ;
112
134
}
113
135
114
- bool _rtc_localtime (time_t timestamp , struct tm * time_info ) {
115
- if ((( int32_t ) timestamp ) < 0 ) {
136
+ bool _rtc_localtime (time_t timestamp , struct tm * time_info , rtc_leap_year_support_t leap_year_support ) {
137
+ if (time_info == NULL ) {
116
138
return false;
117
- }
139
+ }
140
+
141
+ uint32_t seconds = (uint32_t )timestamp ;
118
142
119
- time_info -> tm_sec = timestamp % 60 ;
120
- timestamp = timestamp / 60 ; // timestamp in minutes
121
- time_info -> tm_min = timestamp % 60 ;
122
- timestamp = timestamp / 60 ; // timestamp in hours
123
- time_info -> tm_hour = timestamp % 24 ;
124
- timestamp = timestamp / 24 ; // timestamp in days;
143
+ time_info -> tm_sec = seconds % 60 ;
144
+ seconds = seconds / 60 ; // timestamp in minutes
145
+ time_info -> tm_min = seconds % 60 ;
146
+ seconds = seconds / 60 ; // timestamp in hours
147
+ time_info -> tm_hour = seconds % 24 ;
148
+ seconds = seconds / 24 ; // timestamp in days;
125
149
126
- // compute the weekday
127
- // The 1st of January 1970 was a Thursday which is equal to 4 in the weekday
128
- // representation ranging from [0:6]
129
- time_info -> tm_wday = (timestamp + 4 ) % 7 ;
150
+ /* Compute the weekday.
151
+ * The 1st of January 1970 was a Thursday which is equal to 4 in the weekday representation ranging from [0:6].
152
+ */
153
+ time_info -> tm_wday = (seconds + 4 ) % 7 ;
130
154
131
- // years start at 70
155
+ /* Years start at 70. */
132
156
time_info -> tm_year = 70 ;
133
157
while (true) {
134
- if (_rtc_is_leap_year (time_info -> tm_year ) && timestamp >= 366 ) {
158
+ if (_rtc_is_leap_year (time_info -> tm_year , leap_year_support ) && seconds >= 366 ) {
135
159
++ time_info -> tm_year ;
136
- timestamp -= 366 ;
137
- } else if (!_rtc_is_leap_year (time_info -> tm_year ) && timestamp >= 365 ) {
160
+ seconds -= 366 ;
161
+ } else if (!_rtc_is_leap_year (time_info -> tm_year , leap_year_support ) && seconds >= 365 ) {
138
162
++ time_info -> tm_year ;
139
- timestamp -= 365 ;
163
+ seconds -= 365 ;
140
164
} else {
141
- // the remaining days are less than a years
165
+ /* The remaining days are less than a years. */
142
166
break ;
143
167
}
144
168
}
145
169
146
- time_info -> tm_yday = timestamp ;
170
+ time_info -> tm_yday = seconds ;
147
171
148
- // convert days into seconds and find the current month
149
- timestamp *= SECONDS_BY_DAY ;
172
+ /* Convert days into seconds and find the current month. */
173
+ seconds *= SECONDS_BY_DAY ;
150
174
time_info -> tm_mon = 11 ;
151
- bool leap = _rtc_is_leap_year (time_info -> tm_year );
175
+ bool leap = _rtc_is_leap_year (time_info -> tm_year , leap_year_support );
152
176
for (uint32_t i = 0 ; i < 12 ; ++ i ) {
153
- if ((uint32_t ) timestamp < seconds_before_month [leap ][i ]) {
177
+ if ((uint32_t ) seconds < seconds_before_month [leap ][i ]) {
154
178
time_info -> tm_mon = i - 1 ;
155
179
break ;
156
180
}
157
181
}
158
182
159
- // remove month from timestamp and compute the number of days.
160
- // note: unlike other fields, days are not 0 indexed.
161
- timestamp -= seconds_before_month [leap ][time_info -> tm_mon ];
162
- time_info -> tm_mday = (timestamp / SECONDS_BY_DAY ) + 1 ;
183
+ /* Remove month from timestamp and compute the number of days.
184
+ * Note: unlike other fields, days are not 0 indexed.
185
+ */
186
+ seconds -= seconds_before_month [leap ][time_info -> tm_mon ];
187
+ time_info -> tm_mday = (seconds / SECONDS_BY_DAY ) + 1 ;
163
188
164
189
return true;
165
190
}
0 commit comments