diff --git a/components/seo/TimezoneConverterSEO.tsx b/components/seo/TimezoneConverterSEO.tsx
new file mode 100644
index 0000000..1cdf586
--- /dev/null
+++ b/components/seo/TimezoneConverterSEO.tsx
@@ -0,0 +1,127 @@
+import CodeExample from "../CodeExample";
+
+export default function TimezoneConverterSEO() {
+ return (
+
+
+
+ Easily convert times between different timezones with this free tool.
+ Perfect for remote software teams who need to schedule syncs,
+ meetings, and standups across multiple timezones. No more mental math
+ or timezone confusion.
+
+
+
+
+ Features:
+
+ -
+ Instant Conversion:
Select a time and see it converted
+ across all major timezones instantly.
+
+ -
+ Common Timezones:
Pre-configured with the most common
+ timezones for distributed teams including US, Europe, and Asia.
+
+ -
+ Open Source:
Made with love by the developers building
+ Jam.
+
+
+
+
+
+ How to use the Timezone Converter:
+
+ -
+ Step 1:
Select your source timezone and enter the time
+ you want to convert.
+
+ -
+ Step 2:
View the converted times across all selected
+ timezones.
+
+ -
+ Step 3:
Toggle timezones on/off to customize your
+ view.
+
+
+
+
+
+ Why Use a Timezone Converter?
+
+ Remote teams often span multiple continents and timezones. Scheduling
+ meetings that work for everyone can be challenging. A timezone
+ converter helps you:
+
+
+ -
+ Schedule Meetings:
Find times that work across
+ different regions without manual calculations.
+
+ -
+ Avoid Confusion:
Eliminate timezone math errors that
+ can lead to missed meetings.
+
+ -
+ Plan Ahead:
See how a proposed meeting time affects
+ team members in different locations.
+
+
+
+
+
+ Convert Timezones in JavaScript:
+
+ If you need to convert timezones programmatically in your own
+ applications, here is a code snippet using the Intl API:
+
+
+
+
+
+
+ FAQs:
+
+ -
+ Does this tool handle Daylight Saving Time?
Yes, the
+ converter automatically accounts for DST changes in each timezone.
+
+ -
+ What timezones are supported?
We support all major
+ timezones including US (PT, MT, CT, ET), Europe (GMT, CET), and Asia
+ (IST, SGT, JST).
+
+ -
+ Can I use this for scheduling recurring meetings?
Yes,
+ simply select the date and time for your meeting and see how it
+ translates across timezones.
+
+
+
+
+ );
+}
+
+const jsCodeExample = `function convertTimezone(date: Date, fromTz: string, toTz: string): string {
+ const formatter = new Intl.DateTimeFormat('en-US', {
+ timeZone: toTz,
+ hour: '2-digit',
+ minute: '2-digit',
+ hour12: true,
+ weekday: 'short',
+ month: 'short',
+ day: 'numeric',
+ });
+
+ return formatter.format(date);
+}
+
+// Example usage:
+const nyTime = new Date('2024-01-15T09:00:00');
+console.log(convertTimezone(nyTime, 'America/New_York', 'Europe/London'));
+// Output: "Mon, Jan 15, 02:00 PM"
+`;
diff --git a/components/utils/timezone-converter.utils.ts b/components/utils/timezone-converter.utils.ts
new file mode 100644
index 0000000..f5fef77
--- /dev/null
+++ b/components/utils/timezone-converter.utils.ts
@@ -0,0 +1,158 @@
+export interface TimezoneInfo {
+ id: string;
+ label: string;
+ offset: string;
+}
+
+export const commonTimezones: TimezoneInfo[] = [
+ { id: "America/Los_Angeles", label: "Los Angeles (PT)", offset: "" },
+ { id: "America/Denver", label: "Denver (MT)", offset: "" },
+ { id: "America/Chicago", label: "Chicago (CT)", offset: "" },
+ { id: "America/New_York", label: "New York (ET)", offset: "" },
+ { id: "America/Sao_Paulo", label: "Sao Paulo (BRT)", offset: "" },
+ { id: "Europe/London", label: "London (GMT/BST)", offset: "" },
+ { id: "Europe/Paris", label: "Paris (CET)", offset: "" },
+ { id: "Europe/Berlin", label: "Berlin (CET)", offset: "" },
+ { id: "Asia/Dubai", label: "Dubai (GST)", offset: "" },
+ { id: "Asia/Kolkata", label: "India (IST)", offset: "" },
+ { id: "Asia/Singapore", label: "Singapore (SGT)", offset: "" },
+ { id: "Asia/Tokyo", label: "Tokyo (JST)", offset: "" },
+ { id: "Australia/Sydney", label: "Sydney (AEST)", offset: "" },
+];
+
+export function getTimezoneOffset(timezone: string, date: Date): string {
+ const formatter = new Intl.DateTimeFormat("en-US", {
+ timeZone: timezone,
+ timeZoneName: "shortOffset",
+ });
+ const parts = formatter.formatToParts(date);
+ const offsetPart = parts.find((part) => part.type === "timeZoneName");
+ return offsetPart?.value || "";
+}
+
+export function convertTime(
+ sourceTime: string,
+ sourceDate: string,
+ sourceTimezone: string,
+ targetTimezones: string[]
+): { timezone: string; label: string; time: string; date: string }[] {
+ if (!sourceTime || !sourceDate) {
+ return [];
+ }
+
+ const [hours, minutes] = sourceTime.split(":").map(Number);
+ const [year, month, day] = sourceDate.split("-").map(Number);
+
+ const sourceFormatter = new Intl.DateTimeFormat("en-US", {
+ timeZone: sourceTimezone,
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ hour: "2-digit",
+ minute: "2-digit",
+ hour12: false,
+ });
+
+ const tempDate = new Date(year, month - 1, day, hours, minutes);
+ const sourceParts = sourceFormatter.formatToParts(tempDate);
+
+ const getPartValue = (
+ parts: Intl.DateTimeFormatPart[],
+ type: Intl.DateTimeFormatPartTypes
+ ) => parts.find((p) => p.type === type)?.value || "";
+
+ const sourceYear = parseInt(getPartValue(sourceParts, "year"));
+ const sourceMonth = parseInt(getPartValue(sourceParts, "month"));
+ const sourceDay = parseInt(getPartValue(sourceParts, "day"));
+ const sourceHour = parseInt(getPartValue(sourceParts, "hour"));
+ const sourceMinute = parseInt(getPartValue(sourceParts, "minute"));
+
+ const utcDate = new Date(
+ Date.UTC(sourceYear, sourceMonth - 1, sourceDay, sourceHour, sourceMinute)
+ );
+
+ const sourceOffset = getTimezoneOffsetMinutes(sourceTimezone, utcDate);
+ utcDate.setMinutes(utcDate.getMinutes() - sourceOffset);
+
+ return targetTimezones.map((tz) => {
+ const tzInfo = commonTimezones.find((t) => t.id === tz);
+ const formatter = new Intl.DateTimeFormat("en-US", {
+ timeZone: tz,
+ hour: "2-digit",
+ minute: "2-digit",
+ hour12: true,
+ weekday: "short",
+ month: "short",
+ day: "numeric",
+ });
+
+ const formatted = formatter.format(utcDate);
+ const parts = formatted.split(", ");
+ const weekday = parts[0];
+ const datePart = parts[1];
+ const timePart = parts[2];
+
+ return {
+ timezone: tz,
+ label: tzInfo?.label || tz,
+ time: timePart,
+ date: `${weekday}, ${datePart}`,
+ };
+ });
+}
+
+function getTimezoneOffsetMinutes(timezone: string, date: Date): number {
+ const utcDate = new Date(date.toLocaleString("en-US", { timeZone: "UTC" }));
+ const tzDate = new Date(date.toLocaleString("en-US", { timeZone: timezone }));
+ return (tzDate.getTime() - utcDate.getTime()) / 60000;
+}
+
+export function getCurrentTimeInTimezone(timezone: string): {
+ time: string;
+ date: string;
+} {
+ const now = new Date();
+ const formatter = new Intl.DateTimeFormat("en-US", {
+ timeZone: timezone,
+ hour: "2-digit",
+ minute: "2-digit",
+ hour12: true,
+ weekday: "short",
+ month: "short",
+ day: "numeric",
+ });
+
+ const formatted = formatter.format(now);
+ const parts = formatted.split(", ");
+ const weekday = parts[0];
+ const datePart = parts[1];
+ const timePart = parts[2];
+
+ return {
+ time: timePart,
+ date: `${weekday}, ${datePart}`,
+ };
+}
+
+export function formatTimeForInput(date: Date, timezone: string): string {
+ const formatter = new Intl.DateTimeFormat("en-US", {
+ timeZone: timezone,
+ hour: "2-digit",
+ minute: "2-digit",
+ hour12: false,
+ });
+ const parts = formatter.formatToParts(date);
+ const hour = parts.find((p) => p.type === "hour")?.value || "00";
+ const minute = parts.find((p) => p.type === "minute")?.value || "00";
+ return `${hour}:${minute}`;
+}
+
+export function formatDateForInput(date: Date, timezone: string): string {
+ const formatter = new Intl.DateTimeFormat("en-CA", {
+ timeZone: timezone,
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ });
+ return formatter.format(date);
+}
diff --git a/components/utils/tools-list.ts b/components/utils/tools-list.ts
index e4c3ada..1ad74d0 100644
--- a/components/utils/tools-list.ts
+++ b/components/utils/tools-list.ts
@@ -161,4 +161,10 @@ export const tools = [
"Generate cryptographically secure random strings with configurable character sets. Perfect for API keys, tokens, passwords, and secure identifiers.",
link: "/utilities/random-string-generator",
},
+ {
+ title: "Timezone Converter",
+ description:
+ "Convert times between timezones for remote team scheduling. Perfect for distributed software teams coordinating meetings across multiple timezones.",
+ link: "/utilities/timezone-converter",
+ },
];
diff --git a/pages/utilities/timezone-converter.tsx b/pages/utilities/timezone-converter.tsx
new file mode 100644
index 0000000..c670c16
--- /dev/null
+++ b/pages/utilities/timezone-converter.tsx
@@ -0,0 +1,250 @@
+import { useCallback, useState, useEffect } from "react";
+import { useRouter } from "next/router";
+import PageHeader from "@/components/PageHeader";
+import { Card } from "@/components/ds/CardComponent";
+import { Button } from "@/components/ds/ButtonComponent";
+import { Label } from "@/components/ds/LabelComponent";
+import { Input } from "@/components/ds/InputComponent";
+import { Checkbox } from "@/components/ds/CheckboxComponent";
+import Header from "@/components/Header";
+import { CMDK } from "@/components/CMDK";
+import { useCopyToClipboard } from "@/components/hooks/useCopyToClipboard";
+import TimezoneConverterSEO from "@/components/seo/TimezoneConverterSEO";
+import CallToActionGrid from "@/components/CallToActionGrid";
+import Meta from "@/components/Meta";
+import {
+ commonTimezones,
+ convertTime,
+ formatTimeForInput,
+ formatDateForInput,
+} from "@/components/utils/timezone-converter.utils";
+
+function getQueryParam(
+ value: string | string[] | undefined
+): string | undefined {
+ return Array.isArray(value) ? value[0] : value;
+}
+
+export default function TimezoneConverter() {
+ const router = useRouter();
+ const [sourceTimezone, setSourceTimezone] = useState("America/New_York");
+ const [sourceTime, setSourceTime] = useState("");
+ const [sourceDate, setSourceDate] = useState("");
+ const [selectedTimezones, setSelectedTimezones] = useState