Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

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

67 views
Skip to first unread message

Lynn McGuire

unread,
Feb 28, 2022, 5:36:35 PM2/28/22
to
"Year 2038 problem is still alive and well" by CookiePLMonster
https://cookieplmonster.github.io/2022/02/17/year-2038-problem/

"Year 2038 problem? Wasn’t that supposed to be solved once and for all
years ago? Not quite."

I suspect that as we get closer to 2038, we will find more and more
software subject to the year 2038 problem.
https://en.wikipedia.org/wiki/Year_2038_problem

Lynn


Siri Cruise

unread,
Feb 28, 2022, 8:39:09 PM2/28/22
to
In article <svjioa$afj$1...@dont-email.me>,
Lynn McGuire <lynnmc...@gmail.com> wrote:

> I suspect that as we get closer to 2038, we will find more and more
> software subject to the year 2038 problem.

What is the Y2K38 problem? That programmers think Y2K38 is more
efficient than Year 2038.

--
:-<> Siri Seal of Disavowal #000-001. Disavowed. Denied. Deleted. @
'I desire mercy, not sacrifice.' /|\
Discordia: not just a religion but also a parody. This post / \
I am an Andrea Doria sockpuppet. insults Islam. Mohammed

Richard Damon

unread,
Feb 28, 2022, 10:29:34 PM2/28/22
to
On 2/28/22 8:38 PM, Siri Cruise wrote:
> In article <svjioa$afj$1...@dont-email.me>,
> Lynn McGuire <lynnmc...@gmail.com> wrote:
>
>> I suspect that as we get closer to 2038, we will find more and more
>> software subject to the year 2038 problem.
>
> What is the Y2K38 problem? That programmers think Y2K38 is more
> efficient than Year 2038.
>

https://en.wikipedia.org/wiki/Year_2038_problem

On January 19, 2038 at 3:14:07 UTC, the signed 32 bit Unix timestamp
overflows, and will think it is 1901.

Ben Bacarisse

unread,
Feb 28, 2022, 10:55:55 PM2/28/22
to
I think most OSes have been fixed to use a wider type, have they not?
The more worrying problem is programs that store the timestamp in 32
bits.

There may be systems that don't have a wider native type, but that
should not be insurmountable. After all, that was the case in early
Unix. That's why time(2) takes a pointer. On early 16-bit systems, the
timestamp could not be returned as there was no 32-bit integer type.
The spec was

time(tvec)
int tvec[2];

--
Ben.

Keith Thompson

unread,
Feb 28, 2022, 11:56:00 PM2/28/22
to
Yes.

> and will think it is 1901.

Maybe. It depends on how the time() function is implemented. Some
implementations might return (time_t)(-1) if the calendar time is not
available; that's likely to be interpreted as 1969-12-31 23:59:59 UTC.

(Signed arithmetic overflow has undefined behavior, but that shouldn't
affect the time() function, which is required to return either "the
implementation’s best approximation to the current calendar time" or
(time_t)(-1).)

Of course some time() function implementations might be buggy.

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */

Juha Nieminen

unread,
Mar 1, 2022, 1:24:57 AM3/1/22
to
In comp.lang.c++ Richard Damon <Ric...@damon-family.org> wrote:
> https://en.wikipedia.org/wiki/Year_2038_problem
>
> On January 19, 2038 at 3:14:07 UTC, the signed 32 bit Unix timestamp
> overflows, and will think it is 1901.

Why would it think it's 1901 given that Unix time starts at 1 Jan 1970?

Robert Latest

unread,
Mar 1, 2022, 5:12:49 AM3/1/22
to
["Followup-To:" header set to comp.lang.c.]
Because time_t is signed.

Dan Purgert

unread,
Mar 1, 2022, 5:35:37 AM3/1/22
to
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

["Followup-To:" header set to comp.lang.c++.]
"time" is (well, at least the affected implementation is) a signed
32-bit integer.

"0" (0b0000 [...] 0000) corresponds to 1970-01-01 00:00:00 UTC.
However, when we reach the signed 32-bit maximum value (2,147,483,647 --
0b0111 [...] 1111), the next second will wrap to the minimum
(-2,147,483,648 -- 0b1000 [...] 0000), subtracting 68 years, 18 days
from 1970-01-01 (so, 1901-12-13).


-----BEGIN PGP SIGNATURE-----

