Rocksolid Light

Welcome to novaBBS (click a section below)

mail  files  register  newsreader  groups  login

Message-ID:  

Innovation distinguishes between a leader and a follower. -- Steve Jobs (1955-2011)


devel / comp.lang.c / Re: "Year 2038 problem is still alive and well" by CookiePLMonster

Re: "Year 2038 problem is still alive and well" by CookiePLMonster

<t3hb4u$lfh$1@dont-email.me>

  copy mid

https://www.novabbs.com/devel/article-flat.php?id=21258&group=comp.lang.c#21258

  copy link   Newsgroups: comp.lang.c comp.lang.c++
Path: i2pn2.org!i2pn.org!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail
From: Bonita.M...@gmail.com (Bonita Montero)
Newsgroups: comp.lang.c,comp.lang.c++
Subject: Re: "Year 2038 problem is still alive and well" by CookiePLMonster
Date: Sun, 17 Apr 2022 17:19:06 +0200
Organization: A noiseless patient Spider
Lines: 514
Message-ID: <t3hb4u$lfh$1@dont-email.me>
References: <svjioa$afj$1@dont-email.me>
Mime-Version: 1.0
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Sun, 17 Apr 2022 15:18:54 -0000 (UTC)
Injection-Info: reader02.eternal-september.org; posting-host="22cdc50e98bfff060b5f6dc687099198";
logging-data="22001"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+eZOajhrnP3rcQXmvb1kbvd19GF4RBRYc="
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101
Thunderbird/91.8.0
Cancel-Lock: sha1:QzrvYBXUwf1RfAVQ3ZbgUHLpXkU=
In-Reply-To: <svjioa$afj$1@dont-email.me>
Content-Language: de-DE
 by: Bonita Montero - Sun, 17 Apr 2022 15:19 UTC

I think Window's FILETIME and SYSTEM_TIME are the most elegant ways
to handle date-arithmetics. I've implemented this platform-independent
in C++:

// system_time.h:

#pragma once
#if defined(_MSC_VER)
#define NOMINMAX
#include <Windows.h>
#endif
#include <cstdint>
#include <compare>
#include <ctime>
#include <stdexcept>
#include <compare>

#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 26812) // prefer enum class over enum
#endif

struct system_time_t
{ std::uint16_t year;
std::uint8_t weekday, month, day;
std::uint8_t hour, minute, second;
std::uint32_t ns100;
};

struct system_time : public system_time_t
{ 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 );
system_time( std::uint64_t timestamp );
system_time( struct tm const &tm );
#if defined(_MSC_VER)
system_time( SYSTEMTIME const &st );
#endif
operator std::uint64_t() const;
system_time &operator =( std::uint64_t timestamp );
system_time &operator =( system_time const & ) = default;
operator tm() const;
system_time &operator =( struct tm const &tm );
#if defined(_MSC_VER)
operator SYSTEMTIME() const;
system_time &operator =( SYSTEMTIME const &st );
#endif
void adjust_weekday();
static std::uint8_t get_weekday( std::uint64_t timestamp );
static_assert(sizeof(time_t) == 8, "time_t must be 64 bit");
static time_t timestamp_to_time_t( uint64_t timestamp );
static uint64_t time_t_to_timestamp( time_t time );
struct date_error : public std::invalid_argument
{
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,
INVALID_WEEKDAY,
TIMESTAMP_BEFORE_TIME_T
};
reason_t reason();
private:
friend struct system_time;
date_error( reason_t reason );
reason_t m_reason;
};
private:
void assignFileTime( std::uint64_t timestamp );
static uint64_t const
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;
};

// may throw invalid_argument

inline
system_time::system_time( std::uint64_t timestamp )
{ assignFileTime( timestamp );
}

// may throw invalid_argument

inline
system_time &system_time::operator =( std::uint64_t timestamp )
{ assignFileTime( timestamp );
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::date_error::reason_t system_time::date_error::reason()
{ return m_reason;
}

// may throw date_error

inline
void system_time::adjust_weekday()
{ weekday = get_weekday( (uint64_t)*this );
}

inline
uint8_t system_time::get_weekday( uint64_t timestamp )
{ return (std::uint32_t)((timestamp - timestamp / WEEK * WEEK) / DAY + 1)
% 7;
}

// may throw date_error

inline
std::strong_ordering operator <=>( system_time_t const &left,
system_time_t const &right )
{ return (uint64_t)(system_time const &)left <=> (uint64_t)(system_time
const &)right;
}

// may throw date_error

inline
std::strong_ordering operator <=>( system_time_t const &left,
std::uint64_t right )
{ return (uint64_t)(system_time const &)left <=> right;
}

// may throw date_error

inline
std::strong_ordering operator <=>( std::uint64_t left, system_time_t
const &right )
{ return left <=> (uint64_t)(system_time const &)right;
}

#if defined(_MSC_VER)
#pragma warning(pop)
#endif

// system_time.cpp

#include <stdexcept>
#include <cassert>
#include <limits>
#include <algorithm>
#include <array>
#include <system_error>
#if defined(__cpp_lib_hardware_interference_size)
#include <new>
#endif
#include "system_time.h"

using namespace std;

#if defined(__cpp_lib_hardware_interference_size)
#define CL_SIZE std::hardware_destructive_interference_size
#else
#define CL_SIZE 64
#endif

//#define SYSTEM_TIME_BINARY_SEARCH

// 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( (uint64_t)*this );
}

// may throw date_error

