diff --git a/zeitzonen/index.html b/zeitzonen/index.html index 9da9803..1381d77 100644 --- a/zeitzonen/index.html +++ b/zeitzonen/index.html @@ -431,21 +431,21 @@ // Timezone data const majorTimezones = [ - { label: "Pacific (Los Angeles)", id: "America/Los_Angeles", offset: -8 }, - { label: "Mountain (Denver)", id: "America/Denver", offset: -7 }, - { label: "Central (Chicago)", id: "America/Chicago", offset: -6 }, - { label: "Eastern (New York)", id: "America/New_York", offset: -5 }, - { label: "Brasilien (São Paulo)", id: "America/Sao_Paulo", offset: -3 }, - { label: "Großbritannien (London)", id: "Europe/London", offset: 0 }, - { label: "Westeuropa (Paris, Berlin)", id: "Europe/Berlin", offset: 1 }, - { label: "Osteuropa (Athen)", id: "Europe/Athens", offset: 2 }, - { label: "Moskau", id: "Europe/Moscow", offset: 3 }, - { label: "Dubai", id: "Asia/Dubai", offset: 4 }, - { label: "Indien (Mumbai)", id: "Asia/Kolkata", offset: 5.5 }, - { label: "China (Peking)", id: "Asia/Shanghai", offset: 8 }, - { label: "Japan (Tokio)", id: "Asia/Tokyo", offset: 9 }, - { label: "Australien (Sydney)", id: "Australia/Sydney", offset: 10 }, - { label: "Neuseeland (Auckland)", id: "Pacific/Auckland", offset: 12 } + { label: "Pacific (Los Angeles)", id: "America/Los_Angeles" }, + { label: "Mountain (Denver)", id: "America/Denver" }, + { label: "Central (Chicago)", id: "America/Chicago" }, + { label: "Eastern (New York)", id: "America/New_York" }, + { label: "Brasilien (São Paulo)", id: "America/Sao_Paulo" }, + { label: "Großbritannien (London)", id: "Europe/London" }, + { label: "Westeuropa (Paris, Berlin)", id: "Europe/Berlin" }, + { label: "Osteuropa (Athen)", id: "Europe/Athens" }, + { label: "Moskau", id: "Europe/Moscow" }, + { label: "Dubai", id: "Asia/Dubai" }, + { label: "Indien (Mumbai)", id: "Asia/Kolkata" }, + { label: "China (Peking)", id: "Asia/Shanghai" }, + { label: "Japan (Tokio)", id: "Asia/Tokyo" }, + { label: "Australien (Sydney)", id: "Australia/Sydney" }, + { label: "Neuseeland (Auckland)", id: "Pacific/Auckland" } ]; // Populate timezone dropdowns @@ -618,17 +618,22 @@ container.innerHTML = ''; majorTimezones.forEach(tz => { - // Create a timezone card const card = document.createElement('div'); 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 = `

${tz.label}

