Use GetSystemTimeAsFileTime() and FileTimeToLocalFileTime() on
Windows or the time_t-capable Functions on Unix and this class:
Header:
#pragma once
#if defined(_MSC_VER)
#define NOMINMAX
#include <Windows.h>
#endif
#include <cstdint>
#include <compare>
#include <ctime>
#include <stdexcept>
#include <compare>
#include <memory>
#include <mutex>
#include <algorithm>
#include <vector>
#include <functional>
#include <atomic>
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 26812) // prefer enum class over enum
#pragma warning(disable: 26495) // members not intitialized
#endif
struct st_timestamp
{
constexpr st_timestamp() = default;
st_timestamp( st_timestamp const & ) = default;
constexpr st_timestamp( std::uint64_t timestamp ) noexcept;
constexpr operator std::uint64_t() const noexcept;
constexpr explicit operator std::int64_t() const noexcept;
constexpr explicit operator bool() const noexcept;
constexpr st_timestamp &operator =( st_timestamp const & ) = default;
constexpr st_timestamp &operator =( std::uint64_t timestamp ) noexcept;
private:
std::uint64_t m_timestamp;
};
struct system_time
{
std::uint16_t year;
std::uint8_t weekday, month, day;
std::uint8_t hour, minute, second;
std::uint32_t ns100;
enum reason_t : uint8_t
{
TIMESTAMP_BEYOND_63_BIT = 1,
YEAR_OUT_OF_RANGE,
MONTH_OUT_OF_RANGE,
DAY_OUT_OF_RANGE,
HOUR_OUT_OF_RANGE,
MINUTE_OUT_OF_RANGE,
SECOND_OUT_OF_RANGE,
NS100_OUT_OF_RANGE,
TIMESTAMP_BEFORE_TIME_T,
WRONG_LEAP_SECOND
};
struct leap_second_t
{
std::uint16_t year;
bool jun30, dec31;
};
struct date_error : public std::invalid_argument
{
reason_t reason();
date_error() = delete;
date_error( date_error const & ) = delete;
private:
friend struct system_time;
date_error( reason_t reason );
reason_t m_reason;
};
system_time() {}
system_time( system_time const & ) = default;
system_time( std::uint16_t year, std::uint8_t month, std::uint8_t day,
std::uint8_t hour, std::uint8_t minute, std::uint8_t second,
std::uint32_t ns100 );
explicit system_time( st_timestamp ts );
system_time( struct tm const &tm );
#if defined(_MSC_VER)
system_time( SYSTEMTIME const &st );
#endif
operator st_timestamp() const;
st_timestamp to_timestamp( reason_t *reason = nullptr ) const noexcept;
system_time &operator =( st_timestamp ts );
void from_timestamp( st_timestamp ts, reason_t *reason = nullptr )
noexcept;
system_time &operator =( system_time const & ) = default;
operator tm() const;
struct tm to_tm( reason_t *reason ) const noexcept;
system_time &operator =( struct tm const &tm );
bool from_tm( struct tm const &tm, reason_t *reason ) noexcept;
#if defined(_MSC_VER)
operator SYSTEMTIME() const;
SYSTEMTIME to_systemtime( reason_t *reason ) const noexcept;
system_time &operator =( SYSTEMTIME const &st );
bool from_systemtime( SYSTEMTIME const &st, reason_t *reason ) noexcept;
#endif
bool adjust_weekday( reason_t *reason = nullptr ) noexcept;
static std::uint8_t get_weekday( st_timestamp timestamp ) noexcept;
static_assert(sizeof(time_t) == 8, "time_t must be 64 bit");
static time_t timestamp_to_time_t( st_timestamp ts, reason_t *reason =
nullptr ) noexcept;
static st_timestamp time_t_to_timestamp( time_t time, reason_t *reason
= nullptr ) noexcept;
static void leap_seconds( std::function<leap_second_t ( bool )> const
&fn );
private:
friend struct st_timestamp;
static std::atomic<std::shared_ptr<std::vector<leap_second_t>>>
g_leapSeconds;
static constexpr std::uint64_t
MILLISECOND = 10'000,
SECOND = 1'000 * MILLISECOND,
MINUTE = 60 * SECOND,
HOUR = 60 * MINUTE,
DAY = 24 * HOUR,
WEEK = 7 * DAY,
NON_LEAP_YEAR = 365 * DAY,
LEAP_YEAR = 366 * DAY,
FOUR_YEARS_W_LJ = LEAP_YEAR + 3 * NON_LEAP_YEAR,
FOUR_YEARS_WO_LJ = 4 * NON_LEAP_YEAR,
FIRST_QUARTER_CENTURY = 25 * FOUR_YEARS_W_LJ,
REMAINING_QUARTER_CENUTRIES = 25 * FOUR_YEARS_W_LJ - DAY,
FOUR_HUNDRED_YEARS = FIRST_QUARTER_CENTURY + 3 *
REMAINING_QUARTER_CENUTRIES,
TIME_T_IN_TIMESTAMP_BEGIN = 0x19DB1DED53E800,
LAST_TIMESTAMP_IN_TIME_T = 0xD69433CCD5,
LEAP_SECOND_BIT = std::numeric_limits<int64_t>::min();
};
constexpr st_timestamp::st_timestamp( std::uint64_t timestamp ) noexcept :
m_timestamp( timestamp )
{
}
constexpr st_timestamp::operator std::uint64_t() const noexcept
{
return m_timestamp & ~system_time::LEAP_SECOND_BIT;
}
constexpr st_timestamp::operator std::int64_t() const noexcept
{
return m_timestamp;
}
constexpr st_timestamp::operator bool() const noexcept
{
return m_timestamp & system_time::LEAP_SECOND_BIT;
}
constexpr st_timestamp &st_timestamp::operator =( std::uint64_t
timestamp ) noexcept
{
m_timestamp = timestamp;
return *this;
}
// may throw invalid_argument
inline system_time::system_time( st_timestamp ts )
{
*this = ts;
}
// may throw invalid_argument
inline system_time &system_time::operator =( st_timestamp ts )
{
reason_t reason;
from_timestamp( ts, &reason );
if( reason ) [[likely]]
throw date_error( reason );
return *this;
}
inline system_time::system_time( struct tm const &tm )
{
*this = tm;
}
#if defined(_MSC_VER)
inline system_time::system_time( SYSTEMTIME const &st )
{
*this = st;
}
#endif
inline typename system_time::reason_t system_time::date_error::reason()
{
return m_reason;
}
inline uint8_t system_time::get_weekday( st_timestamp ts ) noexcept
{
using namespace std;
return (uint32_t)(((uint64_t)ts - (uint64_t)ts / WEEK * WEEK) / DAY +
1) % 7;
}
// may throw date_error
inline std::strong_ordering operator <=>( system_time const &left,
system_time const &right )
{
return (std::uint64_t)(st_timestamp)left <=> (uint64_t)(st_timestamp)right;
}
// may throw date_error
inline std::strong_ordering operator <=>( system_time const &left,
st_timestamp right )
{
return (std::uint64_t)(st_timestamp)left <=> (std::uint64_t)right;
}
// may throw date_error
inline std::strong_ordering operator <=>( st_timestamp left, system_time
const &right )
{
return (std::uint64_t)left <=> (std::uint64_t)(st_timestamp)left;
}
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
// .cpp:
#include <stdexcept>
#include <cassert>
#include <limits>
#include <algorithm>
#include <array>
#include <system_error>
#include <functional>
#if defined(__cpp_lib_hardware_interference_size)
#include <new>
#endif
#include "system_time.h"
using namespace std;
#if defined(__cpp_lib_hardware_interference_size)
constexpr size_t CL_SIZE = std::hardware_destructive_interference_size;
#else
constexpr size_t CL_SIZE = 64;
#endif
constexpr bool SYSTEM_TIME_BINARY_SEARCH = false;
// may throw date_error
system_time::system_time( std::uint16_t year, std::uint8_t month,
std::uint8_t day, std::uint8_t hour, std::uint8_t minute, std::uint8_t
second, std::uint32_t ns100 )
{
this->year = year;
this->month = month;
this->day = day;
this->hour = hour;
this->minute = minute;
this->second = second;
this->ns100 = ns100;
this->weekday = get_weekday( (st_timestamp)*this );
}
// may throw date_error
void system_time::from_timestamp( st_timestamp ts, reason_t *reason )
noexcept
{
auto reasonableErr = [&]( reason_t retReason ) -> void
{
if( reason ) [[likely]]
*reason = retReason;
memset( this, 0, sizeof *this );
};
uint64_t tsCalc = (uint64_t)ts;
bool leapSecond = (bool)ts;
weekday = get_weekday( ts );
tsCalc += LEAP_YEAR; // 1601 - 1600 = leap year
uint16_t y400, y100, y4, y;
y400 = (uint16_t)(tsCalc / FOUR_HUNDRED_YEARS);
tsCalc -= y400 * FOUR_HUNDRED_YEARS;
bool isLeapYear;
auto leapQuad = [&]()
{
if( tsCalc >= LEAP_YEAR ) [[likely]]
// y >= 1
tsCalc -= LEAP_YEAR,
y = (uint16_t)(1 + tsCalc / NON_LEAP_YEAR), // 1 ... 3
tsCalc -= tsCalc / NON_LEAP_YEAR * NON_LEAP_YEAR,
isLeapYear = false;
else
// y == 0
y = 0,
isLeapYear = true;
};
if( tsCalc >= FIRST_QUARTER_CENTURY ) [[likely]]
{
// (y % 400) >= 100
y100 = (uint16_t)(1 + (tsCalc - FIRST_QUARTER_CENTURY) /
REMAINING_QUARTER_CENUTRIES); // 1 ... 3
tsCalc -= FIRST_QUARTER_CENTURY + (tsCalc - FIRST_QUARTER_CENTURY) /
REMAINING_QUARTER_CENUTRIES * REMAINING_QUARTER_CENUTRIES;
if( tsCalc >= FOUR_YEARS_WO_LJ ) [[likely]]
// (y % 400) >= 100 && (y % 100) >= 4
y4 = (uint16_t)(1 + (tsCalc - FOUR_YEARS_WO_LJ) / FOUR_YEARS_W_LJ),
// 1 ... 24
tsCalc -= FOUR_YEARS_WO_LJ + (tsCalc - FOUR_YEARS_WO_LJ) /
FOUR_YEARS_W_LJ * FOUR_YEARS_W_LJ,
leapQuad();
else
// (y % 400) >= 100 && (y % 100) < 4
y4 = 0,
y = (uint16_t)(tsCalc / NON_LEAP_YEAR),
tsCalc -= tsCalc / NON_LEAP_YEAR * NON_LEAP_YEAR,
isLeapYear = false;
}
else
// (y % 400) < 100
y100 = 0,
y4 = (uint16_t)(tsCalc / FOUR_YEARS_W_LJ),
tsCalc -= tsCalc / FOUR_YEARS_W_LJ * FOUR_YEARS_W_LJ,
leapQuad();
year = 1600 + 400 * y400 + 100 * y100 + 4 * y4 + y;
{
static uint64_t const monthOffsets[2][12 + 1] alignas(CL_SIZE) =
{
{ 0 * DAY, 31 * DAY, 59 * DAY, 90 * DAY, 120 * DAY, 151 * DAY, 181 *
DAY, 212 * DAY, 243 * DAY, 273 * DAY, 304 * DAY, 334 * DAY, 999 * DAY },
{ 0 * DAY, 31 * DAY, 60 * DAY, 91 * DAY, 121 * DAY, 152 * DAY, 182 *
DAY, 213 * DAY, 244 * DAY, 274 * DAY, 305 * DAY, 335 * DAY, 999 * DAY }
};
uint64_t const *pMonthOffsets = monthOffsets[isLeapYear];
size_t moHit;
if constexpr( !SYSTEM_TIME_BINARY_SEARCH )
for( moHit = 0; tsCalc >= pMonthOffsets[moHit + 1]; ++moHit );
else
{
size_t lower = 0, upper = 12, mid;
moHit = -1;
do
{
mid = (lower + upper) / 2;
if( pMonthOffsets[mid] <= tsCalc )
moHit = mid,
lower = mid + 1;
else
upper = mid;
} while( lower != upper );
assert( moHit != -1 );
}
uint64_t mo = pMonthOffsets[moHit];
uint8_t dy = (uint8_t)((tsCalc - mo) / DAY);
tsCalc -= mo + dy * DAY;
month = 1 + (uint8_t)moHit;
day = 1 + dy;
}
hour = (uint8_t)(tsCalc / HOUR);
tsCalc %= HOUR;
minute = (uint8_t)(tsCalc / MINUTE);
tsCalc %= MINUTE;
second = (uint8_t)(tsCalc / SECOND);
if( leapSecond ) [[unlikely]]
{
if( second != 59 || minute != 59 || hour != 23 || (month != 6 || day
!= 30) && (month != 12 || day != 31) ) [[unlikely]]
return reasonableErr( WRONG_LEAP_SECOND );
shared_ptr leapSeconds( g_leapSeconds.load( memory_order_relaxed ) );
size_t lower = 0, upper = leapSeconds->size();
for( ; ; )
{
size_t mid = (lower + upper) / 2;
leap_second_t const &ls = (*leapSeconds)[mid];
if( ls.year == year ) [[unlikely]]
if( month == 6 && ls.jun30 || month == 12 && ls.dec31 ) [[likely]]
{
++second;
break;
}
else
return reasonableErr( WRONG_LEAP_SECOND );
bool midIsLower = ls.year < year;
(midIsLower ? lower : upper) = mid + midIsLower;
if( lower == upper ) [[unlikely]]
return reasonableErr( WRONG_LEAP_SECOND );
}
}
tsCalc %= SECOND;
ns100 = (uint32_t)tsCalc;
}
// may throw date_error
// may throw system_error
// may throw bad_alloc
extern system_time::leap_second_t const leapSecondsInit2016[26];
st_timestamp system_time::to_timestamp( reason_t *reason ) const noexcept
{
using namespace std;
auto reasonableErr = [&]( reason_t retReason ) -> st_timestamp
{
if( reason ) [[likely]]
*reason = retReason;
return st_timestamp( -1 );
};
if( year < 1601 || year > 30828 ) [[unlikely]]
return reasonableErr( YEAR_OUT_OF_RANGE );
if( month < 1 || month > 12 ) [[unlikely]]
return reasonableErr( MONTH_OUT_OF_RANGE );
bool isLeapYear = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
static uint8_t const monthLengths[2][1 + 12] alignas(CL_SIZE) =
{
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
if( day == 0 || day > monthLengths[isLeapYear][month] ) [[unlikely]]
return reasonableErr( DAY_OUT_OF_RANGE );
if( hour >= 24 ) [[unlikely]]
return reasonableErr( HOUR_OUT_OF_RANGE );
if( minute >= 60 ) [[unlikely]]
return reasonableErr( MINUTE_OUT_OF_RANGE );
uint8_t sec = second;
uint64_t leapSecondBit = 0;
if( sec >= 60 ) [[unlikely]]
{
if( sec != 60 || minute != 59 || hour == 23 || (month != 6 || day !=
30) && (month != 12 || day != 31) ) [[unlikely]]
return reasonableErr( SECOND_OUT_OF_RANGE );
shared_ptr leapSeconds( g_leapSeconds.load( memory_order_relaxed ) );
if( !leapSeconds ) [[unlikely]]
{
size_t i = 0;
leap_seconds(
[&]( bool reset ) -> leap_second_t
{
i = !reset ? i : 0;
if( i != size( ::leapSecondsInit2016 ) ) [[likely]]
return ::leapSecondsInit2016[i++];
else
return leap_second_t { 0xFFFFu, false, false };
} );
leapSeconds = g_leapSeconds.load( memory_order_relaxed );
}
size_t lower = 0, upper = leapSeconds->size();
for( ; ; )
{
size_t mid = (lower + upper) / 2;
leap_second_t const &ls = (*leapSeconds)[mid];
if( ls.year == year ) [[unlikely]]
if( month == 6 && ls.jun30 || month == 12 && ls.dec31 ) [[likely]]
{
--sec, leapSecondBit = LEAP_SECOND_BIT;
break;
}
else
return reasonableErr( SECOND_OUT_OF_RANGE );
bool midIsLower = ls.year < year;
(midIsLower ? lower : upper) = mid + midIsLower;
if( lower == upper ) [[unlikely]]
return reasonableErr( SECOND_OUT_OF_RANGE );
}
}
if( ns100 >= 10'000'000 ) [[unlikely]]
return reasonableErr( NS100_OUT_OF_RANGE );
uint16_t yr = year - 1600;
uint64_t timestamp = yr / 400 * FOUR_HUNDRED_YEARS;
yr %= 400;
auto leapQuad = [&]()
{
timestamp += yr / 4 * FOUR_YEARS_W_LJ;
yr %= 4;
if( yr >= 1 ) [[likely]]
timestamp += LEAP_YEAR + (yr - 1) * NON_LEAP_YEAR;
};
if( yr >= 100 ) [[likely]]
{
timestamp += FIRST_QUARTER_CENTURY;
yr -= 100;
timestamp += yr / 100 * REMAINING_QUARTER_CENUTRIES;
yr %= 100;
if( yr >= 4 ) [[likely]]
timestamp += FOUR_YEARS_WO_LJ,
yr -= 4,
leapQuad();
else
timestamp += yr * NON_LEAP_YEAR;
}
else
leapQuad();
timestamp -= LEAP_YEAR; // - (1.1.1601 - 1.1.1600)
static uint16_t const monthOffsets[2][1 + 12] alignas(CL_SIZE) =
{
{ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
{ 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
};
timestamp += (monthOffsets[isLeapYear][month] + day - 1) * DAY;
timestamp += hour * HOUR;
timestamp += minute * MINUTE;
timestamp += sec * SECOND;
timestamp += ns100;
if( timestamp > (uint64_t)numeric_limits<int64_t>::max() ) [[unlikely]]
return reasonableErr( TIMESTAMP_BEYOND_63_BIT );
if( reason ) [[likely]]
*reason = (reason_t)0;
return timestamp | leapSecondBit;
}
// may throw date_error
system_time::operator st_timestamp() const
{
reason_t reason;
st_timestamp ts = to_timestamp( &reason );
if( reason ) [[unlikely]]
throw date_error( reason );
return ts;
}
#if defined(_MSC_VER)
// may throw date_error
system_time::operator SYSTEMTIME() const
{
reason_t reason;
SYSTEMTIME st = to_systemtime( &reason );
if( reason ) [[unlikely]]
throw date_error( reason );
return st;
}
SYSTEMTIME system_time::to_systemtime( reason_t *reason ) const noexcept
{
SYSTEMTIME st;
st.wYear = year;
st.wDay = day;
st.wHour = hour;
st.wMinute = minute;
st.wSecond = second;
st.wMilliseconds = (WORD)(ns100 / 10'000);
reason_t lReason;
st_timestamp timestamp = to_timestamp( &lReason );
if( lReason ) [[unlikely]]
{
if( reason ) [[likely]]
*reason = lReason;
memset( &st, 0, sizeof st );
return st;
}
st.wDayOfWeek = get_weekday( timestamp );;
return st;
}
// may throw date_error
system_time &system_time::operator =( SYSTEMTIME const &st )
{
reason_t reason;
from_systemtime( st, &reason );
if( reason ) [[unlikely]]
throw date_error( reason );
return *this;
}
bool system_time::from_systemtime( SYSTEMTIME const &st, reason_t
*reason ) noexcept
{
system_time stAux;
stAux.year = st.wYear;
stAux.month = (uint8_t)st.wMonth;
stAux.day = (uint8_t)st.wDay;
stAux.hour = (uint8_t)st.wHour;
stAux.minute = (uint8_t)st.wMinute;
stAux.second = (uint8_t)st.wSecond;
stAux.ns100 = (uint32_t)st.wMilliseconds * 10'000;
reason_t lReason;
st_timestamp timestamp = to_timestamp( &lReason );
if( lReason ) [[unlikely]]
{
if( reason ) [[likely]]
*reason = lReason;
return false;
}
stAux.weekday = get_weekday( timestamp );
*this = stAux;
return true;
}
#endif
// may throw date_error
system_time::operator tm() const
{
reason_t reason;
struct tm tm( to_tm( &reason ) );
if( reason ) [[unlikely]]
throw date_error( reason );
return tm;
}
struct tm system_time::to_tm( reason_t *reason ) const noexcept
{
using namespace std;
auto reasonableErr = [&]( reason_t retReason ) -> struct tm
{
if( reason ) [[likely]]
*reason = retReason;
return tm( -1 );
};
if( year < 1900 || year - 1900u > (unsigned)numeric_limits<int>::max()
) [[unlikely]]
return reasonableErr( YEAR_OUT_OF_RANGE );
reason_t lReason;
uint64_t timestamp = to_timestamp( &lReason );
if( lReason ) [[unlikely]]
return reasonableErr( lReason );
uint8_t wd = get_weekday( timestamp );
struct tm tm;
tm.tm_year = year - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = day;
uint16_t const monthOffsets[2][1 + 12] alignas(CL_SIZE) =
{
{ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
{ 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
};
tm.tm_yday = monthOffsets[year % 4 == 0 && year % 100 != 0 || year %
400 == 0][month] + day - 1;
tm.tm_hour = hour;
tm.tm_min = minute;
tm.tm_sec = second;
tm.tm_wday = wd;
return tm;
}
// may throw date_error
system_time &system_time::operator =( struct tm const &tm )
{
reason_t reason;
if( !from_tm( tm, &reason ) ) [[unlikely]]
throw date_error( reason );
return *this;
}
bool system_time::from_tm( struct tm const &tm, reason_t *reason ) noexcept
{
auto reasonableErr = [&]( reason_t retReason ) -> bool
{
if( reason ) [[unlikely]]
*reason = retReason;
return false;
};
if( tm.tm_year < 1601 - 1900 || tm.tm_year > 30828 - 1900 ) [[unlikely]]
return reasonableErr( YEAR_OUT_OF_RANGE );
system_time stAux;
stAux.year = tm.tm_year + 1900;
stAux.month = tm.tm_mon + 1;
stAux.day = tm.tm_mday;
stAux.hour = tm.tm_hour;
stAux.minute = tm.tm_min;
stAux.second = tm.tm_sec;
stAux.ns100 = 0;
reason_t lReason;
uint64_t timestamp = stAux.to_timestamp( &lReason );
if( lReason ) [[unlikely]]
return reasonableErr( lReason );
stAux.weekday = get_weekday( timestamp );
*this = stAux;
return true;
}
// may throw date_error
time_t system_time::timestamp_to_time_t( st_timestamp ts, reason_t
*reason ) noexcept
{
if( (uint64_t)ts < TIME_T_IN_TIMESTAMP_BEGIN ) [[unlikely]]
{
if( reason ) [[likely]]
*reason = TIMESTAMP_BEFORE_TIME_T;
return -1;
}
return ((uint64_t)ts - TIME_T_IN_TIMESTAMP_BEGIN) / SECOND;
}
// may throw date_error
st_timestamp system_time::time_t_to_timestamp( time_t time, reason_t
*reason ) noexcept
{
if( time > LAST_TIMESTAMP_IN_TIME_T ) [[unlikely]]
{
if( reason ) [[likely]]
*reason = TIMESTAMP_BEYOND_63_BIT;
return -1;
}
return st_timestamp( TIME_T_IN_TIMESTAMP_BEGIN + time * SECOND );
}
// may throw date_error
inline bool system_time::adjust_weekday( reason_t *reason ) noexcept
{
reason_t lReason;
uint64_t timestamp = to_timestamp( &lReason );
if( lReason ) [[unlikely]]
{
if( reason ) [[likely]]
*reason = lReason;
return false;
}
weekday = get_weekday( timestamp );
return true;
}
system_time::date_error::date_error( reason_t reason ) :
invalid_argument(
[]( reason_t reason ) -> char const *
{
static struct err_map_t
{
reason_t reason;
char const *what;
} const errMaps[] =
{
err_map_t { TIMESTAMP_BEYOND_63_BIT, "system_time - timestamp beyond
63 bit (14.9.30828 22:48:5.4775807)" },
err_map_t { YEAR_OUT_OF_RANGE, "system_time - year out of range" },
err_map_t { MONTH_OUT_OF_RANGE, "system_time - month out of range" },
err_map_t { DAY_OUT_OF_RANGE, "system_time - day out of range" },
err_map_t { HOUR_OUT_OF_RANGE, "system_time - hour out of range" },
err_map_t { MINUTE_OUT_OF_RANGE, "system_time - minute out of range" },
err_map_t { SECOND_OUT_OF_RANGE, "system_time - second out of range" },
err_map_t { NS100_OUT_OF_RANGE, "system_time - 100ns-steps out of
range" },
err_map_t { TIMESTAMP_BEFORE_TIME_T, "system_time - timestamp before
time_t (1.1.1970 00:00.0)" }
};
for( err_map_t const &errMap : errMaps )
if( errMap.reason == reason ) [[unlikely]]
return errMap.what;
return "";
}( reason ) ),
m_reason( reason )
{
}
// may throw bad_alloc
// may throw invalid_argument if there's a duplicate leap year from the
feeder
// may throw date_error if a year is out of range (< 1601 || > 30828)
// may throw invalid_argument if leap year from feeder has no leaps
void system_time::leap_seconds( std::function<leap_second_t ( bool )>
const &feeder )
{
using namespace std;
auto feedLoop = [&]<typename Fn>( Fn fn ) -> size_t
requires requires( Fn fn, leap_second_t const &ls ) { { fn( ls ) }; }
{
size_t i = 0;
for( leap_second_t ls; (ls = feeder( i++ == 0 )).year != 0xFFFFu; fn(
ls ) );
return i;
};
vector<leap_second_t> leaps;
leaps.reserve( feedLoop( [&]( leap_second_t const & ) { } ) );
feedLoop( [&]( leap_second_t const &ls ) { leaps.emplace_back( ls ); } );
sort( leaps.begin(), leaps.end(),
[]( leap_second_t &lhs, leap_second_t &rhs ) -> bool { return lhs.year
< rhs.year; } );
for( leap_second_t const &lp : leaps )
if( &lp != &leaps[0] && lp.year == (&lp)[-1].year ) [[unlikely]]
throw invalid_argument( "duplicate leap year" );
else if( lp.year < 1601 || lp.year > 30828 ) [[unlikely]]
throw date_error( YEAR_OUT_OF_RANGE );
else if( !lp.jun30 && !lp.dec31 ) [[unlikely]]
throw invalid_argument( "leap year without leaps" );
g_leapSeconds.store( make_shared<vector<leap_second_t>>( move( leaps )
), memory_order_release );
}
atomic<shared_ptr<vector<system_time::leap_second_t>>>
system_time::g_leapSeconds;
static system_time::leap_second_t const leapSecondsInit2016[26] =
{
{ 1972, true, true },
{ 1973, false, true },
{ 1974, false, true },
{ 1975, false, true },
{ 1976, false, true },
{ 1977, false, true },
{ 1978, false, true },
{ 1979, false, true },
{ 1981, true, false },
{ 1982, true, false },
{ 1983, true, false },
{ 1985, true, false },
{ 1987, false, true },
{ 1989, false, true },
{ 1990, false, true },
{ 1992, true, false },
{ 1993, true, false },
{ 1994, true, false },
{ 1995, false, true },
{ 1997, true, false },
{ 1998, false, true },
{ 2005, false, true },
{ 2008, false, true },
{ 2012, true, false },
{ 2015, true, false },
{ 2016, false, true }
};
This is similar to Windows' FILETIME (100ns-intervals since 1.1.1601
00:00:00.0) and you can convert the C-timestamps also to st_timestamp.
st_timestamp is a 64 bit timestamp which uses the highes bit to indi-
cate if the timestamp is within a leap-second. If you - implicitly
or explicitly - convert the st_timestamp to an uint64_t the highest
bit is stripped and you can calculate with it like with a Unix-time-
stamp, but with a higher resolution. You can implicitly or explicitly
convert back an uint64_t-value to a st_timestamp independently on if
the highest bit is set or not. The bit is only honored if you convert
this timestamp to a system_time object, i.e. the second-part is set
to 60 if the st_timestamp corresponds to a legal leap-second.