Index: common/mptTime.cpp =================================================================== --- common/mptTime.cpp (revision 16961) +++ common/mptTime.cpp (working copy) @@ -13,7 +13,12 @@ #include "mptStringBuffer.h" -#include +#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE) +#include +#if 0 +#include +#endif +#endif #if MPT_OS_WINDOWS #include @@ -81,23 +86,8 @@ #endif // MODPLUG_TRACKER -Unix::Unix() - : Value(0) -{ - return; -} +#if MPT_CXX_BEFORE(20) || defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE) -Unix::Unix(int64 unixtime) - : Value(unixtime) -{ - return; -} - -Unix::operator int64 () const -{ - return Value; -} - static int32 ToDaynum(int32 year, int32 month, int32 day) { month = (month + 9) % 12; @@ -128,77 +118,55 @@ day = static_cast(dd); } -mpt::Date::Unix Unix::FromUTC(tm timeUtc) +mpt::Date::Unix UnixFromUTC(UTC timeUtc) { - int32 daynum = ToDaynum(timeUtc.tm_year+1900, timeUtc.tm_mon+1, timeUtc.tm_mday); - int64 seconds = static_cast(daynum - ToDaynum(1970,1,1))*24*60*60 + timeUtc.tm_hour*60*60 + timeUtc.tm_min*60 + timeUtc.tm_sec; - return mpt::Date::Unix(seconds); + int32 daynum = ToDaynum(timeUtc.year, timeUtc.month, timeUtc.day); + int64 seconds = static_cast(daynum - ToDaynum(1970, 1, 1)) * 24 * 60 * 60 + timeUtc.hours * 60 * 60 + timeUtc.minutes * 60 + timeUtc.seconds; + return Unix{seconds}; } -tm Unix::AsUTC() const +mpt::Date::UTC UnixAsUTC(Unix tp) { - int64 tmp = Value; + int64 tmp = tp.value; int64 seconds = tmp % 60; tmp /= 60; int64 minutes = tmp % 60; tmp /= 60; int64 hours = tmp % 24; tmp /= 24; int32 year = 0, month = 0, day = 0; FromDaynum(static_cast(tmp) + ToDaynum(1970,1,1), year, month, day); - tm result = {}; - result.tm_year = year - 1900; - result.tm_mon = month - 1; - result.tm_mday = day; - result.tm_hour = static_cast(hours); - result.tm_min = static_cast(minutes); - result.tm_sec = static_cast(seconds); + mpt::Date::UTC result = {}; + result.year = year; + result.month = month; + result.day = day; + result.hours = static_cast(hours); + result.minutes = static_cast(minutes); + result.seconds = static_cast(seconds); return result; } -mpt::ustring ToShortenedISO8601(tm date) +#endif + +mpt::ustring ToShortenedISO8601(mpt::Date::UTC date) { - // We assume date in UTC here. - // There are too many differences in supported format specifiers in strftime() - // and strftime does not support reduced precision ISO8601 at all. - // Just do the formatting ourselves. mpt::ustring result; mpt::ustring tz = U_("Z"); - if(date.tm_year == 0) + if(date.year == 0) { return result; } - result += mpt::ufmt::dec0<4>(date.tm_year + 1900); - if(date.tm_mon < 0 || date.tm_mon > 11) + result += mpt::ufmt::dec0<4>(date.year); + result += U_("-") + mpt::ufmt::dec0<2>(date.month); + result += U_("-") + mpt::ufmt::dec0<2>(date.day); + if(date.hours == 0 && date.minutes == 0 && date.seconds) { return result; } - result += U_("-") + mpt::ufmt::dec0<2>(date.tm_mon + 1); - if(date.tm_mday < 1 || date.tm_mday > 31) - { - return result; - } - result += U_("-") + mpt::ufmt::dec0<2>(date.tm_mday); - if(date.tm_hour == 0 && date.tm_min == 0 && date.tm_sec == 0) - { - return result; - } - if(date.tm_hour < 0 || date.tm_hour > 23) - { - return result; - } - if(date.tm_min < 0 || date.tm_min > 59) - { - return result; - } result += U_("T"); - if(date.tm_isdst > 0) + result += mpt::ufmt::dec0<2>(date.hours) + U_(":") + mpt::ufmt::dec0<2>(date.minutes); + if(date.seconds == 0) { - tz = U_("+01:00"); - } - result += mpt::ufmt::dec0<2>(date.tm_hour) + U_(":") + mpt::ufmt::dec0<2>(date.tm_min); - if(date.tm_sec < 0 || date.tm_sec > 61) - { return result + tz; } - result += U_(":") + mpt::ufmt::dec0<2>(date.tm_sec); + result += U_(":") + mpt::ufmt::dec0<2>(date.seconds); result += tz; return result; } Index: common/mptTime.h =================================================================== --- common/mptTime.h (revision 16961) +++ common/mptTime.h (working copy) @@ -12,9 +12,14 @@ #include "openmpt/all/BuildSettings.hpp" +#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE) +#include +#endif #include +#if MPT_CXX_BEFORE(20) || defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE) #include +#endif OPENMPT_NAMESPACE_BEGIN @@ -44,22 +49,124 @@ #endif // MODPLUG_TRACKER -class Unix +struct UTC { + int year = 0; + unsigned int month = 0; + unsigned int day = 0; + int32 hours = 0; + int32 minutes = 0; + int64 seconds = 0; + friend bool operator==(const UTC& lhs, const UTC& rhs) + { + return true + && lhs.year == rhs.year + && lhs.month == rhs.month + && lhs.day == rhs.day + && lhs.hours == rhs.hours + && lhs.minutes == rhs.minutes + && lhs.seconds == rhs.seconds + ; + } + friend bool operator!=(const UTC& lhs, const UTC& rhs) + { + return false + || lhs.year != rhs.year + || lhs.month != rhs.month + || lhs.day != rhs.day + || lhs.hours != rhs.hours + || lhs.minutes != rhs.minutes + || lhs.seconds != rhs.seconds + ; + } +}; + +#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE) + +using Unix = std::chrono::system_clock::time_point; + +inline Unix UnixNow() +{ + return std::chrono::system_clock::now(); +} + +inline int64 UnixAsSeconds(Unix tp) +{ + return std::chrono::duration_cast(tp.time_since_epoch()).count(); +} + +inline Unix UnixFromSeconds(int64 seconds) +{ + return std::chrono::system_clock::time_point{std::chrono::seconds{seconds}}; +} + +inline mpt::Date::Unix UnixFromUTC(UTC utc) +{ + std::chrono::year_month_day ymd = + std::chrono::year{utc.year} / + std::chrono::month{utc.month} / + std::chrono::day{utc.day}; + std::chrono::hh_mm_ss hms{ + std::chrono::hours{utc.hours} + + std::chrono::minutes{utc.minutes} + + std::chrono::seconds{utc.seconds}}; + return std::chrono::system_clock::time_point{static_cast(ymd)} + hms.to_duration(); +} + +inline mpt::Date::UTC UnixAsUTC(Unix tp) +{ + std::chrono::sys_days dp = std::chrono::floor(tp); + std::chrono::year_month_day ymd{dp}; + std::chrono::hh_mm_ss hms{tp - dp}; + mpt::Date::UTC result; + result.year = static_cast(ymd.year()); + result.month = static_cast(ymd.month()); + result.day = static_cast(ymd.day()); + result.hours = hms.hours().count(); + result.minutes = hms.minutes().count(); + result.seconds = hms.seconds().count(); + return result; +} + +#else + // int64 counts 1s since 1970-01-01T00:00Z -private: - int64 Value; -public: - Unix(); - explicit Unix(int64 unixtime); - operator int64 () const; -public: - static mpt::Date::Unix FromUTC(tm timeUtc); - tm AsUTC() const; +struct Unix +{ + int64 value{}; + friend bool operator==(const Unix &a, const Unix &b) + { + return a.value == b.value; + } + friend bool operator!=(const Unix &a, const Unix &b) + { + return a.value != b.value; + } }; -mpt::ustring ToShortenedISO8601(tm date); // i.e. 2015-01-15T18:32:01Z +inline Unix UnixNow() +{ + return Unix{static_cast(time(nullptr))}; +} +inline int64 UnixAsSeconds(Unix tp) +{ + return tp.value; +} + +inline Unix UnixFromSeconds(int64 seconds) +{ + return Unix{seconds}; +} + +mpt::Date::Unix UnixFromUTC(UTC timeUtc); + +mpt::Date::UTC UnixAsUTC(Unix tp); + +#endif + +mpt::ustring ToShortenedISO8601(UTC date); // i.e. 2015-01-15T18:32:01Z + } // namespace Date } // namespace mpt Index: common/versionNumber.h =================================================================== --- common/versionNumber.h (revision 16961) +++ common/versionNumber.h (working copy) @@ -18,6 +18,6 @@ #define VER_MAJORMAJOR 1 #define VER_MAJOR 31 #define VER_MINOR 00 -#define VER_MINORMINOR 09 +#define VER_MINORMINOR 10 OPENMPT_NAMESPACE_END Index: mptrack/dlg_misc.cpp =================================================================== --- mptrack/dlg_misc.cpp (revision 16961) +++ mptrack/dlg_misc.cpp (working copy) @@ -1311,18 +1311,8 @@ for(const auto &entry : editHistory) { totalTime += entry.openTime; - // Date - CString sDate; - if(entry.HasValidDate()) - { - TCHAR szDate[32]; - _tcsftime(szDate, std::size(szDate), _T("%d %b %Y, %H:%M:%S"), &entry.loadDate); - sDate = szDate; - } else - { - sDate = _T(""); - } + CString sDate = entry.HasValidDate() ? CTime(mpt::Date::UnixAsSeconds(mpt::Date::UnixFromUTC(entry.loadDate))).Format(_T("%d %b %Y, %H:%M:%S")) : CString(_T("")); // Time + stuff uint32 duration = mpt::saturate_round(entry.openTime / HISTORY_TIMER_PRECISION); s += MPT_CFORMAT("Loaded {}, open for {}h {}m {}s\r\n")( Index: mptrack/MainFrm.cpp =================================================================== --- mptrack/MainFrm.cpp (revision 16961) +++ mptrack/MainFrm.cpp (working copy) @@ -2784,8 +2784,10 @@ const bool isAutoUpdate = CUpdateCheck::IsAutoUpdateFromMessage(wparam, lparam); const UpdateCheckResult &result = CUpdateCheck::MessageAsResult(wparam, lparam); CUpdateCheck::AcknowledgeSuccess(result); - if(result.CheckTime != time_t{}) - TrackerSettings::Instance().UpdateLastUpdateCheck = mpt::Date::Unix(result.CheckTime); + if(result.CheckTime != mpt::Date::Unix{}) + { + TrackerSettings::Instance().UpdateLastUpdateCheck = result.CheckTime; + } if(!isAutoUpdate) { if(m_UpdateOptionsDialog) Index: mptrack/Moddoc.cpp =================================================================== --- mptrack/Moddoc.cpp (revision 16961) +++ mptrack/Moddoc.cpp (working copy) @@ -133,7 +133,7 @@ , m_InstrumentUndo(*this) { // Set the creation date of this file (or the load time if we're loading an existing file) - time(&m_creationTime); + m_creationTime = mpt::Date::UnixNow(); ReinitRecordState(); Index: mptrack/Moddoc.h =================================================================== --- mptrack/Moddoc.h (revision 16961) +++ mptrack/Moddoc.h (working copy) @@ -14,10 +14,10 @@ #include "Sndfile.h" #include "../common/misc_util.h" +#include "../common/mptTime.h" #include "Undo.h" #include "Notification.h" #include "UpdateHints.h" -#include OPENMPT_NAMESPACE_BEGIN @@ -130,7 +130,7 @@ CSampleUndo m_SampleUndo; CInstrumentUndo m_InstrumentUndo; SplitKeyboardSettings m_SplitKeyboardSettings; // this is maybe not the best place to keep them, but it should do the job - time_t m_creationTime; + mpt::Date::Unix m_creationTime; std::atomic m_modifiedAutosave = false; // Modified since last autosave? @@ -221,7 +221,7 @@ CInstrumentUndo &GetInstrumentUndo() { return m_InstrumentUndo; } SplitKeyboardSettings &GetSplitKeyboardSettings() { return m_SplitKeyboardSettings; } - time_t GetCreationTime() const { return m_creationTime; } + mpt::Date::Unix GetCreationTime() const { return m_creationTime; } // operations public: Index: mptrack/TrackerSettings.cpp =================================================================== --- mptrack/TrackerSettings.cpp (revision 16961) +++ mptrack/TrackerSettings.cpp (working copy) @@ -326,7 +326,7 @@ // Update , UpdateEnabled(conf, U_("Update"), U_("Enabled"), true) , UpdateInstallAutomatically(conf, U_("Update"), U_("InstallAutomatically"), false) - , UpdateLastUpdateCheck(conf, U_("Update"), U_("LastUpdateCheck"), mpt::Date::Unix(time_t())) + , UpdateLastUpdateCheck(conf, U_("Update"), U_("LastUpdateCheck"), mpt::Date::Unix{}) , UpdateUpdateCheckPeriod_DEPRECATED(conf, U_("Update"), U_("UpdateCheckPeriod"), 7) , UpdateIntervalDays(conf, U_("Update"), U_("UpdateCheckIntervalDays"), 7) , UpdateChannel(conf, U_("Update"), U_("Channel"), UpdateChannelRelease) @@ -822,6 +822,10 @@ conf.Forget(UpdateUpdateURL_DEPRECATED.GetPath()); conf.Forget(UpdateSendGUID_DEPRECATED.GetPath()); } + if(storedVersion < MPT_V("1.31.00.10")) + { + UpdateLastUpdateCheck = mpt::Date::Unix{}; + } #endif // MPT_ENABLE_UPDATE if(storedVersion < MPT_V("1.29.00.39")) Index: mptrack/TrackerSettings.h =================================================================== --- mptrack/TrackerSettings.h (revision 16961) +++ mptrack/TrackerSettings.h (working copy) @@ -471,32 +471,12 @@ template<> inline SettingValue ToSettingValue(const mpt::Date::Unix &val) { - time_t t = val; - const tm* lastUpdate = gmtime(&t); - CString outDate; - if(lastUpdate) - { - outDate.Format(_T("%04d-%02d-%02d %02d:%02d"), lastUpdate->tm_year + 1900, lastUpdate->tm_mon + 1, lastUpdate->tm_mday, lastUpdate->tm_hour, lastUpdate->tm_min); - } - return SettingValue(mpt::ToUnicode(outDate), "UTC"); + return SettingValue(mpt::ufmt::val(mpt::Date::UnixAsSeconds(val)), "UnixTime"); } template<> inline mpt::Date::Unix FromSettingValue(const SettingValue &val) { - MPT_ASSERT(val.GetTypeTag() == "UTC"); - std::string s = mpt::ToCharset(mpt::Charset::Locale, val.as()); - tm lastUpdate; - MemsetZero(lastUpdate); - if(sscanf(s.c_str(), "%04d-%02d-%02d %02d:%02d", &lastUpdate.tm_year, &lastUpdate.tm_mon, &lastUpdate.tm_mday, &lastUpdate.tm_hour, &lastUpdate.tm_min) == 5) - { - lastUpdate.tm_year -= 1900; - lastUpdate.tm_mon--; - } - time_t outTime = mpt::Date::Unix::FromUTC(lastUpdate); - if(outTime < 0) - { - outTime = 0; - } - return mpt::Date::Unix(outTime); + MPT_ASSERT(val.GetTypeTag() == "UnixTime"); + return mpt::Date::UnixFromSeconds(mpt::ConvertStringTo(val.as())); } struct FontSetting Index: mptrack/UpdateCheck.cpp =================================================================== --- mptrack/UpdateCheck.cpp (revision 16961) +++ mptrack/UpdateCheck.cpp (working copy) @@ -453,11 +453,11 @@ return; } // Do we actually need to run the update check right now? - const time_t now = time(nullptr); - const time_t lastCheck = TrackerSettings::Instance().UpdateLastUpdateCheck.Get(); + const mpt::Date::Unix now = mpt::Date::UnixNow(); + const mpt::Date::Unix lastCheck = TrackerSettings::Instance().UpdateLastUpdateCheck.Get(); // Check update interval. Note that we always check for updates when the system time had gone backwards (i.e. when the last update check supposedly happened in the future). - const double secsSinceLastCheck = difftime(now, lastCheck); - if(secsSinceLastCheck > 0.0 && secsSinceLastCheck < updateCheckPeriod * 86400.0) + const int64 secsSinceLastCheck = mpt::Date::UnixAsSeconds(now) - mpt::Date::UnixAsSeconds(lastCheck); + if(secsSinceLastCheck > 0 && secsSinceLastCheck < updateCheckPeriod * 86400) { loadPersisted = true; } @@ -475,7 +475,7 @@ ); if(Reporting::Confirm(msg, _T("OpenMPT Update")) == cnfNo) { - TrackerSettings::Instance().UpdateLastUpdateCheck = mpt::Date::Unix(now); + TrackerSettings::Instance().UpdateLastUpdateCheck = now; return; } } @@ -650,7 +650,7 @@ { std::vector data = GetFileReader(f).ReadRawDataAsByteVector(); nlohmann::json::parse(mpt::buffer_cast(data)).get(); - result.CheckTime = time_t{}; + result.CheckTime = mpt::Date::Unix{}; result.json = data; loaded = true; } @@ -781,7 +781,7 @@ // Now, evaluate the downloaded data. UpdateCheckResult result; - result.CheckTime = time(nullptr); + result.CheckTime = mpt::Date::UnixNow(); try { nlohmann::json::parse(mpt::buffer_cast(resultHTTP.Data)).get(); @@ -1315,7 +1315,7 @@ { if(!result.IsFromCache()) { - TrackerSettings::Instance().UpdateLastUpdateCheck = mpt::Date::Unix(result.CheckTime); + TrackerSettings::Instance().UpdateLastUpdateCheck = result.CheckTime; } } @@ -1612,14 +1612,10 @@ if(changedPath == TrackerSettings::Instance().UpdateLastUpdateCheck.GetPath()) { CString updateText; - const time_t t = TrackerSettings::Instance().UpdateLastUpdateCheck.Get(); - if(t > 0) + const mpt::Date::Unix t = TrackerSettings::Instance().UpdateLastUpdateCheck.Get(); + if(t != mpt::Date::Unix{}) { - const tm* const lastUpdate = localtime(&t); - if(lastUpdate != nullptr) - { - updateText.Format(_T("The last successful update check was run on %04d-%02d-%02d, %02d:%02d."), lastUpdate->tm_year + 1900, lastUpdate->tm_mon + 1, lastUpdate->tm_mday, lastUpdate->tm_hour, lastUpdate->tm_min); - } + updateText += CTime(mpt::Date::UnixAsSeconds(t)).Format(_T("The last successful update check was run on %F, %R.")); } updateText += _T("\r\n"); SetDlgItemText(IDC_LASTUPDATE, updateText); Index: mptrack/UpdateCheck.h =================================================================== --- mptrack/UpdateCheck.h (revision 16961) +++ mptrack/UpdateCheck.h (working copy) @@ -14,7 +14,7 @@ #include "mpt/uuid/uuid.hpp" -#include +#include "../common/mptTime.h" #include @@ -40,9 +40,12 @@ struct UpdateCheckResult { - time_t CheckTime = time_t{}; + mpt::Date::Unix CheckTime = mpt::Date::Unix{}; std::vector json; - bool IsFromCache() const noexcept { return CheckTime == time_t{}; } + bool IsFromCache() const noexcept + { + return CheckTime == mpt::Date::Unix{}; + } }; class CUpdateCheck Index: soundlib/ITTools.cpp =================================================================== --- soundlib/ITTools.cpp (revision 16961) +++ soundlib/ITTools.cpp (working copy) @@ -637,15 +637,15 @@ void ITHistoryStruct::ConvertToMPT(FileHistory &mptHistory) const { // Decode FAT date and time - MemsetZero(mptHistory.loadDate); + mptHistory.loadDate = mpt::Date::UTC{}; if(fatdate != 0 || fattime != 0) { - mptHistory.loadDate.tm_year = ((fatdate >> 9) & 0x7F) + 80; - mptHistory.loadDate.tm_mon = Clamp((fatdate >> 5) & 0x0F, 1, 12) - 1; - mptHistory.loadDate.tm_mday = Clamp(fatdate & 0x1F, 1, 31); - mptHistory.loadDate.tm_hour = Clamp((fattime >> 11) & 0x1F, 0, 23); - mptHistory.loadDate.tm_min = Clamp((fattime >> 5) & 0x3F, 0, 59); - mptHistory.loadDate.tm_sec = Clamp((fattime & 0x1F) * 2, 0, 59); + mptHistory.loadDate.year = ((fatdate >> 9) & 0x7F) + 1980; + mptHistory.loadDate.month = Clamp((fatdate >> 5) & 0x0F, 1, 12); + mptHistory.loadDate.day = Clamp(fatdate & 0x1F, 1, 31); + mptHistory.loadDate.hours = Clamp((fattime >> 11) & 0x1F, 0, 23); + mptHistory.loadDate.minutes = Clamp((fattime >> 5) & 0x3F, 0, 59); + mptHistory.loadDate.seconds = Clamp((fattime & 0x1F) * 2, 0, 59); } mptHistory.openTime = static_cast(runtime * (HISTORY_TIMER_PRECISION / 18.2)); } @@ -657,8 +657,8 @@ // Create FAT file dates if(mptHistory.HasValidDate()) { - fatdate = static_cast(mptHistory.loadDate.tm_mday | ((mptHistory.loadDate.tm_mon + 1) << 5) | ((mptHistory.loadDate.tm_year - 80) << 9)); - fattime = static_cast((mptHistory.loadDate.tm_sec / 2) | (mptHistory.loadDate.tm_min << 5) | (mptHistory.loadDate.tm_hour << 11)); + fatdate = static_cast(mptHistory.loadDate.day | (mptHistory.loadDate.month << 5) | ((mptHistory.loadDate.year - 1980) << 9)); + fattime = static_cast((mptHistory.loadDate.seconds / 2) | (mptHistory.loadDate.minutes << 5) | (mptHistory.loadDate.hours << 11)); } else { fatdate = 0; Index: soundlib/Load_dmf.cpp =================================================================== --- soundlib/Load_dmf.cpp (revision 16961) +++ soundlib/Load_dmf.cpp (working copy) @@ -911,9 +911,9 @@ m_songArtist = mpt::ToUnicode(mpt::Charset::CP437, mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.composer)); FileHistory mptHistory; - mptHistory.loadDate.tm_mday = Clamp(fileHeader.creationDay, uint8(1), uint8(31)); - mptHistory.loadDate.tm_mon = Clamp(fileHeader.creationMonth, uint8(1), uint8(12)) - 1; - mptHistory.loadDate.tm_year = fileHeader.creationYear; + mptHistory.loadDate.day = Clamp(fileHeader.creationDay, uint8(1), uint8(31)); + mptHistory.loadDate.month = Clamp(fileHeader.creationMonth, uint8(1), uint8(12)); + mptHistory.loadDate.year = 1900 + fileHeader.creationYear; m_FileHistory.clear(); m_FileHistory.push_back(mptHistory); Index: soundlib/Load_gt2.cpp =================================================================== --- soundlib/Load_gt2.cpp (revision 16961) +++ soundlib/Load_gt2.cpp (working copy) @@ -1214,9 +1214,9 @@ m_playBehaviour.set(kApplyOffsetWithoutNote); FileHistory mptHistory; - mptHistory.loadDate.tm_mday = Clamp(fileHeader.day, 1, 31); - mptHistory.loadDate.tm_mon = Clamp(fileHeader.month, 1, 12) - 1; - mptHistory.loadDate.tm_year = fileHeader.year - 1900; + mptHistory.loadDate.day = Clamp(fileHeader.day, 1, 31); + mptHistory.loadDate.month = Clamp(fileHeader.month, 1, 12); + mptHistory.loadDate.year = fileHeader.year; m_FileHistory.push_back(mptHistory); m_nChannels = 32; Index: soundlib/Load_it.cpp =================================================================== --- soundlib/Load_it.cpp (revision 16961) +++ soundlib/Load_it.cpp (working copy) @@ -1352,17 +1352,9 @@ } else if(pModDoc != nullptr) { // Current ("new") timestamp - const time_t creationTime = pModDoc->GetCreationTime(); - - MemsetZero(mptHistory.loadDate); - //localtime_s(&loadDate, &creationTime); - const tm* const p = localtime(&creationTime); - if (p != nullptr) - mptHistory.loadDate = *p; - else - sndFile.AddToLog(LogError, U_("Unable to retrieve current time.")); - - mptHistory.openTime = (uint32)(difftime(time(nullptr), creationTime) * HISTORY_TIMER_PRECISION); + const mpt::Date::Unix creationTime = pModDoc->GetCreationTime(); + mptHistory.loadDate = mpt::Date::UnixAsUTC(creationTime); + mptHistory.openTime = static_cast(mpt::round((mpt::Date::UnixAsSeconds(mpt::Date::UnixNow()) - mpt::Date::UnixAsSeconds(creationTime)) * HISTORY_TIMER_PRECISION)); #endif // MODPLUG_TRACKER } Index: soundlib/Load_mod.cpp =================================================================== --- soundlib/Load_mod.cpp (revision 16961) +++ soundlib/Load_mod.cpp (working copy) @@ -2180,12 +2180,12 @@ && mpt::is_in_range(info.dateMinute, 0, 59) && mpt::is_in_range(info.dateSecond, 0, 59)) { FileHistory mptHistory; - mptHistory.loadDate.tm_year = info.dateYear; - mptHistory.loadDate.tm_mon = info.dateMonth - 1; - mptHistory.loadDate.tm_mday = info.dateDay; - mptHistory.loadDate.tm_hour = info.dateHour; - mptHistory.loadDate.tm_min = info.dateMinute; - mptHistory.loadDate.tm_sec = info.dateSecond; + mptHistory.loadDate.year = info.dateYear + 1900; + mptHistory.loadDate.month = info.dateMonth; + mptHistory.loadDate.day = info.dateDay; + mptHistory.loadDate.hours = info.dateHour; + mptHistory.loadDate.minutes = info.dateMinute; + mptHistory.loadDate.seconds = info.dateSecond; m_FileHistory.push_back(mptHistory); } } Index: soundlib/Sndfile.cpp =================================================================== --- soundlib/Sndfile.cpp (revision 16961) +++ soundlib/Sndfile.cpp (working copy) @@ -56,15 +56,15 @@ mpt::ustring FileHistory::AsISO8601() const { - tm date = loadDate; + mpt::Date::UTC date = loadDate; if(openTime > 0) { // Calculate the date when editing finished. double openSeconds = static_cast(openTime) / HISTORY_TIMER_PRECISION; - tm tmpLoadDate = loadDate; - int64 loadDateSinceEpoch = mpt::Date::Unix::FromUTC(tmpLoadDate); + mpt::Date::UTC tmpLoadDate = loadDate; + int64 loadDateSinceEpoch = mpt::Date::UnixAsSeconds(mpt::Date::UnixFromUTC(tmpLoadDate)); int64 saveDateSinceEpoch = loadDateSinceEpoch + mpt::saturate_round(openSeconds); - date = mpt::Date::Unix(saveDateSinceEpoch).AsUTC(); + date = mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds(saveDateSinceEpoch)); } return mpt::Date::ToShortenedISO8601(date); } Index: soundlib/Sndfile.h =================================================================== --- soundlib/Sndfile.h (revision 16961) +++ soundlib/Sndfile.h (working copy) @@ -243,13 +243,16 @@ struct FileHistory { // Date when the file was loaded in the the tracker or created. - tm loadDate = {}; + mpt::Date::UTC loadDate = {}; // Time the file was open in the editor, in 1/18.2th seconds (frequency of a standard DOS timer, to keep compatibility with Impulse Tracker easy). uint32 openTime = 0; // Return the date as a (possibly truncated if not enough precision is available) ISO 8601 formatted date. mpt::ustring AsISO8601() const; // Returns true if the date component is valid. Some formats only store edit time, not edit date. - bool HasValidDate() const { return loadDate.tm_mday != 0; } + bool HasValidDate() const + { + return loadDate != mpt::Date::UTC{}; + } }; Index: src/mpt/base/detect_quirks.hpp =================================================================== --- src/mpt/base/detect_quirks.hpp (revision 16961) +++ src/mpt/base/detect_quirks.hpp (working copy) @@ -145,4 +145,34 @@ +#if MPT_CXX_AT_LEAST(20) +#if MPT_LIBCXX_MS && MPT_OS_WINDOWS +#if defined(NTDDI_VERSION) +#if (NTDDI_VERSION < 0x0A000007) // < Windows 10 1903 +// std::chrono timezones require Windows 10 1903 with VS2022 as of 2022-01-22. +// See +// and . +#define MPT_LIBCXX_QUIRK_NO_CHRONO_DATE +#endif +#else +#define MPT_LIBCXX_QUIRK_NO_CHRONO_DATE +#endif +#endif +#if MPT_LIBCXX_GNU_BEFORE(11) +#define MPT_LIBCXX_QUIRK_NO_CHRONO_DATE +#elif MPT_LIBCXX_LLVM_BEFORE(7000) +#define MPT_LIBCXX_QUIRK_NO_CHRONO_DATE +#endif +#if MPT_LIBCXX_MS +// Causes massive memory leaks. +// See +// / . +#define MPT_LIBCXX_QUIRK_NO_CHRONO_DATE_PARSE +#elif MPT_LIBCXX_GNU +#define MPT_LIBCXX_QUIRK_NO_CHRONO_DATE_PARSE +#endif +#endif + + + #endif // MPT_BASE_DETECT_QUIRKS_HPP Index: test/test.cpp =================================================================== --- test/test.cpp (revision 16961) +++ test/test.cpp (working copy) @@ -777,48 +777,15 @@ } -namespace { - -struct Gregorian { - int Y,M,D,h,m,s; - static Gregorian FromTM(tm t) { - Gregorian g; - g.Y = t.tm_year + 1900; - g.M = t.tm_mon + 1; - g.D = t.tm_mday; - g.h = t.tm_hour; - g.m = t.tm_min; - g.s = t.tm_sec; - return g; - } - static tm ToTM(Gregorian g) { - tm t; - MemsetZero(t); - t.tm_year = g.Y - 1900; - t.tm_mon = g.M - 1; - t.tm_mday = g.D; - t.tm_hour = g.h; - t.tm_min = g.m; - t.tm_sec = g.s; - return t; - } -}; - -inline bool operator ==(Gregorian a, Gregorian b) { - return a.Y == b.Y && a.M == b.M && a.D == b.D && a.h == b.h && a.m == b.m && a.s == b.s; +static int64 TestDate1(int s, int m, int h, unsigned int D, unsigned int M, int Y) { + return mpt::Date::UnixAsSeconds(mpt::Date::UnixFromUTC(mpt::Date::UTC{Y,M,D,h,m,s})); } +static mpt::Date::UTC TestDate2(int s, int m, int h, unsigned int D, unsigned int M, int Y) { + return mpt::Date::UTC{Y,M,D,h,m,s}; } -static int64 TestDate1(int s, int m, int h, int D, int M, int Y) { - return mpt::Date::Unix::FromUTC(Gregorian::ToTM(Gregorian{Y,M,D,h,m,s})); -} -static Gregorian TestDate2(int s, int m, int h, int D, int M, int Y) { - return Gregorian{Y,M,D,h,m,s}; -} - - static MPT_NOINLINE void TestMisc1() { @@ -1086,30 +1053,30 @@ VERIFY_EQUAL( 1413064016, TestDate1( 56, 46, 21, 11, 10, 2014 )); VERIFY_EQUAL( 1413064100, TestDate1( 20, 48, 21, 11, 10, 2014 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 0).AsUTC()), TestDate2( 0, 0, 0, 1, 1, 1970 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 3600).AsUTC()), TestDate2( 0, 0, 1, 1, 1, 1970 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 86400).AsUTC()), TestDate2( 0, 0, 0, 2, 1, 1970 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 31536000).AsUTC()), TestDate2( 0, 0, 0, 1, 1, 1971 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 100000000).AsUTC()), TestDate2( 40, 46, 9, 3, 3, 1973 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 951782400).AsUTC()), TestDate2( 0, 0, 0, 29, 2, 2000 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1000000000).AsUTC()), TestDate2( 40, 46, 1, 9, 9, 2001 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1044057600).AsUTC()), TestDate2( 0, 0, 0, 1, 2, 2003 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1044144000).AsUTC()), TestDate2( 0, 0, 0, 2, 2, 2003 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1046476800).AsUTC()), TestDate2( 0, 0, 0, 1, 3, 2003 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1064966400).AsUTC()), TestDate2( 0, 0, 0, 1, 10, 2003 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1077926399).AsUTC()), TestDate2( 59, 59, 23, 27, 2, 2004 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1077926400).AsUTC()), TestDate2( 0, 0, 0, 28, 2, 2004 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1077926410).AsUTC()), TestDate2( 10, 0, 0, 28, 2, 2004 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078012799).AsUTC()), TestDate2( 59, 59, 23, 28, 2, 2004 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078012800).AsUTC()), TestDate2( 0, 0, 0, 29, 2, 2004 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078012820).AsUTC()), TestDate2( 20, 0, 0, 29, 2, 2004 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078099199).AsUTC()), TestDate2( 59, 59, 23, 29, 2, 2004 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078099200).AsUTC()), TestDate2( 0, 0, 0, 1, 3, 2004 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078099230).AsUTC()), TestDate2( 30, 0, 0, 1, 3, 2004 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1078185599).AsUTC()), TestDate2( 59, 59, 23, 1, 3, 2004 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1096588800).AsUTC()), TestDate2( 0, 0, 0, 1, 10, 2004 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1413064016).AsUTC()), TestDate2( 56, 46, 21, 11, 10, 2014 )); - VERIFY_EQUAL(Gregorian::FromTM(mpt::Date::Unix( 1413064100).AsUTC()), TestDate2( 20, 48, 21, 11, 10, 2014 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 0)), TestDate2( 0, 0, 0, 1, 1, 1970 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 3600)), TestDate2( 0, 0, 1, 1, 1, 1970 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 86400)), TestDate2( 0, 0, 0, 2, 1, 1970 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 31536000)), TestDate2( 0, 0, 0, 1, 1, 1971 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 100000000)), TestDate2( 40, 46, 9, 3, 3, 1973 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 951782400)), TestDate2( 0, 0, 0, 29, 2, 2000 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1000000000)), TestDate2( 40, 46, 1, 9, 9, 2001 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1044057600)), TestDate2( 0, 0, 0, 1, 2, 2003 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1044144000)), TestDate2( 0, 0, 0, 2, 2, 2003 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1046476800)), TestDate2( 0, 0, 0, 1, 3, 2003 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1064966400)), TestDate2( 0, 0, 0, 1, 10, 2003 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1077926399)), TestDate2( 59, 59, 23, 27, 2, 2004 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1077926400)), TestDate2( 0, 0, 0, 28, 2, 2004 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1077926410)), TestDate2( 10, 0, 0, 28, 2, 2004 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1078012799)), TestDate2( 59, 59, 23, 28, 2, 2004 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1078012800)), TestDate2( 0, 0, 0, 29, 2, 2004 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1078012820)), TestDate2( 20, 0, 0, 29, 2, 2004 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1078099199)), TestDate2( 59, 59, 23, 29, 2, 2004 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1078099200)), TestDate2( 0, 0, 0, 1, 3, 2004 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1078099230)), TestDate2( 30, 0, 0, 1, 3, 2004 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1078185599)), TestDate2( 59, 59, 23, 1, 3, 2004 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1096588800)), TestDate2( 0, 0, 0, 1, 10, 2004 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1413064016)), TestDate2( 56, 46, 21, 11, 10, 2014 )); + VERIFY_EQUAL(mpt::Date::UnixAsUTC(mpt::Date::UnixFromSeconds( 1413064100)), TestDate2( 20, 48, 21, 11, 10, 2014 )); #ifdef MODPLUG_TRACKER @@ -2069,12 +2036,12 @@ // Edit history VERIFY_EQUAL_NONCONT(sndFile.GetFileHistory().size() > 15, true); const FileHistory &fh = sndFile.GetFileHistory().front(); - VERIFY_EQUAL_NONCONT(fh.loadDate.tm_year, 111); - VERIFY_EQUAL_NONCONT(fh.loadDate.tm_mon, 5); - VERIFY_EQUAL_NONCONT(fh.loadDate.tm_mday, 14); - VERIFY_EQUAL_NONCONT(fh.loadDate.tm_hour, 21); - VERIFY_EQUAL_NONCONT(fh.loadDate.tm_min, 8); - VERIFY_EQUAL_NONCONT(fh.loadDate.tm_sec, 32); + VERIFY_EQUAL_NONCONT(fh.loadDate.year, 2011); + VERIFY_EQUAL_NONCONT(fh.loadDate.month, 6); + VERIFY_EQUAL_NONCONT(fh.loadDate.day, 14); + VERIFY_EQUAL_NONCONT(fh.loadDate.hours, 21); + VERIFY_EQUAL_NONCONT(fh.loadDate.minutes, 8); + VERIFY_EQUAL_NONCONT(fh.loadDate.seconds, 32); VERIFY_EQUAL_NONCONT((uint32)((double)fh.openTime / HISTORY_TIMER_PRECISION), 31); // Macros