A web UI to assist in setting GMT watches.
  • HTML 54.3%
  • TypeScript 45.4%
  • Dockerfile 0.3%
Find a file
Joerg Ziefle ad86247e53 feat: add watch UI with three timezone controls
Implement SVG watch dial interface with timezone selectors.
- SVG viewBox 220x220 with center at (110, 110)
- 24-hour bezel ring at radius 108 with hour markers
- Inner dial at radius 93.5 with 12-hour markers
- White hands: hour, minute, second (analog movement)
- Orange GMT hand (24-hour scale)
- Three dropdowns: local time, 1st timezone, 2nd timezone (bezel)
- Digital time displays with city/timezone labels
- Watch emoji favicon to avoid 404
- Comprehensive timezone coverage across all continents

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-05 17:12:59 +01:00
src feat: implement three-timezone watch logic 2026-01-05 17:12:51 +01:00
.dockerignore feat: add Docker configuration 2026-01-05 17:12:25 +01:00
.gitignore feat: initial project setup 2026-01-05 17:12:18 +01:00
CLAUDE.md feat: initial project setup 2026-01-05 17:12:18 +01:00
docker-compose.yml feat: add Docker configuration 2026-01-05 17:12:25 +01:00
Dockerfile feat: add Docker configuration 2026-01-05 17:12:25 +01:00
index.html feat: add watch UI with three timezone controls 2026-01-05 17:12:59 +01:00
package-lock.json feat: add watch UI with three timezone controls 2026-01-05 17:12:59 +01:00
package.json feat: initial project setup 2026-01-05 17:12:18 +01:00
README.md feat: initial project setup 2026-01-05 17:12:18 +01:00
tsconfig.json feat: initial project setup 2026-01-05 17:12:18 +01:00
vite.config.ts feat: configure Vite dev server 2026-01-05 17:12:30 +01:00

GMT Watch Setting Assistant

A browser-based tool for visualizing and setting GMT watches. Shows local time on standard hands, a 24-hour GMT hand for a selected timezone, and a rotatable 24-hour bezel for a second timezone.

What is a GMT Watch?

A GMT watch displays time in multiple timezones simultaneously using three elements:

  1. Standard hands (hour/minute/second) - Display local time on a 12-hour dial
  2. 24-hour GMT hand - An additional hand that completes one rotation per 24 hours, reading against the 24-hour bezel
  3. Rotatable 24-hour bezel - A ring with markers from 0-24 that can be rotated to track a second timezone

The GMT hand reads against the bezel markers to show a second timezone. For example, if your GMT hand shows your home time and you rotate the bezel to align with a destination timezone offset, you can read the destination time by looking at where the GMT hand points on the bezel.

This tool simulates a Tudor Black Bay GMT style watch with discrete (ticking) second hand motion matching quartz watch behavior.

Usage

# Install dependencies
npm install

# Start development server
npm run dev

# Build for production
npm run build

Open the dev server in your browser. Select timezones from the dropdowns:

  • GMT Hand Timezone: Sets the timezone for the 24-hour GMT hand
  • Bezel Timezone: Rotates the bezel to show offset from your local time

The GMT hand reads against the bezel markers to display the second timezone.

Architecture

index.html
├── <svg id="watch-dial">
│   ├── <g id="bezel"> ─────────────── Rotates for TZ2 offset
│   │   └── 24-hour markers (0-24)
│   ├── <g id="dial-face">
│   │   └── 12-hour markers + indices
│   └── <g id="hands">
│       ├── <line id="hour-hand">
│       ├── <line id="minute-hand">
│       ├── <line id="second-hand">
│       └── <line id="gmt-hand"> ───── 24-hour hand for TZ1
├── <select id="tz1-select"> ────────── GMT hand timezone
└── <select id="tz2-select"> ────────── Bezel timezone

src/
├── main.ts ─────────── Entry point, DOM setup, update loop
├── time.ts ─────────── Time calculations, timezone conversion
└── dial.ts ─────────── SVG manipulation, rotation functions

Data Flow

setInterval(1000ms)
       │
       ▼
  Date.now()
       │
       ├──► toLocalTime() ──► hour/min/sec angles ──► CSS rotate(hour-hand, etc)
       │
       ├──► toTimezone(tz1) ──► 24-hour angle ──► CSS rotate(gmt-hand)
       │
       └──► getOffset(local, tz2) ──► bezel rotation ──► CSS rotate(bezel)

Design Decisions

Why This Structure

  • time.ts isolated for testability - Pure functions, no DOM dependencies
  • dial.ts handles all SVG mutations - Single place for transform logic
  • main.ts orchestrates - Owns the setInterval, coordinates updates
  • No state management needed - Time is derived fresh each tick from Date.now()

Invariants

  • Hour hand completes 360° in 12 hours (30°/hour, 0.5°/minute for continuous movement)
  • Minute hand completes 360° in 60 minutes (6°/minute)
  • Second hand completes 360° in 60 seconds (6°/second)
  • GMT hand completes 360° in 24 hours (15°/hour)
  • Bezel rotation = offset between local timezone and TZ2 (15° per hour of offset)

Tradeoffs

  • Discrete vs smooth second hand: Chose discrete (tick) to match quartz watch behavior and reduce update frequency to 1Hz
  • Inline SVG vs external file: Inline allows simpler DOM queries but makes HTML longer
  • Two separate TZ selectors vs unified UI: Separate is clearer - each control maps to one watch element (GMT hand or bezel)
  • Timezone offset via absolute time difference: Handles DST correctly by calculating actual wall-clock times in both zones rather than naive hour subtraction, which fails when one zone has shifted and the other hasn't