constrain fastA2I to [0-9] (vice [0-9A])
[inav.git] / src / main / common / time.c
blobf1204c57f4adb08b42146b0cc0123d48c3110f67
1 /*
2 * This file is part of INAV.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
6 * You can obtain one at http://mozilla.org/MPL/2.0/.
8 * Alternatively, the contents of this file may be used under the terms
9 * of the GNU General Public License Version 3, as described below:
11 * This file is free software: you may copy, redistribute and/or modify
12 * it under the terms of the GNU General Public License as published by the
13 * Free Software Foundation, either version 3 of the License, or (at your
14 * option) any later version.
16 * This file is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
19 * Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see http://www.gnu.org/licenses/.
24 * @author Alberto Garcia Hierro <alberto@garciahierro.com>
27 #include "common/maths.h"
28 #include "common/printf.h"
29 #include "common/time.h"
30 #include "common/utils.h"
32 #include "config/parameter_group_ids.h"
34 #include "drivers/time.h"
36 #include "fc/settings.h"
38 // For the "modulo 4" arithmetic to work, we need a leap base year
39 #define REFERENCE_YEAR 2000
40 // Offset (seconds) from the UNIX epoch (1970-01-01) to 2000-01-01
41 #define EPOCH_2000_OFFSET 946684800
43 #define MILLIS_PER_SECOND 1000
45 // rtcTime_t when the system was started.
46 // Calculated in rtcSet().
47 static rtcTime_t started = 0;
49 static const uint16_t days[4][12] =
51 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
52 { 366, 397, 425, 456, 486, 517, 547, 578, 609, 639, 670, 700},
53 { 731, 762, 790, 821, 851, 882, 912, 943, 974, 1004, 1035, 1065},
54 {1096, 1127, 1155, 1186, 1216, 1247, 1277, 1308, 1339, 1369, 1400, 1430},
57 PG_REGISTER_WITH_RESET_TEMPLATE(timeConfig_t, timeConfig, PG_TIME_CONFIG, 1);
59 PG_RESET_TEMPLATE(timeConfig_t, timeConfig,
60 .tz_offset = SETTING_TZ_OFFSET_DEFAULT,
61 .tz_automatic_dst = SETTING_TZ_AUTOMATIC_DST_DEFAULT,
64 static rtcTime_t dateTimeToRtcTime(const dateTime_t *dt)
66 unsigned int second = dt->seconds; // 0-59
67 unsigned int minute = dt->minutes; // 0-59
68 unsigned int hour = dt->hours; // 0-23
69 unsigned int day = dt->day - 1; // 0-30
70 unsigned int month = dt->month - 1; // 0-11
71 unsigned int year = dt->year - REFERENCE_YEAR; // 0-99
72 int32_t unixTime = (((year / 4 * (365 * 4 + 1) + days[year % 4][month] + day) * 24 + hour) * 60 + minute) * 60 + second + EPOCH_2000_OFFSET;
73 return rtcTimeMake(unixTime, dt->millis);
76 static void rtcTimeToDateTime(dateTime_t *dt, rtcTime_t t)
78 int32_t unixTime = t / MILLIS_PER_SECOND - EPOCH_2000_OFFSET;
79 dt->seconds = unixTime % 60;
80 unixTime /= 60;
81 dt->minutes = unixTime % 60;
82 unixTime /= 60;
83 dt->hours = unixTime % 24;
84 unixTime /= 24;
86 unsigned int years = unixTime / (365 * 4 + 1) * 4;
87 unixTime %= 365 * 4 + 1;
89 unsigned int year;
90 for (year = 3; year > 0; year--) {
91 if (unixTime >= days[year][0]) {
92 break;
96 unsigned int month;
97 for (month = 11; month > 0; month--) {
98 if (unixTime >= days[year][month]) {
99 break;
103 dt->year = years + year + REFERENCE_YEAR;
104 dt->month = month + 1;
105 dt->day = unixTime - days[year][month] + 1;
106 dt->millis = t % MILLIS_PER_SECOND;
109 static void rtcGetDefaultDateTime(dateTime_t *dateTime)
111 dateTime->year = 0;
112 dateTime->month = 1;
113 dateTime->day = 1;
114 dateTime->hours = 0;
115 dateTime->minutes = 0;
116 dateTime->seconds = 0;
117 dateTime->millis = 0;
120 static bool rtcIsDateTimeValid(dateTime_t *dateTime)
122 return (dateTime->year >= REFERENCE_YEAR) &&
123 (dateTime->month >= 1 && dateTime->month <= 12) &&
124 (dateTime->day >= 1 && dateTime->day <= 31) &&
125 (dateTime->hours <= 23) &&
126 (dateTime->minutes <= 59) &&
127 (dateTime->seconds <= 59) &&
128 (dateTime->millis <= 999);
131 #if defined(RTC_AUTOMATIC_DST)
132 static int lastSundayOfMonth(int currentYear, int wantedMonth)
134 int days[] = { 31 , 29 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 };
135 days[1] -= (currentYear % 4) || (!(currentYear % 100) && (currentYear % 400));
136 int w = currentYear * 365 + (currentYear - 1) / 4 - (currentYear - 1) / 100 + (currentYear - 1) / 400 + 6;
138 for (int m = 0; m < 12; m++) {
139 w = (w + days[m]) % 7;
140 if (m == wantedMonth - 1) {
141 return days[m] - w;
144 return 0;
147 static int nthSundayOfMonth(int lastSunday, int nth)
149 while (lastSunday > 7 * nth) {
150 lastSunday -= 7;
152 return lastSunday;
155 static bool isDST(rtcTime_t t)
157 dateTime_t dateTime;
158 rtcTimeToDateTime(&dateTime, t);
159 int lastSunday;
160 switch ((tz_automatic_dst_e) timeConfig()->tz_automatic_dst) {
161 case TZ_AUTO_DST_OFF:
162 break;
163 case TZ_AUTO_DST_EU: // begins at 1:00 a.m. on the last Sunday of March and ends at 1:00 a.m. on the last Sunday of October
164 if (dateTime.month < 3 || dateTime.month > 10) {
165 return false;
167 if (dateTime.month > 3 && dateTime.month < 10) {
168 return true;
170 lastSunday = lastSundayOfMonth(dateTime.year, dateTime.month);
171 if ((dateTime.day < lastSunday) || (dateTime.day > lastSunday)) {
172 return !(dateTime.month == 3);
174 if (dateTime.day == lastSunday) {
175 if (dateTime.month == 3) {
176 return dateTime.hours >= 1;
178 if (dateTime.month == 10) {
179 return dateTime.hours < 1;
182 break;
183 case TZ_AUTO_DST_USA: // begins at 2:00 a.m. on the second Sunday of March and ends at 2:00 a.m. on the first Sunday of November
184 if (dateTime.month < 3 || dateTime.month > 11) {
185 return false;
187 if (dateTime.month > 3 && dateTime.month < 11) {
188 return true;
190 lastSunday = lastSundayOfMonth(dateTime.year, dateTime.month);
191 if (dateTime.month == 3) {
192 int secondSunday = nthSundayOfMonth(lastSunday, 2);
193 if (dateTime.day == secondSunday) {
194 return dateTime.hours >= 2;
196 return dateTime.day > secondSunday;
198 if (dateTime.month == 11) {
199 int firstSunday = nthSundayOfMonth(lastSunday, 1);
200 if (dateTime.day == firstSunday) {
201 return dateTime.hours < 2;
203 return dateTime.day < firstSunday;
205 break;
207 return false;
209 #endif
211 static void dateTimeWithOffset(dateTime_t *dateTimeOffset, const dateTime_t *dateTimeInitial, int16_t *minutes, bool automatic_dst)
213 rtcTime_t initialTime = dateTimeToRtcTime(dateTimeInitial);
214 rtcTime_t offsetTime = rtcTimeMake(rtcTimeGetSeconds(&initialTime) + *minutes * 60, rtcTimeGetMillis(&initialTime));
215 #if defined(RTC_AUTOMATIC_DST)
216 if (automatic_dst && isDST(offsetTime)) {
217 // Add one hour. Tell the caller that the
218 // offset has changed.
219 *minutes += 60;
220 offsetTime += 60 * 60 * MILLIS_PER_SECOND;
222 #else
223 UNUSED(automatic_dst);
224 #endif
225 rtcTimeToDateTime(dateTimeOffset, offsetTime);
228 static bool dateTimeFormat(char *buf, dateTime_t *dateTime, int16_t offset, bool automatic_dst)
230 dateTime_t local;
232 int tz_hours = 0;
233 int tz_minutes = 0;
234 bool retVal = true;
236 // Apply offset if necessary
237 if (offset != 0 || automatic_dst) {
238 dateTimeWithOffset(&local, dateTime, &offset, automatic_dst);
239 tz_hours = offset / 60;
240 tz_minutes = ABS(offset % 60);
241 dateTime = &local;
244 if (!rtcIsDateTimeValid(dateTime)) {
245 rtcGetDefaultDateTime(&local);
246 dateTime = &local;
247 retVal = false;
250 // XXX: Changes to this format might require updates in
251 // dateTimeSplitFormatted()
252 tfp_sprintf(buf, "%04u-%02u-%02uT%02u:%02u:%02u.%03u%c%02d:%02d",
253 dateTime->year, dateTime->month, dateTime->day,
254 dateTime->hours, dateTime->minutes, dateTime->seconds, dateTime->millis,
255 tz_hours >= 0 ? '+' : '-', ABS(tz_hours), tz_minutes);
257 return retVal;
260 rtcTime_t rtcTimeMake(int32_t secs, uint16_t millis)
262 return ((rtcTime_t)secs) * MILLIS_PER_SECOND + millis;
265 int32_t rtcTimeGetSeconds(rtcTime_t *t)
267 return *t / MILLIS_PER_SECOND;
270 uint16_t rtcTimeGetMillis(rtcTime_t *t)
272 return *t % MILLIS_PER_SECOND;
275 bool dateTimeFormatUTC(char *buf, dateTime_t *dt)
277 return dateTimeFormat(buf, dt, 0, false);
280 bool dateTimeFormatLocal(char *buf, dateTime_t *dt)
282 return dateTimeFormat(buf, dt, timeConfig()->tz_offset, true);
285 void dateTimeUTCToLocal(dateTime_t *localDateTime, const dateTime_t *utcDateTime)
287 int16_t offset = timeConfig()->tz_offset;
288 dateTimeWithOffset(localDateTime, utcDateTime, &offset, true);
291 bool dateTimeSplitFormatted(char *formatted, char **date, char **time)
293 // Just look for the T and replace it with a zero
294 // XXX: Keep in sync with dateTimeFormat()
295 for (char *p = formatted; *p; p++) {
296 if (*p == 'T') {
297 *date = formatted;
298 *time = (p+1);
299 *p = '\0';
300 return true;
303 return false;
306 bool rtcHasTime(void)
308 return started != 0;
311 bool rtcGet(rtcTime_t *t)
313 if (!rtcHasTime()) {
314 return false;
316 *t = started + millis();
317 return true;
320 bool rtcSet(rtcTime_t *t)
322 started = *t - millis();
323 return true;
326 bool rtcGetDateTime(dateTime_t *dt)
328 rtcTime_t t;
329 if (rtcGet(&t)) {
330 rtcTimeToDateTime(dt, t);
331 return true;
333 // No time stored, fill dt with 0000-01-01T00:00:00.000
334 rtcGetDefaultDateTime(dt);
335 return false;
338 bool rtcGetDateTimeLocal(dateTime_t *dt)
340 if (rtcGetDateTime(dt)) {
341 dateTimeUTCToLocal(dt, dt);
342 return true;
344 return false;
347 bool rtcSetDateTime(dateTime_t *dt)
349 rtcTime_t t = dateTimeToRtcTime(dt);
350 return rtcSet(&t);