/*** Copyright (C) 2021-2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuCivilTime.cpp File: AuClock.cpp Date: 2023-09-18 Date: 2021-6-13 Author: Reece ***/ #include #include "AuCivilTime.hpp" #include "Time.hpp" #if defined(AURORA_COMPILER_MSVC) || \ defined(_CRT_USE_CONFORMING_ANNEX_K_TIME) #define TIME_MSFT_LIKE #endif #if defined(TIME_MSFT_LIKE) #define timegm _mkgmtime #endif using sys_clock = std::chrono::system_clock; // more stds to remove static sys_clock::duration gEpoch; static sys_clock::duration gUnixDelta; static auto InitEpoch() { std::tm start{0, 15, 10, 29, 7, 101, 0, 0, 0}; auto epoch = sys_clock::from_time_t(timegm(&start)).time_since_epoch(); std::tm unixStart{}; unixStart.tm_mday = 1; unixStart.tm_year = 70; // dont care what the spec says, you can't trust some ms stls // sys_clock can have its own epoch for all we care auto nixEpoch = sys_clock::from_time_t(timegm(&unixStart)).time_since_epoch(); gUnixDelta = epoch - nixEpoch; gEpoch = epoch; return 0; } static auto ___ = InitEpoch(); sys_clock::duration __NormalizeEpoch(sys_clock::duration sysEpoch) { return sysEpoch - gEpoch; } sys_clock::duration __DecodeEpoch(sys_clock::duration auroraEpoch) { return auroraEpoch + gEpoch; } template static auto TimeFromDurationSinceEpoch(Duration_t in) { auto duration = std::chrono::duration_cast(in); return std::chrono::time_point(__DecodeEpoch(duration)); } template static time_t CalculateTimeT(AuUInt64 in) { return sys_clock::to_time_t(TimeFromDurationSinceEpoch(Duration_t(in))); } namespace Aurora::Time { AUKN_SYM time_t SToCTime(AuInt64 time) { return CalculateTimeT(time); } AUKN_SYM time_t NSToCTime(AuInt64 time) { return CalculateTimeT(time); } AUKN_SYM time_t MSToCTime(AuInt64 time) { return CalculateTimeT(time); } AUKN_SYM AuInt64 ConvertAuroraToUnixMS(AuInt64 in) { return std::chrono::duration_cast(std::chrono::milliseconds(in) + gUnixDelta).count(); } AUKN_SYM AuInt64 ConvertAuroraToUnixNS(AuInt64 in) { return std::chrono::duration_cast(std::chrono::nanoseconds(in) + gUnixDelta).count(); } AUKN_SYM AuInt64 ConvertUnixToAuroraMS(AuInt64 in) { return std::chrono::duration_cast(std::chrono::milliseconds(in) - gUnixDelta).count(); } AUKN_SYM AuInt64 ConvertUnixToAuroraNS(AuInt64 in) { return std::chrono::duration_cast(std::chrono::nanoseconds(in) - gUnixDelta).count(); } AUKN_SYM tm ToCivilTime(AuInt64 time, ETimezoneShift shift) { std::tm ret {}; auto timet = MSToCTime(time); if (shift == ETimezoneShift::eUTC) { #if defined(TIME_MSFT_LIKE) auto tm = gmtime_s(&ret, &timet); #else auto tm = gmtime_r(&timet, &ret); #endif #if defined(TIME_MSFT_LIKE) SysAssert(!tm, "couldn't convert civil time"); #else SysAssert(tm, "couldn't convert civil time"); #endif } else { #if defined(TIME_MSFT_LIKE) if (localtime_s(&ret, &timet)) #else if (!localtime_r(&timet, &ret)) #endif { SysPushErrorGeneric("Couldn't convert local civil time"); #if 0 SysPanic(); #else return ToCivilTime(time, ETimezoneShift::eUTC); #endif } } tm _; _.CopyFrom(ret); return _; } AUKN_SYM AuInt64 FromCivilTime(const tm &time, ETimezoneShift shift) { ::tm tm; time_t timet; time.CopyTo(tm); if (shift == ETimezoneShift::eUTC) { tm.tm_isdst = 0; timet = timegm(&tm); } else { tm.tm_isdst = -1; // out of the 2 crts i've bothered to check, out of 3, this is legal timet = mktime(&tm); } if ((timet == 0) || (timet == -1)) { return 0; } return std::chrono::duration_cast(__NormalizeEpoch(std::chrono::system_clock::from_time_t(timet).time_since_epoch())).count(); } AUKN_SYM tm NormalizeCivilTimezone(const Time::tm &time, ETimezoneShift shift) { if ((time.isdst.ValueOr(-1) == 0) && (shift == ETimezoneShift::eUTC)) { return time; } return ToCivilTime(FromCivilTime(time, shift)); } }