Store epoch times as 64-bit floats

iudqnolq | 53 points

I strongly disagree with this. It only makes sense to use floats if you want time to have increasing precision the closer you get to 1970. From a symmetry group perspective, that makes no sense; the amount you care about the incremental precision of time measurement is invariant with respect to the present date. (Translation invariance). This means you want uniformly distributed time samples, hence an integer format.

If you think microsecond precision is good enough for anybody, then you could consider using an integer where the LSB represents 1 microsecond. But don't use floating point time, unless your specific application cares more about time scale invariance than time translation invariance.

jbay808 | 10 months ago

Yeah this doesn't hold water. If you need the precision, float isn't suitable since he precision drops of. If you don't need the precision, no need to use float.

Just use int64.

mungoman2 | 10 months ago

From the post:

> Why am I not using float64 time already??? [fp used ot be slow and was not standardized, but it got faster and after a while you could count on having IEEE-754]

This is not the reason.

Until IEEE-754 fp was terrible. As in buggy terrible. It took years of pain to learn how to get it right. And you can see this in the standard: it's a very short standard, super easy to understand, trivial, really...if you ignore the enormous amount explanations, corner cases, special cases, surprising symmetries, and other copious testament to the well-meaning Mistakes that Were Made in the years before. Intel provided a ton of trade secret understanding from the mistakes in the 8087, and UCB maths prof was intimately involved. It's an incredible achievement.

The history of the standard is quite exciting but has been covered on HN several times before.

gumby | 10 months ago

on February 25, 1991 American Patriot Missile battery in Dhara failed to track incoming missile because they used floating point time instead of integers. 28 dead 100 injured

time precision was decreasing with time since system was booted and after about 100h of operation this accumulated enough to system believe that incoming missile is already unreachable using existing calculations and caused it to not react at all

I think that is great example why changing precision depending on time might be very bad idea.

imagine that afer Y2038 that slight drop in precision would cause some critical system to fail in unexpected way

Szpadel | 10 months ago

Use 128 bit integer in femptoseconds, with epoch being the begining of the universe. And it should be signed, because what if there was a previous universe.

Jokes aside, Unix Epoch shouls be replaced by Tai time on every computer.

https://en.m.wikipedia.org/wiki/International_Atomic_Time

ClumsyPilot | 10 months ago

I think it was Microsoft BASIC that stored time as a floating point number. What a bad idea. Converting from it to an integer and then converting back to float would cause the time to "drift" due to rounding errors.

It's the same reason why you should never use floating point numbers for account balances.

WalterBright | 10 months ago

64 bit integers are just as cheap, and that's where everyone is going.

not2b | 10 months ago

yeah...no. don't use floats for this.

int64 "nanoseconds since the epoch" seems to be emerging as the de facto standard.

used in Golang [0] and adopted by Python starting in 3.7 [1]

that PEP also spells out some of the problems with using a float64:

> The Python time.time() function returns the current time as a floating-point number which is usually a 64-bit binary floating-point number (in the IEEE 754 format).

> The problem is that the float type starts to lose nanoseconds after 104 days. Converting from nanoseconds (int) to seconds (float) and then back to nanoseconds (int) to check if conversions lose precision:

0: https://pkg.go.dev/time#Time

1: https://peps.python.org/pep-0564/

evil-olive | 10 months ago

Posting blog like this how you get rejected from your dream job.

snailtrail | 10 months ago

