| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049 |
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Parv's Time Zone Converter</title>
- <link rel="icon" type="image/x-icon" href="favicon.ico">
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
- <style>
- :root {
- --primary-color: #4a6fa5;
- --secondary-color: #6c757d;
- --success-color: #28a745;
- --warning-color: #ffc107;
- --danger-color: #dc3545;
- --bg-color: #f8f9fa;
- --card-bg: #ffffff;
- --text-color: #212529;
- --border-color: #dee2e6;
- --shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
- --hover-color: #e9ecef;
- --slider-height: 6px;
- --slider-thumb-size: 18px;
- }
- .dark-mode {
- --primary-color: #6b8cbe;
- --secondary-color: #a0a0a0;
- --success-color: #3bd264;
- --warning-color: #ffd54f;
- --danger-color: #e57373;
- --bg-color: #121212;
- --card-bg: #1e1e1e;
- --text-color: #f0f0f0;
- --border-color: #444444;
- --shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
- --hover-color: #2d2d2d;
- --slider-height: 8px;
- --slider-thumb-size: 22px;
- }
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- transition: background-color 0.3s, color 0.3s;
- }
- body {
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
- background-color: var(--bg-color);
- color: var(--text-color);
- line-height: 1.6;
- padding: 20px;
- }
- .container {
- max-width: 1400px;
- margin: 0 auto;
- }
- header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 20px;
- padding-bottom: 10px;
- border-bottom: 1px solid var(--border-color);
- flex-wrap: wrap;
- gap: 15px;
- }
- h1 {
- font-size: 2rem;
- color: var(--primary-color);
- }
- .controls {
- display: flex;
- gap: 15px;
- flex-wrap: wrap;
- }
- button {
- background-color: var(--primary-color);
- color: white;
- border: none;
- padding: 8px 15px;
- border-radius: 4px;
- cursor: pointer;
- font-size: 0.9rem;
- display: flex;
- align-items: center;
- gap: 5px;
- }
- button:hover {
- opacity: 0.9;
- }
- .mode-toggle {
- background-color: var(--secondary-color);
- }
- .mode-toggle.active {
- background-color: var(--success-color);
- }
- .timezone-grid {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
- gap: 20px;
- margin-bottom: 30px;
- }
- .timezone-card {
- background-color: var(--card-bg);
- border-radius: 8px;
- padding: 20px;
- box-shadow: var(--shadow);
- border: 1px solid var(--border-color);
- position: relative;
- transition: transform 0.2s;
- }
- .timezone-card:hover {
- transform: translateY(-5px);
- }
- .timezone-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 15px;
- }
- .timezone-name {
- font-weight: bold;
- font-size: 1.2rem;
- color: var(--primary-color);
- }
- .time-container {
- display: flex;
- align-items: center;
- gap: 10px;
- margin-bottom: 10px;
- }
- .time-display {
- font-size: 2rem;
- font-weight: bold;
- }
- .edit-time-btn {
- background: none;
- border: none;
- color: var(--secondary-color);
- cursor: pointer;
- font-size: 1.2rem;
- padding: 5px;
- border-radius: 4px;
- transition: all 0.2s;
- }
- .edit-time-btn:hover {
- background-color: var(--hover-color);
- color: var(--primary-color);
- }
- .time-input-container {
- display: none;
- margin-bottom: 15px;
- }
- .time-input {
- padding: 8px 12px;
- border: 1px solid var(--border-color);
- border-radius: 4px;
- width: 100%;
- font-size: 1rem;
- margin-bottom: 8px;
- }
- .input-actions {
- display: flex;
- gap: 8px;
- }
- .input-btn {
- flex: 1;
- padding: 6px;
- font-size: 0.85rem;
- }
- .date-display {
- color: var(--secondary-color);
- margin-bottom: 5px;
- }
- .gmt-offset {
- font-size: 0.9rem;
- color: var(--secondary-color);
- margin-bottom: 5px;
- }
- .dst-note {
- font-size: 0.85rem;
- color: var(--success-color);
- font-style: italic;
- margin-top: 10px;
- }
- .delete-btn {
- position: absolute;
- top: 10px;
- right: 10px;
- background: none;
- border: none;
- color: var(--secondary-color);
- cursor: pointer;
- font-size: 1.2rem;
- }
- .delete-btn:hover {
- color: var(--danger-color);
- }
- .slider-container {
- margin: 20px 0;
- position: relative;
- }
- .slider-marks {
- display: flex;
- justify-content: space-between;
- padding: 0 10px;
- margin-top: 5px;
- font-size: 0.85rem;
- color: var(--secondary-color);
- }
- .slider {
- width: 100%;
- height: var(--slider-height);
- margin: 15px 0;
- -webkit-appearance: none;
- appearance: none;
- background: linear-gradient(to right, #4a6fa5 0%, #6c757d 100%);
- outline: none;
- border-radius: 3px;
- position: relative;
- }
- .slider::-webkit-slider-thumb {
- -webkit-appearance: none;
- appearance: none;
- width: var(--slider-thumb-size);
- height: var(--slider-thumb-size);
- border-radius: 50%;
- background: var(--primary-color);
- cursor: pointer;
- box-shadow: 0 0 10px rgba(0,0,0,0.3);
- transition: all 0.2s;
- }
- .slider::-webkit-slider-thumb:hover {
- transform: scale(1.2);
- background: var(--success-color);
- }
- .slider-info {
- display: flex;
- justify-content: space-between;
- font-size: 0.85rem;
- color: var(--secondary-color);
- margin-top: 5px;
- }
- .slider-value {
- font-weight: bold;
- color: var(--primary-color);
- }
- .add-timezone {
- margin-top: 30px;
- padding: 20px;
- background-color: var(--card-bg);
- border-radius: 8px;
- box-shadow: var(--shadow);
- }
- .add-timezone h2 {
- margin-bottom: 15px;
- color: var(--primary-color);
- }
- .form-group {
- margin-bottom: 15px;
- }
- label {
- display: block;
- margin-bottom: 5px;
- font-weight: bold;
- }
- select, input {
- width: 100%;
- padding: 10px;
- border: 1px solid var(--border-color);
- border-radius: 4px;
- background-color: var(--card-bg);
- color: var(--text-color);
- }
- .search-box {
- position: relative;
- }
- .search-results {
- position: absolute;
- top: 100%;
- left: 0;
- right: 0;
- background-color: var(--card-bg);
- border: 1px solid var(--border-color);
- border-top: none;
- border-radius: 0 0 4px 4px;
- max-height: 200px;
- overflow-y: auto;
- z-index: 100;
- display: none;
- }
- .search-result-item {
- padding: 10px;
- cursor: pointer;
- }
- .search-result-item:hover {
- background-color: var(--hover-color);
- }
- footer {
- margin-top: 40px;
- text-align: center;
- padding: 20px;
- border-top: 1px solid var(--border-color);
- color: var(--secondary-color);
- }
- .time-zone-examples {
- display: flex;
- gap: 15px;
- margin-top: 15px;
- flex-wrap: wrap;
- }
- .time-zone-example {
- background: var(--hover-color);
- padding: 8px 12px;
- border-radius: 4px;
- font-size: 0.9rem;
- cursor: pointer;
- transition: all 0.2s;
- }
- .time-zone-example:hover {
- background: var(--primary-color);
- color: white;
- }
- @media (max-width: 768px) {
- .timezone-grid {
- grid-template-columns: 1fr;
- }
-
- header {
- flex-direction: column;
- align-items: flex-start;
- }
-
- .controls {
- width: 100%;
- justify-content: center;
- }
- }
- </style>
- </head>
- <body>
- <div class="container">
- <header>
- <h1>Parv's Time Zone Converter</h1>
- <div class="controls">
- <button id="theme-toggle">
- <i class="fas fa-moon"></i> Toggle Dark Mode
- </button>
- <button id="refresh-btn">
- <i class="fas fa-sync-alt"></i> Refresh Times
- </button>
- <button id="real-time-mode" class="mode-toggle active">
- <i class="fas fa-clock"></i> Real-Time Mode
- </button>
- <button id="converter-mode" class="mode-toggle">
- <i class="fas fa-sliders-h"></i> Converter Mode
- </button>
- </div>
- </header>
- <div class="timezone-grid" id="timezone-container">
- <!-- Timezone cards will be generated here -->
- </div>
- <div class="add-timezone">
- <h2>Add Time Zone</h2>
- <div class="form-group">
- <label for="timezone-search">Search Time Zones:</label>
- <div class="search-box">
- <input type="text" id="timezone-search" placeholder="Start typing to search time zones...">
- <div class="search-results" id="search-results"></div>
- </div>
- <div class="time-zone-examples">
- <div class="time-zone-example">America/New_York</div>
- <div class="time-zone-example">Asia/Kolkata</div>
- <div class="time-zone-example">Europe/London</div>
- <div class="time-zone-example">Australia/Sydney</div>
- </div>
- </div>
- <button id="add-timezone-btn">
- <i class="fas fa-plus"></i> Add Selected Time Zone
- </button>
- </div>
- <footer>
- <p>Made by Parv Ashwani © 2025 | All Major Time Zones Supported</p>
- </footer>
- </div>
- <script>
- document.addEventListener('DOMContentLoaded', function() {
- // DOM elements
- const themeToggle = document.getElementById('theme-toggle');
- const refreshBtn = document.getElementById('refresh-btn');
- const realTimeModeBtn = document.getElementById('real-time-mode');
- const converterModeBtn = document.getElementById('converter-mode');
- const timezoneContainer = document.getElementById('timezone-container');
- const timezoneSearch = document.getElementById('timezone-search');
- const searchResults = document.getElementById('search-results');
- const addTimezoneBtn = document.getElementById('add-timezone-btn');
-
- // Application state
- let isRealTimeMode = true;
- let allTimeZones = [];
- let selectedTimezone = null;
- let baseTime = new Date();
- let updateInterval = null;
-
- // Default time zones
- const defaultTimeZones = [
- { id: 'America/New_York', name: 'EDT/EST', label: 'Eastern Time' },
- { id: 'Asia/Kolkata', name: 'IST', label: 'India Standard Time' },
- { id: 'America/Chicago', name: 'CDT/CST', label: 'Central Time' },
- { id: 'America/Los_Angeles', name: 'PDT/PST', label: 'Pacific Time' }
- ];
-
- // User-added time zones
- let customTimeZones = JSON.parse(localStorage.getItem('customTimeZones')) || [];
-
- // Initialize the app
- function init() {
- loadAllTimeZones();
- renderAllTimeZones();
- startRealTimeUpdates();
-
- // Set up event listeners
- setupEventListeners();
-
- // Add example time zone handlers
- document.querySelectorAll('.time-zone-example').forEach(example => {
- example.addEventListener('click', function() {
- timezoneSearch.value = this.textContent;
- handleTimezoneSearch();
- });
- });
- }
-
- // Load all available time zones
- function loadAllTimeZones() {
- try {
- allTimeZones = Intl.supportedValuesOf('timeZone').map(tz => {
- return {
- id: tz,
- name: tz.split('/').pop().replace(/_/g, ' '),
- label: tz
- };
- });
- } catch (e) {
- console.error("Could not load time zones:", e);
- // Fallback to a subset of time zones
- allTimeZones = [
- { id: 'America/New_York', name: 'New York', label: 'Eastern Time' },
- { id: 'Europe/London', name: 'London', label: 'Greenwich Mean Time' },
- { id: 'Asia/Tokyo', name: 'Tokyo', label: 'Japan Standard Time' },
- { id: 'Australia/Sydney', name: 'Sydney', label: 'Australian Eastern Time' },
- { id: 'Europe/Paris', name: 'Paris', label: 'Central European Time' },
- { id: 'Asia/Dubai', name: 'Dubai', label: 'Gulf Standard Time' },
- { id: 'America/Los_Angeles', name: 'Los Angeles', label: 'Pacific Time' },
- { id: 'America/Chicago', name: 'Chicago', label: 'Central Time' },
- { id: 'Asia/Kolkata', name: 'Kolkata', label: 'India Standard Time' },
- { id: 'Asia/Shanghai', name: 'Shanghai', label: 'China Standard Time' }
- ];
- }
-
- // Sort time zones by name
- allTimeZones.sort((a, b) => a.name.localeCompare(b.name));
- }
-
- // Set up event listeners
- function setupEventListeners() {
- // Theme toggle
- themeToggle.addEventListener('click', toggleTheme);
-
- // Refresh button
- refreshBtn.addEventListener('click', refreshAllTimes);
-
- // Mode toggles
- realTimeModeBtn.addEventListener('click', () => setMode(true));
- converterModeBtn.addEventListener('click', () => setMode(false));
-
- // Time zone search
- timezoneSearch.addEventListener('input', handleTimezoneSearch);
-
- // Add time zone button
- addTimezoneBtn.addEventListener('click', addSelectedTimezone);
- }
-
- // Toggle between dark and light mode
- function toggleTheme() {
- document.body.classList.toggle('dark-mode');
- localStorage.setItem('theme', document.body.classList.contains('dark-mode') ? 'dark' : 'light');
-
- // Update icon
- const icon = themeToggle.querySelector('i');
- if (document.body.classList.contains('dark-mode')) {
- icon.className = 'fas fa-sun';
- themeToggle.innerHTML = '<i class="fas fa-sun"></i> Toggle Light Mode';
- } else {
- icon.className = 'fas fa-moon';
- themeToggle.innerHTML = '<i class="fas fa-moon"></i> Toggle Dark Mode';
- }
- }
-
- // Set the application mode (real-time or converter)
- function setMode(isRealTime) {
- isRealTimeMode = isRealTime;
-
- // Update UI
- if (isRealTimeMode) {
- realTimeModeBtn.classList.add('active');
- converterModeBtn.classList.remove('active');
- startRealTimeUpdates();
- } else {
- realTimeModeBtn.classList.remove('active');
- converterModeBtn.classList.add('active');
- stopRealTimeUpdates();
-
- // Set base time to current time when switching to converter mode
- baseTime = new Date();
- updateAllTimes();
- }
-
- // Update sliders
- updateAllSliders();
- }
-
- // Start real-time updates
- function startRealTimeUpdates() {
- if (updateInterval) clearInterval(updateInterval);
- updateInterval = setInterval(updateAllTimes, 1000);
- }
-
- // Stop real-time updates
- function stopRealTimeUpdates() {
- if (updateInterval) {
- clearInterval(updateInterval);
- updateInterval = null;
- }
- }
-
- // Refresh all times
- function refreshAllTimes() {
- baseTime = new Date();
- updateAllTimes();
- updateAllSliders();
- }
-
- // Render all time zone cards
- function renderAllTimeZones() {
- timezoneContainer.innerHTML = '';
-
- // Add default time zones
- defaultTimeZones.forEach(tz => {
- timezoneContainer.appendChild(createTimeZoneCard(tz));
- });
-
- // Add custom time zones
- customTimeZones.forEach(tz => {
- timezoneContainer.appendChild(createTimeZoneCard(tz));
- });
-
- updateAllTimes();
- updateAllSliders();
- }
-
- // Create a time zone card
- function createTimeZoneCard(timezone) {
- const card = document.createElement('div');
- card.className = 'timezone-card';
- card.dataset.timezone = timezone.id;
-
- const isDefault = defaultTimeZones.some(tz => tz.id === timezone.id);
-
- card.innerHTML = `
- ${!isDefault ? '<button class="delete-btn"><i class="fas fa-times"></i></button>' : ''}
- <div class="timezone-header">
- <div class="timezone-name">${timezone.name}</div>
- <div class="timezone-label">${timezone.label}</div>
- </div>
- <div class="date-display">Loading date...</div>
- <div class="time-container">
- <div class="time-display">Loading time...</div>
- <button class="edit-time-btn" title="Edit time"><i class="fas fa-edit"></i></button>
- </div>
- <div class="gmt-offset">Loading GMT offset...</div>
- <div class="dst-note"></div>
- <div class="time-input-container">
- <input type="text" class="time-input" placeholder="e.g., 6:26 am" pattern="\\d{1,2}:\\d{2}\\s*[ap]m" required>
- <div class="input-actions">
- <button class="save-time-btn input-btn">Save</button>
- <button class="cancel-time-btn input-btn">Cancel</button>
- </div>
- </div>
- <div class="slider-container">
- <div class="slider-marks">
- <span>12am</span>
- <span>3am</span>
- <span>6am</span>
- <span>9am</span>
- <span>12pm</span>
- <span>3pm</span>
- <span>6pm</span>
- <span>9pm</span>
- </div>
- <input type="range" class="slider" min="0" max="1440" value="720">
- <div class="slider-info">
- <span>12:00 AM</span>
- <span class="slider-value">12:00 PM</span>
- <span>11:59 PM</span>
- </div>
- </div>
- `;
-
- // Add delete functionality for custom time zones
- const deleteBtn = card.querySelector('.delete-btn');
- if (deleteBtn) {
- deleteBtn.addEventListener('click', function() {
- removeTimeZone(timezone.id);
- });
- }
-
- // Add time editing functionality
- const editBtn = card.querySelector('.edit-time-btn');
- const timeContainer = card.querySelector('.time-container');
- const timeInputContainer = card.querySelector('.time-input-container');
- const timeInput = card.querySelector('.time-input');
- const saveBtn = card.querySelector('.save-time-btn');
- const cancelBtn = card.querySelector('.cancel-time-btn');
-
- editBtn.addEventListener('click', function() {
- timeContainer.style.display = 'none';
- timeInputContainer.style.display = 'block';
- timeInput.value = card.querySelector('.time-display').textContent.trim();
- });
-
- cancelBtn.addEventListener('click', function() {
- timeContainer.style.display = 'flex';
- timeInputContainer.style.display = 'none';
- });
-
- saveBtn.addEventListener('click', function() {
- const timeStr = timeInput.value;
- const timeObj = parseTimeInput(timeStr);
-
- if (!timeObj) {
- alert('Invalid time format. Please use HH:MM AM/PM (e.g., 6:26 am)');
- return;
- }
-
- const enteredTotalMinutes = timeObj.hours * 60 + timeObj.minutes;
-
- // Get current time in this time zone
- const timezoneId = card.dataset.timezone;
- const currentTzTimeStr = baseTime.toLocaleTimeString('en-US', {
- timeZone: timezoneId,
- hour: '2-digit',
- minute: '2-digit',
- hour12: false
- });
-
- const [currentHours, currentMinutes] = currentTzTimeStr.split(':').map(Number);
- const currentTotalMinutes = currentHours * 60 + currentMinutes;
-
- // Calculate the difference in minutes
- const diffMinutes = enteredTotalMinutes - currentTotalMinutes;
-
- // Adjust baseTime
- baseTime = new Date(baseTime.getTime() + diffMinutes * 60000);
-
- // Update all times and sliders
- updateAllTimes();
- updateAllSliders();
-
- // Revert to display mode
- timeContainer.style.display = 'flex';
- timeInputContainer.style.display = 'none';
- });
-
- // Add slider functionality
- const slider = card.querySelector('.slider');
- const sliderValue = card.querySelector('.slider-value');
-
- slider.addEventListener('input', function() {
- const totalMinutes = parseInt(this.value);
- const hours = Math.floor(totalMinutes / 60);
- const minutes = totalMinutes % 60;
-
- // Update slider value display
- const timeStr = formatTimeForSlider(hours, minutes);
- sliderValue.textContent = timeStr;
-
- if (!isRealTimeMode) {
- adjustTimeWithSlider(timezone.id, totalMinutes);
- }
- });
-
- return card;
- }
-
- // Parse time input in HH:MM AM/PM format
- function parseTimeInput(str) {
- const match = str.match(/(\d{1,2}):(\d{2})\s*([ap]m)/i);
- if (!match) return null;
-
- let hours = parseInt(match[1]);
- const minutes = parseInt(match[2]);
- const period = match[3].toLowerCase();
-
- if (period === 'pm' && hours < 12) hours += 12;
- if (period === 'am' && hours === 12) hours = 0;
-
- return { hours, minutes };
- }
-
- // Format time for slider display
- function formatTimeForSlider(hours, minutes) {
- const period = hours >= 12 ? 'PM' : 'AM';
- const displayHours = hours % 12 || 12;
- return `${displayHours}:${minutes.toString().padStart(2, '0')} ${period}`;
- }
-
- // Update all time displays
- function updateAllTimes() {
- const cards = timezoneContainer.querySelectorAll('.timezone-card');
- const now = isRealTimeMode ? new Date() : baseTime;
-
- cards.forEach(card => {
- const timezone = card.dataset.timezone;
-
- try {
- // Format time
- const timeStr = now.toLocaleTimeString('en-US', {
- timeZone: timezone,
- hour: '2-digit',
- minute: '2-digit',
- hour12: true
- });
-
- // Format date
- const dateStr = now.toLocaleDateString('en-US', {
- timeZone: timezone,
- weekday: 'short',
- month: 'short',
- day: 'numeric',
- year: 'numeric'
- });
-
- // Get GMT offset
- const offset = getGMTOffset(now, timezone);
-
- // Update DOM
- card.querySelector('.time-display').textContent = timeStr;
- card.querySelector('.date-display').textContent = dateStr;
- card.querySelector('.gmt-offset').textContent = offset;
-
- // Check for DST
- const isDST = isDaylightSavingTime(now, timezone);
- const dstNote = card.querySelector('.dst-note');
-
- if (isDST !== null) {
- if (isDST) {
- dstNote.textContent = `Daylight Saving Time observed (${getDSTName(timezone)})`;
- } else {
- dstNote.textContent = 'Standard Time observed';
- }
- } else {
- dstNote.textContent = '';
- }
- } catch (e) {
- console.error(`Error updating time for ${timezone}:`, e);
- }
- });
- }
-
- // Update all sliders based on current time
- function updateAllSliders() {
- const cards = timezoneContainer.querySelectorAll('.timezone-card');
- const now = isRealTimeMode ? new Date() : baseTime;
-
- cards.forEach(card => {
- const timezone = card.dataset.timezone;
-
- try {
- const timeStr = now.toLocaleTimeString('en-US', {
- timeZone: timezone,
- hour: '2-digit',
- hour12: false,
- minute: '2-digit'
- });
-
- const [hours, minutes] = timeStr.split(':').map(Number);
- const totalMinutes = hours * 60 + minutes;
-
- const slider = card.querySelector('.slider');
- const sliderValue = card.querySelector('.slider-value');
-
- slider.value = totalMinutes;
- sliderValue.textContent = formatTimeForSlider(hours, minutes);
- } catch (e) {
- console.error(`Error updating slider for ${timezone}:`, e);
- }
- });
- }
-
- // Get GMT offset string
- function getGMTOffset(date, timezone) {
- try {
- const formatter = new Intl.DateTimeFormat('en-US', {
- timeZone: timezone,
- timeZoneName: 'shortOffset'
- });
-
- const parts = formatter.formatToParts(date);
- const offset = parts.find(part => part.type === 'timeZoneName')?.value || '';
- return offset.replace('GMT', 'GMT');
- } catch (e) {
- return "Unknown offset";
- }
- }
-
- // Check if daylight saving time is observed
- function isDaylightSavingTime(date, timezone) {
- try {
- // Try to get timezone name which often indicates DST
- const formatter = new Intl.DateTimeFormat('en-US', {
- timeZone: timezone,
- timeZoneName: 'long'
- });
-
- const tzName = formatter.resolvedOptions().timeZoneName;
- if (!tzName) return null;
-
- // Check if the timezone name suggests DST
- return formatter.format(date).includes("Daylight") ||
- tzName.includes("Daylight") ||
- /[+-]\d{2}:\d{2}/.test(tzName);
- } catch (e) {
- console.error(`Error checking DST for ${timezone}:`, e);
- return null;
- }
- }
-
- // Get DST name for timezone
- function getDSTName(timezone) {
- if (timezone.includes('New_York')) return 'EDT';
- if (timezone.includes('Chicago')) return 'CDT';
- if (timezone.includes('Los_Angeles')) return 'PDT';
- if (timezone.includes('London') || timezone.includes('Europe')) return 'BST';
- return 'DST';
- }
-
- // Handle timezone search
- function handleTimezoneSearch() {
- const query = timezoneSearch.value.toLowerCase();
-
- if (query.length < 2) {
- searchResults.style.display = 'none';
- return;
- }
-
- const results = allTimeZones.filter(tz =>
- tz.id.toLowerCase().includes(query) ||
- tz.name.toLowerCase().includes(query) ||
- tz.label.toLowerCase().includes(query)
- ).slice(0, 10); // Limit to 10 results
-
- displaySearchResults(results);
- }
-
- // Display search results
- function displaySearchResults(results) {
- searchResults.innerHTML = '';
-
- if (results.length === 0) {
- searchResults.innerHTML = '<div class="search-result-item">No results found</div>';
- } else {
- results.forEach(tz => {
- const item = document.createElement('div');
- item.className = 'search-result-item';
- item.textContent = `${tz.name} (${tz.id})`;
- item.dataset.timezone = tz.id;
-
- item.addEventListener('click', () => {
- selectedTimezone = tz;
- timezoneSearch.value = `${tz.name} (${tz.id})`;
- searchResults.style.display = 'none';
- });
-
- searchResults.appendChild(item);
- });
- }
-
- searchResults.style.display = 'block';
- }
-
- // Add selected time zone
- function addSelectedTimezone() {
- if (!selectedTimezone) {
- alert('Please select a time zone from the search results.');
- return;
- }
-
- // Check if already added
- if ([...defaultTimeZones, ...customTimeZones].some(tz => tz.id === selectedTimezone.id)) {
- alert('This time zone is already added.');
- return;
- }
-
- customTimeZones.push(selectedTimezone);
- localStorage.setItem('customTimeZones', JSON.stringify(customTimeZones));
-
- timezoneContainer.appendChild(createTimeZoneCard(selectedTimezone));
- updateAllTimes();
- updateAllSliders();
-
- // Reset selection
- selectedTimezone = null;
- timezoneSearch.value = '';
- }
-
- // Remove a time zone
- function removeTimeZone(timezoneId) {
- customTimeZones = customTimeZones.filter(tz => tz.id !== timezoneId);
- localStorage.setItem('customTimeZones', JSON.stringify(customTimeZones));
- renderAllTimeZones();
- }
-
- // Adjust time with slider
- function adjustTimeWithSlider(timezoneId, minutes) {
- const cards = timezoneContainer.querySelectorAll('.timezone-card');
- const adjustedCard = Array.from(cards).find(card => card.dataset.timezone === timezoneId);
-
- if (!adjustedCard) return;
-
- // Calculate the new base time based on slider adjustment
- const now = new Date();
- const adjustedTime = new Date(now);
-
- // Get the current time in the adjusted timezone
- const currentHours = now.toLocaleTimeString('en-US', {
- timeZone: timezoneId,
- hour: '2-digit',
- hour12: false
- });
-
- const currentMinutes = now.toLocaleTimeString('en-US', {
- timeZone: timezoneId,
- minute: '2-digit'
- });
-
- const currentTotalMinutes = parseInt(currentHours) * 60 + parseInt(currentMinutes);
- const minuteDifference = minutes - currentTotalMinutes;
-
- // Adjust the base time
- baseTime = new Date(now.getTime() + minuteDifference * 60000);
-
- // Update all times
- updateAllTimes();
-
- // Update all sliders except the one being adjusted
- cards.forEach(card => {
- if (card.dataset.timezone !== timezoneId) {
- const tz = card.dataset.timezone;
-
- try {
- const timeStr = baseTime.toLocaleTimeString('en-US', {
- timeZone: tz,
- hour: '2-digit',
- hour12: false,
- minute: '2-digit'
- });
-
- const [hours, minutes] = timeStr.split(':').map(Number);
- const totalMinutes = hours * 60 + minutes;
-
- const slider = card.querySelector('.slider');
- const sliderValue = card.querySelector('.slider-value');
-
- slider.value = totalMinutes;
- sliderValue.textContent = formatTimeForSlider(hours, minutes);
- } catch (e) {
- console.error(`Error updating slider for ${tz}:`, e);
- }
- }
- });
- }
-
- // Initialize the app
- init();
-
- // Load saved theme
- if (localStorage.getItem('theme') === 'dark') {
- document.body.classList.add('dark-mode');
- themeToggle.innerHTML = '<i class="fas fa-sun"></i> Toggle Light Mode';
- }
- });
- </script>
- </body>
- </html>
|