iQIzBAEBCgAdFiEE3asj+xn6fYUcweBnbWVw5UznKGAFAmId9vgACgkQbWVw5Uzn
KGCDuA//XvZNX+J0XHEp2SKM8i3KGco/MmE46eLq0FaK/FdfGyF6OK6dVN70ot+l
rVPPVUuu61nJxx5zJJNiMRoVXQciwqLKPqm2c8Uc/euJ+LDrjm0+XUUPbjXhjoKg
OcqT+86fkHiM7MzmJCNoveb09GChCkKuj6xYAP7RzOBLCNudk/D+S/A26rjpqwFi
NN/nP9mAtvYLvfGJbllZj957QELo+4fxGcZh1vyUxi7T4sVYjXmPynP788lyiHTV
jubNU1xu18sXddiCaj7occfRFZ634i+dCGJXjd2S0ODAzyGduO6AGlR9ev14zypW
D8k8eOwujn4wl+RtZq+VOcc99Je6iUt6t6rv1MVZKVTQzJhkpchALAjIfUOBy8Ln
50VEwiKYjl+w3nC83kz9hdN0K03cIKWHStGca2KB78+qJi0SeQFjxmIJPMblNBay
tJVEO9k4+uvQ2LKz3LVtIEUaV4MjrZnSMetXNWKQs4I3NJFXk1xD5LkQ0iqKFU7w
Mfv8EiIZfe0NDz5jRZI/YfSXJVFtYaq1Eoz6Sq2lCmQyusWcqiE0JKjpBBHKvLud
fMemG/PSeg/qr7fMmBKc285+gpLhlKMVEDdsEcl+Kv0yc0cAxUUpEQLpsUWBhOyW
7e7QqwUe1E0Ey4KiwwaPxo3Q/GkJlkvj/CDwoA0AzWIkgR4L9Dk=
=G24+
-----END PGP SIGNATURE-----

--
|_|O|_|
|_|_|O| Github: https://github.com/dpurgert
|O|O|O| PGP: DDAB 23FB 19FA 7D85 1CC1 E067 6D65 70E5 4CE7 2860

John McCue

unread,
Mar 1, 2022, 2:18:40 PM3/1/22
to
trimmed followups to comp.lang.c

In comp.lang.c Siri Cruise <chine...@yahoo.com> wrote:
> In article <svjioa$afj$1...@dont-email.me>,
<snip>

> What is the Y2K38 problem? That programmers think Y2K38 is more
> efficient than Year 2038.

That is the press, saying 2038 is not "1337" :)

--
[t]csh(1) - "An elegant shell, for a more... civilized age."
- Paraphrasing Star Wars

Vir Campestris

unread,
Mar 3, 2022, 4:45:45 PM3/3/22
to
I'm not going to go all the way through the thread, sorry. But...

We've been here before. Y2K problem.

Some people hit it in 1990 with 10 year contracts.

Almost nobody hit in 2000.

A few did, but the world didn't end.

There will be work to do, but WTH it's 16 years away.

Andy

Kaz Kylheku

unread,
Mar 4, 2022, 2:22:42 AM3/4/22
to
On 2022-02-28, Lynn McGuire <lynnmc...@gmail.com> wrote:
> "Year 2038 problem is still alive and well" by CookiePLMonster
> https://cookieplmonster.github.io/2022/02/17/year-2038-problem/
>
> "Year 2038 problem? Wasn’t that supposed to be solved once and for all
> years ago? Not quite."

We should adopt a cyclic three digit year for some contexts. The main
problem with two digit years is that a century is too small of a time
span, which causes ambiguities, such as that someone born in `19 could
be a toddler, or 103 years old.

A three-digit year will fix most such things. 919 versus 019. Only if
you have contracts that span most of a millennium and such do you have
issues. For most human activities, it's sufficient, easily understood,
and free of issues. It never overflows; it doesn't accumulate more and
more precision with advancing time, always requiring the same amount of
storage. Arithmetic is easy. If you have two dates, d1 and d0, if d1 -
d0 <= 500y, then d0 is considered in the past. For instance, year 853 is
considered in the past relative to year 159.

--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

Juha Nieminen

unread,
Mar 4, 2022, 4:56:48 AM3/4/22
to
In comp.lang.c++ Kaz Kylheku <480-99...@kylheku.com> wrote:
> We should adopt a cyclic three digit year for some contexts. The main
> problem with two digit years is that a century is too small of a time
> span, which causes ambiguities, such as that someone born in `19 could
> be a toddler, or 103 years old.

It's indeed curious how common it is to express dates, such as dates
of birth, with a two-digit year. Some countries have, for example,
date-of-birth based personal IDs, and the date is almost invariably
of the form 010203 where the last two digits are the year.

Given that such IDs were most commonly adopted in the earlier half
of the 1900's it might have made sense back then, but once the millenium
drew closer it became more and more of a problem to distinguish between
newly born people and people who had their 100th birthday.

