Date Arithmetic in Different Languages
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
| Language | Code | Returns |
|---|---|---|
| JavaScript | date2 - date1 | Milliseconds |
| Python | date2 - date1 | timedelta (days, seconds) |
| Java | ChronoUnit.DAYS.between(d1, d2) | long (days) |
| Go | d2.Sub(d1).Hours() / 24 | float64 (hours→days) |
| PHP | $d1->diff($d2)->days | int (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?
| Language | Jan 31 + 1 month | Result | Logic |
|---|---|---|---|
| JavaScript | setMonth(1) | Mar 3 | Feb 31 → Mar 3 (overflow) |
| Python | replace(month=2) | ValueError | Feb 31 does not exist |
| Java | plusMonths(1) | Feb 28 | Clamps to last day of Feb |
| Go | AddDate(0, 1, 0) | Mar 3 | Same overflow as JS |
| PHP | modify('+1 month') | Mar 3 | Same 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
| Operation | JavaScript | Python | Java | Go | PHP |
|---|---|---|---|---|---|
| Add days | setDate | + timedelta | plusDays | AddDate(0,0,n) | modify('+n days') |
| Add months | setMonth | replace | plusMonths | AddDate(0,n,0) | modify('+n months') |
| Difference | subtraction → ms | subtraction → timedelta | ChronoUnit.DAYS.between | Sub → Duration | diff → DateInterval |
| Format | toLocaleDateString | strftime | DateTimeFormatter | Format | format |
| Immutable | No | Yes | Yes | Yes | No |
Key Takeaways
- JavaScript
Dateis 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, Javajava.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.