Created
March 1, 2021 15:20
-
-
Save dwilliamson/18fd198bb79bcfb17daab448a66b76ee to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <TinyCRT/TinyCRT.h> | |
#include <TinyCRT/TinyWindows.h> | |
namespace | |
{ | |
constexpr uint64_t time100NSecsPerSec = 10000000; | |
uint64_t FileTimeToU64(FILETIME ft) | |
{ | |
// Assume little-endian | |
uint64_t time; | |
memcpy(&time, &ft, sizeof(time)); | |
return time / time100NSecsPerSec; | |
} | |
} | |
extern "C" uint64_t time(uint64_t* timer) | |
{ | |
// Time now as a single integer | |
SYSTEMTIME st_now; | |
GetSystemTime(&st_now); | |
FILETIME ft_now; | |
SystemTimeToFileTime(&st_now, &ft_now); | |
// Reference time in 1970 as a single integer | |
SYSTEMTIME st_then; | |
st_then.wYear = 1970; | |
st_then.wMonth = 1; | |
st_then.wDayOfWeek = 0; | |
st_then.wDay = 1; | |
st_then.wHour = 0; | |
st_then.wMinute = 0; | |
st_then.wSecond = 0; | |
st_then.wMilliseconds = 0; | |
FILETIME ft_then; | |
SystemTimeToFileTime(&st_then, &ft_then); | |
// Calculate difference assuming 100-nanosecond interval resolution | |
uint64_t t_now = FileTimeToU64(ft_now); | |
uint64_t t_then = FileTimeToU64(ft_then); | |
uint64_t t_since = t_now - t_then; | |
if (timer != nullptr) | |
{ | |
*timer = t_since; | |
} | |
return t_since; | |
} | |
extern "C" uint64_t _time64(uint64_t* timer) | |
{ | |
return time(timer); | |
} | |
namespace | |
{ | |
uint32_t CountLeapYears(uint32_t days_since_1601) | |
{ | |
// (365 * 1601 / 365.0 - 1) * 0.2425 | |
constexpr uint64_t Base = 388; | |
double nb_leap_years = ((days_since_1601 + 365 * 1601) / 365.0 - 1) * 0.2425; | |
return uint32_t(nb_leap_years) - Base; | |
} | |
int GetEndOfMonth(uint32_t month, bool is_leap_year) | |
{ | |
constexpr int EndOfMonthDays[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; | |
uint32_t end_of_month = EndOfMonthDays[month]; | |
return is_leap_year && month > 1 ? end_of_month + 1 : end_of_month; | |
} | |
tm MakeTime64(uint64_t time) | |
{ | |
// Get number of days and seconds left over | |
constexpr uint32_t NbSecondsPerHour = 60 * 60; | |
constexpr uint32_t NbSecondsPerDay = NbSecondsPerHour * 24; | |
auto nb_days_since_1970 = uint32_t(time / NbSecondsPerDay); | |
uint32_t second_this_day = time % NbSecondsPerDay; | |
// Make day count relative to 1601 | |
constexpr uint32_t NbDaysIn4Years = 4 * 365 + 1; | |
constexpr uint32_t NbDaysInCentury = 25 * NbDaysIn4Years - 1; | |
uint32_t nb_days_since_1601 = nb_days_since_1970 + (3 * NbDaysInCentury + 17 * NbDaysIn4Years + 365); | |
// Count number of leap years/days so far | |
uint32_t nb_leap_days = CountLeapYears(nb_days_since_1601 + 307); | |
uint32_t nb_leap_years = CountLeapYears(nb_days_since_1601); | |
bool is_leap_year = nb_leap_days > nb_leap_years; | |
// Get year relative to 1900 | |
tm t; | |
uint32_t year_since_1601 = (nb_days_since_1601 - nb_leap_days) / 365; | |
t.tm_year = year_since_1601 - 299; | |
// Get number of days since the start of the year | |
t.tm_yday = nb_days_since_1601 - (year_since_1601 * 365 + nb_leap_years); | |
// Search for the current month | |
uint32_t month = 0; | |
while (t.tm_yday >= GetEndOfMonth(month + 1, is_leap_year)) | |
{ | |
month++; | |
} | |
// Populate output time struct | |
uint32_t second_this_hour = second_this_day % NbSecondsPerHour; | |
t.tm_mon = month; | |
t.tm_mday = 1 + t.tm_yday - GetEndOfMonth(month, is_leap_year); | |
t.tm_wday = (nb_days_since_1601 + 1) % 7; | |
t.tm_hour = second_this_day / NbSecondsPerHour; | |
t.tm_min = second_this_hour / 60; | |
t.tm_sec = second_this_hour % 60; | |
t.tm_isdst = 0; | |
return t; | |
} | |
} | |
extern "C" tm* gmtime(const time_t* source_time) | |
{ | |
assert(source_time != nullptr); | |
static tm t; | |
t = MakeTime64(*source_time); | |
return &t; | |
} | |
extern "C" tm* _gmtime64(const uint64_t* source_time) | |
{ | |
assert(source_time != nullptr); | |
static tm t; | |
t = MakeTime64(*source_time); | |
return &t; | |
} | |
extern "C" int _gmtime64_s(struct tm* dest, const uint64_t* source_time) | |
{ | |
if (dest == nullptr) | |
{ | |
return EINVAL; | |
} | |
*dest = {-1, -1, -1, -1, -1, -1, -1, -1, -1}; | |
if (source_time == nullptr) | |
{ | |
return EINVAL; | |
} | |
*dest = MakeTime64(*source_time); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment