timesite/app.js
2025-07-14 18:43:59 -07:00

155 lines
6.0 KiB
JavaScript

let serverTimeOffset = 0;
let intervalId = null;
function formatLocalTime(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
const tenths = Math.floor(date.getMilliseconds() / 100);
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${tenths}`;
}
function getTimezone() {
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const offset = new Date().getTimezoneOffset();
const offsetHours = Math.floor(Math.abs(offset) / 60);
const offsetMinutes = Math.abs(offset) % 60;
const offsetSign = offset <= 0 ? '+' : '-';
const offsetString = `UTC${offsetSign}${offsetHours.toString().padStart(2, '0')}:${offsetMinutes.toString().padStart(2, '0')}`;
return `${timezone} (${offsetString})`;
}
function formatUTCTime(date) {
const year = date.getUTCFullYear();
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
const day = String(date.getUTCDate()).padStart(2, '0');
const hours = String(date.getUTCHours()).padStart(2, '0');
const minutes = String(date.getUTCMinutes()).padStart(2, '0');
const seconds = String(date.getUTCSeconds()).padStart(2, '0');
const tenths = Math.floor(date.getUTCMilliseconds() / 100);
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${tenths}`;
}
function updateDisplay() {
const now = new Date();
const correctedTime = new Date(now.getTime() + serverTimeOffset);
// Local time with timezone (using corrected time)
const timeString = formatLocalTime(correctedTime);
const timezone = now.toLocaleTimeString('en-US', { timeZoneName: 'short' }).split(' ').pop();
document.getElementById('time-display').textContent = `${timeString} ${timezone}`;
// UTC time (using corrected time)
const utcTimeString = formatUTCTime(correctedTime);
document.getElementById('utc-display').textContent = `${utcTimeString} UTC`;
// Display offset
const offsetMs = Math.abs(serverTimeOffset);
// If serverTimeOffset is positive, server is ahead, so local clock is slow
// If serverTimeOffset is negative, server is behind, so local clock is fast
const status = serverTimeOffset > 0 ? 'slow' : 'fast';
if (offsetMs < 10000) { // Less than 10 seconds
document.getElementById('offset-display').textContent = `${Math.round(offsetMs)}ms ${status}`;
} else {
const offsetSeconds = (offsetMs / 1000).toFixed(1);
document.getElementById('offset-display').textContent = `${offsetSeconds}s ${status}`;
}
document.getElementById('timezone-display').style.display = 'none';
}
async function fetchServerTime() {
try {
const roundtrips = [];
// Check if we're on localhost
const isLocalhost = window.location.hostname === 'localhost' ||
window.location.hostname === '127.0.0.1' ||
window.location.hostname.startsWith('192.168.') ||
window.location.hostname.startsWith('10.');
const url = isLocalhost ? '/api/time' : window.location.href;
// Make three sequential requests to measure latency
for (let i = 0; i < 3; i++) {
const startTime = Date.now();
const response = await fetch(url, {
method: isLocalhost ? 'GET' : 'HEAD'
});
const endTime = Date.now();
const roundtripTime = endTime - startTime;
if (isLocalhost) {
// Parse JSON response from our backend
const timeData = await response.json();
const serverTime = timeData.utc_epoch_milliseconds;
const localTime = startTime + (roundtripTime / 2);
const offset = serverTime - localTime;
console.log('Local backend sync:', {
serverTime: new Date(serverTime).toISOString(),
localTime: new Date(localTime).toISOString(),
roundtripTime: roundtripTime + 'ms',
offset: offset + 'ms'
});
roundtrips.push({
roundtripTime,
offset
});
} else {
const serverDateHeader = response.headers.get('Date');
if (serverDateHeader) {
const serverTime = new Date(serverDateHeader).getTime();
const localTime = startTime + (roundtripTime / 2); // Estimate time when server processed request
const offset = serverTime - localTime;
roundtrips.push({
roundtripTime,
offset
});
}
}
}
if (roundtrips.length > 0) {
// Calculate average offset
const avgOffset = roundtrips.reduce((sum, rt) => sum + rt.offset, 0) / roundtrips.length;
serverTimeOffset = avgOffset;
// Log for debugging
console.log('Time sync complete:', {
roundtrips: roundtrips.map(rt => rt.roundtripTime + 'ms'),
averageOffset: avgOffset.toFixed(3) + 'ms'
});
}
} catch (error) {
console.error('Failed to fetch server time:', error);
}
}
async function init() {
await fetchServerTime();
updateDisplay();
if (intervalId) {
clearInterval(intervalId);
}
intervalId = setInterval(updateDisplay, 100);
setInterval(fetchServerTime, 60000);
}
init();
document.addEventListener('visibilitychange', () => {
if (!document.hidden) {
fetchServerTime();
}
});