Convert Unix Timestamp to Date in JavaScript (And Back)
Unix timestamps are everywhere -- API responses, database records, log files, JWT tokens. Converting between timestamps and human-readable dates in JavaScript is straightforward, but there is one gotcha that catches everyone at least once.
The One Thing You Must Know
Unix timestamps are in seconds. JavaScript Date uses milliseconds.
// Unix timestamp (seconds since Jan 1, 1970)
const unixTimestamp = 1711036800;
// WRONG -- this gives you a date in 1970
new Date(unixTimestamp);
// Thu Jan 20 1970 ...
// RIGHT -- multiply by 1000
new Date(unixTimestamp * 1000);
// Thu Mar 21 2024 ...
If your date is showing up in January 1970, you forgot to multiply by 1000. If it is showing a date thousands of years in the future, you multiplied a millisecond timestamp that did not need it.
Quick check: If the number has 10 digits, it is seconds. If it has 13 digits, it is milliseconds.
function toDate(timestamp) {
// Auto-detect seconds vs milliseconds
if (timestamp < 1e12) {
return new Date(timestamp * 1000);
}
return new Date(timestamp);
}
Unix Timestamp to Date
Basic Conversion
const timestamp = 1711036800;
const date = new Date(timestamp * 1000);
console.log(date.toISOString()); // "2024-03-21T16:00:00.000Z"
console.log(date.toLocaleDateString()); // "3/21/2024" (varies by locale)
console.log(date.toLocaleTimeString()); // "4:00:00 PM" (varies by locale)
Formatted Output with Intl.DateTimeFormat
Intl.DateTimeFormat is the modern, built-in way to format dates. No libraries needed:
const timestamp = 1711036800;
const date = new Date(timestamp * 1000);
// US format
new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
timeZoneName: 'short'
}).format(date);
// "March 21, 2024 at 04:00 PM UTC"
// ISO-like format
new Intl.DateTimeFormat('sv-SE', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
}).format(date);
// "2024-03-21"
// Relative time
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
const diffDays = Math.round((date - new Date()) / (1000 * 60 * 60 * 24));
rtf.format(diffDays, 'day');
// "342 days ago" (depends on current date)
Specific Timezone
const timestamp = 1711036800;
const date = new Date(timestamp * 1000);
// Display in a specific timezone
new Intl.DateTimeFormat('en-US', {
timeZone: 'America/New_York',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false
}).format(date);
// "03/21/2024, 12:00"
// Tokyo time
new Intl.DateTimeFormat('en-US', {
timeZone: 'Asia/Tokyo',
dateStyle: 'full',
timeStyle: 'long'
}).format(date);
// "Friday, March 22, 2024 at 1:00:00 AM JST"
Date to Unix Timestamp
Current Time
// Method 1: Date.now() returns milliseconds
const msTimestamp = Date.now(); // 1711036800000
const unixTimestamp = Math.floor(Date.now() / 1000); // 1711036800
// Method 2: getTime()
const date = new Date();
const unix = Math.floor(date.getTime() / 1000);
// Method 3: Unary + operator
const unix2 = Math.floor(+new Date() / 1000);
Specific Date
// From a date string
const unix = Math.floor(new Date('2024-03-21T16:00:00Z').getTime() / 1000);
// 1711036800
// From components (month is 0-indexed!)
const unix2 = Math.floor(new Date(2024, 2, 21, 16, 0, 0).getTime() / 1000);
// Note: month 2 = March
// Start of today
const startOfDay = Math.floor(
new Date(new Date().setHours(0, 0, 0, 0)).getTime() / 1000
);
Watch out for month indexing. new Date(2024, 0, 1) is January 1, not February 1. This zero-indexed month is one of JavaScript's most notorious footguns.
Using Libraries
date-fns
import { fromUnixTime, getUnixTime, format } from 'date-fns';
// Timestamp to date
const date = fromUnixTime(1711036800);
console.log(format(date, 'yyyy-MM-dd HH:mm:ss'));
// "2024-03-21 16:00:00"
// Date to timestamp
const timestamp = getUnixTime(new Date('2024-03-21'));
// 1711036800
date-fns is tree-shakeable -- you only import the functions you use. Bundle size for just fromUnixTime + format is around 7KB gzipped.
dayjs
import dayjs from 'dayjs';
// Timestamp to formatted date
dayjs.unix(1711036800).format('YYYY-MM-DD HH:mm:ss');
// "2024-03-21 16:00:00"
// Date to timestamp
dayjs('2024-03-21').unix();
// 1711036800
// With timezone plugin
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.unix(1711036800).tz('America/New_York').format('YYYY-MM-DD HH:mm z');
// "2024-03-21 12:00 EDT"
dayjs is 2KB gzipped. It is the smallest full-featured date library.
Common Recipes
Time Elapsed Since Timestamp
function timeAgo(unixTimestamp) {
const seconds = Math.floor(Date.now() / 1000) - unixTimestamp;
const intervals = [
{ label: 'year', seconds: 31536000 },
{ label: 'month', seconds: 2592000 },
{ label: 'week', seconds: 604800 },
{ label: 'day', seconds: 86400 },
{ label: 'hour', seconds: 3600 },
{ label: 'minute', seconds: 60 },
{ label: 'second', seconds: 1 }
];
for (const interval of intervals) {
const count = Math.floor(seconds / interval.seconds);
if (count >= 1) {
return `${count} ${interval.label}${count > 1 ? 's' : ''} ago`;
}
}
return 'just now';
}
timeAgo(Math.floor(Date.now() / 1000) - 3600); // "1 hour ago"
Validate a Timestamp
function isValidUnixTimestamp(ts) {
const num = Number(ts);
if (!Number.isFinite(num)) return false;
// Reasonable range: 1970-01-01 to 2100-01-01
const seconds = num < 1e12 ? num : num / 1000;
return seconds >= 0 && seconds <= 4102444800;
}
Timestamps in APIs
When building or consuming APIs, always document whether timestamps are seconds or milliseconds. The convention:
- Most APIs (Unix standard): seconds (10 digits)
- JavaScript/Java: milliseconds (13 digits)
- Python
time.time(): seconds (float with decimals)
If you need to quickly inspect or convert timestamps during development, devdash.io has an epoch converter that shows the conversion both ways and handles both seconds and milliseconds automatically. Handy for debugging API responses without writing throwaway code.
Summary
// Timestamp -> Date
new Date(unixSeconds * 1000)
// Date -> Timestamp
Math.floor(date.getTime() / 1000)
// Current timestamp
Math.floor(Date.now() / 1000)
// Format it
new Intl.DateTimeFormat('en-US', options).format(date)
That covers 95% of timestamp work in JavaScript. The key is remembering the seconds-vs-milliseconds conversion, using Intl.DateTimeFormat for display formatting, and being explicit about timezones when they matter.