Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
function initializeDynamicClocks() {
const allElements = document.querySelectorAll('.dynamic-clock');
if (allElements.length === 0) return;
const groups = new Map();
allElements.forEach(element => {
const timezone = element.getAttribute('data-timezone') || 'UTC';
const format = element.getAttribute('data-format') || 'h:mm - D/M/YYYY';
const showOffset = element.getAttribute('data-show-offset') === 'true';
const key = timezone + '|' + format + '|' + showOffset;
if (!groups.has(key)) groups.set(key, []);
groups.get(key).push(element);
});
groups.forEach((elements, key) => {
const [timezone, format, showOffsetRaw] = key.split('|');
const showOffset = showOffsetRaw === 'true';
function getTimeParts() {
const offsetMatch = timezone.match(/^([+-]?)(\d{1,2})(?::(\d{2}))?$/);
let h24, m, s, day, month, year, offsetMinutes;
if (offsetMatch) {
const sign = offsetMatch[1] === '-' ? -1 : 1;
const hours = parseInt(offsetMatch[2]);
const mins = parseInt(offsetMatch[3] || 0);
offsetMinutes = sign * (hours * 60 + mins);
const utc = Date.now() + (new Date().getTimezoneOffset() * 60000);
const date = new Date(utc + (offsetMinutes * 60000));
h24 = date.getUTCHours();
m = date.getUTCMinutes();
s = date.getUTCSeconds();
day = date.getUTCDate();
month = date.getUTCMonth();
year = date.getUTCFullYear();
} else {
try {
const now = new Date();
const parts = new Intl.DateTimeFormat('en-US', {
timeZone: timezone,
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
hour12: false
}).formatToParts(now);
const dateParts = new Intl.DateTimeFormat('en-US', {
timeZone: timezone,
day: 'numeric',
month: 'numeric',
year: 'numeric'
}).formatToParts(now);
const get = (arr, type) => {
const part = arr.find(p => p.type === type);
return part ? part.value : '0';
};
h24 = parseInt(get(parts, 'hour')) % 24;
m = parseInt(get(parts, 'minute'));
s = parseInt(get(parts, 'second'));
day = parseInt(get(dateParts, 'day'));
month = parseInt(get(dateParts, 'month')) - 1;
year = parseInt(get(dateParts, 'year'));
const utcDate = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' }));
const tzDate = new Date(now.toLocaleString('en-US', { timeZone: timezone }));
offsetMinutes = (tzDate - utcDate) / 60000;
} catch (e) {
elements.forEach(el => el.textContent = 'Invalid timezone');
return null;
}
}
return { h24, m, s, day, month, year, offsetMinutes };
}
const monthNames = ['January','February','March','April','May','June','July','August','September','October','November','December'];
const dayNames = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
const tokens = [
{ token: 'YYYY', placeholder: '%%0%%', type: 'date' },
{ token: 'YY', placeholder: '%%17%%', type: 'date' },
{ token: 'MMMM', placeholder: '%%1%%', type: 'date' },
{ token: 'MMM', placeholder: '%%2%%', type: 'date' },
{ token: 'MM', placeholder: '%%3%%', type: 'date' },
{ token: 'M', placeholder: '%%4%%', type: 'date' },
{ token: 'dddd', placeholder: '%%5%%', type: 'date' },
{ token: 'ddd', placeholder: '%%6%%', type: 'date' },
{ token: 'DD', placeholder: '%%7%%', type: 'date' },
{ token: 'D', placeholder: '%%8%%', type: 'date' },
{ token: 'HH', placeholder: '%%9%%', type: 'time24' },
{ token: 'H', placeholder: '%%10%%', type: 'time24' },
{ token: 'hh', placeholder: '%%11%%', type: 'time12' },
{ token: 'h', placeholder: '%%12%%', type: 'time12' },
{ token: 'mm', placeholder: '%%13%%', type: 'time' },
{ token: 'm', placeholder: '%%14%%', type: 'time' },
{ token: 'ss', placeholder: '%%15%%', type: 'time' },
{ token: 's', placeholder: '%%16%%', type: 'time' },
];
const has12Hour = tokens.some(t => t.type === 'time12' && format.includes(t.token));
const has24Hour = tokens.some(t => t.type === 'time24' && format.includes(t.token));
function formatTime(parts) {
const { h24, m, s, day, month, year, offsetMinutes } = parts;
const h12 = h24 % 12 || 12;
const ampm = h24 >= 12 ? 'PM' : 'AM';
const pad = n => String(n).padStart(2, '0');
const dateObj = new Date(year, month, day);
const dayOfWeek = dateObj.getDay();
const values = {
'%%0%%': year,
'%%17%%': String(year).slice(-2),
'%%1%%': monthNames[month],
'%%2%%': monthNames[month].substring(0, 3),
'%%3%%': pad(month + 1),
'%%4%%': month + 1,
'%%5%%': dayNames[dayOfWeek],
'%%6%%': dayNames[dayOfWeek].substring(0, 3),
'%%7%%': pad(day),
'%%8%%': day,
'%%9%%': pad(h24),
'%%10%%': h24,
'%%11%%': pad(h12),
'%%12%%': h12,
'%%13%%': pad(m),
'%%14%%': m,
'%%15%%': pad(s),
'%%16%%': s,
};
let result = format;
const escapedLiterals = [];
result = result.replace(/\\(.)/g, (match, char) => {
const idx = escapedLiterals.length;
escapedLiterals.push(char);
return '%%ESC' + idx + '%%';
});
tokens.forEach(({ token, placeholder }) => {
result = result.replace(token, placeholder);
});
if (has12Hour && !has24Hour) {
const timePlaceholders = ['%%11%%', '%%12%%', '%%13%%', '%%14%%', '%%15%%', '%%16%%'];
let lastIndex = -1;
let lastLength = 0;
timePlaceholders.forEach(ph => {
const idx = result.indexOf(ph);
if (idx !== -1 && idx > lastIndex) {
lastIndex = idx;
lastLength = ph.length;
}
});
if (lastIndex !== -1) {
const insertAt = lastIndex + lastLength;
result = result.slice(0, insertAt) + ' ' + ampm + result.slice(insertAt);
}
}
Object.keys(values).forEach(placeholder => {
result = result.replace(placeholder, values[placeholder]);
});
escapedLiterals.forEach((char, idx) => {
result = result.replace('%%ESC' + idx + '%%', char);
});
if (showOffset) {
const sign = offsetMinutes >= 0 ? '+' : '-';
const absMinutes = Math.abs(offsetMinutes);
const offH = Math.floor(absMinutes / 60);
const offM = absMinutes % 60;
result += ' UTC' + sign + offH + (offM ? ':' + pad(offM) : '');
}
return result;
}
function update() {
const parts = getTimeParts();
if (parts !== null) elements.forEach(el => el.textContent = formatTime(parts));
}
update();
const now = new Date();
const hasSeconds = format.includes('s');
const interval = hasSeconds ? 1000 : 60000;
const msUntilNext = hasSeconds
? 1000 - now.getMilliseconds()
: (60 - now.getSeconds()) * 1000 - now.getMilliseconds();
setTimeout(() => {
update();
setInterval(update, interval);
}, msUntilNext);
});
}
jQuery(function($) {
initializeDynamicClocks();
});