155 lines
6.0 KiB
JavaScript
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();
|
|
}
|
|
}); |