Formatting a date in JavaScript gets messy pretty quickly. A date that looks normal in the US may be confusing in the UK, and manually changing the hour doesn’t properly handle time zones or daylight saving time.
This tool lets you choose a date, locale, time zone and display style, then generates the formatted result and the JavaScript code needed to reproduce it.
It also shows the ISO date and Unix timestamp, which are usually more useful when sending dates to an API or saving them in a database.
Everything uses the browser’s native Intl.DateTimeFormat API. No date library needed.
This is part of Copy, Run & Customize, a series of small projects you can open, change and use in your own work.
Copy the three blocks below, then open the TeknoFenomen Code Playground to run and edit it.
HTML
<main class="date-page">
<section class="date-card">
<header class="tool-header">
<span class="eyebrow">DATE & TIME TOOL</span>
<h1>JavaScript Date Formatter</h1>
<p>
Format a date for different countries and time zones,
then copy the generated JavaScript.
</p>
</header>
<div class="controls-grid">
<label class="control full-width">
<span>Date and time</span>
<input id="dateInput" type="datetime-local">
</label>
<label class="control">
<span>Locale</span>
<select id="localeSelect">
<option value="en-US">English — United States</option>
<option value="en-GB">English — United Kingdom</option>
<option value="de-DE">German — Germany</option>
<option value="fr-FR">French — France</option>
<option value="tr-TR">Turkish — Türkiye</option>
<option value="ja-JP">Japanese — Japan</option>
</select>
</label>
<label class="control">
<span>Time zone</span>
<select id="timezoneSelect">
<option value="local">Browser local time</option>
<option value="UTC">UTC</option>
<option value="America/New_York">New York</option>
<option value="Europe/London">London</option>
<option value="Europe/Berlin">Berlin</option>
<option value="Europe/Istanbul">Istanbul</option>
<option value="Asia/Dubai">Dubai</option>
<option value="Asia/Tokyo">Tokyo</option>
<option value="Australia/Sydney">Sydney</option>
</select>
</label>
<label class="control">
<span>Date style</span>
<select id="dateStyleSelect">
<option value="full">Full</option>
<option value="long">Long</option>
<option value="medium" selected>Medium</option>
<option value="short">Short</option>
<option value="none">Hide date</option>
</select>
</label>
<label class="control">
<span>Time style</span>
<select id="timeStyleSelect">
<option value="long">Long</option>
<option value="medium">Medium</option>
<option value="short" selected>Short</option>
<option value="none">Hide time</option>
</select>
</label>
<label class="control">
<span>Hour format</span>
<select id="hourFormatSelect">
<option value="auto">Locale default</option>
<option value="12">12-hour clock</option>
<option value="24">24-hour clock</option>
</select>
</label>
</div>
<div class="main-actions">
<button id="currentButton" type="button">
Use current time
</button>
<button id="formatButton" class="primary-button" type="button">
Format date
</button>
</div>
<div
id="statusBox"
class="status-box neutral"
role="status"
aria-live="polite"
>
<span class="status-dot"></span>
<div>
<strong id="statusTitle">Ready</strong>
<p id="statusMessage">
Choose a date and formatting options.
</p>
</div>
</div>
<section id="resultSection" class="result-section" hidden>
<div class="formatted-result">
<span class="result-label">FORMATTED RESULT</span>
<strong id="formattedOutput"></strong>
<button id="copyResultButton" type="button">
Copy result
</button>
</div>
<div class="date-details">
<div class="detail-card">
<span>ISO 8601</span>
<code id="isoOutput"></code>
</div>
<div class="detail-card">
<span>Unix timestamp</span>
<code id="unixOutput"></code>
</div>
<div class="detail-card">
<span>Resolved time zone</span>
<code id="resolvedTimezone"></code>
</div>
</div>
<div class="generated-code">
<div class="code-heading">
<div>
<strong>Generated JavaScript</strong>
<span>Ready to use in your project</span>
</div>
<button id="copyCodeButton" type="button">
Copy code
</button>
</div>
<pre><code id="codeOutput"></code></pre>
</div>
</section>
</section>
</main>
CSS
:root {
color-scheme: dark;
font-family:
Inter, ui-sans-serif, system-ui, -apple-system,
BlinkMacSystemFont, "Segoe UI", sans-serif;
--background: #080b12;
--panel: #111722;
--panel-light: #182131;
--input: #090e17;
--border: #2b3547;
--text: #edf1f7;
--muted: #96a2b5;
--accent: #7663ff;
--accent-hover: #8d7fff;
--success: #43d991;
--danger: #ff6d7b;
}
* {
box-sizing: border-box;
}
[hidden] {
display: none !important;
}
body {
min-height: 100vh;
margin: 0;
background:
radial-gradient(circle at top, #1b2542 0, transparent 40%),
var(--background);
color: var(--text);
}
button,
input,
select {
font: inherit;
}
button {
padding: 10px 15px;
border: 1px solid var(--border);
border-radius: 10px;
background: var(--panel-light);
color: var(--text);
cursor: pointer;
transition:
border-color 0.2s,
background 0.2s,
transform 0.15s;
}
button:hover {
border-color: #59677e;
background: #222d40;
}
button:active {
transform: translateY(1px);
}
button:focus-visible,
input:focus-visible,
select:focus-visible {
outline: 2px solid #aaa0ff;
outline-offset: 2px;
}
.date-page {
width: min(940px, calc(100% - 30px));
margin: 0 auto;
padding: 50px 0;
}
.date-card {
padding: 28px;
border: 1px solid var(--border);
border-radius: 20px;
background: rgba(17, 23, 34, 0.96);
box-shadow: 0 25px 70px rgba(0, 0, 0, 0.35);
}
.tool-header {
max-width: 680px;
margin-bottom: 26px;
}
.eyebrow,
.result-label {
color: #aaa0ff;
font-size: 0.72rem;
font-weight: 800;
letter-spacing: 0.16em;
}
h1 {
margin: 8px 0 10px;
font-size: clamp(1.8rem, 6vw, 2.8rem);
line-height: 1.1;
}
.tool-header p {
margin: 0;
color: var(--muted);
line-height: 1.65;
}
.controls-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 15px;
}
.control {
display: flex;
min-width: 0;
flex-direction: column;
gap: 8px;
}
.control.full-width {
grid-column: 1 / -1;
}
.control > span {
font-size: 0.84rem;
font-weight: 700;
}
input,
select {
width: 100%;
min-width: 0;
height: 46px;
padding: 0 13px;
border: 1px solid var(--border);
border-radius: 10px;
outline: none;
background: var(--input);
color: var(--text);
}
input:focus,
select:focus {
border-color: var(--accent);
}
.main-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 18px;
}
.primary-button {
min-width: 125px;
border-color: var(--accent);
background: var(--accent);
color: white;
font-weight: 750;
}
.primary-button:hover {
border-color: var(--accent-hover);
background: var(--accent-hover);
}
.status-box {
display: flex;
align-items: flex-start;
gap: 12px;
margin-top: 18px;
padding: 14px 15px;
border: 1px solid var(--border);
border-radius: 12px;
background: #0c121d;
}
.status-dot {
flex: 0 0 auto;
width: 10px;
height: 10px;
margin-top: 5px;
border-radius: 50%;
background: var(--muted);
}
.status-box strong {
display: block;
margin-bottom: 3px;
}
.status-box p {
margin: 0;
color: var(--muted);
font-size: 0.88rem;
line-height: 1.5;
}
.status-box.success {
border-color: rgba(67, 217, 145, 0.45);
}
.status-box.success .status-dot {
background: var(--success);
box-shadow: 0 0 12px rgba(67, 217, 145, 0.6);
}
.status-box.error {
border-color: rgba(255, 109, 123, 0.5);
}
.status-box.error .status-dot {
background: var(--danger);
box-shadow: 0 0 12px rgba(255, 109, 123, 0.55);
}
.result-section {
margin-top: 25px;
}
.formatted-result {
position: relative;
padding: 24px;
border: 1px solid rgba(118, 99, 255, 0.55);
border-radius: 14px;
background:
linear-gradient(
135deg,
rgba(118, 99, 255, 0.16),
transparent 65%
),
var(--input);
}
.formatted-result strong {
display: block;
max-width: calc(100% - 130px);
margin-top: 9px;
overflow-wrap: anywhere;
font-size: clamp(1.25rem, 4vw, 1.9rem);
line-height: 1.35;
}
.formatted-result button {
position: absolute;
top: 50%;
right: 18px;
transform: translateY(-50%);
}
.formatted-result button:active {
transform: translateY(calc(-50% + 1px));
}
.date-details {
display: grid;
grid-template-columns: 1.6fr 0.8fr 1fr;
gap: 11px;
margin-top: 13px;
}
.detail-card {
min-width: 0;
padding: 14px;
border: 1px solid var(--border);
border-radius: 11px;
background: var(--input);
}
.detail-card span {
display: block;
margin-bottom: 7px;
color: var(--muted);
font-size: 0.73rem;
font-weight: 700;
text-transform: uppercase;
}
.detail-card code {
display: block;
overflow: hidden;
color: #dce7f8;
font-family:
"SFMono-Regular", Consolas, "Liberation Mono", monospace;
font-size: 0.78rem;
text-overflow: ellipsis;
white-space: nowrap;
}
.generated-code {
overflow: hidden;
margin-top: 14px;
border: 1px solid var(--border);
border-radius: 13px;
background: var(--input);
}
.code-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: 15px;
padding: 12px 14px;
border-bottom: 1px solid var(--border);
background: var(--panel-light);
}
.code-heading > div {
display: flex;
flex-direction: column;
gap: 3px;
}
.code-heading strong {
font-size: 0.88rem;
}
.code-heading span {
color: var(--muted);
font-size: 0.73rem;
}
.code-heading button {
padding: 8px 12px;
font-size: 0.8rem;
}
pre {
max-height: 360px;
overflow: auto;
margin: 0;
padding: 17px;
}
pre code {
color: #dce7f8;
font-family:
"SFMono-Regular", Consolas, "Liberation Mono", monospace;
font-size: 0.85rem;
line-height: 1.65;
}
@media (max-width: 720px) {
.date-page {
width: min(100% - 20px, 940px);
padding: 20px 0;
}
.date-card {
padding: 18px;
}
.date-details {
grid-template-columns: 1fr;
}
}
@media (max-width: 540px) {
.controls-grid {
grid-template-columns: 1fr;
}
.control.full-width {
grid-column: auto;
}
.main-actions {
display: grid;
grid-template-columns: 1fr 1fr;
}
.formatted-result {
padding: 20px;
}
.formatted-result strong {
max-width: none;
margin-bottom: 18px;
}
.formatted-result button {
position: static;
width: 100%;
transform: none;
}
.formatted-result button:active {
transform: translateY(1px);
}
}
JavaScript
const dateInput = document.querySelector("#dateInput");
const localeSelect = document.querySelector("#localeSelect");
const timezoneSelect = document.querySelector("#timezoneSelect");
const dateStyleSelect = document.querySelector("#dateStyleSelect");
const timeStyleSelect = document.querySelector("#timeStyleSelect");
const hourFormatSelect = document.querySelector("#hourFormatSelect");
const currentButton = document.querySelector("#currentButton");
const formatButton = document.querySelector("#formatButton");
const copyResultButton = document.querySelector("#copyResultButton");
const copyCodeButton = document.querySelector("#copyCodeButton");
const statusBox = document.querySelector("#statusBox");
const statusTitle = document.querySelector("#statusTitle");
const statusMessage = document.querySelector("#statusMessage");
const resultSection = document.querySelector("#resultSection");
const formattedOutput = document.querySelector("#formattedOutput");
const isoOutput = document.querySelector("#isoOutput");
const unixOutput = document.querySelector("#unixOutput");
const resolvedTimezone = document.querySelector("#resolvedTimezone");
const codeOutput = document.querySelector("#codeOutput");
let currentFormattedResult = "";
let currentGeneratedCode = "";
function setStatus(type, title, message) {
statusBox.className = `status-box ${type}`;
statusTitle.textContent = title;
statusMessage.textContent = message;
}
function padNumber(number) {
return String(number).padStart(2, "0");
}
function toLocalInputValue(date) {
const year = date.getFullYear();
const month = padNumber(date.getMonth() + 1);
const day = padNumber(date.getDate());
const hours = padNumber(date.getHours());
const minutes = padNumber(date.getMinutes());
return `${year}-${month}-${day}T${hours}:${minutes}`;
}
function getFormattingOptions() {
const options = {};
if (dateStyleSelect.value !== "none") {
options.dateStyle = dateStyleSelect.value;
}
if (timeStyleSelect.value !== "none") {
options.timeStyle = timeStyleSelect.value;
}
if (hourFormatSelect.value === "12") {
options.hour12 = true;
}
if (hourFormatSelect.value === "24") {
options.hour12 = false;
}
if (timezoneSelect.value !== "local") {
options.timeZone = timezoneSelect.value;
}
return options;
}
function buildGeneratedCode(date, locale, options) {
const optionsCode = JSON.stringify(options, null, 2);
return `const date = new Date("${date.toISOString()}");
const formatter = new Intl.DateTimeFormat(
"${locale}",
${optionsCode}
);
const formattedDate = formatter.format(date);
console.log(formattedDate);
// ${currentFormattedResult}`;
}
function formatDate() {
try {
if (!dateInput.value) {
throw new Error("Choose a date and time first.");
}
const options = getFormattingOptions();
if (!options.dateStyle && !options.timeStyle) {
throw new Error(
"The date and time styles cannot both be hidden."
);
}
const date = new Date(dateInput.value);
if (Number.isNaN(date.getTime())) {
throw new Error("The selected date is not valid.");
}
const locale = localeSelect.value;
const formatter = new Intl.DateTimeFormat(
locale,
options
);
currentFormattedResult = formatter.format(date);
const resolvedOptions = formatter.resolvedOptions();
const unixTimestamp = Math.floor(date.getTime() / 1000);
formattedOutput.textContent = currentFormattedResult;
isoOutput.textContent = date.toISOString();
unixOutput.textContent = unixTimestamp;
resolvedTimezone.textContent = resolvedOptions.timeZone;
currentGeneratedCode = buildGeneratedCode(
date,
locale,
options
);
codeOutput.textContent = currentGeneratedCode;
resultSection.hidden = false;
setStatus(
"success",
"Date formatted",
`Displayed using ${locale} and ${resolvedOptions.timeZone}.`
);
} catch (error) {
resultSection.hidden = true;
setStatus(
"error",
"Could not format the date",
error.message
);
}
}
function fallbackCopy(text) {
const temporaryInput = document.createElement("textarea");
temporaryInput.value = text;
temporaryInput.setAttribute("readonly", "");
temporaryInput.style.position = "fixed";
temporaryInput.style.left = "-9999px";
temporaryInput.style.opacity = "0";
document.body.appendChild(temporaryInput);
temporaryInput.select();
const copied = document.execCommand("copy");
temporaryInput.remove();
return copied;
}
async function copyText(text) {
if (navigator.clipboard && window.isSecureContext) {
try {
await navigator.clipboard.writeText(text);
return true;
} catch {
return fallbackCopy(text);
}
}
return fallbackCopy(text);
}
function showTemporaryButtonText(button, text) {
const originalText = button.textContent;
button.textContent = text;
setTimeout(() => {
button.textContent = originalText;
}, 1500);
}
currentButton.addEventListener("click", () => {
dateInput.value = toLocalInputValue(new Date());
formatDate();
});
formatButton.addEventListener("click", formatDate);
copyResultButton.addEventListener("click", async () => {
const copied = await copyText(currentFormattedResult);
if (copied) {
showTemporaryButtonText(copyResultButton, "Copied!");
setStatus(
"success",
"Result copied",
"The formatted date is now in your clipboard."
);
} else {
setStatus(
"error",
"Copy failed",
"Your browser blocked clipboard access."
);
}
});
copyCodeButton.addEventListener("click", async () => {
const copied = await copyText(currentGeneratedCode);
if (copied) {
showTemporaryButtonText(copyCodeButton, "Copied!");
setStatus(
"success",
"Code copied",
"The generated JavaScript is ready to paste."
);
} else {
setStatus(
"error",
"Copy failed",
"Your browser blocked clipboard access."
);
}
});
[
dateInput,
localeSelect,
timezoneSelect,
dateStyleSelect,
timeStyleSelect,
hourFormatSelect
].forEach((control) => {
control.addEventListener("change", formatDate);
});
dateInput.value = toLocalInputValue(new Date());
formatDate();
Why use Intl.DateTimeFormat?
Manually joining the day, month and year might work for one format, but it doesn’t deal properly with different languages and regional preferences.
For example:
const date = new Date("2026-08-27T15:30:00Z");
console.log(
new Intl.DateTimeFormat("en-US").format(date)
);
// 8/27/2026
console.log(
new Intl.DateTimeFormat("en-GB").format(date)
);
// 27/08/2026
console.log(
new Intl.DateTimeFormat("de-DE").format(date)
);
// 27.8.2026
The same date can also be displayed in different time zones without manually adding or subtracting hours:
const formatter = new Intl.DateTimeFormat("en-US", {
dateStyle: "full",
timeStyle: "long",
timeZone: "America/New_York"
});
console.log(formatter.format(new Date()));
One easy mistake to avoid
A datetime-local input doesn’t contain a time zone. The browser treats its value as a date and time in the user’s local zone.
For APIs and databases, an ISO date is usually safer:
const isoDate = new Date().toISOString();
For a Unix timestamp in seconds:
const unixTimestamp = Math.floor(Date.now() / 1000);
Use ISO or Unix values for storage and transport, then format the date for the user when it is displayed.