I remember one colleague concerned with casting a Java 64-bit millisecond timestamp to double seconds when crossing languages (over to a somewhat TypeScript-like financial modeling DSL developed in the mid-late 1990s, where it's a bit clunky to deal with numbers in any format other than doubles).

I added a comment apologizing to software archaeologists on 12 October 287,396 C.E., when the precision of double timestamps (with 1970-01-01T00:00:00 UTC epoch) drops to 2 milliseconds, and the reviewer was fine after that.

KMag | 10 months ago

It's saddening to see the number of people who critique the idea of storing time as an unsigned integer by immediately responding that that means that times before 1970 cannot exist. This bespeaks a continuing poor knowledge of the subject, despite all of the "falsehoods that programmers believe about" documentation that has grown up.

* https://github.com/kdeldycke/awesome-falsehood#dates-and-tim...

Microsoft, for one example, has been modelling times as a 64-bit unsigned 100-nanosecond count since 1601 (proleptic-Gregorian proleptic-UTC) for about 30 years, now.

* https://learn.microsoft.com/en-us/windows/win32/api/minwinba...

Daniel J. Bernstein in the late 1990s chose a 0 point for an unsigned count so far back that it pre-dates most estimates of the point of the Big Bang.

* http://cr.yp.to/libtai/tai64.html

1970 is not the mandatory origin for every timescale. (Indeed, in the early years of Unix itself there wasn't even a stable origin for time.) It is not a valid reason for dismissing the idea of storing time as an unsigned integer.

It's also sad to note that the headlined page's first sentence has one of the very falsehoods that programmers believe about time in it.

JdeBP | 10 months ago

> Even through the 90s, long after many system calls became formalized, floating point math was much more expensive than integer math.

IIRC the main reason floating point numbers weren’t used in system calls back in the 90-ties was that kernel code couldn’t use the CPUs floating point registers, or they would have to be saved at the entry point of each system call and restored before returning to user space. This was deemed too expensive.

bjornsing | 10 months ago

UUIDv7 is a good way to store time in UUIDs. See https://www.ietf.org/archive/id/draft-peabody-dispatch-new-u...

wood_spirit | 10 months ago

The article fails to mention the earliest standard addressing this issue, DJB's taitime:

https://cr.yp.to/proto/utctai.html

johnea | 10 months ago

Great choice! Far away points in time will be less precise, but really: how good is anyone at predicting the future anyway?

(in other words a terrible choice, though not for the reason above).

gumby | 10 months ago

This would have been a good April Fools joke.

theandrewbailey | 10 months ago

Floating-point time loses the ability to represent milliseconds, microseconds, and nanoseconds exactly.

C already has (since the 2011 standard) `struct timespec`, a structure consisting of a `time_t` representing seconds since the epoch (typically 64 bits signed) and a `long` representing nanoseconds. It's admittedly awkward for arithmetic, but it doesn't overflow for another 292 billion years. It gives about 200 times better precision than 64-bit floating-point (for the interval from 2004 to 2038).

32-bit signed time_t is still a problem, particularly for embedded systems that are difficult to update. (A lot of such systems probably don't need to know the current time.)

A 64-bit signed integer representing nanoseconds since the epoch doesn't overflow until 2554.

Floating-point time is likely to be good enough in a lot of contexts -- but the fact that it's non-standard, unlike `time_t` and `struct timespec`, is a big disadvantage.

_kst_ | 10 months ago

FWIW, Java does this with a long and an int: https://docs.oracle.com/javase/8/docs/api/java/time/Instant....

binkHN | 10 months ago

I think Microsoft did it better back in 1993. They’re also using 64-bit numbers for datetime, but these numbers are integers, expressed in 100 nanoseconds ticks. That representation is used in NTFS, other components of Windows kernel and userland, .NET runtime including the modern Linux-running version, and quite a few other places.

Y2038 is a non-problem, the representable range is about ±29000 years around the epoch. The extinction of the dinosaurs is not representable, but I think it’s OK in practice, only a small minority of software needs dates that far in the past or future.

Accuracy is 2.5 times better than FP64, and is equal over the complete range.

Const-me | 10 months ago

I made a universal timeline[0] that scales from milliseconds to "all of time", and for various reasons I store the time as years BC/AD in JS floats (64 bits). It goes to 2^1024, or approximately 1.7977e+308 years. Not the most efficient, since all the numbers < -13.8e9 are wasted, but it means the raw number matches how we commonly talk about deep time. (Historical dates are stored as an ISO-8601 string and converted).

0. https://timepasses.net

perilunar | 10 months ago

No thanks. For one, hashing becomes ambiguous. Under no circumstances are NaN and NaN equal to each other. That will cause mayhem in any system looking for an iota of resiliency.

junon | 10 months ago

Nah. I think we should move to 500 bit times. That'll give us the Planck time resolution from the Big Bang through the heat death of the universe.

johnklos | 10 months ago

I might be missing something but is this actually arguing for blindly and naively taking time-since-epoch as fp value and then think of the smallest (which would change over time!) possible epsilon??

Disappointing to me how visually appealing this site is for how useless the words/idea themselves are.

loxias | 10 months ago

Let's store time as integer Planck times since the Big Bang. 256 bits ought to be enough for anybody.

shpx | 10 months ago

swift has TimeInterval which is a double and can hold an epoch offset. https://developer.apple.com/documentation/foundation/timeint...

benmmurphy | 10 months ago
[deleted]
| 10 months ago

Nah. uint64. The end.

foobarbazetc | 10 months ago

[dead]

sposeray | a year ago

Real chad developers use string.

deafpolygon | 10 months ago