You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
110 lines
2.9 KiB
110 lines
2.9 KiB
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include "common_utils/datetime.h"
|
|
|
|
const char *DT_WKDAY_NAMES[] = {
|
|
[MONDAY] = "Mon",
|
|
[TUESDAY] = "Tue",
|
|
[WEDNESDAY] = "Wed",
|
|
[THURSDAY] = "Thu",
|
|
[FRIDAY] = "Fri",
|
|
[SATURDAY] = "Sat",
|
|
[SUNDAY] = "Sun"
|
|
};
|
|
|
|
const char *DT_WKDAY_NAMES_FULL[] = {
|
|
[MONDAY] = "Monday",
|
|
[TUESDAY] = "Tuesday",
|
|
[WEDNESDAY] = "Wednesday",
|
|
[THURSDAY] = "Thursday",
|
|
[FRIDAY] = "Friday",
|
|
[SATURDAY] = "Saturday",
|
|
[SUNDAY] = "Sunday"
|
|
};
|
|
|
|
const char *DT_MONTH_NAMES[] = {
|
|
[JANUARY] = "Jan",
|
|
[FEBRUARY] = "Feb",
|
|
[MARCH] = "Mar",
|
|
[APRIL] = "Apr",
|
|
[MAY] = "May",
|
|
[JUNE] = "Jun",
|
|
[JULY] = "Jul",
|
|
[AUGUST] = "Aug",
|
|
[SEPTEMBER] = "Sep",
|
|
[OCTOBER] = "Oct",
|
|
[NOVEMBER] = "Nov",
|
|
[DECEMBER] = "Dec"
|
|
};
|
|
|
|
const char *DT_MONTH_NAMES_FULL[] = {
|
|
[JANUARY] = "January",
|
|
[FEBRUARY] = "February",
|
|
[MARCH] = "March",
|
|
[APRIL] = "April",
|
|
[MAY] = "May",
|
|
[JUNE] = "June",
|
|
[JULY] = "July",
|
|
[AUGUST] = "August",
|
|
[SEPTEMBER] = "September",
|
|
[OCTOBER] = "October",
|
|
[NOVEMBER] = "November",
|
|
[DECEMBER] = "December"
|
|
};
|
|
|
|
static const uint16_t MONTH_LENGTHS[] = { /* 1-based, normal year */
|
|
[JANUARY]=31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
|
};
|
|
_Static_assert(sizeof(MONTH_LENGTHS) / sizeof(uint16_t) == 13, "Months array length");
|
|
|
|
static const uint16_t MONTH_YEARDAYS[] = { /* 1-based */
|
|
[JANUARY]=0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 // // days until 1st of month
|
|
};
|
|
_Static_assert(sizeof(MONTH_YEARDAYS) / sizeof(uint16_t) == 13, "Months array length");
|
|
|
|
_Static_assert(MONDAY < SUNDAY, "Weekday ordering");
|
|
|
|
bool datetime_is_valid(const datetime_t *dt)
|
|
{
|
|
if (dt == NULL) return false;
|
|
|
|
// check month first to avoid out-of-bounds read from the MONTH_LENGTHS table
|
|
if (!(dt->month >= JANUARY && dt->month <= DECEMBER)) return false;
|
|
|
|
int monthlen = MONTH_LENGTHS[dt->month];
|
|
if (dt->month == FEBRUARY && is_leap_year(dt->year)) {
|
|
monthlen = 29;
|
|
}
|
|
|
|
return dt->sec < 60 &&
|
|
dt->min < 60 &&
|
|
dt->hour < 24 &&
|
|
dt->wkday >= MONDAY &&
|
|
dt->wkday <= SUNDAY &&
|
|
dt->year >= DT_START_YEAR &&
|
|
dt->year <= DT_END_YEAR &&
|
|
dt->day >= 1 &&
|
|
dt->day <= monthlen;
|
|
}
|
|
|
|
bool datetime_set_weekday(datetime_t *dt)
|
|
{
|
|
dt->wkday = MONDAY; // prevent the validator func erroring out on invalid weekday
|
|
if (!datetime_is_valid(dt)) return false;
|
|
dt->wkday = date_weekday(dt->year, dt->month, dt->day);
|
|
return true;
|
|
}
|
|
|
|
enum weekday date_weekday(uint16_t year, enum month month, uint8_t day)
|
|
{
|
|
uint16_t days = (DT_START_WKDAY - MONDAY) + (year - DT_START_YEAR) * 365 + MONTH_YEARDAYS[month] + (day - 1);
|
|
|
|
for (uint16_t i = DT_START_YEAR; i <= year; i++) {
|
|
if (is_leap_year(i) && (i < year || month > FEBRUARY)) days++;
|
|
}
|
|
|
|
return MONDAY + days % 7;
|
|
}
|
|
|
|
|