371 lines
8.9 KiB
C++
371 lines
8.9 KiB
C++
/*
|
|
* mptTime.h
|
|
* ---------
|
|
* Purpose: Various time utility functions.
|
|
* Notes : (currently none)
|
|
* Authors: OpenMPT Devs
|
|
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
|
|
*/
|
|
|
|
|
|
#pragma once
|
|
|
|
#include "openmpt/all/BuildSettings.hpp"
|
|
|
|
#if MPT_CXX_AT_LEAST(20) && !defined(MPT_LIBCXX_QUIRK_NO_CHRONO_DATE)
|
|
#include <chrono>
|
|
#include <exception>
|
|
#endif
|
|
#include <string>
|
|
|
|
#include <ctime>
|
|
|
|
#if MPT_WINNT_AT_LEAST(MPT_WIN_8)
|
|
#define MPT_FALLBACK_TIMEZONE_WINDOWS_HISTORIC
|
|
#define MPT_FALLBACK_TIMEZONE_WINDOWS_CURRENT
|
|
#define MPT_FALLBACK_TIMEZONE_C
|
|
#elif MPT_WINNT_AT_LEAST(MPT_WIN_XP)
|
|
#define MPT_FALLBACK_TIMEZONE_WINDOWS_CURRENT
|
|
#define MPT_FALLBACK_TIMEZONE_C
|
|
#else
|
|
#define MPT_FALLBACK_TIMEZONE_C
|
|
#endif
|
|
|
|
|
|
|
|
OPENMPT_NAMESPACE_BEGIN
|
|
|
|
|
|
|
|
namespace mpt
|
|
{
|
|
namespace Date
|
|
{
|
|
|
|
#if defined(MODPLUG_TRACKER)
|
|
|
|
#if MPT_OS_WINDOWS
|
|
|
|
namespace ANSI
|
|
{
|
|
// uint64 counts 100ns since 1601-01-01T00:00Z
|
|
|
|
uint64 Now();
|
|
|
|
mpt::ustring ToUString(uint64 time100ns); // i.e. 2015-01-15 18:32:01.718
|
|
|
|
} // namespacee ANSI
|
|
|
|
#endif // MPT_OS_WINDOWS
|
|
|
|
#endif // MODPLUG_TRACKER
|
|
|
|
enum class LogicalTimezone
|
|
{
|
|
Unspecified,
|
|
UTC,
|
|
#if defined(MODPLUG_TRACKER)
|
|
Local,
|
|
#endif // MODPLUG_TRACKER
|
|
};
|
|
|
|
template <LogicalTimezone tz>
|
|
struct Gregorian
|
|
{
|
|
int year = 0;
|
|
unsigned int month = 0;
|
|
unsigned int day = 0;
|
|
int32 hours = 0;
|
|
int32 minutes = 0;
|
|
int64 seconds = 0;
|
|
friend bool operator==(const Gregorian<tz>& lhs, const Gregorian<tz>& 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 Gregorian<tz>& lhs, const Gregorian<tz>& 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
|
|
;
|
|
}
|
|
};
|
|
|
|
using AnyGregorian = Gregorian<LogicalTimezone::Unspecified>;
|
|
|
|
using UTC = Gregorian<LogicalTimezone::UTC>;
|
|
|
|
#if defined(MODPLUG_TRACKER)
|
|
using Local = Gregorian<LogicalTimezone::Local>;
|
|
#endif // MODPLUG_TRACKER
|
|
|
|
template <LogicalTimezone TZ>
|
|
inline Gregorian<TZ> interpret_as_timezone(AnyGregorian gregorian)
|
|
{
|
|
Gregorian<TZ> result;
|
|
result.year = gregorian.year;
|
|
result.month = gregorian.month;
|
|
result.day = gregorian.day;
|
|
result.hours = gregorian.hours;
|
|
result.minutes = gregorian.minutes;
|
|
result.seconds = gregorian.seconds;
|
|
return result;
|
|
}
|
|
|
|
template <LogicalTimezone TZ>
|
|
inline Gregorian<LogicalTimezone::Unspecified> forget_timezone(Gregorian<TZ> gregorian)
|
|
{
|
|
Gregorian<LogicalTimezone::Unspecified> result;
|
|
result.year = gregorian.year;
|
|
result.month = gregorian.month;
|
|
result.day = gregorian.day;
|
|
result.hours = gregorian.hours;
|
|
result.minutes = gregorian.minutes;
|
|
result.seconds = gregorian.seconds;
|
|
return result;
|
|
}
|
|
|
|
namespace nochrono
|
|
{
|
|
|
|
// int64 counts 1s since 1970-01-01T00:00Z
|
|
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;
|
|
}
|
|
};
|
|
|
|
inline Unix UnixNow()
|
|
{
|
|
return Unix{static_cast<int64>(std::time(nullptr))};
|
|
}
|
|
|
|
inline int64 UnixAsSeconds(Unix tp)
|
|
{
|
|
return tp.value;
|
|
}
|
|
|
|
inline Unix UnixFromSeconds(int64 seconds)
|
|
{
|
|
return Unix{seconds};
|
|
}
|
|
|
|
Unix UnixFromUTC(UTC timeUtc);
|
|
|
|
UTC UnixAsUTC(Unix tp);
|
|
|
|
#if defined(MODPLUG_TRACKER)
|
|
|
|
Unix UnixFromLocal(Local timeLocal);
|
|
|
|
Local UnixAsLocal(Unix tp);
|
|
|
|
#endif // MODPLUG_TRACKER
|
|
|
|
} // namespace nochrono
|
|
|
|
#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)
|
|
{
|
|
try
|
|
{
|
|
return std::chrono::system_clock::time_point{
|
|
std::chrono::sys_days {
|
|
std::chrono::year{ utc.year } /
|
|
std::chrono::month{ utc.month } /
|
|
std::chrono::day{ utc.day }
|
|
} +
|
|
std::chrono::hours{ utc.hours } +
|
|
std::chrono::minutes{ utc.minutes } +
|
|
std::chrono::seconds{ utc.seconds }};
|
|
} catch(const std::exception &)
|
|
{
|
|
return mpt::Date::UnixFromSeconds(mpt::Date::nochrono::UnixAsSeconds(mpt::Date::nochrono::UnixFromUTC(utc)));
|
|
}
|
|
}
|
|
|
|
inline mpt::Date::UTC UnixAsUTC(Unix tp)
|
|
{
|
|
try
|
|
{
|
|
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 = static_cast<int32>(hms.hours().count());
|
|
result.minutes = static_cast<int32>(hms.minutes().count());
|
|
result.seconds = static_cast<int64>(hms.seconds().count());
|
|
return result;
|
|
} catch(const std::exception &)
|
|
{
|
|
return mpt::Date::nochrono::UnixAsUTC(mpt::Date::nochrono::UnixFromSeconds(mpt::Date::UnixAsSeconds(tp)));
|
|
}
|
|
}
|
|
|
|
#if defined(MODPLUG_TRACKER)
|
|
|
|
inline mpt::Date::Unix UnixFromLocal(Local local)
|
|
{
|
|
try
|
|
{
|
|
std::chrono::time_point<std::chrono::local_t, std::chrono::seconds> local_tp =
|
|
std::chrono::local_days {
|
|
std::chrono::year{ local.year } /
|
|
std::chrono::month{ local.month } /
|
|
std::chrono::day{ local.day }
|
|
} +
|
|
std::chrono::hours{ local.hours } +
|
|
std::chrono::minutes{ local.minutes } +
|
|
std::chrono::seconds{ local.seconds };
|
|
return std::chrono::zoned_time{std::chrono::current_zone(), local_tp}.get_sys_time();
|
|
} catch(const std::exception &)
|
|
{
|
|
return mpt::Date::UnixFromSeconds(mpt::Date::nochrono::UnixAsSeconds(mpt::Date::nochrono::UnixFromLocal(local)));
|
|
}
|
|
}
|
|
|
|
inline mpt::Date::Local UnixAsLocal(Unix tp)
|
|
{
|
|
try
|
|
{
|
|
std::chrono::zoned_time local_tp{ std::chrono::current_zone(), tp };
|
|
std::chrono::local_days dp = std::chrono::floor<std::chrono::days>(local_tp.get_local_time());
|
|
std::chrono::year_month_day ymd{dp};
|
|
std::chrono::hh_mm_ss hms{local_tp.get_local_time() - dp};
|
|
mpt::Date::Local 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 = static_cast<int32>(hms.hours().count());
|
|
result.minutes = static_cast<int32>(hms.minutes().count());
|
|
result.seconds = static_cast<int64>(hms.seconds().count());
|
|
return result;
|
|
} catch(const std::exception &)
|
|
{
|
|
return mpt::Date::nochrono::UnixAsLocal(mpt::Date::nochrono::UnixFromSeconds(mpt::Date::UnixAsSeconds(tp)));
|
|
}
|
|
}
|
|
|
|
#endif // MODPLUG_TRACKER
|
|
|
|
#else
|
|
|
|
using Unix = nochrono::Unix;
|
|
|
|
using nochrono::UnixNow;
|
|
using nochrono::UnixAsSeconds;
|
|
using nochrono::UnixFromSeconds;
|
|
|
|
using nochrono::UnixFromUTC;
|
|
using nochrono::UnixAsUTC;
|
|
|
|
#if defined(MODPLUG_TRACKER)
|
|
|
|
using nochrono::UnixFromLocal;
|
|
using nochrono::UnixAsLocal;
|
|
|
|
#endif // MODPLUG_TRACKER
|
|
|
|
#endif
|
|
|
|
mpt::ustring ToShortenedISO8601(AnyGregorian date); // i.e. 2015-01-15T18:32:01
|
|
|
|
mpt::ustring ToShortenedISO8601(UTC date); // i.e. 2015-01-15T18:32:01Z
|
|
|
|
#ifdef MODPLUG_TRACKER
|
|
mpt::ustring ToShortenedISO8601(Local date); // i.e. 2015-01-15T18:32:01
|
|
#endif // MODPLUG_TRACKER
|
|
|
|
} // namespace Date
|
|
} // namespace mpt
|
|
|
|
|
|
|
|
#ifdef MODPLUG_TRACKER
|
|
|
|
namespace Util
|
|
{
|
|
|
|
#if MPT_OS_WINDOWS
|
|
|
|
// RAII wrapper around timeBeginPeriod/timeEndPeriod/timeGetTime (on Windows).
|
|
// This clock is monotonic, even across changing its resolution.
|
|
// This is needed to synchronize time in Steinberg APIs (ASIO and VST).
|
|
class MultimediaClock
|
|
{
|
|
private:
|
|
uint32 m_CurrentPeriod;
|
|
private:
|
|
void Init();
|
|
void SetPeriod(uint32 ms);
|
|
void Cleanup();
|
|
public:
|
|
MultimediaClock();
|
|
MultimediaClock(uint32 ms);
|
|
~MultimediaClock();
|
|
public:
|
|
// Sets the desired resolution in milliseconds, returns the obtained resolution in milliseconds.
|
|
// A parameter of 0 causes the resolution to be reset to system defaults.
|
|
// A return value of 0 means the resolution is unknown, but timestamps will still be valid.
|
|
uint32 SetResolution(uint32 ms);
|
|
// Returns obtained resolution in milliseconds.
|
|
// A return value of 0 means the resolution is unknown, but timestamps will still be valid.
|
|
uint32 GetResolution() const;
|
|
// Returns current instantaneous timestamp in milliseconds.
|
|
// The epoch (offset) of the timestamps is undefined but constant until the next system reboot.
|
|
// The resolution is the value returned from GetResolution().
|
|
uint32 Now() const;
|
|
// Returns current instantaneous timestamp in nanoseconds.
|
|
// The epoch (offset) of the timestamps is undefined but constant until the next system reboot.
|
|
// The resolution is the value returned from GetResolution() in milliseconds.
|
|
uint64 NowNanoseconds() const;
|
|
};
|
|
|
|
#endif // MPT_OS_WINDOWS
|
|
|
|
} // namespace Util
|
|
|
|
#endif // MODPLUG_TRACKER
|
|
|
|
|
|
|
|
OPENMPT_NAMESPACE_END
|