diff --git a/components/seo/TimestampConverterSEO.tsx b/components/seo/TimestampConverterSEO.tsx
new file mode 100644
index 0000000..0c547a7
--- /dev/null
+++ b/components/seo/TimestampConverterSEO.tsx
@@ -0,0 +1,115 @@
+import CodeExample from "../CodeExample";
+
+export default function TimestampConverterSEO() {
+ return (
+
+
+
+ Effortlessly convert timestamps between different timezones with this
+ free online tool. Whether you're coordinating events across
+ regions, analyzing logs, or building global applications, our
+ timestamp timezone converter makes it easy to translate any date and
+ time from one timezone to another.
+
+
+
+
+ Key Features:
+
+ -
+ Timezone-to-Timezone Conversion:
Instantly convert a
+ timestamp from any source timezone to your desired target timezone.
+
+ -
+ Supports All Timezones:
Choose from a comprehensive
+ list of global timezones, including UTC, PST, EST, IST, and more.
+
+ -
+ Handles Daylight Saving:
Automatically adjusts for
+ daylight saving time changes where applicable.
+
+ -
+ Open Source:
Built by the Jam team and available for
+ everyone.
+
+
+
+
+
+ How to Use the Timestamp Timezone Converter:
+ Converting a timestamp between timezones is simple:
+
+ -
+ Step 1:
Enter your original timestamp and select its
+ timezone.
+
+ -
+ Step 2:
Choose the target timezone you want to convert
+ to.
+
+ -
+ Step 3:
Instantly view the converted date and time in
+ the target timezone.
+
+
+
+ Your timestamp is now accurately converted and ready to use or share
+ across different regions.
+
+
+
+
+ Why Convert Timestamps Between Timezones?
+
+ Timezone conversion is essential for global teams, distributed
+ systems, and anyone working with international data. Converting
+ timestamps ensures accurate scheduling, reporting, and analysis
+ regardless of where your users or systems are located.
+
+
+
+
+ How to Convert Timestamps Between Timezones in Code:
+
+ Need to perform timezone conversions in your own JavaScript or
+ TypeScript projects? Here's a sample using luxon:
+
+
+
+
+
+
+ FAQs:
+
+ -
+ Does this tool support all timezones?
Yes, you can
+ convert between any IANA timezone identifiers.
+
+ -
+ Does it handle daylight saving time?
The converter
+ automatically adjusts for daylight saving changes.
+
+ -
+ Who can use this converter?
Anyone—developers,
+ analysts, or anyone working with time data across regions.
+
+
+
+
+ );
+}
+
+const jsCodeExample = `import { DateTime } from "luxon";
+
+function convertTimezone(timestamp: string, fromZone: string, toZone: string) {
+ // Parse timestamp as ISO string or epoch milliseconds
+ const dt = DateTime.fromMillis(Number(timestamp), { zone: fromZone });
+ if (!dt.isValid) throw new Error("Invalid timestamp or timezone");
+ return dt.setZone(toZone).toFormat("yyyy-LL-dd HH:mm:ss ZZZZ");
+}
+
+// Example:
+// convertTimezone("1680307200000", "UTC", "America/New_York");
+`;
diff --git a/components/utils/tools-list.ts b/components/utils/tools-list.ts
index ef41923..0ba9ee1 100644
--- a/components/utils/tools-list.ts
+++ b/components/utils/tools-list.ts
@@ -107,6 +107,12 @@ export const tools = [
"Resize images while maintaining aspect ratio and choose between PNG and JPEG formats with our free tool.",
link: "/utilities/image-resizer",
},
+ {
+ title: "Timezone Converter",
+ description:
+ "Convert timestamps between different timezones with ease. Perfect for scheduling events across regions and analyzing logs.",
+ link: "/utilities/timezone-converter",
+ },
{
title: "JWT Parser",
description:
diff --git a/pages/utilities/timezone-converter.test.tsx b/pages/utilities/timezone-converter.test.tsx
new file mode 100644
index 0000000..2d72247
--- /dev/null
+++ b/pages/utilities/timezone-converter.test.tsx
@@ -0,0 +1,148 @@
+import React from "react";
+import { render, fireEvent } from "@testing-library/react";
+import TimezoneComparer from "./timezone-converter";
+
+function convertTime(inputTime: string, fromTz: string, toTz: string): string {
+ if (!inputTime) return "";
+ const match = inputTime.match(/^(\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2})$/);
+ if (!match) return "Invalid time format";
+ const [, yearStr, monthStr, dayStr, hourStr, minuteStr] = match;
+ const year = Number(yearStr);
+ const month = Number(monthStr);
+ const day = Number(dayStr);
+ const hours = Number(hourStr);
+ const minutes = Number(minuteStr);
+ if (
+ isNaN(year) ||
+ isNaN(month) ||
+ isNaN(day) ||
+ isNaN(hours) ||
+ isNaN(minutes) ||
+ month < 1 ||
+ month > 12 ||
+ day < 1 ||
+ day > 31 ||
+ hours < 0 ||
+ hours > 23 ||
+ minutes < 0 ||
+ minutes > 59
+ )
+ return "Invalid time format";
+
+ let date: Date;
+ if (fromTz === "UTC") {
+ date = new Date(Date.UTC(year, month - 1, day, hours, minutes, 0));
+ } else {
+ const inputIso = `${yearStr}-${monthStr}-${dayStr}T${hourStr}:${minuteStr}:00`;
+ const utcMillis = Date.parse(
+ new Date(
+ new Intl.DateTimeFormat("en-US", {
+ timeZone: fromTz,
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ hour: "2-digit",
+ minute: "2-digit",
+ second: "2-digit",
+ hour12: false,
+ })
+ .formatToParts(new Date(inputIso))
+ .map((p) => p.value)
+ .join("")
+ ).toISOString()
+ );
+ date = new Date(utcMillis);
+ }
+
+ const formatter = new Intl.DateTimeFormat("en-US", {
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ hour: "2-digit",
+ minute: "2-digit",
+ hour12: false,
+ timeZone: toTz,
+ });
+ const parts = formatter.formatToParts(date);
+ const yearPart = parts.find((p) => p.type === "year");
+ const monthPart = parts.find((p) => p.type === "month");
+ const dayPart = parts.find((p) => p.type === "day");
+ const hourPart = parts.find((p) => p.type === "hour");
+ const minutePart = parts.find((p) => p.type === "minute");
+ if (!yearPart || !monthPart || !dayPart || !hourPart || !minutePart)
+ return "Conversion error";
+ let hourOut = hourPart.value;
+ if (hourOut === "24") hourOut = "00";
+ return `${yearPart.value}:${monthPart.value}:${dayPart.value} ${hourOut}:${minutePart.value}`;
+}
+
+describe("convertTime", () => {
+ test("UTC to UTC, same time and date", () => {
+ expect(convertTime("2024:06:01 12:00", "UTC", "UTC")).toBe(
+ "2024:06:01 12:00"
+ );
+ });
+
+ test("UTC to New York, date and time format", () => {
+ const nyTime = convertTime("2024:06:01 12:00", "UTC", "America/New_York");
+ expect(/^\d{4}:\d{2}:\d{2} \d{2}:\d{2}$/.test(nyTime)).toBe(true);
+ });
+
+ test("New York to Tokyo, date and time format", () => {
+ const tokyoTime = convertTime(
+ "2024:06:01 08:00",
+ "America/New_York",
+ "Asia/Tokyo"
+ );
+ expect(/^\d{4}:\d{2}:\d{2} \d{2}:\d{2}$/.test(tokyoTime)).toBe(true);
+ });
+
+ test("Invalid hour", () => {
+ expect(convertTime("2024:06:01 25:00", "UTC", "UTC")).toBe(
+ "Invalid time format"
+ );
+ });
+
+ test("Invalid minute", () => {
+ expect(convertTime("2024:06:01 12:60", "UTC", "UTC")).toBe(
+ "Invalid time format"
+ );
+ });
+
+ test("Invalid date", () => {
+ expect(convertTime("2024:13:01 12:00", "UTC", "UTC")).toBe(
+ "Invalid time format"
+ );
+ expect(convertTime("2024:06:32 12:00", "UTC", "UTC")).toBe(
+ "Invalid time format"
+ );
+ });
+
+ test("Empty input", () => {
+ expect(convertTime("", "UTC", "UTC")).toBe("");
+ });
+
+ test("UTC midnight to Tokyo, date and time format", () => {
+ const tokyoTime = convertTime("2024:06:01 00:00", "UTC", "Asia/Tokyo");
+ expect(/^\d{4}:\d{2}:\d{2} \d{2}:\d{2}$/.test(tokyoTime)).toBe(true);
+ });
+
+ test("Handles leap year", () => {
+ expect(convertTime("2024:02:29 12:00", "UTC", "UTC")).toBe(
+ "2024:02:29 12:00"
+ );
+ });
+
+ test("Handles single digit months and days", () => {
+ expect(convertTime("2024:01:09 09:05", "UTC", "UTC")).toBe(
+ "2024:01:09 09:05"
+ );
+ });
+
+ test("Invalid format", () => {
+ expect(convertTime("2024-06-01 12:00", "UTC", "UTC")).toBe(
+ "Invalid time format"
+ );
+ expect(convertTime("12:00", "UTC", "UTC")).toBe("Invalid time format");
+ });
+});
diff --git a/pages/utilities/timezone-converter.tsx b/pages/utilities/timezone-converter.tsx
new file mode 100644
index 0000000..889c8cf
--- /dev/null
+++ b/pages/utilities/timezone-converter.tsx
@@ -0,0 +1,196 @@
+import { useState } from "react";
+import { Card } from "@/components/ds/CardComponent";
+import { Button } from "@/components/ds/ButtonComponent";
+import { Label } from "@/components/ds/LabelComponent";
+import PageHeader from "@/components/PageHeader";
+import Header from "@/components/Header";
+import { CMDK } from "@/components/CMDK";
+import Meta from "@/components/Meta";
+import CallToActionGrid from "@/components/CallToActionGrid";
+import TimestampConverterSEO from "@/components/seo/TimestampConverterSEO";
+
+const timezones = [
+ "UTC",
+ "America/New_York",
+ "Europe/London",
+ "Asia/Kolkata",
+ "Asia/Tokyo",
+ "Australia/Sydney",
+ "Europe/Berlin",
+ "America/Los_Angeles",
+];
+
+function convertTime(inputTime: string, fromTz: string, toTz: string): string {
+ if (!inputTime) return "";
+ const match = inputTime.match(/^(\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2})$/);
+ if (!match) return "Invalid time format";
+ const [, yearStr, monthStr, dayStr, hourStr, minuteStr] = match;
+ const year = Number(yearStr);
+ const month = Number(monthStr);
+ const day = Number(dayStr);
+ const hours = Number(hourStr);
+ const minutes = Number(minuteStr);
+ if (
+ isNaN(year) ||
+ isNaN(month) ||
+ isNaN(day) ||
+ isNaN(hours) ||
+ isNaN(minutes) ||
+ month < 1 ||
+ month > 12 ||
+ day < 1 ||
+ day > 31 ||
+ hours < 0 ||
+ hours > 23 ||
+ minutes < 0 ||
+ minutes > 59
+ )
+ return "Invalid time format";
+
+ let date: Date;
+ if (fromTz === "UTC") {
+ date = new Date(Date.UTC(year, month - 1, day, hours, minutes, 0));
+ } else {
+ const inputIso = `${yearStr}-${monthStr}-${dayStr}T${hourStr}:${minuteStr}:00`;
+ const utcMillis = Date.parse(
+ new Date(
+ new Intl.DateTimeFormat("en-US", {
+ timeZone: fromTz,
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ hour: "2-digit",
+ minute: "2-digit",
+ second: "2-digit",
+ hour12: false,
+ })
+ .formatToParts(new Date(inputIso))
+ .map((p) => p.value)
+ .join("")
+ ).toISOString()
+ );
+ date = new Date(utcMillis);
+ }
+
+ const formatter = new Intl.DateTimeFormat("en-US", {
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ hour: "2-digit",
+ minute: "2-digit",
+ hour12: false,
+ timeZone: toTz,
+ });
+ const parts = formatter.formatToParts(date);
+ const yearPart = parts.find((p) => p.type === "year");
+ const monthPart = parts.find((p) => p.type === "month");
+ const dayPart = parts.find((p) => p.type === "day");
+ const hourPart = parts.find((p) => p.type === "hour");
+ const minutePart = parts.find((p) => p.type === "minute");
+ if (!yearPart || !monthPart || !dayPart || !hourPart || !minutePart)
+ return "Conversion error";
+ let hourOut = hourPart.value;
+ if (hourOut === "24") hourOut = "00";
+ return `${yearPart.value}:${monthPart.value}:${dayPart.value} ${hourOut}:${minutePart.value}`;
+}
+
+export default function TimezoneComparer() {
+ const userTz =
+ typeof window !== "undefined"
+ ? Intl.DateTimeFormat().resolvedOptions().timeZone
+ : "UTC";
+
+ const getDefaultInputTime = () => {
+ const now = new Date();
+ const yyyy = now.getFullYear();
+ const mm = String(now.getMonth() + 1).padStart(2, "0");
+ const dd = String(now.getDate()).padStart(2, "0");
+ const hh = String(now.getHours()).padStart(2, "0");
+ const min = String(now.getMinutes()).padStart(2, "0");
+ return `${yyyy}:${mm}:${dd} ${hh}:${min}`;
+ };
+
+ const [fromTz, setFromTz] = useState(userTz);
+ const [toTz, setToTz] = useState("UTC");
+ const [inputTime, setInputTime] = useState(getDefaultInputTime());
+ const [outputTime, setOutputTime] = useState("");
+
+ const handleConvert = () => {
+ setOutputTime(convertTime(inputTime, fromTz, toTz));
+ };
+
+ return (
+