For example in Finland the format of personal IDs is DDMMYY-xxxx
where the xxxx is a four-character unique code consisting of letters
and numbers. To solve the millenium problem, what they decided to do
was that for people born in 2000 and after that - would be a + instead,
which feels like quite a kludge.

I think it would have been better to just have them have IDs of the
form DDMMYYYY-xxxx. Would have been much clearer and nicer-looking.

Mut...@dastardlyhq.com

unread,
Mar 4, 2022, 6:30:26 AM3/4/22
to
On Fri, 4 Mar 2022 09:56:03 -0000 (UTC)
Juha Nieminen <nos...@thanks.invalid> wrote:
>In comp.lang.c++ Kaz Kylheku <480-99...@kylheku.com> wrote:
>> We should adopt a cyclic three digit year for some contexts. The main
>> problem with two digit years is that a century is too small of a time
>> span, which causes ambiguities, such as that someone born in `19 could
>> be a toddler, or 103 years old.
>
>It's indeed curious how common it is to express dates, such as dates
>of birth, with a two-digit year. Some countries have, for example,
>date-of-birth based personal IDs, and the date is almost invariably
>of the form 010203 where the last two digits are the year.

I think the issue is that in most non IT cases the century doesn't matter.
People just know that if a young man gives his year of birth as 99 or 01 its not
going to be 1899 or 1901. Unfortunately this indifference to the century
carried over to programmers who then only used 2 digits in their code. This
*might* have just been valid back in the 50s when every byte really did
matter but nobody after the 60s really had any excuse to do this.

At least the unix 4 byte date had a reasonable excuse that back in the early
70s when unix was designed even 32 bit machines were stuff of the future
for anything except high end mainframes like the System 360 et al.

Jorgen Grahn

unread,
Apr 17, 2022, 10:48:06 AM4/17/22
to
On Fri, 2022-03-04, Juha Nieminen wrote:
> In comp.lang.c++ Kaz Kylheku <480-99...@kylheku.com> wrote:
>> We should adopt a cyclic three digit year for some contexts. The main
>> problem with two digit years is that a century is too small of a time
>> span, which causes ambiguities, such as that someone born in `19 could
>> be a toddler, or 103 years old.
>
> It's indeed curious how common it is to express dates, such as dates
> of birth, with a two-digit year.

I don't see two-digit years much these days, in Sweden. It's mainly
as expiry dates on food and things; perhaps because it takes a tenth
of a second extra to stamp "20" onto a carton of milk.

Perhaps we're lucky. Pretty much everyone has embraced ISO yyyy-mm-dd
here. It's ugly (I prefer the older d.m.yyyy) but it has four-digit
years and it sorts well.

> Some countries have, for example,
> date-of-birth based personal IDs, and the date is almost invariably
> of the form 010203 where the last two digits are the year.
>
> Given that such IDs were most commonly adopted in the earlier half
> of the 1900's it might have made sense back then, but once the millenium
> drew closer it became more and more of a problem to distinguish between
> newly born people and people who had their 100th birthday.
>
> For example in Finland the format of personal IDs is DDMMYY-xxxx
> where the xxxx is a four-character unique code consisting of letters
> and numbers. To solve the millenium problem, what they decided to do
> was that for people born in 2000 and after that - would be a + instead,
> which feels like quite a kludge.
>
> I think it would have been better to just have them have IDs of the
> form DDMMYYYY-xxxx. Would have been much clearer and nicer-looking.

Sweden uses YYMMDD-xxxx, but I don't regard that as a date ... and a
piece of code which tries to calculate someone's age or date of birth
from it is obviously broken. Doubly so because foreigners with unknown
year of birth get made-up YY figures.

Doesn't seem to be used that way too much, fortunately. You
sometimes hear about people over 100 being offered baby products,
but nothing more serious.

Oddly, nowadays you're sometimes asked to enter your ID with the
century included. I have to enter it for internet banking, just
before the 2FA thing. I never figured out why; it doesn't seem to be
an official thing.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Paavo Helde

unread,
Apr 17, 2022, 11:06:25 AM4/17/22
to
17.04.2022 17:47 Jorgen Grahn kirjutas:
> On Fri, 2022-03-04, Juha Nieminen wrote:
>> For example in Finland the format of personal IDs is DDMMYY-xxxx
>> where the xxxx is a four-character unique code consisting of letters
>> and numbers. To solve the millenium problem, what they decided to do
>> was that for people born in 2000 and after that - would be a + instead,
>> which feels like quite a kludge.
>>
>> I think it would have been better to just have them have IDs of the
>> form DDMMYYYY-xxxx. Would have been much clearer and nicer-looking.
>
> Sweden uses YYMMDD-xxxx, but I don't regard that as a date ... and a
> piece of code which tries to calculate someone's age or date of birth
> from it is obviously broken. Doubly so because foreigners with unknown
> year of birth get made-up YY figures.

