Feature/add timezone converter tool - #432
Merged
shamilahmdt merged 5 commits intoJul 28, 2026
Merged
Conversation
Implements the Timezone Converter tool for issue shamilahmdt#193 (Add a New Tool to Dev Utilities Sandbox), following the existing Dev Utilities patterns: tool component, sidebar entry, home-grid card, and route registration. - src/pages/DevUtilities/devutilities/TimezoneConverter.jsx (new): Client-side timezone converter and live world clock built entirely on the Intl API (no backend/network calls). Lets a user pick a source timezone and a date/time (or use the live current time), then view that instant rendered across a pinnable list of world clock timezones. Includes a `localToUtcIso` helper that resolves a wall-clock date/time typed in a given timezone to the correct UTC instant via an offset-correction loop (handles DST correctly), and a `formatInZone` helper that renders a date plus its UTC offset for an arbitrary timezone. Falls back to a static list of common timezones when `Intl.supportedValuesOf` is unavailable. Matches the existing light/dark theme conventions used by sibling tools such as TimestampConverter. - src/App.jsx: Imports the new TimezoneConverter component and registers its route at /devutilities/timezone-converter, alongside the existing /devutilities/timestamp route. - src/config/sidebarSections.js: Adds a "Timezone Converter" entry to the Dev Utilities sidebar section so the tool is reachable from the app navigation. - src/pages/DevUtilities/DevUtilities.jsx: Adds a "Timezone Converter" card to the Dev Utilities home grid, with title, description, search keywords, and icon, positioned next to the Timestamp card.
Adds a "Meeting Overlap Finder" section to the Timezone Converter tool
so it can answer the actual scheduling question users have when
comparing timezones: "is there a time that works for everyone?"
- src/pages/DevUtilities/devutilities/TimezoneConverter.jsx:
- New `getLocalHour(date, timeZone)` helper: returns the local
hour-of-day (0-23) for a given instant in an arbitrary IANA
timezone, using Intl.DateTimeFormat with hourCycle "h23".
- New `collapseHourRanges(hours)` helper: takes a set of individually
"true" hour indices and collapses contiguous runs into readable
"HH:00–HH:00" range strings (e.g. turns [14,15,16] into
"14:00–17:00") so overlap results read as ranges instead of a list
of matching hours.
- New `workStart` / `workEnd` state (default 09:00–18:00), with two
dropdowns in the UI to configure the working-hours window that
applies to every pinned zone's own local time.
- New `overlapGrid` memo: builds a 24-hour grid anchored to the UTC
calendar day of the current reference date/time. For each of the
24 UTC hours, it computes each pinned zone's local hour and
whether that hour falls inside [workStart, workEnd) for that zone,
then flags the hour as a full overlap when every pinned zone is
simultaneously in its working window. Recomputes whenever the
reference date, pinned zones, or working-hour bounds change.
- New UI: a horizontal per-zone hour grid (green cells = inside that
zone's working hours, muted cells = outside), an "All zones (UTC)"
summary row that outlines the hours where every zone overlaps, and
a results banner listing the collapsed overlap ranges in UTC (or a
message when no such hour exists). The grid scrolls horizontally
on narrow viewports via `overflow-x-auto`.
Previously the world clock's pinned zone list always reset to the same
5 hardcoded defaults (UTC, America/New_York, Europe/London, Asia/Kolkata,
Asia/Tokyo) on every page load/reload, discarding any zones the user
added or removed. This mirrors the localStorage pattern already used
for favorite utilities in src/pages/DevUtilities/DevUtilities.jsx
(FAVORITES_STORAGE_KEY / lazy useState initializer / mirroring effect)
so pinned zones behave the same way.
- src/pages/DevUtilities/devutilities/TimezoneConverter.jsx:
- Added `PINNED_ZONES_STORAGE_KEY` ("timezone_converter_pinned_zones")
and extracted the previous hardcoded default array into
`DEFAULT_PINNED_ZONES`.
- Added `loadPinnedZones()`, used as the lazy initializer for the
`pinnedZones` state: reads and JSON-parses the saved list from
localStorage, guards against `window` being undefined (SSR/build
safety), and falls back to `DEFAULT_PINNED_ZONES` if the value is
missing, invalid JSON, not an array, empty, or fails to parse
(logging the error rather than throwing).
- Added a `useEffect` that writes `pinnedZones` back to localStorage
as JSON every time the list changes (zone added or removed via
the existing `addZone` / `removeZone` handlers), so the list
survives page reloads and future visits.
Adds a "DST" badge to each pinned zone in the World Clock so users can
tell at a glance which zones are currently offset by daylight saving
time, since that's a common source of scheduling mistakes (a zone's
UTC offset silently shifts twice a year).
- src/pages/DevUtilities/devutilities/TimezoneConverter.jsx:
- Extracted a shared `getOffsetMinutes(date, timeZone)` helper that
returns a zone's UTC offset in minutes (positive = ahead of UTC)
at a given instant, by diffing the zone's wall-clock reading
(via Intl.DateTimeFormat.formatToParts) against the instant's
actual UTC time.
- Refactored `localToUtcIso`'s offset-correction loop to use
`getOffsetMinutes` instead of duplicating the same
formatToParts/Date.UTC logic inline; behavior is unchanged
(verified by re-deriving the same wall-clock -> UTC instant for
both a UTC+5:30 zone and a zone with a non-integer historical
offset case via DST, e.g. Asia/Kolkata and America/New_York).
- Added `isObservingDst(date, timeZone)`: samples a zone's offset in
January and July of the reference date's year. Since DST always
shifts local clocks forward relative to standard time (regardless
of hemisphere/season labelling), the smaller of the two sampled
offsets is the zone's standard offset; a zone is "in DST" at the
given date when its current offset exceeds that standard offset.
Zones that don't observe DST have identical Jan/Jul samples and
always return false.
- World Clock rows now compute `isObservingDst(referenceDate, zone)`
per pinned zone and render an amber "DST" pill next to the zone
name when true, with a title tooltip explaining what it means.
|
@Vikkesh is attempting to deploy a commit to the shamilahmdt's projects Team on Vercel. A member of the Team first needs to authorize it. |
👋 Hello, @Vikkesh!Thank you for your contribution to DevTasks. The maintainer has been notified and will review your pull request shortly. If any changes are needed, we will let you know in the comments below. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📝 Description
Adds a new Timezone Converter utility to the Dev Utilities Sandbox. It's fully client-side (no backend calls), built entirely on the
IntlAPI, and follows the existing Dev Utilities design conventions (monochrome, light/dark theme, card + sidebar registration).Beyond a basic converter, it also includes:
localStorage(same pattern as the existing favorite-utilities feature) so it survives page reloads instead of resetting to defaults.Files changed
src/pages/DevUtilities/devutilities/TimezoneConverter.jsx(new) — the tool component: conversion logic (localToUtcIso,getOffsetMinutes), DST detection (isObservingDst), the overlap-finder grid (overlapGrid,collapseHourRanges), and localStorage persistence for pinned zones.src/App.jsx— imports the component and registers the route/devutilities/timezone-converter.src/config/sidebarSections.js— adds the sidebar nav entry under Dev Utilities.src/pages/DevUtilities/DevUtilities.jsx— adds the tool's card (title, description, search keywords, icon) to the Dev Utilities home grid.🔗 Related Issue
Closes #193
🎯 Type of Change
fix: Bug fixfeat: New featurestyle: UI/Theme changes (Monochrome)chore: Build/Maintenance✅ Checklist
📸 Screenshots (if applicable)
| Before | After |



| | |