void system_time::assignFileTime( uint64_t timestamp )
{ if( timestamp > (uint64_t)numeric_limits<int64_t>::max() )
throw date_error( date_error::TIMESTAMP_BEYOND_63_BIT );
weekday = get_weekday( timestamp );
timestamp += LEAP_YEAR; // 1601 - 1600 = leap year
uint64_t tsCalc = timestamp;
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 % 4) >= 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 % 4) < 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 alignas(CL_SIZE) monthOffsets[2][12 + 1] =
{
{ 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];
#if defined(SYSTEM_TIME_BINARY_SEARCH)
size_t lower = 0, upper = 12, hit = -1, mid;
do
{
mid = (lower + upper) / 2;
if( pMonthOffsets[mid] <= tsCalc )
hit = mid,
lower = mid + 1;
else
upper = mid;
} while( lower != upper );
#else
size_t hit;
for( hit = 0; tsCalc >= pMonthOffsets[hit + 1]; ++hit );
#endif
assert(hit != -1);
uint8_t dy = (uint8_t)((tsCalc - pMonthOffsets[hit]) / DAY);
tsCalc -= pMonthOffsets[hit] + dy * DAY;
month = 1 + (uint8_t)hit;
day = 1 + dy;
}
hour = (uint8_t)(tsCalc / HOUR);
tsCalc %= HOUR;
minute = (uint8_t)(tsCalc / MINUTE);
tsCalc %= MINUTE;
second = (uint8_t)(tsCalc / SECOND);
tsCalc %= SECOND;
ns100 = (uint32_t)tsCalc;
}

// may throw date_error

system_time::operator std::uint64_t() const
{ if( year < 1601 || year > 30828 ) [[unlikely]]
throw date_error( date_error::YEAR_OUT_OF_RANGE );
if( month < 1 || month > 12 ) [[unlikely]]
throw date_error( date_error::MONTH_OUT_OF_RANGE );
bool isLeapYear = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
static
uint8_t const alignas(CL_SIZE) monthLengths[2][1 + 12] =
{
{ 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]]
throw date_error( date_error::DAY_OUT_OF_RANGE );
if( hour >= 24 ) [[unlikely]]
throw date_error( date_error::HOUR_OUT_OF_RANGE );
if( minute >= 60 ) [[unlikely]]
throw date_error( date_error::MINUTE_OUT_OF_RANGE );
if( second >= 60 ) [[unlikely]]
throw date_error( date_error::SECOND_OUT_OF_RANGE );
if( ns100 >= 10'000'000 ) [[unlikely]]
throw date_error( date_error::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 alignas(CL_SIZE) monthOffsets[2][1 + 12] =
{
{ 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 += second * SECOND;
timestamp += ns100;
if( timestamp > (uint64_t)numeric_limits<int64_t>::max() )
throw date_error( date_error::NS100_OUT_OF_RANGE );
return timestamp;
}

#if defined(_MSC_VER)
// may throw date_error

system_time::operator SYSTEMTIME() const
{ SYSTEMTIME st;
st.wYear = year;
st.wDay = day;
st.wHour = hour;
st.wMinute = minute;
st.wSecond = second;
st.wMilliseconds = (WORD)(ns100 / 10'000);
st.wDayOfWeek = get_weekday( (uint64_t)*this );;
return st;
}

// may throw date_error

system_time &system_time::operator =( SYSTEMTIME const &st )
{ 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;
stAux.weekday = get_weekday( (uint64_t)stAux );
return *this = stAux;
} #endif

// may throw date_error

system_time::operator tm() const
{ if( year < 1900 || year - 1900u > (unsigned)numeric_limits<int>::max()
) [[unlikely]]
throw date_error( date_error::YEAR_OUT_OF_RANGE );
uint8_t wd = get_weekday( (uint64_t)*this );
struct tm tm;
tm.tm_year = year - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = day;
uint16_t const alignas(CL_SIZE) monthOffsets[2][1 + 12] =
{
{ 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 )
{ if( tm.tm_year < 1601 - 1900 || tm.tm_year > 30828 - 1900 ) [[unlikely]]
throw date_error( date_error::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;
uint64_t timestamp = (uint64_t)stAux;
*this = stAux;
weekday = get_weekday( timestamp );
return *this;
}

// may throw date_error

time_t system_time::timestamp_to_time_t( uint64_t timestamp )
{ if( timestamp < TIME_T_IN_TIMESTAMP_BEGIN )
throw date_error( date_error::TIMESTAMP_BEFORE_TIME_T );
return (timestamp - TIME_T_IN_TIMESTAMP_BEGIN) / SECOND;
}

// may throw date_error

uint64_t system_time::time_t_to_timestamp( time_t time )
{ if( time > LAST_TIMESTAMP_IN_TIME_T )
throw date_error( date_error::TIMESTAMP_BEYOND_63_BIT );
return TIME_T_IN_TIMESTAMP_BEGIN + time * SECOND;
}

system_time::date_error::date_error( reason_t reason ) :
invalid_argument(
[]( reason_t reason ) -> char const *
{
struct err_map_t
{
reason_t reason;
char const *what;
};
static
array<err_map_t, 10> const errMaps =
{
err_map_t { TIMESTAMP_BEYOND_63_BIT, "system_time - timestamp beyond
63 bit (14.9.30828 2: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 { INVALID_WEEKDAY, "system_time - invalid weekday" },
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 )
return errMap.what;
return "";
}( reason ) ),
m_reason( reason )
{ }

SubjectRepliesAuthor
o "Year 2038 problem is still alive and well" by CookiePLMonster

By: Lynn McGuire on Mon, 28 Feb 2022

96Lynn McGuire
server_pubkey.txt

rocksolid light 0.9.8
clearnet tor