Estonia uses fully numeric CYYMMDDXXXY, where C encodes both sex and
century (3=19xx male, 4=19xx female, etc), XXX is the unique part and Y
is a checksum number.

At least we have a working scheme for some centuries. Not sure though if
the idea of encoding the sex inside the ID number will long that last.
But this bit can be always redefined to be just a part of XXX, so no
real problem.


Bonita Montero

unread,
Apr 17, 2022, 11:19:10 AM4/17/22
to
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 )
{
}

Manfred

unread,
Apr 17, 2022, 3:10:27 PM4/17/22
to
On 4/17/22 4:47 PM, Jorgen Grahn wrote:
> On Fri, 2022-03-04, Juha Nieminen wrote:
>> In comp.lang.c++ Kaz Kylheku <480-99...@kylheku.com> wrote:
>>> We should adopt a cyclic three digit year for some contexts. The main
>>> problem with two digit years is that a century is too small of a time
>>> span, which causes ambiguities, such as that someone born in `19 could
>>> be a toddler, or 103 years old.
>>
>> It's indeed curious how common it is to express dates, such as dates
>> of birth, with a two-digit year.
>
> I don't see two-digit years much these days, in Sweden. It's mainly
> as expiry dates on food and things; perhaps because it takes a tenth
> of a second extra to stamp "20" onto a carton of milk.
>

It's not about printing cost, it's about standardization. For example,
GS1 (the authority that standardizes barcode labels on /everything/ we
buy in retail stores) defines expiry date formats as YYMMDD, and, more
importantly, it standardizes that as a /fixed/ length string.

Changing that part would have a major impact on nowadays's supply
management chains.
Now, one could argue that the fixed length requirement is justified for
strings encoded in barcodes only, but printing the human readable
version different from the encoded version might possibly have normative
implications, hence the result.

Bo Persson

unread,
Apr 17, 2022, 3:25:53 PM4/17/22
to
Stupid system design, probably.

A little checking tells us that YY=10 means 1910, because if you are
born 2010 you are to young for internet banking.

And if YY=09, it must mean 2009 because there aren't anyone from 1909 or
earlier around anymore.

But why bother with user-friendlyness? If I enter 70, maybe I was really
born in 1870?

Keith Thompson

unread,
Apr 17, 2022, 3:38:59 PM4/17/22
to
Bo Persson <b...@bo-persson.se> writes:
[...]
> Stupid system design, probably.
>
> A little checking tells us that YY=10 means 1910, because if you are
> born 2010 you are to young for internet banking.
>
> And if YY=09, it must mean 2009 because there aren't anyone from 1909
> or earlier around anymore.
>
> But why bother with user-friendlyness? If I enter 70, maybe I was
> really born in 1870?

In fact there are currently approximately 39 living people who were born
in 1909 or earlier. The oldest was born in 1903 and is now 119 years
old. None of them are Swedish, though.

https://en.wikipedia.org/wiki/List_of_the_oldest_living_people

Ben

unread,
Apr 17, 2022, 3:40:25 PM4/17/22
to
Bo Persson <b...@bo-persson.se> writes:

> A little checking tells us that YY=10 means 1910, because if you are
> born 2010 you are to young for internet banking.
>
> And if YY=09, it must mean 2009 because there aren't anyone from 1909
> or earlier around anymore.

The oldest people live to around 120 years at the moment and that will
become both more common and the upper limit will extend (climate change
and pandemics permitting).

> But why bother with user-friendlyness? If I enter 70, maybe I was
> really born in 1870?

Don't confuse convenience of data entry eith what's need in an ID
string. It's excellent to accept 70 as 1970, and maybe offer on-click
confirmation after the full DoB has been entered, but some ID systems
might need to know if xxxx-07-xxxx is a very old person or a teenager.

--
Ben.

Siri Cruise

unread,
Apr 17, 2022, 4:37:08 PM4/17/22
to
In article <jc37th...@mid.individual.net>,
Bo Persson <b...@bo-persson.se> wrote:

> >>> We should adopt a cyclic three digit year for some contexts. The main
> >>> problem with two digit years is that a century is too small of a time
> >>> span, which causes ambiguities, such as that someone born in `19 could
> >>> be a toddler, or 103 years old.

The Y2K38 isn't about representing the year. It isn't even about
32 bit time_t. A system can convert to 64 bit time_t and
recompile libraries. If that's all, it's trivially easy to fix.
The problem is everywhere there is an embedded 32 bit time stamp,
like tar files which cannot be easily identified and updated, or
very, important, and irreplaceable programs whose source code is
lost.
0 new messages