Converting Time Units, and Principles for Handling Time in Code
Converting between milliseconds, seconds, minutes, hours, and days is arithmetic — and the reason it goes wrong in practice isn't difficulty, it's that units aren't visible in the code. setTimeout takes milliseconds, a JWT's exp uses seconds, and cache TTLs vary by library. This tool does the conversion; the sections below are about avoiding the mistake in the first place.
Conversion reference
A few values are worth memorizing because you'll meet them while reading code: 86400 seconds in a day, 3600 in an hour, 604800 in a week. They turn up constantly in cache headers and token expiry settings.
| Unit | Milliseconds | Seconds | Minutes | Hours |
|---|---|---|---|---|
| 1 second | 1,000 | 1 | 0.0167 | 0.000278 |
| 1 minute | 60,000 | 60 | 1 | 0.0167 |
| 1 hour | 3,600,000 | 3,600 | 60 | 1 |
| 1 day | 86,400,000 | 86,400 | 1,440 | 24 |
| 1 week | 604,800,000 | 604,800 | 10,080 | 168 |
| 30 days | 2,592,000,000 | 2,592,000 | 43,200 | 720 |
| 1 year (365 days) | 31,536,000,000 | 31,536,000 | 525,600 | 8,760 |
How to use it
- Enter the value and its current unit.
- Read the equivalent in the other units.
- If the number is going into a config file, don't paste the computed constant — see the readability section below.
Which API uses which unit
The mismatch between JavaScript's milliseconds and the Unix standard's seconds is the single most common source of these bugs. Putting Date.now() straight into a JWT's exp makes the value 1000× too large, pushing expiry tens of thousands of years out; conversely, feeding a seconds timestamp to new Date() yields a date near 1970. The second mistake is caught immediately because the wrong date is visible, while the first passes silently and becomes a security problem.
| Target | Unit | Example |
|---|---|---|
| JavaScript setTimeout / setInterval | Milliseconds | setTimeout(fn, 5000) → 5 seconds |
| Date.now() | Milliseconds | 1735689600000 |
| Unix timestamp (standard) | Seconds | 1735689600 |
| JWT exp / iat / nbf | Seconds | Math.floor(Date.now()/1000) |
| HTTP Cache-Control max-age | Seconds | max-age=3600 → 1 hour |
| Cookie Max-Age | Seconds | Max-Age=86400 → 1 day |
| Redis EXPIRE / PEXPIRE | Seconds / milliseconds | Different commands — easy to confuse |
| Python time.time() | Seconds (fractional) | 1735689600.123 |
| Go time.Duration | Nanosecond-based | 5 * time.Second |
| Java System.currentTimeMillis() | Milliseconds | 1735689600000 |
Make units visible in code
Nobody reads setTimeout(fn, 900000) and thinks '15 minutes'. Rather than baking in a precomputed constant, leave the multiplication in place: compilers and runtimes fold constants, so there's no performance difference, and the intent is immediately legible.
Naming helps too. Writing timeoutMs instead of timeout, and expiresInSeconds instead of expiresIn, means a wrong-unit argument gets caught in review. In TypeScript you can go further and distinguish units at the type level with branded types.
// bad: what is this number? setTimeout(refresh, 900000); const TTL = 604800; // good: unit and intent are visible const SECOND = 1000; const MINUTE = 60 * SECOND; const HOUR = 60 * MINUTE; const DAY = 24 * HOUR; setTimeout(refresh, 15 * MINUTE); const SESSION_TTL_SECONDS = 7 * 24 * 60 * 60; // 7 days
A day isn't always 86,400 seconds
Arithmetically a day is 86,400 seconds, but a calendar day may not be. In regions observing daylight saving, the spring-forward day is 23 hours and the fall-back day is 25. So computing 'same time tomorrow' by adding 86,400,000 milliseconds gives an answer that's off by an hour on those two days.
Getting it right means using calendar-aware APIs rather than millisecond arithmetic. In JavaScript, the Temporal API models this distinction explicitly; until then, libraries like date-fns and Luxon offer timezone-aware addition. Leap seconds exist too, but Unix time is defined to ignore them, so application code rarely has to care.
'A month' and 'a year' likewise have no fixed length. Treating a month as 30 days is only an approximation, so anything that must be exact — subscription renewal dates, for instance — needs calendar arithmetic.
Common uses
- Converting a human-scale cache TTL into seconds (Cache-Control, Redis)
- Computing token expiry configuration values
- Turning millisecond performance measurements into readable units
- Interpreting duration fields in logs
- Designing batch job intervals
- Matching a timeout value to the unit a library expects
Frequently Asked Questions
- Is a Unix timestamp in seconds or milliseconds?
- The standard is seconds, but JavaScript's Date.now() and Java's currentTimeMillis() return milliseconds, so both forms circulate. Count the digits: a current timestamp is 10 digits in seconds and 13 in milliseconds. Mistaking a 13-digit value for seconds lands you around the year 50,000.
- Is treating a year as 365 days acceptable?
- Fine for rough conversions, not for exact date math. Leap years occur every four years (skipping centuries but including multiples of 400), averaging 365.2425 days. For subscription expiry or age calculations, use calendar APIs.
- Why doesn't setTimeout fire exactly on time?
- setTimeout guarantees a minimum delay, not a precise firing moment: the callback runs once the event loop is free after that delay, so a busy main thread pushes it later. Browsers also enforce a 4ms floor on nested timers and heavily throttle timers in background tabs. When timing must be precise, consider requestAnimationFrame or a Web Worker.
- What if I need finer precision than milliseconds?
- In browsers, performance.now() offers microsecond-level precision and is monotonic, so unlike Date.now() it isn't affected by system clock changes — always prefer it for measuring elapsed time. Note that browsers deliberately reduce its resolution in some configurations as a Spectre mitigation.
- Can I convert timezones here?
- This tool handles durations. To express a moment in another region's local time, use the Timezone Converter. They're different problems: one is multiplication, the other involves offsets and DST rules.
- How do I avoid confusing 'seconds' with 'a timestamp in seconds'?
- Naming is the practical answer. Call durations durationSeconds or ttlSeconds, and give instants an At suffix — expiresAt, createdAt. The distinction then shows up in the code, and mistakes like adding a duration to an instant become visible on sight.
💡 Note: When a timestamp looks wrong, count its digits first: 10 means seconds, 13 means milliseconds. That single check explains a surprising share of time bugs.