#pragma once #include "minwinbase.h" #include #include #include inline constexpr int64_t HUNDRED_NS_PER_SECOND = 10000000LL; inline constexpr int64_t HUNDRED_NS_PER_MILLISECOND = 10000LL; inline constexpr int64_t SECONDS_PER_DAY = 86400LL; inline constexpr uint64_t TICKS_PER_DAY = static_cast(SECONDS_PER_DAY) * HUNDRED_NS_PER_SECOND; inline constexpr uint64_t UNIX_TIME_ZERO = 11644473600ULL * 10000000ULL; inline constexpr uint64_t MAX_VALID_FILETIME = 0x8000000000000000ULL; inline constexpr int64_t DAYS_TO_UNIX_EPOCH = 134774LL; struct CivilDate { int year; unsigned month; unsigned day; }; inline int64_t daysFromCivil(int year, unsigned month, unsigned day) { year -= month <= 2 ? 1 : 0; const int era = (year >= 0 ? year : year - 399) / 400; const unsigned yoe = static_cast(year - era * 400); const unsigned doy = (153 * (month + (month > 2 ? -3 : 9)) + 2) / 5 + day - 1; const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + yoe / 400 + doy; return era * 146097 + static_cast(doe) - 719468; } inline CivilDate civilFromDays(int64_t z) { z += 719468; const int64_t era = (z >= 0 ? z : z - 146096) / 146097; const unsigned doe = static_cast(z - era * 146097); const unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; const int64_t y = static_cast(yoe) + era * 400; const unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); const unsigned mp = (5 * doy + 2) / 153; const unsigned d = doy - (153 * mp + 2) / 5 + 1; const unsigned m = mp + (mp < 10 ? 3 : -9); return {static_cast(y + (m <= 2)), m, d}; } inline bool isLeapYear(int year) { if ((year % 4) != 0) { return false; } if ((year % 100) != 0) { return true; } return (year % 400) == 0; } inline unsigned daysInMonth(int year, unsigned month) { static const unsigned baseDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; unsigned idx = month - 1; unsigned value = baseDays[idx]; if (month == 2 && isLeapYear(year)) { value += 1; } return value; } inline bool validateSystemTime(const SYSTEMTIME &st) { if (st.wYear < 1601) { return false; } if (st.wMonth < 1 || st.wMonth > 12) { return false; } if (st.wDay < 1 || st.wDay > static_cast(daysInMonth(st.wYear, static_cast(st.wMonth)))) { return false; } if (st.wHour < 0 || st.wHour > 23) { return false; } if (st.wMinute < 0 || st.wMinute > 59) { return false; } if (st.wSecond < 0 || st.wSecond > 59) { return false; } if (st.wMilliseconds < 0 || st.wMilliseconds > 999) { return false; } return true; } inline bool systemTimeToUnixParts(const SYSTEMTIME &st, int64_t &secondsOut, uint32_t &hundredsOut) { if (!validateSystemTime(st)) { return false; } int64_t days = daysFromCivil(st.wYear, static_cast(st.wMonth), static_cast(st.wDay)); int64_t secondsOfDay = static_cast(st.wHour) * 3600LL + static_cast(st.wMinute) * 60LL + st.wSecond; secondsOut = days * SECONDS_PER_DAY + secondsOfDay; hundredsOut = static_cast(st.wMilliseconds) * static_cast(HUNDRED_NS_PER_MILLISECOND); return true; } inline uint64_t fileTimeToDuration(const FILETIME &value) { return (static_cast(value.dwHighDateTime) << 32) | value.dwLowDateTime; } inline bool fileTimeToUnixParts(const FILETIME &ft, int64_t &secondsOut, uint32_t &hundredsOut) { uint64_t ticks = fileTimeToDuration(ft); if (ticks >= UNIX_TIME_ZERO) { uint64_t diff = ticks - UNIX_TIME_ZERO; secondsOut = static_cast(diff / HUNDRED_NS_PER_SECOND); hundredsOut = static_cast(diff % HUNDRED_NS_PER_SECOND); } else { uint64_t diff = UNIX_TIME_ZERO - ticks; secondsOut = -static_cast(diff / HUNDRED_NS_PER_SECOND); uint64_t rem = diff % HUNDRED_NS_PER_SECOND; if (rem != 0) { secondsOut -= 1; rem = HUNDRED_NS_PER_SECOND - rem; } hundredsOut = static_cast(rem); } return true; } inline FILETIME fileTimeFromDuration(uint64_t ticks100ns) { FILETIME result; result.dwLowDateTime = static_cast(ticks100ns & 0xFFFFFFFFULL); result.dwHighDateTime = static_cast(ticks100ns >> 32); return result; } inline bool unixPartsToFileTime(int64_t seconds, uint32_t hundreds, FILETIME &out) { if (hundreds >= HUNDRED_NS_PER_SECOND) { return false; } #if defined(__SIZEOF_INT128__) __int128 total = static_cast<__int128>(seconds) * HUNDRED_NS_PER_SECOND; total += static_cast<__int128>(hundreds); total += static_cast<__int128>(UNIX_TIME_ZERO); if (total < 0 || total > static_cast<__int128>(std::numeric_limits::max())) { return false; } uint64_t ticks = static_cast(total); #else long double total = static_cast(seconds) * static_cast(HUNDRED_NS_PER_SECOND); total += static_cast(hundreds); total += static_cast(UNIX_TIME_ZERO); if (total < 0.0L || total > static_cast(std::numeric_limits::max())) { return false; } uint64_t ticks = static_cast(total); #endif out = fileTimeFromDuration(ticks); return true; } inline bool unixPartsToTimespec(int64_t seconds, uint32_t hundreds, struct timespec &out) { if (hundreds >= HUNDRED_NS_PER_SECOND) { return false; } if (seconds > static_cast(std::numeric_limits::max()) || seconds < static_cast(std::numeric_limits::min())) { return false; } out.tv_sec = static_cast(seconds); out.tv_nsec = static_cast(hundreds) * 100L; return true; } inline bool tmToUnixSeconds(const struct tm &tmValue, int64_t &secondsOut) { int year = tmValue.tm_year + 1900; int month = tmValue.tm_mon + 1; int day = tmValue.tm_mday; int hour = tmValue.tm_hour; int minute = tmValue.tm_min; int second = tmValue.tm_sec; if (month < 1 || month > 12) { return false; } if (day < 1 || day > static_cast(daysInMonth(year, static_cast(month)))) { return false; } if (hour < 0 || hour > 23) { return false; } if (minute < 0 || minute > 59) { return false; } if (second < 0 || second > 60) { return false; } if (second == 60) { second = 59; } int64_t days = daysFromCivil(year, static_cast(month), static_cast(day)); secondsOut = days * SECONDS_PER_DAY + static_cast(hour) * 3600LL + static_cast(minute) * 60LL + second; return true; } inline bool shouldIgnoreFileTimeParam(const FILETIME *ft) { if (!ft) { return true; } if (ft->dwLowDateTime == 0 && ft->dwHighDateTime == 0) { return true; } if (ft->dwLowDateTime == 0xFFFFFFFF && ft->dwHighDateTime == 0xFFFFFFFF) { return true; } return false; }