|
[Ref] mptTime: Use std::chrono for gregorian date calculations in C++20 mode.
[Ref] mptTime: Use std::chrono::system_clock for unix time instead of time_t in C++20 mode. This automatically makes all date handling year2038-correct in C++20 mode.
[Ref] mptTime: Use a custom gregorian date type mpt::Date::UTC instead of C struct tm.
[Ref] UpdateCheck: Use mpt::Date::Unix instead of time_t.
[Mod] UpdateCheck: Store last update timestamp as Unix seconds instead of YYYY-MM-DD HH:MM. On update, reset to 0.
[Mod] Edit History: For IT/MPTM, only ever store timestamps in UTC internally instead of localtime. Only convert to localtime when displaying to the user.
[Mod] Edit History: For other formats, also assume that the date is stored in UTC.
[Ref] mptTime: Disable std::chrono date handling completely for MSVC when targeting Windows earlier than Windows 10 1903 due to <https://github.com/microsoft/STL/issues/1911> and <https://github.com/microsoft/STL/issues/2163>.
[Ref] mptTime: Disable std::chrono for date parsing in VS2022 due to <https://developercommunity.visualstudio.com/t/stdchronoget-tzdb-list-memory-leak/1644641> / <https://github.com/microsoft/STL/issues/2504>.
chrono-v8.patch (34,303 bytes)
Index: common/mptTime.cpp
===================================================================
--- common/mptTime.cpp (revision 16961)
+++ common/mptTime.cpp (working copy)
@@ -13,7 +13,12 @@
#include "mptStringBuffer.h"
-#include <time.h>
+#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE)
+#include <chrono>
+#if 0
+#include <format>
+#endif
+#endif
#if MPT_OS_WINDOWS
#include <windows.h>
@@ -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<int32>(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<int64>(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<int64>(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<int32>(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<int32>(hours);
- result.tm_min = static_cast<int32>(minutes);
- result.tm_sec = static_cast<int32>(seconds);
+ mpt::Date::UTC result = {};
+ result.year = year;
+ result.month = month;
+ result.day = day;
+ result.hours = static_cast<int32>(hours);
+ result.minutes = static_cast<int32>(minutes);
+ result.seconds = static_cast<int64>(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 <chrono>
+#endif
#include <string>
+#if MPT_CXX_BEFORE(20) || defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE)
#include <time.h>
+#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<std::chrono::seconds>(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<std::chrono::seconds> 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<std::chrono::sys_days>(ymd)} + hms.to_duration();
+}
+
+inline mpt::Date::UTC UnixAsUTC(Unix tp)
+{
+ std::chrono::sys_days dp = std::chrono::floor<std::chrono::days>(tp);
+ std::chrono::year_month_day ymd{dp};
+ std::chrono::hh_mm_ss hms{tp - dp};
+ mpt::Date::UTC result;
+ result.year = static_cast<int>(ymd.year());
+ result.month = static_cast<unsigned int>(ymd.month());
+ result.day = static_cast<unsigned int>(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<int64>(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("<unknown date>");
- }
+ CString sDate = entry.HasValidDate() ? CTime(mpt::Date::UnixAsSeconds(mpt::Date::UnixFromUTC(entry.loadDate))).Format(_T("%d %b %Y, %H:%M:%S")) : CString(_T("<unknown date>"));
// Time + stuff
uint32 duration = mpt::saturate_round<uint32>(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 <time.h>
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<bool> 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<mpt::ustring>());
- 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<int64>(val.as<mpt::ustring>()));
}
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<std::byte> data = GetFileReader(f).ReadRawDataAsByteVector();
nlohmann::json::parse(mpt::buffer_cast<std::string>(data)).get<Update::versions>();
- 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<std::string>(resultHTTP.Data)).get<Update::versions>();
@@ -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 <time.h>
+#include "../common/mptTime.h"
#include <atomic>
@@ -40,9 +40,12 @@
struct UpdateCheckResult
{
- time_t CheckTime = time_t{};
+ mpt::Date::Unix CheckTime = mpt::Date::Unix{};
std::vector<std::byte> 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<uint32>(runtime * (HISTORY_TIMER_PRECISION / 18.2));
}
@@ -657,8 +657,8 @@
// Create FAT file dates
if(mptHistory.HasValidDate())
{
- fatdate = static_cast<uint16>(mptHistory.loadDate.tm_mday | ((mptHistory.loadDate.tm_mon + 1) << 5) | ((mptHistory.loadDate.tm_year - 80) << 9));
- fattime = static_cast<uint16>((mptHistory.loadDate.tm_sec / 2) | (mptHistory.loadDate.tm_min << 5) | (mptHistory.loadDate.tm_hour << 11));
+ fatdate = static_cast<uint16>(mptHistory.loadDate.day | (mptHistory.loadDate.month << 5) | ((mptHistory.loadDate.year - 1980) << 9));
+ fattime = static_cast<uint16>((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<uint8, uint8>(fileHeader.day, 1, 31);
- mptHistory.loadDate.tm_mon = Clamp<uint8, uint8>(fileHeader.month, 1, 12) - 1;
- mptHistory.loadDate.tm_year = fileHeader.year - 1900;
+ mptHistory.loadDate.day = Clamp<uint8, uint8>(fileHeader.day, 1, 31);
+ mptHistory.loadDate.month = Clamp<uint8, uint8>(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<uint32>(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<double>(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<int64>(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 <https://github.com/microsoft/STL/issues/1911>
+// and <https://github.com/microsoft/STL/issues/2163>.
+#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 <https://developercommunity.visualstudio.com/t/stdchronoget-tzdb-list-memory-leak/1644641>
+// / <https://github.com/microsoft/STL/issues/2504>.
+#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
|