Time differences: subtracting times and formatting durations
“How long is 9:00 to 17:30?” “What time is 3 days 4 hours from now?” — time arithmetic is everyday, but day boundaries and negative values trip people up. This article walks through the operations.
Basic subtraction
“9:00 to 17:30”:
17:30 - 09:00 = 8:30 (8 hours 30 minutes) Convert times to minutes for clean math:
17:30 → 17 × 60 + 30 = 1050 min
09:00 → 9 × 60 + 0 = 540 min
diff = 1050 - 540 = 510 min = 8h 30m Crossing midnight
“23:00 to 7:00 next day”:
7:00 - 23:00 = -16:00 (negative) Wrong. Add 24 hours when the wrap is detected:
(7 + 24):00 - 23:00 = 31:00 - 23:00 = 8:00 Or include the date:
2024-01-02 07:00 - 2024-01-01 23:00 = 8 hours Detecting wrap
Simple rule:
let diff = end - start; // minutes
if (diff < 0) diff += 24 * 60; // crossed midnight, add a day Doesn’t generalize past one day — for ranges spanning multiple dates, use full date arithmetic.
DST and “summer time”
DST start day is 23 hours; end day is 25 hours:
- Spring — 3:00 jumps to 4:00, the day is 23 hours.
- Fall — 2:00 falls back to 1:00, the day is 25 hours.
“24h = 1 day” breaks. Compute in UTC, or use a library (date-fns, Luxon, etc.).
Japan has no DST (so it doesn’t matter locally) but global products must handle it.
ISO 8601 duration notation
Standard format for a “duration”:
P3Y2M5D — 3 years 2 months 5 days
PT2H30M — 2 hours 30 minutes
P1DT12H — 1 day and 12 hours
P1W — 1 week - Starts with
P. - Before
T— date components; after — time components. - Letters —
Y(year),M(month, before T),W(week),D(day),H(hour),M(minute, after T),S(second).
Used in API responses and e.g., YouTube video lengths:
PT4M13S → 4 minutes 13 seconds Human-readable relative times
“3 hours 25 minutes ago”, “tomorrow morning”:
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
rtf.format(-1, 'day'); // "yesterday"
rtf.format(2, 'hour'); // "in 2 hours"
rtf.format(-30, 'minute'); // "30 minutes ago" Japanese:
const rtf = new Intl.RelativeTimeFormat('ja', { numeric: 'auto' });
rtf.format(-1, 'day'); // "昨日"
rtf.format(2, 'hour'); // "2 時間後" Elapsed-time formatting
“1:23:45” elapsed:
function formatDuration(seconds) {
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
}
formatDuration(5025); // "1:23:45" Same shape as YouTube timestamps and stopwatches.
Adding to a timestamp
const start = new Date('2024-01-15T10:00:00');
const end = new Date(start.getTime() + 90 * 60 * 1000); // +90 minutes
end.toISOString(); // "2024-01-15T11:30:00.000Z" Date.getTime() is milliseconds:
- 1 second = 1,000.
- 1 minute = 60,000.
- 1 hour = 3,600,000.
- 1 day = 86,400,000.
Working hours with breaks
“9:00–18:00 with 12:00–13:00 lunch”:
- Compute total span.
- Subtract overlap with break.
function workHoursInRange(start, end, breakStart, breakEnd) {
let total = end - start;
if (start < breakEnd && end > breakStart) {
const overlap = Math.min(end, breakEnd) - Math.max(start, breakStart);
total -= overlap;
}
return total;
} Months and years
“1 year 3 months from now”:
- Months aren’t fixed length (28, 29, 30, 31 days).
- Years differ by leap status (365 / 366).
- “Same day next month” needs explicit handling.
function addMonths(date, months) {
const d = new Date(date);
const targetMonth = d.getMonth() + months;
d.setDate(1);
d.setMonth(targetMonth);
const lastDay = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
d.setDate(Math.min(date.getDate(), lastDay));
return d;
} “March 31 + 1 month = April 30” — month-end clamping is required.
Unit conversions
| Unit | Seconds |
|---|---|
| sec | 1 |
| min | 60 |
| hour | 3,600 |
| day | 86,400 |
| week | 604,800 |
| month (30 days) | 2,592,000 |
| year (365 days) | 31,536,000 |
“What’s 1,000,000 seconds in days?” → 1,000,000 / 86,400 ≒ 11.57 days.
Across time zones
“Tokyo 10:00 vs NY 21:00 (previous day)“:
- Tokyo = UTC+9.
- NY = UTC-5 (UTC-4 during DST).
- Difference is 14 hours (13 during DST).
Convert to UTC and subtract:
const tokyo = new Date('2024-01-15T10:00:00+09:00');
const ny = new Date('2024-01-14T21:00:00-05:00');
const diff = (tokyo - ny) / (1000 * 60 * 60); // 0 hours (same instant) 24-hour vs 12-hour clock
- 24h — 00:00 to 23:59.
- 12h — 12:00 AM to 11:59 PM.
12-hour pitfalls:
- 12:00 AM = midnight (0:00).
- 12:00 PM = noon (12:00).
- 12:01 AM ≠ 12:01 PM (off by 12 hours).
Backends and databases should standardize on 24-hour.
Summary
- Convert to minutes, subtract, convert back.
- Add 24 hours (or use full dates) to handle midnight crossing.
- ISO 8601 duration —
P3Y2M5D,PT2H30M. - Months and years aren’t constant in seconds — handle them explicitly.
- For cross-zone differences, normalize to UTC.
For computing time differences and adding offsets, the time-duration calculator on this site handles common cases.