Date Arithmetic in Different Languages

May 28, 20265 min read

Why Date Arithmetic Is Hard

Dates look simple — add days, subtract months, find the difference. But edge cases multiply fast: leap years, month lengths, daylight saving transitions, and time zone offsets all introduce complexity. Every language handles these differently.

Adding Days to a Date

JavaScript

const date = new Date('2026-01-15');
date.setDate(date.getDate() + 30);
// 2026-02-14 — handles month rollover automatically

JavaScript's Date mutates in place and auto-normalizes overflow. setDate(35) on January silently becomes February 4.

Python

from datetime import date, timedelta

d = date(2026, 1, 15)
result = d + timedelta(days=30)
# datetime.date(2026, 2, 14)

Python's timedelta is explicit and immutable — the original date is never modified.

Java

import java.time.LocalDate;

LocalDate date = LocalDate.of(2026, 1, 15);
LocalDate result = date.plusDays(30);
// 2026-02-14

Java 8+ java.time is immutable and fluent. Avoid the legacy Date and Calendar classes.

Go

import "time"

date := time.Date(2026, 1, 15, 0, 0, 0, 0, time.UTC)
result := date.AddDate(0, 0, 30)
// 2026-02-14

Go's AddDate takes years, months, days — making complex offsets straightforward.

PHP

$date = new DateTime('2026-01-15');
$date->modify('+30 days');
// 2026-02-14

PHP's modify accepts natural language strings like "+1 month" or "next Tuesday".

Calculating the Difference Between Dates

LanguageCodeReturns
JavaScriptdate2 - date1Milliseconds
Pythondate2 - date1timedelta (days, seconds)
JavaChronoUnit.DAYS.between(d1, d2)long (days)
God2.Sub(d1).Hours() / 24float64 (hours→days)
PHP$d1->diff($d2)->daysint (days)

JavaScript — Convert Milliseconds to Days

const msPerDay = 1000 * 60 * 60 * 24;
const days = Math.round((date2 - date1) / msPerDay);

Use Math.round, not Math.floor, to handle daylight saving offsets that shift the result by an hour.

Python — Full Interval

delta = date2 - date1
print(delta.days)       # total days
print(delta.seconds)    # remaining seconds (0-86399)
print(delta.total_seconds())  # exact seconds as float

Adding Months: The Dangerous Operation

Adding months is where most date bugs live. What happens when you add one month to January 31?

LanguageJan 31 + 1 monthResultLogic
JavaScriptsetMonth(1)Mar 3Feb 31 → Mar 3 (overflow)
Pythonreplace(month=2)ValueErrorFeb 31 does not exist
JavaplusMonths(1)Feb 28Clamps to last day of Feb
GoAddDate(0, 1, 0)Mar 3Same overflow as JS
PHPmodify('+1 month')Mar 3Same overflow as JS

Only Java clamps to the month's last day by default. Other languages overflow into the next month. If you want "end of month" semantics, you must implement it explicitly:

function addMonths(date, months) {
  const result = new Date(date);
  result.setMonth(result.getMonth() + months);
  // If day overflowed, clamp to last day of target month
  if (result.getDate() !== date.getDate()) {
    result.setDate(0); // Last day of previous month
  }
  return result;
}

Time Zone Pitfalls

JavaScript

// These produce different dates depending on the system time zone
new Date('2026-01-15');        // Parsed as UTC midnight
new Date('2026-01-15T00:00');  // Parsed as local midnight

Date-only strings (YYYY-MM-DD) are UTC. Date-time strings without a zone offset are local. This inconsistency causes bugs when servers and clients are in different zones.

Python

from datetime import datetime, timezone

# Naive — no zone info, do NOT use for arithmetic across zones
naive = datetime(2026, 1, 15, 12, 0)

# Aware — carries zone info
aware = datetime(2026, 1, 15, 12, 0, tzinfo=timezone.utc)

Always use aware datetimes when time zones matter. Subtracting naive datetimes across DST boundaries produces incorrect intervals.

Quick Reference Table

OperationJavaScriptPythonJavaGoPHP
Add dayssetDate+ timedeltaplusDaysAddDate(0,0,n)modify('+n days')
Add monthssetMonthreplaceplusMonthsAddDate(0,n,0)modify('+n months')
Differencesubtraction → mssubtraction → timedeltaChronoUnit.DAYS.betweenSub → Durationdiff → DateInterval
FormattoLocaleDateStringstrftimeDateTimeFormatterFormatformat
ImmutableNoYesYesYesNo

Key Takeaways

  • JavaScript Date is mutable — all others return new objects
  • Adding months to day-31 dates causes overflow in most languages — Java clamps, others roll over
  • Date-only strings parse as UTC; date-time strings parse as local — this JavaScript inconsistency causes subtle bugs
  • Always use time-zone-aware objects when arithmetic crosses DST boundaries
  • Use built-in libraries (Python datetime, Java java.time) instead of hand-rolled date math

Try It Yourself

Calculate exact age differences and date intervals with our Age Calculator. Enter two dates and see the precise difference in years, months, and days.