Zeitzonen: Verbesserung der Zeitzonenberechnung und -darstellung, einschließlich Fehlerbehandlung und Anpassung der Offset-Berechnung.
This commit is contained in:
@@ -431,21 +431,21 @@
|
|||||||
|
|
||||||
// Timezone data
|
// Timezone data
|
||||||
const majorTimezones = [
|
const majorTimezones = [
|
||||||
{ label: "Pacific (Los Angeles)", id: "America/Los_Angeles", offset: -8 },
|
{ label: "Pacific (Los Angeles)", id: "America/Los_Angeles" },
|
||||||
{ label: "Mountain (Denver)", id: "America/Denver", offset: -7 },
|
{ label: "Mountain (Denver)", id: "America/Denver" },
|
||||||
{ label: "Central (Chicago)", id: "America/Chicago", offset: -6 },
|
{ label: "Central (Chicago)", id: "America/Chicago" },
|
||||||
{ label: "Eastern (New York)", id: "America/New_York", offset: -5 },
|
{ label: "Eastern (New York)", id: "America/New_York" },
|
||||||
{ label: "Brasilien (São Paulo)", id: "America/Sao_Paulo", offset: -3 },
|
{ label: "Brasilien (São Paulo)", id: "America/Sao_Paulo" },
|
||||||
{ label: "Großbritannien (London)", id: "Europe/London", offset: 0 },
|
{ label: "Großbritannien (London)", id: "Europe/London" },
|
||||||
{ label: "Westeuropa (Paris, Berlin)", id: "Europe/Berlin", offset: 1 },
|
{ label: "Westeuropa (Paris, Berlin)", id: "Europe/Berlin" },
|
||||||
{ label: "Osteuropa (Athen)", id: "Europe/Athens", offset: 2 },
|
{ label: "Osteuropa (Athen)", id: "Europe/Athens" },
|
||||||
{ label: "Moskau", id: "Europe/Moscow", offset: 3 },
|
{ label: "Moskau", id: "Europe/Moscow" },
|
||||||
{ label: "Dubai", id: "Asia/Dubai", offset: 4 },
|
{ label: "Dubai", id: "Asia/Dubai" },
|
||||||
{ label: "Indien (Mumbai)", id: "Asia/Kolkata", offset: 5.5 },
|
{ label: "Indien (Mumbai)", id: "Asia/Kolkata" },
|
||||||
{ label: "China (Peking)", id: "Asia/Shanghai", offset: 8 },
|
{ label: "China (Peking)", id: "Asia/Shanghai" },
|
||||||
{ label: "Japan (Tokio)", id: "Asia/Tokyo", offset: 9 },
|
{ label: "Japan (Tokio)", id: "Asia/Tokyo" },
|
||||||
{ label: "Australien (Sydney)", id: "Australia/Sydney", offset: 10 },
|
{ label: "Australien (Sydney)", id: "Australia/Sydney" },
|
||||||
{ label: "Neuseeland (Auckland)", id: "Pacific/Auckland", offset: 12 }
|
{ label: "Neuseeland (Auckland)", id: "Pacific/Auckland" }
|
||||||
];
|
];
|
||||||
|
|
||||||
// Populate timezone dropdowns
|
// Populate timezone dropdowns
|
||||||
@@ -618,17 +618,22 @@
|
|||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
majorTimezones.forEach(tz => {
|
majorTimezones.forEach(tz => {
|
||||||
// Create a timezone card
|
|
||||||
const card = document.createElement('div');
|
const card = document.createElement('div');
|
||||||
card.className = 'timezone-card';
|
card.className = 'timezone-card';
|
||||||
|
|
||||||
const dateInTimezone = getDateInTimezone(referenceDate, tz.id);
|
|
||||||
|
|
||||||
|
// Format time and date using Intl.DateTimeFormat for the specific timezone
|
||||||
|
const timeFormatter = new Intl.DateTimeFormat('de-DE', { timeZone: tz.id, hour: '2-digit', minute: '2-digit' });
|
||||||
|
const dateFormatter = new Intl.DateTimeFormat('de-DE', { timeZone: tz.id, year: 'numeric', month: '2-digit', day: '2-digit' });
|
||||||
|
|
||||||
|
const timeStr = timeFormatter.format(referenceDate);
|
||||||
|
const dateStr = dateFormatter.format(referenceDate);
|
||||||
|
const offsetStr = getTimezoneOffsetString(referenceDate, tz.id);
|
||||||
|
|
||||||
card.innerHTML = `
|
card.innerHTML = `
|
||||||
<h3>${tz.label}</h3>
|
<h3>${tz.label}</h3>
|
||||||
<div class="time">${dateInTimezone.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</div>
|
<div class="time">${timeStr}</div>
|
||||||
<div class="date">${dateInTimezone.toLocaleDateString()}</div>
|
<div class="date">${dateStr}</div>
|
||||||
<div class="timezone-offset">${getTimezoneOffsetString(dateInTimezone, tz.id)}</div>
|
<div class="timezone-offset">${offsetStr}</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
container.appendChild(card);
|
container.appendChild(card);
|
||||||
@@ -637,66 +642,119 @@
|
|||||||
|
|
||||||
// Calculate and show timezone results
|
// Calculate and show timezone results
|
||||||
document.getElementById('calculate-btn').addEventListener('click', () => {
|
document.getElementById('calculate-btn').addEventListener('click', () => {
|
||||||
const dateStr = dateInput.value;
|
const dateStr = dateInput.value; // "YYYY-MM-DD"
|
||||||
const timeStr = timeInput.value;
|
const timeStr = timeInput.value; // "HH:mm"
|
||||||
const sourceTimezone = sourceTimezoneSelect.value;
|
const sourceTimezoneId = sourceTimezoneSelect.value;
|
||||||
const targetTimezone = targetTimezoneSelect.value;
|
const targetTimezoneId = targetTimezoneSelect.value;
|
||||||
|
|
||||||
if (!dateStr || !timeStr) {
|
if (!dateStr || !timeStr || !sourceTimezoneId || !targetTimezoneId) {
|
||||||
alert('Bitte gib ein Datum und eine Zeit ein');
|
alert('Bitte gib ein Datum, eine Zeit und beide Zeitzonen an.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create date object from inputs
|
try {
|
||||||
const [year, month, day] = dateStr.split('-').map(Number);
|
// --- Korrekte Logik zur Erstellung des Referenz-Zeitpunkts ---
|
||||||
const [hours, minutes] = timeStr.split(':').map(Number);
|
|
||||||
|
|
||||||
// Erstelle ein Datum im lokalen Format
|
|
||||||
const inputDate = new Date(year, month - 1, day, hours, minutes);
|
|
||||||
|
|
||||||
// Wir erstellen eine Referenzzeit, die der Eingabe in der Quell-Zeitzone entspricht
|
|
||||||
const utcMilliseconds = Date.UTC(year, month - 1, day, hours, minutes);
|
|
||||||
const sourceOffset = getTimezoneOffsetHours(sourceTimezone) * 60 * 60 * 1000;
|
|
||||||
const referenceDate = new Date(utcMilliseconds - sourceOffset);
|
|
||||||
|
|
||||||
// Zeige nur die Quell- und Zielzeitzone an
|
// 1. Parse input date/time components
|
||||||
displayConversionResult(referenceDate, sourceTimezone, targetTimezone);
|
const [year, month, day] = dateStr.split('-').map(Number);
|
||||||
|
const [hours, minutes] = timeStr.split(':').map(Number);
|
||||||
|
|
||||||
|
// 2. Helper function to get offset in hours for a given date and timezone
|
||||||
|
function getOffsetHours(date, timezone) {
|
||||||
|
try {
|
||||||
|
const formatter = new Intl.DateTimeFormat('en-US', {
|
||||||
|
timeZone: timezone,
|
||||||
|
timeZoneName: 'longOffset' // e.g., GMT-07:00 or GMT+02:00
|
||||||
|
});
|
||||||
|
const parts = formatter.formatToParts(date);
|
||||||
|
const offsetPart = parts.find(part => part.type === 'timeZoneName');
|
||||||
|
if (offsetPart) {
|
||||||
|
// Extract hours and minutes from GMT string (e.g., "GMT-7", "GMT+5:30")
|
||||||
|
const match = offsetPart.value.match(/GMT([-+])(\d{1,2})(?::(\d{2}))?/);
|
||||||
|
if (match) {
|
||||||
|
const sign = match[1] === '-' ? -1 : 1;
|
||||||
|
const hourOffset = parseInt(match[2], 10);
|
||||||
|
const minuteOffset = match[3] ? parseInt(match[3], 10) : 0;
|
||||||
|
return sign * (hourOffset + minuteOffset / 60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(`Could not determine offset for timezone ${timezone}`, e);
|
||||||
|
}
|
||||||
|
// Fallback: Browser's local offset for the given date (less accurate for the *target* zone)
|
||||||
|
console.warn(`Falling back to local offset for timezone ${timezone}`);
|
||||||
|
return -date.getTimezoneOffset() / 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Estimate the UTC time by initially assuming the input time *was* UTC.
|
||||||
|
// We need a Date object to pass to getOffsetHours.
|
||||||
|
const guessedUtcMillis = Date.UTC(year, month - 1, day, hours, minutes);
|
||||||
|
const tempDate = new Date(guessedUtcMillis);
|
||||||
|
|
||||||
|
// 4. Get the actual offset of the source timezone *at that time*.
|
||||||
|
const sourceOffsetHours = getOffsetHours(tempDate, sourceTimezoneId);
|
||||||
|
|
||||||
|
// 5. Calculate the correct UTC milliseconds.
|
||||||
|
// The input time (e.g., 20:00 Berlin) corresponds to a UTC time that is
|
||||||
|
// 'sourceOffsetHours' earlier than 20:00 UTC.
|
||||||
|
// So, we subtract the offset from the initial UTC guess.
|
||||||
|
const correctUtcMillis = guessedUtcMillis - (sourceOffsetHours * 3600 * 1000);
|
||||||
|
|
||||||
|
// 6. Create the final Date object representing the correct moment in time.
|
||||||
|
const referenceDate = new Date(correctUtcMillis);
|
||||||
|
|
||||||
|
// 7. Display the results using this correct referenceDate.
|
||||||
|
displayConversionResult(referenceDate, sourceTimezoneId, targetTimezoneId);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler bei der Zeitzonenberechnung:", error);
|
||||||
|
// Add more specific error message if Intl fails (e.g., invalid timezone ID)
|
||||||
|
if (error instanceof RangeError) {
|
||||||
|
alert(`Fehler: Eine der angegebenen Zeitzonen ('${sourceTimezoneId}' oder '${targetTimezoneId}') ist ungültig oder wird vom Browser nicht unterstützt.`);
|
||||||
|
} else {
|
||||||
|
alert("Ein unerwarteter Fehler bei der Berechnung der Zeit ist aufgetreten. Bitte überprüfe die Eingabe.");
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Display conversion result between source and target timezone
|
// Display conversion result between source and target timezone
|
||||||
function displayConversionResult(referenceDate, sourceTimezone, targetTimezone) {
|
function displayConversionResult(referenceDate, sourceTimezoneId, targetTimezoneId) {
|
||||||
const container = document.getElementById('timezone-result');
|
const container = document.getElementById('timezone-result');
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
const sourceTz = majorTimezones.find(tz => tz.id === sourceTimezone);
|
const sourceTzData = majorTimezones.find(tz => tz.id === sourceTimezoneId);
|
||||||
const targetTz = majorTimezones.find(tz => tz.id === targetTimezone);
|
const targetTzData = majorTimezones.find(tz => tz.id === targetTimezoneId);
|
||||||
|
|
||||||
if (!sourceTz || !targetTz) return;
|
if (!sourceTzData || !targetTzData) {
|
||||||
|
console.error("Source or target timezone data not found in majorTimezones list.");
|
||||||
// Quell-Zeitzone Karte
|
return;
|
||||||
const sourceCard = document.createElement('div');
|
}
|
||||||
sourceCard.className = 'timezone-card';
|
|
||||||
const dateInSourceTimezone = getDateInTimezone(referenceDate, sourceTimezone);
|
|
||||||
|
|
||||||
sourceCard.innerHTML = `
|
|
||||||
<h3>${sourceTz.label}</h3>
|
|
||||||
<div class="time">${dateInSourceTimezone.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</div>
|
|
||||||
<div class="date">${dateInSourceTimezone.toLocaleDateString()}</div>
|
|
||||||
<div class="timezone-offset">${getTimezoneOffsetString(dateInSourceTimezone, sourceTimezone)}</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Ziel-Zeitzone Karte
|
|
||||||
const targetCard = document.createElement('div');
|
|
||||||
targetCard.className = 'timezone-card';
|
|
||||||
const dateInTargetTimezone = getDateInTimezone(referenceDate, targetTimezone);
|
|
||||||
|
|
||||||
targetCard.innerHTML = `
|
|
||||||
<h3>${targetTz.label}</h3>
|
|
||||||
<div class="time">${dateInTargetTimezone.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</div>
|
|
||||||
<div class="date">${dateInTargetTimezone.toLocaleDateString()}</div>
|
|
||||||
<div class="timezone-offset">${getTimezoneOffsetString(dateInTargetTimezone, targetTimezone)}</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
|
// --- Funktion zum Erstellen einer Karte ---
|
||||||
|
function createResultCard(timezoneData, dateToFormat) {
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'timezone-card';
|
||||||
|
|
||||||
|
const timeFormatter = new Intl.DateTimeFormat('de-DE', { timeZone: timezoneData.id, hour: '2-digit', minute: '2-digit' });
|
||||||
|
const dateFormatter = new Intl.DateTimeFormat('de-DE', { timeZone: timezoneData.id, year: 'numeric', month: '2-digit', day: '2-digit' });
|
||||||
|
|
||||||
|
const timeStr = timeFormatter.format(dateToFormat);
|
||||||
|
const dateStr = dateFormatter.format(dateToFormat);
|
||||||
|
const offsetStr = getTimezoneOffsetString(dateToFormat, timezoneData.id);
|
||||||
|
|
||||||
|
card.innerHTML = `
|
||||||
|
<h3>${timezoneData.label}</h3>
|
||||||
|
<div class="time">${timeStr}</div>
|
||||||
|
<div class="date">${dateStr}</div>
|
||||||
|
<div class="timezone-offset">${offsetStr}</div>
|
||||||
|
`;
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erstelle Quell- und Ziel-Karten
|
||||||
|
const sourceCard = createResultCard(sourceTzData, referenceDate);
|
||||||
|
const targetCard = createResultCard(targetTzData, referenceDate);
|
||||||
|
|
||||||
container.appendChild(sourceCard);
|
container.appendChild(sourceCard);
|
||||||
|
|
||||||
// Pfeil zwischen den Karten
|
// Pfeil zwischen den Karten
|
||||||
@@ -708,78 +766,41 @@
|
|||||||
container.appendChild(targetCard);
|
container.appendChild(targetCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to get a date object in a specific timezone
|
|
||||||
function getDateInTimezone(date, timezone) {
|
|
||||||
// Nutze die eingebaute Zeitzonenunterstützung von JavaScript
|
|
||||||
const options = { timeZone: timezone };
|
|
||||||
const formatter = new Intl.DateTimeFormat('en-US', {
|
|
||||||
...options,
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'numeric',
|
|
||||||
day: 'numeric',
|
|
||||||
hour: 'numeric',
|
|
||||||
minute: 'numeric',
|
|
||||||
second: 'numeric',
|
|
||||||
hour12: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const parts = formatter.formatToParts(date);
|
|
||||||
const dateObj = {};
|
|
||||||
|
|
||||||
parts.forEach(part => {
|
|
||||||
if (part.type !== 'literal') {
|
|
||||||
dateObj[part.type] = part.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Date(
|
|
||||||
dateObj.year,
|
|
||||||
parseInt(dateObj.month, 10) - 1,
|
|
||||||
dateObj.day,
|
|
||||||
dateObj.hour,
|
|
||||||
dateObj.minute,
|
|
||||||
dateObj.second
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to get timezone offset string (e.g., GMT+1)
|
// Helper function to get timezone offset string (e.g., GMT+1)
|
||||||
function getTimezoneOffsetString(date, timezone) {
|
function getTimezoneOffsetString(date, timezone) {
|
||||||
if (timezone) {
|
try {
|
||||||
// Ermittle den tatsächlichen Offset für das Datum in der Zeitzone
|
// Nutze Intl.DateTimeFormat, um den Offset für das spezifische Datum und die Zeitzone zu erhalten
|
||||||
const testDate = new Date(date); // Wir erstellen eine Kopie
|
|
||||||
|
|
||||||
// Formatiere das Datum in der angegebenen Zeitzone und hole den Offset
|
|
||||||
const formatter = new Intl.DateTimeFormat('en-US', {
|
const formatter = new Intl.DateTimeFormat('en-US', {
|
||||||
timeZone: timezone,
|
timeZone: timezone,
|
||||||
timeZoneName: 'longOffset'
|
timeZoneName: 'longOffset' // Gibt z.B. "GMT+01:00" zurück
|
||||||
});
|
});
|
||||||
|
|
||||||
// Extrahiere den GMT-Offset-String aus der formatierten Ausgabe
|
// Formatiere das Datum, um den Offset-String zu extrahieren
|
||||||
const formattedDate = formatter.format(testDate);
|
const formattedParts = formatter.formatToParts(date);
|
||||||
const offsetMatch = formattedDate.match(/GMT([-+]\d+)/);
|
const offsetPart = formattedParts.find(part => part.type === 'timeZoneName');
|
||||||
|
|
||||||
if (offsetMatch) {
|
if (offsetPart) {
|
||||||
return `GMT${offsetMatch[1]}`;
|
// Extrahiere den numerischen Teil (z.B. +1, -8)
|
||||||
}
|
const match = offsetPart.value.match(/GMT([-+]\d{1,2}(?::\d{2})?)/);
|
||||||
|
if (match && match[1]) {
|
||||||
// Fallback: Verwende den statischen Offset aus majorTimezones
|
// Vereinfache zu GMT+X oder GMT-X
|
||||||
const tz = majorTimezones.find(t => t.id === timezone);
|
const numericOffset = parseInt(match[1].split(':')[0], 10);
|
||||||
if (tz) {
|
const sign = numericOffset >= 0 ? '+' : '-';
|
||||||
const sign = tz.offset >= 0 ? '+' : '-';
|
return `GMT${sign}${Math.abs(numericOffset)}`;
|
||||||
return `GMT${sign}${Math.abs(tz.offset)}`;
|
}
|
||||||
|
return offsetPart.value; // Fallback auf den vollen String
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Konnte Offset für Zeitzone ${timezone} nicht ermitteln:`, error);
|
||||||
|
// Fallback: Verwende den Offset der lokalen Zeitzone des Browsers für dieses Datum
|
||||||
|
const localOffsetMinutes = date.getTimezoneOffset();
|
||||||
|
const offsetHours = -localOffsetMinutes / 60;
|
||||||
|
const sign = offsetHours >= 0 ? '+' : '-';
|
||||||
|
return `GMT${sign}${Math.abs(offsetHours)} (Lokal)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wenn keine Zeitzone angegeben oder gefunden wurde, verwende den lokalen Offset
|
|
||||||
const offset = date.getTimezoneOffset() * -1 / 60;
|
|
||||||
const sign = offset >= 0 ? '+' : '-';
|
|
||||||
return `GMT${sign}${Math.abs(offset)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to get timezone offset in hours
|
// Sicherer Fallback, falls alles fehlschlägt
|
||||||
function getTimezoneOffsetHours(timezone) {
|
return 'GMT?';
|
||||||
const tz = majorTimezones.find(t => t.id === timezone);
|
|
||||||
return tz ? tz.offset : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate initial timezone cards
|
// Generate initial timezone cards
|
||||||
|
|||||||
Reference in New Issue
Block a user