UnixTimestamp.com

How Floating Point Arithmetic Silently Corrupts Date Calculations

A glowing calendar grid fractures into floating binary digits, illustrating how floating point arithmetic corrupts date calculations.

When you store a date or a duration in a floating point number, tiny rounding errors sneak in because most decimal fractions can't be represented exactly in binary. That means a value you expect to be 1.1 days is actually stored as something like 1.100000000000000088, and when you multiply, divide, or accumulate those values across thousands of date calculations, the errors compound until a timestamp lands on the wrong second, minute, or even day.

The fix is almost always to keep dates and durations as integers (whole seconds, milliseconds, or nanoseconds) and never route them through a double precision float. Below is exactly why the corruption happens, where it bites in real code, and how to avoid it.

Why binary floats can't hold decimal fractions

A 64 bit floating point number follows the IEEE 754 floating point standard, which splits those 64 bits into a sign, an 11-bit exponent, and a 52-bit fraction (the mantissa). Numbers get stored as sign × mantissa × 2^exponent. The catch: this only represents fractions that are sums of powers of two.

  • 0.5 is 2^-1, so it stores perfectly.
  • 0.25 is 2^-2, also perfect.
  • 0.1 is not any finite sum of powers of two, so it gets rounded to the nearest representable value.

That is why 0.1 + 0.2 famously equals 0.30000000000000004 in almost every language that uses IEEE 754. The floating point precision loss isn't a bug in your code; it's baked into the format. With about 15 to 17 significant decimal digits of fractional precision, a double precision float hides the error most of the time, right up until it doesn't.

Try it yourself: open any JavaScript console and type 0.1 + 0.2 === 0.3. It returns false. That single line is the root of countless date bugs.

Where floating point silently breaks dates

Date math looks harmless, but it constantly divides and multiplies by numbers that aren't powers of two: 60 seconds, 24 hours, 365.25 days, 1000 milliseconds. Every one of those operations can nudge a value off its exact integer, and the trouble usually appears in a few predictable places.

  • Storing timestamps as days. Spreadsheet-style serial dates (like Excel's day-based system) store a moment as a floating number of days. A time of 10:00:00 becomes 0.41666666..., which never lands cleanly.
  • Converting units with division. Turning milliseconds into fractional hours with ms / 3600000 introduces error, and converting back with multiplication doesn't cancel it out.
  • Accumulating durations in a loop. Adding 0.1 seconds 10,000 times gives you noticeably more or less than 1000 seconds.
  • Averaging or interpolating timestamps. Midpoint calculations between two floats drift, which matters for animation timing, sensor logs, and financial ticks.

Unix timestamps dodge most of this because they are plain integers counting whole seconds since 1970. If you keep your time in that integer form, as shown in this complete developer guide to timestamp conversion, the rounding demons never get a foothold.

Real examples of corrupted date math

Here is the classic accumulation trap. You'd expect adding 0.1 a hundred times to give 10, but floating numbers disagree:

let total = 0;
for (let i = 0; i < 100; i++) {
  total += 0.1;
}
console.log(total);
// 9.99999999999998  (not 10)

Now translate that to dates. Say a scheduler adds 0.1 hours (6 minutes) per tick and stores the running time as a float of hours. After enough ticks, the drift crosses a whole second, and an event that should fire at 09:00:00 fires at 08:59:59. That single second can push a log entry into the wrong day near midnight.

Another common failure comes from day-based serial dates:

# A moment stored as fractional days
serial = 45000.7291666667   # meant to be 17:30:00

# Convert the fraction back to seconds
seconds = (serial - int(serial)) * 86400
print(seconds)   # 62999.99999... instead of 63000

That 62999.99... truncates to 62999 seconds, which is 17:29:59, one second early. If you rely on serial dates in a spreadsheet, converting epoch values carefully matters; the same math traps appear when you convert epoch time to datetime in Excel.

The scary part is that these errors are silent . No exception, no warning. The value just quietly sits one second off, and you find out weeks later when a report doesn't reconcile.

How to avoid floating point date bugs

The core rule: never let a date or duration live inside a float. Keep everything as integers and only convert to human-readable form at the very last step.

  • Store time as integer units. Use whole seconds, milliseconds, or nanoseconds since the epoch. Integers have no fractional rounding, so they never drift.
  • Do arithmetic in the smallest unit. If you need sub-second precision, work in integer nanoseconds, not fractional seconds.
  • Use integer division carefully. To split a timestamp into hours and minutes, use modulo and integer division rather than multiplying by fractions.
  • Reach for dedicated date types. Languages ship purpose-built types (Python's datetime and timedelta, JavaScript's Date which stores integer milliseconds, Java's Instant) that avoid float storage internally.
  • Use decimal types for money-and-time hybrids. When you truly need decimal fractions (billing rates per hour), use a decimal or fixed-point type instead of floating point arithmetic.

On the command line, tools that speak in integer epoch seconds sidestep the whole problem. If you script date work in shells, the techniques in this walkthrough on converting a Unix timestamp to a date in Linux stay integer-clean from start to finish.

Rule of thumb: if a duration or timestamp ever appears with a decimal point in your data, treat it as a red flag and switch to integer units before doing any math.

Quick reference for safe date storage

Approach Safe? Why
Integer Unix seconds Yes No fraction, no rounding, no drift.
Integer milliseconds / nanoseconds Yes Sub-second precision stays exact as whole numbers.
Fractional days (serial dates) Risky Time-of-day fractions rarely land on exact values.
Fractional hours in a float No Accumulates error every operation.
Double precision seconds with decimals No Precision loss grows with large timestamps.

One subtle detail: a double precision float only has 52 bits of mantissa, so once a timestamp gets large (billions of seconds), it can no longer represent every individual second exactly. Past roughly 2^53 in the smaller units, consecutive integers start skipping. That is why storing nanoseconds in a float is doomed for modern dates but storing them in a 64-bit integer works perfectly.

Tool that calculates the exact difference between two dates without floating point errors

Calculate date differences without floating point drift

Skip risky fractional-day math. Our date difference tool computes the exact gap between two dates using integer-clean arithmetic, so floating point precision loss never corrupts your result.

Check a date difference →

Because 0.1 and 0.2 can't be stored exactly in binary. The IEEE 754 floating point standard only represents fractions built from powers of two, so both values get rounded first. Adding the two rounded numbers produces 0.30000000000000004, which is close but not exactly 0.3.

Not when stored as integers, which is the standard form. A plain integer count of seconds since 1970 has no fractional part, so no rounding occurs. Problems only appear if you convert that integer into fractional days or hours using a double precision float somewhere in your pipeline.

It is risky. Time of day becomes a fraction of a day, and most times (like 10:00 or 17:30) don't land on exactly representable values. When you convert the fraction back to seconds, you can end up one second early or late, which occasionally shifts an event into the wrong day.

A double precision float carries about 15 to 17 significant decimal digits thanks to its 52-bit mantissa. For integers it stays exact only up to 2^53. Beyond that, consecutive whole numbers start getting skipped, which is why large nanosecond timestamps must live in 64-bit integers instead.

Use integer units (seconds, milliseconds, or nanoseconds) and do all arithmetic on those whole numbers. Rely on your language's dedicated date types, which store time as integers internally. For decimal-heavy needs like hourly billing, use a decimal or fixed-point type rather than binary floating point.