Skip to content

Instantly share code, notes, and snippets.

@simonw
Created December 11, 2024 02:43
Show Gist options
  • Save simonw/3cb034fa3d327f1114da18219f2192fd to your computer and use it in GitHub Desktop.
Save simonw/3cb034fa3d327f1114da18219f2192fd to your computer and use it in GitHub Desktop.

Timezone Meeting Planner with Persistent URL

human (Dec 9, 2024, 10:21 AM)

A timezone meeting planner - it lets you select two timezones using a select datalist against Intl.supportedValuesOf('timeZone') and as soon as you have selected the two shows you a timezone comparison view that looks a bit like this

It persists the selected timezones in the #fragment URL

assistant (Dec 9, 2024, 10:21 AM)

I'll help you create a timezone meeting planner with vanilla JavaScript and HTML. I'll use modern browser APIs and maintain the design pattern you've shown.

create Timezone Meeting Planner

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Timezone Meeting Planner</title>
  <style>
  * {
    box-sizing: border-box;
  }

  body {
    font-family: Helvetica, Arial, sans-serif;
    margin: 0;
    padding: 20px;
    line-height: 1.4;
  }

  .timezone-selectors {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;
    margin-bottom: 30px;
  }

  .timezone-input {
    font-size: 16px;
    width: 100%;
    padding: 8px;
    border: 1px solid #ccc;
    border-radius: 4px;
  }

  .comparison-table {
    width: 100%;
    border-collapse: collapse;
  }

  .comparison-table th {
    text-align: left;
    padding: 12px;
    background: #f0f0f0;
    border: 1px solid #ddd;
  }

  .comparison-table td {
    padding: 12px;
    border: 1px solid #ddd;
  }

  .comparison-table tr:nth-child(even) {
    background: #f8f8f8;
  }

  .timezone-label {
    color: #4a5568;
    font-size: 1.2em;
    margin-bottom: 8px;
    font-weight: bold;
  }
  </style>
</head>
<body>
  <div class="timezone-selectors">
    <div>
      <div class="timezone-label">First Timezone</div>
      <input 
        type="text" 
        id="timezone1" 
        class="timezone-input" 
        list="timezone-list" 
        placeholder="Select timezone...">
    </div>
    <div>
      <div class="timezone-label">Second Timezone</div>
      <input 
        type="text" 
        id="timezone2" 
        class="timezone-input" 
        list="timezone-list" 
        placeholder="Select timezone...">
    </div>
  </div>

  <datalist id="timezone-list"></datalist>

  <table class="comparison-table" id="comparison-table">
    <thead>
      <tr>
        <th>UTC-time</th>
        <th id="tz1-header">First Location</th>
        <th id="tz2-header">Second Location</th>
      </tr>
    </thead>
    <tbody id="comparison-body">
    </tbody>
  </table>

<script type="module">
const timezones = Intl.supportedValuesOf('timeZone')
const tzDatalist = document.getElementById('timezone-list')
const tz1Input = document.getElementById('timezone1')
const tz2Input = document.getElementById('timezone2')
const tz1Header = document.getElementById('tz1-header')
const tz2Header = document.getElementById('tz2-header')
const comparisonBody = document.getElementById('comparison-body')

// Populate datalist with all supported timezones
timezones.forEach(tz => {
  const option = document.createElement('option')
  option.value = tz
  tzDatalist.appendChild(option)
})

function updateURL(tz1, tz2) {
  const hash = `#${encodeURIComponent(tz1)},${encodeURIComponent(tz2)}`
  window.history.replaceState(null, '', hash)
}

function loadFromURL() {
  const hash = window.location.hash.slice(1)
  if (hash) {
    const [tz1, tz2] = hash.split(',').map(decodeURIComponent)
    if (timezones.includes(tz1) && timezones.includes(tz2)) {
      tz1Input.value = tz1
      tz2Input.value = tz2
      updateComparison()
    }
  }
}

function formatTime(date, timezone) {
  return new Intl.DateTimeFormat('en-US', {
    hour: 'numeric',
    minute: 'numeric',
    hour12: true,
    timeZone: timezone,
    weekday: 'short'
  }).format(date)
}

function updateComparison() {
  const tz1 = tz1Input.value
  const tz2 = tz2Input.value
  
  if (!timezones.includes(tz1) || !timezones.includes(tz2)) return
  
  tz1Header.textContent = tz1.split('/').pop().replace(/_/g, ' ')
  tz2Header.textContent = tz2.split('/').pop().replace(/_/g, ' ')
  
  updateURL(tz1, tz2)
  
  // Clear existing rows
  comparisonBody.innerHTML = ''
  
  // Create 24 hourly comparisons
  const now = new Date()
  now.setMinutes(0, 0, 0)
  
  for (let i = 0; i < 24; i++) {
    const row = document.createElement('tr')
    
    const utcCell = document.createElement('td')
    utcCell.textContent = now.toISOString().replace('T', ' at ').slice(0, -5)
    
    const tz1Cell = document.createElement('td')
    tz1Cell.textContent = formatTime(now, tz1)
    
    const tz2Cell = document.createElement('td')
    tz2Cell.textContent = formatTime(now, tz2)
    
    row.appendChild(utcCell)
    row.appendChild(tz1Cell)
    row.appendChild(tz2Cell)
    comparisonBody.appendChild(row)
    
    now.setHours(now.getHours() + 1)
  }
}

tz1Input.addEventListener('change', updateComparison)
tz2Input.addEventListener('change', updateComparison)

// Load initial state from URL
loadFromURL()
</script>
</body>
</html>

I've created a timezone meeting planner with these key features:

  1. Timezone selection using datalist populated with Intl.supportedValuesOf('timeZone')
  2. URL persistence using the hash fragment
  3. 24-hour comparison view showing times in both selected zones
  4. Clean, responsive design with proper font sizing and styling
  5. Proper error handling for invalid timezones

The interface automatically updates when you select timezones and preserves your selections in the URL. The comparison table shows a full 24-hour view with proper formatting for both timezones.

Try selecting two different timezones from the dropdown lists. The URL will update automatically, and you can share it with others to show the same comparison.

Would you like me to add any additional features or make any adjustments to the styling?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment