Glibc is still not Y2038 compliant by default
ariadne.space
ariadne.space
Given this article, it seems we're still doing it wrong, and that means... 2038 will be "fun" (either the remediation, or the consequences of the lack thereof).
At least most of us in the industry now have a good retirement plan: Fixing the legacy systems in 16 years...
Yes. It's getting close. Windows XP, released in 2001, still had a significant presence in 2017, 16 years after introduction.[1] 42% of companies still had some XP systems running back then. 11% of machines were still running it.
[1] https://community.spiceworks.com/networking/articles/2719-wi...
TFA is about how GNU/Linux systems being deployed right now are not Y2038-safe.
Windows applications have been safe from this issue by default starting from VC8.0 (VS 2005), macOS users since 10.7.
And to be fair Linux itself has used 64b time_t on 64b machines from the start (and since 5.6 — last year — has also migrated i386 to 64b time_t[0], though a few issues will remain forever).
There are many who don’t think like this. Microsoft has dedicated teams that ensure backwards compatibility for legacy applications, and even then people stay on XP. If you have an inherited Ubuntu system that has been happily running a proprietary binary for years, there’s even less guarantee (and certainly no marketing organization making that guarantee) that an upgrade won’t break things. And so you fall far behind LTS.
au contraire, it's quite easy to keep museum-level ancient Linux distros running
https://news.ycombinator.com/item?id=28046997
Working with that, conceptually, isn’t difficult. You can use any dateline as the epoch by subtracting the chosen epoch’s NTP timestamp from the one you received with wraparound.
Making a dateline library use that epoch for formatting dates and times isn’t hard, either, but could be a lot of work.
https://en.wikipedia.org/wiki/Network_Time_Protocol#Timestam...:
NTPv4 introduces a 128-bit date format: 64 bits for the second and 64 bits for the fractional-second. The most-significant 32-bits of this format is the Era Number which resolves rollover ambiguity in most cases. According to Mills, "The 64-bit value for the fraction is enough to resolve the amount of time it takes a photon to pass an electron at the speed of light. The 64-bit second value is enough to provide unambiguous time representation until the universe goes dim."
Won't catch absolutely everything (e.g. local containers), but will probably get pretty close. Especially if modern NTP client versions have some way to resolve the ambiguity, e.g. a hardcoded "it's after 2020".
As long as the device knows it was made in 2021. But the device can't know that by magic; the manufacturer has to store that information in it somehow. Do all device manufacturers do that?
Through if you do it properly you add some fixed known min . time (like manufacturing time, or time of the last system software/is update) and if the NTP time is noticable below the min time you increase the epoch by 1.
E.g. in you case the device could know it's revision had a min. time of idk. 2015. Then if it receives 1973 it knows it's 3 years in the next epoch.
But as anyone can guess there will be devices which didn't implement that at all and will end up in 1970. Or which require setting time by hand and will end up in 1970 because it truncates to 32bits at some point or similar.
Through some embedded devices might happen to avoid it. Like due to special reasons they might use a non-unix epoch.
How many NTP implementations don't consider era or contain bugs if era != 0?
To fix this you need a persistent low water mark on the time, compiled into the NTP program and/or stored in the filesystem (eg the timestamp on the NTP drift file). Then NTP timestamps can be interpreted as spanning the 136 years after the low water mark, using modular arithmetic.
Yep, this is what many GPS receivers do to deal with the 20 year issue. They burn their manufacturing date, so they know which "cycle" they're in.
> The NTP protocol will synchronize correctly, regardless of era, as long as the system clock is set initially within 68 years (a half-era) of the correct time
Which means 2036 will appear to work just fine, as 1970 is only 66 years away. Now jump forward to 2038, and this would not be the case..This "era" thing seems like the exact type of thing that someone would ignore when implementing the protocol, or if it is supported, probably doesn't get tested much. It's like leap seconds. Yeah, the leap second info is conveyed in PTP, but most of the implementations I've seen simply ignore it and just jam the clock when the time jumps.
From my experience the latter is safer because people tend to save datetime strings in a format that's either not standard nor unambiguous.
When we reimplemented the system from an ancient mainframe, we stored the value as a Unix timestamp and a standard representation of local time. It was derived from an old format going backwards and recorded in the new format going forward.
Having both is critical for our successors to figure out wtf is going on. UTC 1/1/70 is an anchor point, and the local representation is canonical at a point in time. I don’t recall how 1968-1970 was handled. Not only do you have to plan for “Berlin time” going away, but for daylight (or double daylight) times changing. Having both entries allows you to reconcile, so the poor bastard figuring out what to purge in 2090 has a fighting chance!
A few decades later, timezones are changed. PST should add 30 minutes, all others stay the same. You only have 4102452000, what should you convert it into? You lost the time zone information, so you can't make the correct decision.
No. Say I have a database with a bunch of timestamps for future events. I calculate the time of those events with my current idea of local time (because my customer or because legal requirements require me to do something at a specific local time), convert to GMT, and store as a timestamp in my database.
In 2024, California decides to no longer observe Daylight Savings Time. All of those current timestamps no longer happen at the proper time in the newer version of America/Los_Angeles.
So there's a big pitfall here, in that the relation between localized time and UTC timestamp changes.
If you need to schedule future events it gets obviously a lot more complicated, you don't need a timestamp you need local time and you need it to be robust to future changes in timezones, DSTs, politics.
If you care about a past moment in time as reported by GPS or a computer clock-- use a UTC timestamp.
If you care about a future time delineated by a precise interval from now-- use a UTC timestamp.
If you care about a future time expected to be delineated in a specific time zone, use a timestamp in local time and note the zone.
If you care about a future time in a customer's time zone no matter where they may be, store the time in an unspecified local time and the customer for whom the time applies. Etc.
It takes a bit of housekeeping, but isn't especially difficult.
> if that actually happens
The IANA time zone database [1] is updated several times a year, it happens all the time
Thus reinventing zoned datetimes badly
> if that actually happens
It happens literally all the time, sometimes with very little heads up e.g. the decision to apply DST or not can be take mere weeks before it actually occurs[0], and something as impactful as an entire calendar day disappearing can happen with 6 months notice[1].
[0] https://egyptindependent.com/egypts-hasty-goodbye-daylight-s..., https://codeofmatt.com/time-zone-chaos-inevitable-in-egypt/
[1] https://www.theguardian.com/world/2011/dec/30/samoa-loses-da...
I do find that having the timestamp at hand makes math easier in a lot of the use cases I've come across. I wonder if there is an easy hybrid solution where you can capture timezone info, timestamp info and have it adjust without a full DB update, even if timezone offsets change. Maybe a mapping table of some kind, with versioned timezone codes.
For past events, you can store timestamps, it doesn't matter, the mapping is known and fixed.
Example: nobody cares if a concert planned on November 19, 2024 at 19:00 in Honolulu starts in 12345600 seconds or 12349200 seconds since now. But everyone cares that everone's calendars and watches are in sync by that time and show specifically that date and that time, regardless of how many times people switched DST or timezones in the years in-between.
That means when daylight savings time rules change or timezone lines are moved the UTC time of these events change.
Unfortunately I don't believe anyone has standardized a format for storing times which are specified local time at a specified location on a specified date. So it is all roll-your-own or use UTC or zoned datetime and be burned when things change.
Of course there are things like HFT where precise time matters a lot, but there you don't schedule things years in advance, all the long-term things like mortgage schedules and insurance terms simply ignore time and timezones.
xfs filesystem being mounted at BLABLABLA supports timestamps until 2038 (0x7fffffff)
Hmmm...Most of these systems being put in production right now will be using 64-bit distributions, which have always used 64-bit time_t. Most of the rest will be using embedded distributions, which often use something other than glibc. So only a fraction of these systems "being put in production right now [which] are going to be around" will be affected.
Dietlibc, uclibc (used by openwrt and the other consumer router replacement firmwares), musl libc, and bionic (since Android is technically an embedded Linux, so should be mentioned as well) dominate the embedded Linux space.
#define _TIME_T_ __int_least64_t
typedef _TIME_T_ time_t;
[1]: https://imagecraft.com/blog/2019/04/embedded-gcc-libraries-n...ldd --version ldd (Debian GLIBC 2.28-10) 2.28 Copyright (C) 2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Written by Roland McGrath and Ulrich Drepper.
And of course, on actual microcontrollers newlib-nano is what you're likely using with a GNU toolchain.
However, glibc documentation (https://www.gnu.org/software/libc/manual/html_node/Feature-T...) says:
> If _TIME_BITS is undefined, the bit size of time_t is architecture dependent. Currently it defaults to 64 bits on most architectures. Although it defaults to 32 bits on some traditional architectures (i686, ARM), this is planned to change and applications should not rely on this.
So it sounds like glibc plans to change the default, just like musl has done.
1. Old situation, time_t is 32-bit
2. Migration starts, time_t is 32-bit by default, -D_TIME_BITS=64 to get 64-bits
3. Migration continues, time_t is 64-bit by default, -D_TIME_BITS=32 to get 32-bits
4. New situation, time_t is 64-bit
And with a long time between these steps. So glibc is at step 2 while musl skipped step 2 and is at step 3. There should be plenty of time between steps. It might make sense to keep it at step 3 until 2037 or so for very restrictive embedded plaforms etc.
Why would you -D_TIME_BITS to something other than the default? It's a hard thing to do safely, since you're choosing to be ABI incompatible (in a way that's not caught at compile or even invocation time) with the system's default.
And then, all you get is a binary that will potentially handle times past 2038 safely, when the whole rest of the system won't.
Note anything that builds on x86-64 or other 64 bit architectures has already figured out how to tolerate 64 bit time_t's.
The major reason it's not safe to build with a 64 bit time_t is because you don't know whether other libraries are. If the C library cuts over, when people/distributions move to the new library version, they know that's not a concern.
Defaults change in tooling all the time, requiring code changes for distributions bundling newer tooling/libraries. This one would require less change than most.
Of course, it's possible to survive a 64 bit time_t and still not be 2038-safe. But at least you can be correct if you have a C library and other libraries that will tolerate 64 bit time_t's.
All libraries in popular distros build on x86-64, which in turn means they use a 64 bit time_t there.
It's time for 32 bit architectures to join 64 bit architectures in having a 64 bit time_t.
Anything that ships today in distributions will ship in embedded systems for 5+ years. And then a lot of those embedded systems (too many) will last a long time. 2026 is getting pretty uncomfortably close to 2038.
Yes for preparing the upgrade seamlessley for developers, administrators and users.
you can prepare your buildscripts before it breaks, you can build it easier as feature/forward/backward toggle
same logic and code with different toggles is better
The big pain, IMO, at this point is the big cutover where all libraries need to move to the new ABI.
This still doesn't prove things as 2038-safe, but it at least means things reasonably can be 2038-safe on 32 bit if they choose... while today it's impossible for practical purposes.
Though I am not certain 64-bit supports 32-bit time so it may be on 4 there already
I am basing this of these two sites and a quick look at the source code though there may be better documentation on it
https://sourceware.org/glibc/wiki/Y2038ProofnessDesign
https://www.gnu.org/software/libc/manual/html_node/64_002dbi...
The bigger issue is all the systems that won't be rebuilt (per several sibling comments).
--
EDIT: fixed grammar.
Even if glibc does it, if other libraries expose time_t in their ABI and don't do the same, then it's the same problem.
Symbol versioning[1,2]. Kind of like aliases, but more awkward. In theory it’s not specific to glibc, in practice hardly anyone else bothers being so careful about their ABI.
Note that dynamically linked musl, which Alpine uses (I think..?), doesn’t understand symbol versioning, though I expect this will only last until the first ABI break in musl.
[1]: https://www.akkadia.org/drepper/symbol-versioning
[2]: https://maskray.me/blog/2020-11-26-all-about-symbol-versioni...
I know that glibc uses symbol versioning to remain ABI-compatible within versions. What I meant was ABI compatibility between 32-bit and 64-bit time_t with the same glibc library. Looks like it uses something like aliases for that (but self-implemented using asm):
https://sourceware.org/git/?p=glibc.git;a=blob;f=misc/sys/se...
https://sourceware.org/git/?p=glibc.git;a=blob;f=misc/sys/cd...
Rebuilds on every codebase everywhere ever would promptly blow up.
IMHO the revolts would come from two camps, a) bureaucracies for whom a build system change would normally take months, and b) individual devs who would be all like "compilation flags?? in MY defaults?! that's LESS likely than you think!".
Worst case scenario, someone forks glibc, removes the offending requirement, proffers commercial support for their fork... and ends up making bank.
Only if they use time_t, but I get the point.
For the bureaucracies of case (a), a change of glibc or, more so, gcc should then take many years if they take the impact of changes seriously.
Why? Because for the majority of codebases, the time_t change won't require any work.
You'd be forcing a compiler error, and it would effectively say "Add -D_TIME_BITS=64. If you're doing something really weird with time_t then you may have to update your code too, but in 95% of the cases, you can just add that flag."
I think the compiler error might be warranted for something like "You are using a function that is 95% of the time insecure or wrong, please add -DGAPING_SECURITY_HOLE if you know what you're doing", but if the error really is just "add a flag, you do not need to think about your code probably", the library authors themselves might as well default it for you.
If step 2 is omitted, then similar great mess will happen on transition from step 1 to step 3.
We need a better plan, like modifying compilers (for i686 and other 32-bit targets) to emit '32-bit time_t code' and '64-bit time_t code' simultaneously, and then resolve to proper functions later, at the link time.
The correct play could very well be to wait to the last second and pay absurd money for scarce experts to fix the systems.
Quite the opposite if you're talking about rPIs: the small/embedded space is where things can live for a long time. They're the primary place to worrying about. Especially if you're talking about industrial processes.
Aside from Android, it is the only ARM distribution that I know of with this property.
The thing is, 2038 is less and less far away with each day that goes by. Especially since real programs often have to use future timevalues (and often further in the future than one would think).
Stuff has to change at some point. A glibc major version which changes the structures and typedefs for all 32 bit code could handle it.
But as it stands now, someone who wants to make a 32 bit program y2038-compliant will have a hard time doing it: they can't safely hand time values to any library that is possibly compiled differently.
This punts it to whom, distro maintainers? They have to try and rebuild everything with the correct define? And the lone end-user who does gcc -o myfile myfile.c gets code that's broken against the system libraries? Or they patch glibc itself and ship a glibc that's ABI incompatible with everyone else. Meh.
Either glibc will do it, or time itself will do it. But when time will do it, it will be much harder breakage.
* https://www.gnu.org/software/libc/
Change the default and release "3.0" with the time_t alteration at the top of the ChangeLog. 2.x can perhaps live for a while for some overlap.
The distros will eventually pick it up, and all code compiled/released for a particular distro version will deal with it in course.
If your code is called by any other code and took in a time_t recompiling would have broken your ABI.
I mean, waiting does mean there will be fewer 32 bit systems: so there's an argument to put off the transition to lower the scope of work.
However, most of the ones that stay around will be embedded and long-lived: just the worst kind of thing to bite us in the future.
That really highlights the benefit of the BSD model, where the OS, libc and all the base packages are shipped as a complete system. OpenBSD switch to 64 bit time_t on all supported platforms back in 2014. We all talked about Y2038 back then, and most agreed that something should be now as quickly as possible. Then the world just sort of forgot. I mean it is solved, but if you're a new developer, you might not know that you need to do something special.
The GNU and Linux world is really good at not breaking ABI compatibility and mostly that's great, but in this case it's a problem and pushing it much further is going to be a problem. It's also going to be weird if the default never change, then we just have a C library that that just keep producing defective programs, unless you add certain flags.
If you exclude like every user land library there is (including the glibc)
You can still run Linux programs linked against decades-old versions of glibc on current systems as they have kept ABI compatibility with the use of symbol versioning, without changing their SONAME ("libc.so.6").
Sure, that does not extend to being able running programs linked against new glibc symbols on old systems, but if you consider that ABI-breaking then the Linux kernel would also be ABI-breaking as you cannot run binaries relying on new kernel syscalls (etc) on older Linux kernels.
At the precise date of the epochalypse, the date will roll over for the current time_t.
But one second before that, it will blow up for a system that peeks 1 second into the future (e.g a control system).
One day before, the date will be screwed up for any program doing addition with one day such as a rental system.
In the years before 2038, systems that do calculations on year scales will fail, like perhaps insurance or mortgage and similar.
What happens in the reverse case on Alpine (or anything using the other approach)? If I build a new program but link against a dependency that predates the switch (say, I upgraded my workstation to a new Alpine release but didn't `make clean`), will I get the same breakage?
If you jump between incompatible versions of a C library without rebuilding, you can expect nothing to work.
Is there a reason musl introduced this in a non-major version? Your point here is perfectly valid, but to me the next obvious question is "what qualifies as an incompatible version?", and after doing some Googling I'm surprised it's not very clear to me. I would have assumed that going from 1.1.X to 1.2.0 was compatible, or IE. two programs compiled against 1.1.0 and 1.2.0 will work fine together, but clearly that's not actually the case.
My question is then is this an "incompatible" release or not? From the version number alone I would have assumed 1.2.0 doesn't require a full recompile of everything, but the commenter I responded to suggested it is 'incompatible' and I don't understand why that is the case with feature releases. Do you need to recompile the world for every musl update and ensure everything is compiled against exactly the same version? Or just the feature version has to match?
Let's say my app uses libc and libfoo, both from the underlying operating system / distribution.
libc has a 32 bit time_t, and libfoo on my system also has a 32 bit time_t. I upgrade to a new operating system where both libc and libfoo have 64 bit time_t. libc was kind enough to include symbol versioning which makes time(NULL) return a 32 bit number to my binary.
Now, libfoo either has to be patched to handle symbol versioning and have a new version inside so that my app can get the old 32 bit time_t, or it will have a subtly broken ABI.
The thing is-- who does this? Libfoo doesn't know when each distro will make the change: it won't correspond to a specific upstream version. Does each distro have to recognize the issue and introduce symbol versions? Etc.
Keep the paychecks coming!
Sorry for my cynicism, it's one coping strategy. I'd also prefer to build reliable software, but alas...
Assuming the code on the receiving end stores the value in 32 bits (and not a time _t which can magically change meaning but a 32 bit integer) then it’s still doomed without rewrite?
I mean even with time_t use you can’t just recompile and make it work because there could be subsequent assumptions that the size of a struct containing a time_t will be a specific size or allocations will be too small or misaligned and so on.
By "pulling it off" you mean, they changed it and planes didn't crash around them?
That’s why it’s such a tricky move to break compat with those apps X because you cant know what they are doing and how.
For binary software, Windows has had a Y2038 compliant proprietary API since Windows NT in the 1990s; most Windows applications use this API so Y2038 is generally not an issue.
The issue only affects a subset of 32-bit binary-only applications where we don’t have the source code any more. Hopefully, any and all applications like that will be decommissioned within the next couple of years.
The issue is, besides having to rewrite code, it’s not just one function. It’s time_64(), but now we need gmtime_64(), strftime_64(), stat_64(), and so on for any and all functions which use timestamps.
The thinking in Linux land is that we won’t have 32-bit applications come 2038 where this matters, because everything will be 64-bit by then.
And they dropped the approach because few to none were going to rewrite existing code to add support for non-standard extensions to fix 40 years out issues. Instead they moved to 64b time_t in 10.7 I think?
And it’s not a new call, dozens of functions touch time_t, plus a few syscalls.
This has seemed to be an unpopular observation, in the past.
The other problem is that more unsafe code in libraries, etc, will happily cooperate with 2038-safe unsigned time_t code, but will start to do bad things shortly before 2038.
Now it's some in house protocol sure you can just update everything instead making the timestamp a relative value to the current era.
For code running locally 64 bits is less of issue, just mainly a problem of ABI breaks.
Honestly, one thing I think people overlook is file-formats... A lot them have 32 bit time fields. Also unlike a network packet the file could actually be from a previous 32 bit era. So those timestamps are ambiguous after 2038.
And lots of code would break because they want to talk about pre-1970 dates.
I'm definitely guilty of it, as I routinely use the timeline of the Great Emu War (1932-10-02/1932-12-10) to test time-bound features.
Wouldn’t it also fail for all code that
a) stores the return value in a plain int and not a time_t as it would truncate (but at least this is a warning)
b) has time_t inside struts and allocates their size or arrays of structs assuming time_t is 4 bytes
c) has time_t inside structs and assumes the byte alignment of fields following it
Etc etc..
B) -- manual calculation of size of structs instead of sizeof() -- yah, maybe it'll happen. I don't see much code this bad. If it's ever compiled on a different word length it's already been fixed.
C) Perhaps. For the most part alignment improves when you have a wider time_t, but you could have people counting the number of 32 bit fields and then needing 16 byte alignment for SSE later. Again, for the most part this penalty has been paid by code compiling on amd64, etc.
E.g. you can't do...
time_t completion = time(NULL) + 30;
do {
time_t remaining = completion - time(NULL);
/* ... */
} while (remaining > 0);
Without risking a sporadic infinite loop. #include <stdint.h>
int64_t completion = (int64_t)time(NULL) + 30;
do {
int64_t remaining = completion - (int64_t)time(NULL);
/* ... */
} while (remaining > 0);
There’s some slowdown doing 64-bit int math on a 32-bit system, but the above works.I'm also in favour of adopting IPv6 ASAP, but so far that has been a much harder sell.
time_t is sufficient within bounds. It is expedient and quite correct in many computer science use cases. It can be extended with small additions for many other use cases.
However those bounds are a set of assumptions and simplifications that shouldn't be forgotten. I agree that the problem would be solved until the next paradigm shift in our understanding of time and the universe, and maybe forever if it turns out that the rules are cruel or we're too stupid to reach a more complex situation. I just wouldn't say once and for all, there's far too much uncertainty there.
'bout the same as tomorrow really, better do nothing, for in the end all is dust.
You'll be in a much bigger universe, but it will be empty except for your local galaxy group.
Whole lotta unknowing beta-testers going to be used to debug old system.
_TIME_BITS=64 is not working for me on an Ubuntu 18 system based on glibc 2.27 (three plus years old), and I see nothing in the header files that switches time_t.
This must be something new?
_FILE_OFFSET_BITS is old, on the other hand.
Edit: I see in the git log that this has 2021 all over it:
https://sourceware.org/git/?p=glibc.git&a=search&h=HEAD&st=c...
Anyway, it's too early to see if this is "bad". The unknown quantity is the behavior of distro people. Distro people have the ability to override this default, such that all the packages have 64 bit time_t, and the toolchain is configured to build that. I have some faith in distro people.
I sure don't want to deal with this. Although knowing corporate America, that's a problem for whoever is CEO in 2037
> Currently it defaults to 64 bits on most architectures. Although it defaults to 32 bits on some traditional architectures (i686, ARM), this is planned to change and applications should not rely on this.
#include <time.h>
#include <stdio.h>
int main() {
printf("size: %lu\n", sizeof(time_t));
return 0;
}
prints 8 on my system when compiled with just "gcc test.c"- dedicated but stuck in the past people and approaches
- bad or even terrible UX
- bad security (either because of bad UX especially bad defaults or because of not consulting crypto experts for crypto stuff)
• 64-bit compiles and applications have a 64-bit time_t in Linux. If your distro and binaries are 64-bit, there isn’t a problem.
• 32-bit compiles and applications still have a 32-bit time_t in mainstream Linux libraries, so that old binaries still run. The timestamp is a rolling one; once Y2038 is hit, the timestamp will be a negative one, but one which is updated and is off by 136 years. The workaround is code like this (this is real production code in my Lua fork, Lunacy[1]):
time_t t;
int64_t tt;
if (lua_isnoneornil(L, 1)) /* called without args? */
t = time(NULL); /* get current time */
else {
// Lunacy only supports getting the current time
lua_pushnil(L);
return 1;
}
if(t < -1) {
tt = (int64_t)t + 4294967296ULL;
} else {
tt = (int64_t)t;
}
if (t == (time_t)(-1))
lua_pushnil(L);
else
lua_pushnumber(L, (lua_Number)tt);
return 1;
This gives us accurate timestamps until 2106. If post 2106 compatibility is needed, we can add 2 ^ 32 again for timestamps with a positive value once the Y2038 rollover happens.• Legacy 32-bit Windows applications using the Posix compatibility layer are not Y2038 compliant. Once Y2038 hits us, the timestamp will always return -1 (in Windows XP, the behavior was to return a number off by 136 years, but this changed in Windows 10). The workaround is to use Windows’ proprietary API for post-Y2038 timestamps. Again, from Lunacy which has a Win32 port:
/* Convert Windows "filetime" in to Lua number */
uint64_t t;
FILETIME win_time = { 0, 0 };
GetSystemTimeAsFileTime(&win_time);
t = win_time.dwHighDateTime & 0xffffffff;
t <<= 32;
t |= (win_time.dwLowDateTime & 0xffffffff);
t /= 10000000;
t -= 11644473600LL;
lua_pushnumber(L, (lua_Number)t);
return 1;
Now, last time I researched this, there wasn’t a “int64_t time_64bit()” style system call in the Linux API so that newly compiled 32-bit binaries can be Y2038 compliant without breaking the ABI by using “time_64bit()” instead of “time()”. This was based on some digging around Stackoverflow just last year, and simple Google searches are still not returning pages saying, in big bold letters “-D_TIME_BITS=64 when building 32-bit apps”.https://sourceware.org/glibc/wiki/Y2038ProofnessDesign talks about 64-bit support on 32-bit systems as if it’s a future pipe dream, but apparently -D_TIME_BITS=64 works right now.
[1] https://github.com/samboy/lunacy
[2] https://en.wikipedia.org/wiki/Time_in_Indiana
[3] To ruin a classic sadistic interview question for sys admin roles, Linux these days returns both the modification time and the mostly useless “status change” timestamp. Facebook once decided to not move forward because I said that file timestamp was “modification time” and not “status change”; if Facebook is still asking that question, their knowledge is out of date.
Then Y2K turned out to be a nothing burger (I'm not saying some systems didn't misbehave left and right but in the grand scheme of things, it was a non event).
I'm pretty sure it's going to the same with Y2038.
"it's going to the same with Y2038" is like saying we can fold our umbrella in a rainstorm because we haven't gotten wet yet.
It's nothing to do with closed or open source. I don't want to build a very big world, and ship a huge amount of binary code that can be found in distributions, to make a 2038-safe app.
This is not always true, as the vendor may have disappeared and can no longer provide new versions of the library for any reason. Though, if you knowingly have such a black box in your code and willingly keep running with it until 2038 then you kinda deserve what is coming.
There are two severe issues which must be addressed for it to be saved. First, the type should be float, not int. The former has an intuitive precision, while the latter does not. Not to mention the sign issues. It is very rare to see a timestamp int which is not used as a kind of fixed point float somewhere in practice, but doing so manually gives rise to endless bugs. float on the other hand, can always be in second, which is much more intuitive, and means the downstream apps fix if precision changes is much easier to get right. For these reasons, upgrading to float64 would be substantially better than upgrading to int64, while being better for most use cases, almost always as fast, and require less thought in downstream.
However, I think we can do even better. Int64 is also too small, as the common nanosecond clocks would only give a century of range, which requires adjusting pretty much every algorithm, even if it only ever deals with a few times. And of course, if I dont care about precession, I want the floating point to deal with it for me.
It almost always makes sense to use a time precision exceeding the most common clock with which it will be used, and with a range covering the most common use cases. Most regular cpus give nanosecond precision, but then float64 would only give a few years of range exactly, which is a bit too short for many applications which want exact precision, and use the standard 1973 start. The long awaited float128 however, would be sufficient for billions of years at pico precision, while maintaining low memory and compute costs. Notably cheaper than int32 was originally. float128 simply makes for a fantastic timestamp format for almost every application. Its extremely convenient compared to the clunky std::chrono, works in C and interop, and is very simple to use.
The only downside is that compiler support, while getting better, remains bad.
Yes, overflow/underflow issues and yada yada, but virtually every type can be mised in its own way.
Imagine how many millions of hours of coder time could have been saved if the C specification included fixedpoint<20,12> (or however you wanna write it), and thus would have created a single standard implementation for all of them. Having not just basic arithmetics, but decent sin, cos, sqrt, atan2 etc, all parth of cmath. The difference now would be minor with soft float beeing ubiquitous, but back when it was faster... Damn.
Many languages support these in their standard libraries. (Eg Python does.)
They are a bit slow compared to ints or floats, but good for precision.
Uh... what? I have in fact never seen a timestamp being used that way. What kind of use cases are we talking about?
> Int64 is also too small, as the common nanosecond clocks would only give a century of range
An unsigned Int64 worth of nanoseconds has a range of over 584 years.
So int64 is half, and I was off by a factor of three doing it in my head... I was wrong no doubt about that. But the order of magnitude is the problem, it should be millenia, or millions of years, not a few centuries.
float128 is even worse - it doesn't exist on some ABIs or exist in some other ways, e.g. old Power/PowerPCs used an IBM format. Do we limit them to their own ABI (extra burden on distros), or use double in which case apps expecting 128bits will have quite a surprise.
gettimeofday is deprecated as of 2008. So even using it is a bug. Using it more than a few times per seconds is almost certainly a bug. The function also uses an ambiguous representation, but and as the docs dont specify, I would have to check the code to se if it relies on microseconds being less than 10^6, and even if I check this for standard glibc, I would not trust other implementations to do the same. However, as we can assume that the pseudo integer type used does not need to use have multiple codes for basic numbers, its easy to se that the range is just under 52 bits. gettimeofday is so old that they didn't even expect that emulated int64 was available. That is, if they used a double instead, it would perfectly cover the same range, require much less code, while simultaneously been easier to get right, support all standard operations you would expect, behave more intuitively in many cases, and be faster on most platforms, though as you say, perhaps slightly slower on soft float platforms. Though I woudnt be so sure, almost every project notices float performance, almost no project notices gettimeofday performance.
The ABI would break by changing the type regardless, numeric types should never be used without explicitly specifying precision, and no, the burden is primarily on compilers. With float128 as a core language feature as it should be, noting that while it should be IEEE... compliant, it may be emulated, the burden on the distros would be small, and likely net beneficial as timestamps is one of those things that cause very rare and hard to replicate bugs absolutely everywhere.
I won't try to. The alternative to your proposal is fixed point integer.
>if you do, use fixed point, you suddenly have other just as common numbers you cant represent.. You really think NaN is a better is a less intuitive result than what whatint64(1)/int64(0) gives?
I think it's less of a problem than float. Most programmers (myself included) don't understand all the nuances of floating point.
>gettimeofday is deprecated as of 2008
The reason I mentioned it is because I recall it turned out to be a problem when porting/emulating Linux apps under Windows, since the equivalent Windows call used to be way more expensive. Apparently it was a significant problem since the call was in wide use. I don't recall any widespread effort to remove gettimeofday (or equivalents), so I suspect it's still is in use?
>With float128 as a core language feature as it should be
Unfortunately it isn't a core language feature - e.g. it can have 80bit precision on some x86s/software combinations. Now, that's worth fixing regardless.
In practice, floating point timestamps have a constant precision throughout their life. That is, the exponent has the same value for a really long time, and you don't get any benefit from the "floating" aspect.
As another poster points out, overflow/NaN/infinity is the one nice thing you get. But you pick up all the idiosyncrasies of floats, too.
As you point out, float64 is worse than int64_t at attaining a given level of precision for a reasonable amount of time.
You do point out that floats "seamlessly upgrade" to increased precision, and this is kind of nice. But if we're picking a 128 bit type, there should be no upgrading needed ever.
It just doesn't make sense.
Its got more to do with how intuitively the timestamp behaves, and that you can use seconds as the unit everywhere and no longer need to keep track of if this wait function took seconds or milliseconds. Using infty to indicate a timeout function should block indefinetly is also much more intuitive than using 0.