-
${dateInTimezone.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
-
${dateInTimezone.toLocaleDateString()}
-
${getTimezoneOffsetString(dateInTimezone, tz.id)}
+
${timeStr}
+
${dateStr}
+
${offsetStr}
`; container.appendChild(card); @@ -637,66 +642,119 @@ // Calculate and show timezone results document.getElementById('calculate-btn').addEventListener('click', () => { - const dateStr = dateInput.value; - const timeStr = timeInput.value; - const sourceTimezone = sourceTimezoneSelect.value; - const targetTimezone = targetTimezoneSelect.value; + const dateStr = dateInput.value; // "YYYY-MM-DD" + const timeStr = timeInput.value; // "HH:mm" + const sourceTimezoneId = sourceTimezoneSelect.value; + const targetTimezoneId = targetTimezoneSelect.value; - if (!dateStr || !timeStr) { - alert('Bitte gib ein Datum und eine Zeit ein'); + if (!dateStr || !timeStr || !sourceTimezoneId || !targetTimezoneId) { + alert('Bitte gib ein Datum, eine Zeit und beide Zeitzonen an.'); return; } - // Create date object from inputs - const [year, month, day] = dateStr.split('-').map(Number); - 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); + try { + // --- Korrekte Logik zur Erstellung des Referenz-Zeitpunkts --- - // Zeige nur die Quell- und Zielzeitzone an - displayConversionResult(referenceDate, sourceTimezone, targetTimezone); + // 1. Parse input date/time components + 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 - function displayConversionResult(referenceDate, sourceTimezone, targetTimezone) { + function displayConversionResult(referenceDate, sourceTimezoneId, targetTimezoneId) { const container = document.getElementById('timezone-result'); container.innerHTML = ''; - const sourceTz = majorTimezones.find(tz => tz.id === sourceTimezone); - const targetTz = majorTimezones.find(tz => tz.id === targetTimezone); + const sourceTzData = majorTimezones.find(tz => tz.id === sourceTimezoneId); + const targetTzData = majorTimezones.find(tz => tz.id === targetTimezoneId); - if (!sourceTz || !targetTz) return; - - // Quell-Zeitzone Karte - const sourceCard = document.createElement('div'); - sourceCard.className = 'timezone-card'; - const dateInSourceTimezone = getDateInTimezone(referenceDate, sourceTimezone); - - sourceCard.innerHTML = ` -

${sourceTz.label}

-
${dateInSourceTimezone.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
-
${dateInSourceTimezone.toLocaleDateString()}
-
${getTimezoneOffsetString(dateInSourceTimezone, sourceTimezone)}
- `; - - // Ziel-Zeitzone Karte - const targetCard = document.createElement('div'); - targetCard.className = 'timezone-card'; - const dateInTargetTimezone = getDateInTimezone(referenceDate, targetTimezone); - - targetCard.innerHTML = ` -

${targetTz.label}

-
${dateInTargetTimezone.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
-
${dateInTargetTimezone.toLocaleDateString()}
-
${getTimezoneOffsetString(dateInTargetTimezone, targetTimezone)}
- `; + if (!sourceTzData || !targetTzData) { + console.error("Source or target timezone data not found in majorTimezones list."); + return; + } + // --- 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 = ` +

${timezoneData.label}

+
${timeStr}
+
${dateStr}
+
${offsetStr}
+ `; + return card; + } + + // Erstelle Quell- und Ziel-Karten + const sourceCard = createResultCard(sourceTzData, referenceDate); + const targetCard = createResultCard(targetTzData, referenceDate); + container.appendChild(sourceCard); // Pfeil zwischen den Karten @@ -708,78 +766,41 @@ 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) function getTimezoneOffsetString(date, timezone) { - if (timezone) { - // Ermittle den tatsächlichen Offset für das Datum in der Zeitzone - const testDate = new Date(date); // Wir erstellen eine Kopie - - // Formatiere das Datum in der angegebenen Zeitzone und hole den Offset + try { + // Nutze Intl.DateTimeFormat, um den Offset für das spezifische Datum und die Zeitzone zu erhalten const formatter = new Intl.DateTimeFormat('en-US', { timeZone: timezone, - timeZoneName: 'longOffset' + timeZoneName: 'longOffset' // Gibt z.B. "GMT+01:00" zurück }); - // Extrahiere den GMT-Offset-String aus der formatierten Ausgabe - const formattedDate = formatter.format(testDate); - const offsetMatch = formattedDate.match(/GMT([-+]\d+)/); + // Formatiere das Datum, um den Offset-String zu extrahieren + const formattedParts = formatter.formatToParts(date); + const offsetPart = formattedParts.find(part => part.type === 'timeZoneName'); - if (offsetMatch) { - return `GMT${offsetMatch[1]}`; - } - - // Fallback: Verwende den statischen Offset aus majorTimezones - const tz = majorTimezones.find(t => t.id === timezone); - if (tz) { - const sign = tz.offset >= 0 ? '+' : '-'; - return `GMT${sign}${Math.abs(tz.offset)}`; + if (offsetPart) { + // Extrahiere den numerischen Teil (z.B. +1, -8) + const match = offsetPart.value.match(/GMT([-+]\d{1,2}(?::\d{2})?)/); + if (match && match[1]) { + // Vereinfache zu GMT+X oder GMT-X + const numericOffset = parseInt(match[1].split(':')[0], 10); + const sign = numericOffset >= 0 ? '+' : '-'; + return `GMT${sign}${Math.abs(numericOffset)}`; + } + 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 - function getTimezoneOffsetHours(timezone) { - const tz = majorTimezones.find(t => t.id === timezone); - return tz ? tz.offset : 0; + // Sicherer Fallback, falls alles fehlschlägt + return 'GMT?'; } // Generate initial timezone cards