#include #include ph_date_t date_from_string(const char str[static 11]) { unsigned year = (str[0] - '0') * 1000 + (str[1] - '0') * 100 + (str[2] - '0') * 10 + (str[3] - '0'); unsigned month = (str[5] - '0') * 10 + (str[6] - '0'); unsigned day = (str[8] - '0') * 10 + (str[9] - '0'); return date_from_numbers(year, month, day); } ph_date_t date_from_numbers(unsigned year, unsigned month, unsigned day) { return year << 9 | month << 5 | day; } struct tm tm_from_date(ph_date_t date) { unsigned year = 0; unsigned month = 0; unsigned day = 0; date_split(date, &year, &month, &day); struct tm time = {.tm_year = year - 1900, .tm_mon = month - 1, .tm_mday = day}; mktime(&time); return time; } ph_date_t date_from_tm(struct tm time) { unsigned year = time.tm_year + 1900; unsigned month = time.tm_mon + 1; unsigned day = time.tm_mday; return date_from_numbers(year, month, day); } ph_date_t current_date() { time_t t = time(NULL); struct tm *time = localtime(&t); unsigned year = time->tm_year + 1900; unsigned month = time->tm_mon + 1; unsigned day = time->tm_mday; return date_from_numbers(year, month, day); } void date_split(ph_date_t date, unsigned *year, unsigned *month, unsigned *day) { if (year) *year = date >> 9; if (month) *month = (date >> 5) & 0xf; if (day) *day = date & 0x1f; } void date_to_string(char str[static 11], ph_date_t date) { unsigned year, month, day; date_split(date, &year, &month, &day); str[0] = '0' + (year / 1000) % 10; str[1] = '0' + (year / 100) % 10; str[2] = '0' + (year / 10) % 10; str[3] = '0' + year % 10; str[4] = '-'; str[5] = '0' + (month / 10) % 10; str[6] = '0' + month % 10; str[7] = '-'; str[8] = '0' + (day / 10) % 10; str[9] = '0' + day % 10; str[10] = '\0'; } bool date_valid(ph_date_t date) { unsigned year, month, day; date_split(date, &year, &month, &day); struct tm tm = {0}; tm.tm_year = year; /* goddammit */ tm.tm_mon = month - 1; tm.tm_mday = day; time_t r = mktime(&tm); /* if mktime fails to represent our time, it returns -1. mktime somewhat * curiously accepts dates outside regular ranges, like negative time, * but adjusts the tm structure to be 'correct', so we can check if any * of the times were modified to see if this is a valid time. */ if (r == -1 || (int)year != tm.tm_year || (int)month != tm.tm_mon + 1|| (int)day != tm.tm_mday) return false; return true; }