From ec1b8ac8acc64f232fb7f31cacc84996cc67645e Mon Sep 17 00:00:00 2001 From: Rahul Keshervani Date: Wed, 6 Aug 2025 19:10:18 +0530 Subject: [PATCH 1/7] timezone converter feature --- components/seo/TimestampConverterSEO.tsx | 98 +++++++++++++ components/utils/tools-list.ts | 6 + pages/utilities/timezone-converter.tsx | 168 +++++++++++++++++++++++ 3 files changed, 272 insertions(+) create mode 100644 components/seo/TimestampConverterSEO.tsx create mode 100644 pages/utilities/timezone-converter.tsx diff --git a/components/seo/TimestampConverterSEO.tsx b/components/seo/TimestampConverterSEO.tsx new file mode 100644 index 0000000..afe337b --- /dev/null +++ b/components/seo/TimestampConverterSEO.tsx @@ -0,0 +1,98 @@ +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: +

+
+ +
+ {jsCodeExample} +
+ +
+

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..26caf8f 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-comparer", + }, { title: "JWT Parser", description: diff --git a/pages/utilities/timezone-converter.tsx b/pages/utilities/timezone-converter.tsx new file mode 100644 index 0000000..c6c5fe6 --- /dev/null +++ b/pages/utilities/timezone-converter.tsx @@ -0,0 +1,168 @@ +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"; + +// Minimal timezone list for demo; replace with a full list or use a package for production +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 ""; + // Parse input as "HH:mm" + const [hours, minutes] = inputTime.split(":").map(Number); + if ( + isNaN(hours) || + isNaN(minutes) || + hours < 0 || + hours > 23 || + minutes < 0 || + minutes > 59 + ) + return "Invalid time format"; + + // Today's date in fromTz + const now = new Date(); + // Create a date in fromTz at the given time + const dateInFromTz = new Date( + Date.UTC( + now.getUTCFullYear(), + now.getUTCMonth(), + now.getUTCDate(), + hours, + minutes + ) + ); + // Offset between fromTz and UTC at this date + const fromOffset = -( + new Date( + dateInFromTz.toLocaleString("en-US", { timeZone: fromTz }) + ).getTimezoneOffset() + ); + // Offset between toTz and UTC at this date + const toOffset = -( + new Date( + dateInFromTz.toLocaleString("en-US", { timeZone: toTz }) + ).getTimezoneOffset() + ); + // Calculate the difference in minutes + const diffMinutes = toOffset - fromOffset; + // Adjust the date + const dateInToTz = new Date(dateInFromTz.getTime() + diffMinutes * 60000); + + // Format output in toTz + return dateInToTz + .toLocaleTimeString("en-US", { + hour: "2-digit", + minute: "2-digit", + hour12: false, + timeZone: toTz, + }) + .replace(/^24:/, "00:"); // handle midnight +} + +export default function TimezoneComparer() { + const userTz = + typeof window !== "undefined" + ? Intl.DateTimeFormat().resolvedOptions().timeZone + : "UTC"; + const [fromTz, setFromTz] = useState(userTz); + const [toTz, setToTz] = useState("UTC"); + const [inputTime, setInputTime] = useState(""); + const [outputTime, setOutputTime] = useState(""); + + const handleConvert = () => { + setOutputTime(convertTime(inputTime, fromTz, toTz)); + }; + + return ( +
+ +
+ + +
+ +
+ +
+ +
+ + + + + + setInputTime(e.target.value)} + /> + +
+
+ + +
+
+
+
+ +
+ + +
+ ); +} From 46cf68bd0aa8d59c7423072dfef43e34c62b8e04 Mon Sep 17 00:00:00 2001 From: Rahul Keshervani Date: Wed, 6 Aug 2025 19:12:16 +0530 Subject: [PATCH 2/7] timezone url change --- components/utils/tools-list.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/utils/tools-list.ts b/components/utils/tools-list.ts index 26caf8f..0ba9ee1 100644 --- a/components/utils/tools-list.ts +++ b/components/utils/tools-list.ts @@ -111,7 +111,7 @@ export const tools = [ title: "Timezone Converter", description: "Convert timestamps between different timezones with ease. Perfect for scheduling events across regions and analyzing logs.", - link: "/utilities/timezone-comparer", + link: "/utilities/timezone-converter", }, { title: "JWT Parser", From 1b937eac82645cd93d37775980ad7efd19fdf23c Mon Sep 17 00:00:00 2001 From: rahul keshervani <30744073+rahulkeshervani@users.noreply.github.com> Date: Wed, 6 Aug 2025 20:44:31 +0530 Subject: [PATCH 3/7] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pages/utilities/timezone-converter.tsx | 64 +++++++++++--------------- 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/pages/utilities/timezone-converter.tsx b/pages/utilities/timezone-converter.tsx index c6c5fe6..75356e4 100644 --- a/pages/utilities/timezone-converter.tsx +++ b/pages/utilities/timezone-converter.tsx @@ -39,44 +39,32 @@ function convertTime( ) return "Invalid time format"; - // Today's date in fromTz + // Today's date in UTC const now = new Date(); - // Create a date in fromTz at the given time - const dateInFromTz = new Date( - Date.UTC( - now.getUTCFullYear(), - now.getUTCMonth(), - now.getUTCDate(), - hours, - minutes - ) - ); - // Offset between fromTz and UTC at this date - const fromOffset = -( - new Date( - dateInFromTz.toLocaleString("en-US", { timeZone: fromTz }) - ).getTimezoneOffset() - ); - // Offset between toTz and UTC at this date - const toOffset = -( - new Date( - dateInFromTz.toLocaleString("en-US", { timeZone: toTz }) - ).getTimezoneOffset() - ); - // Calculate the difference in minutes - const diffMinutes = toOffset - fromOffset; - // Adjust the date - const dateInToTz = new Date(dateInFromTz.getTime() + diffMinutes * 60000); + // Create a UTC date at the given time + const utcDate = new Date(Date.UTC( + now.getUTCFullYear(), + now.getUTCMonth(), + now.getUTCDate(), + hours, + minutes + )); - // Format output in toTz - return dateInToTz - .toLocaleTimeString("en-US", { - hour: "2-digit", - minute: "2-digit", - hour12: false, - timeZone: toTz, - }) - .replace(/^24:/, "00:"); // handle midnight + // Use Intl.DateTimeFormat to get the time in the target timezone + const formatter = new Intl.DateTimeFormat("en-US", { + hour: "2-digit", + minute: "2-digit", + hour12: false, + timeZone: toTz, + }); + const parts = formatter.formatToParts(utcDate); + const hourPart = parts.find(p => p.type === "hour"); + const minutePart = parts.find(p => p.type === "minute"); + if (!hourPart || !minutePart) return "Conversion error"; + let hourStr = hourPart.value; + // handle midnight (24:xx -> 00:xx) + if (hourStr === "24") hourStr = "00"; + return `${hourStr}:${minutePart.value}`; } export default function TimezoneComparer() { @@ -136,7 +124,7 @@ export default function TimezoneComparer() { ))} - + -
+
From 5833b42bdd5570bf650f5d5b86808d28085829d7 Mon Sep 17 00:00:00 2001 From: Rahul Keshervani Date: Wed, 6 Aug 2025 20:50:56 +0530 Subject: [PATCH 4/7] change to 24 hour --- pages/utilities/timezone-converter.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pages/utilities/timezone-converter.tsx b/pages/utilities/timezone-converter.tsx index 75356e4..9cf5675 100644 --- a/pages/utilities/timezone-converter.tsx +++ b/pages/utilities/timezone-converter.tsx @@ -124,14 +124,16 @@ export default function TimezoneComparer() { ))} - + setInputTime(e.target.value)} - /> - From b214b6bf9a137618777b82f78fffb08dd8ed9d8e Mon Sep 17 00:00:00 2001 From: Rahul Keshervani Date: Thu, 7 Aug 2025 00:11:31 +0530 Subject: [PATCH 5/7] adding tests and using prettier --- components/seo/TimestampConverterSEO.tsx | 51 ++++++--- pages/utilities/timezone-converter.test.tsx | 121 ++++++++++++++++++++ pages/utilities/timezone-converter.tsx | 106 +++++++++++------ 3 files changed, 226 insertions(+), 52 deletions(-) create mode 100644 pages/utilities/timezone-converter.test.tsx diff --git a/components/seo/TimestampConverterSEO.tsx b/components/seo/TimestampConverterSEO.tsx index afe337b..0c547a7 100644 --- a/components/seo/TimestampConverterSEO.tsx +++ b/components/seo/TimestampConverterSEO.tsx @@ -5,7 +5,11 @@ export default function TimestampConverterSEO() {

- 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. + 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.

@@ -13,52 +17,62 @@ export default function TimestampConverterSEO() {

Key Features:

  • - Timezone-to-Timezone Conversion:
    Instantly convert a timestamp from any source timezone to your desired target timezone. + 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. + 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. + Handles Daylight Saving:
    Automatically adjusts for + daylight saving time changes where applicable.
  • - Open Source:
    Built by the Jam team and available for everyone. + 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: -

+

Converting a timestamp between timezones is simple:

  • - Step 1:
    Enter your original timestamp and select its timezone. + Step 1:
    Enter your original timestamp and select its + timezone.
  • - Step 2:
    Choose the target timezone you want to convert to. + Step 2:
    Choose the target timezone you want to convert + to.
  • - Step 3:
    Instantly view the converted date and time in the target timezone. + 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. + 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. + 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: + Need to perform timezone conversions in your own JavaScript or + TypeScript projects? Here's a sample using luxon:

@@ -70,13 +84,16 @@ export default function TimestampConverterSEO() {

FAQs:

diff --git a/pages/utilities/timezone-converter.test.tsx b/pages/utilities/timezone-converter.test.tsx new file mode 100644 index 0000000..50bb60b --- /dev/null +++ b/pages/utilities/timezone-converter.test.tsx @@ -0,0 +1,121 @@ +// Minimal test runner for convertTime + +import React from "react"; +import { render, fireEvent } from "@testing-library/react"; +import TimezoneComparer from "./timezone-converter"; + +// Updated convertTime for new input format and output +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 [, year, month, day, hours, minutes] = match.map(Number); + 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"; + const dateStr = `${year}-${String(month).padStart(2, "0")}-${String(day).padStart( + 2, + "0" + )}T${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}: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(dateStr)) + .map((p) => p.value) + .join("") + ).toISOString() + ); + const dateInFromTz = 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(dateInFromTz); + 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 hourStr = hourPart.value; + if (hourStr === "24") hourStr = "00"; + return `${yearPart.value}:${monthPart.value}:${dayPart.value} ${hourStr}:${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"); + }); +}); \ No newline at end of file diff --git a/pages/utilities/timezone-converter.tsx b/pages/utilities/timezone-converter.tsx index 9cf5675..f90f92e 100644 --- a/pages/utilities/timezone-converter.tsx +++ b/pages/utilities/timezone-converter.tsx @@ -9,7 +9,6 @@ import Meta from "@/components/Meta"; import CallToActionGrid from "@/components/CallToActionGrid"; import TimestampConverterSEO from "@/components/seo/TimestampConverterSEO"; -// Minimal timezone list for demo; replace with a full list or use a package for production const timezones = [ "UTC", "America/New_York", @@ -21,17 +20,22 @@ const timezones = [ "America/Los_Angeles", ]; -function convertTime( - inputTime: string, - fromTz: string, - toTz: string -): string { +function convertTime(inputTime: string, fromTz: string, toTz: string): string { if (!inputTime) return ""; - // Parse input as "HH:mm" - const [hours, minutes] = inputTime.split(":").map(Number); + // Expect inputTime as "YYYY:MM:DD HH:MM" + const match = inputTime.match(/^(\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2})$/); + if (!match) return "Invalid time format"; + const [, year, month, day, hours, minutes] = match.map(Number); 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 || @@ -39,32 +43,52 @@ function convertTime( ) return "Invalid time format"; - // Today's date in UTC - const now = new Date(); - // Create a UTC date at the given time - const utcDate = new Date(Date.UTC( - now.getUTCFullYear(), - now.getUTCMonth(), - now.getUTCDate(), - hours, - minutes - )); + // Create a date string in ISO format for the input date and time + const dateStr = `${year}-${String(month).padStart(2, "0")}-${String(day).padStart( + 2, + "0" + )}T${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}:00`; + + // Parse the date as if it's in fromTz, then get the equivalent UTC time + 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(dateStr)) + .map((p) => p.value) + .join("") + ).toISOString() + ); + + const dateInFromTz = new Date(utcMillis); - // Use Intl.DateTimeFormat to get the time in the target timezone 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(utcDate); - const hourPart = parts.find(p => p.type === "hour"); - const minutePart = parts.find(p => p.type === "minute"); - if (!hourPart || !minutePart) return "Conversion error"; + const parts = formatter.formatToParts(dateInFromTz); + 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 hourStr = hourPart.value; - // handle midnight (24:xx -> 00:xx) if (hourStr === "24") hourStr = "00"; - return `${hourStr}:${minutePart.value}`; + return `${yearPart.value}:${monthPart.value}:${dayPart.value} ${hourStr}:${minutePart.value}`; } export default function TimezoneComparer() { @@ -72,9 +96,21 @@ export default function TimezoneComparer() { typeof window !== "undefined" ? Intl.DateTimeFormat().resolvedOptions().timeZone : "UTC"; + + // Get today's date and time in YYYY:MM:DD HH:MM format + 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(""); + const [inputTime, setInputTime] = useState(getDefaultInputTime()); const [outputTime, setOutputTime] = useState(""); const handleConvert = () => { @@ -124,16 +160,17 @@ export default function TimezoneComparer() { ))} - + setInputTime(e.target.value)} - maxLength={5} - /> @@ -148,11 +185,10 @@ export default function TimezoneComparer() { -
- -
+
+ +
- ); } From 4134f143dc857d4d91289b36c5dd7cc1993ffbf3 Mon Sep 17 00:00:00 2001 From: Rahul Keshervani Date: Fri, 8 Aug 2025 17:18:16 +0530 Subject: [PATCH 6/7] adding test and fixing utc to utc conversion --- pages/utilities/timezone-converter.test.tsx | 72 ++++++++++++-------- pages/utilities/timezone-converter.tsx | 74 ++++++++++++--------- 2 files changed, 84 insertions(+), 62 deletions(-) diff --git a/pages/utilities/timezone-converter.test.tsx b/pages/utilities/timezone-converter.test.tsx index 50bb60b..ff9cdc0 100644 --- a/pages/utilities/timezone-converter.test.tsx +++ b/pages/utilities/timezone-converter.test.tsx @@ -1,15 +1,19 @@ -// Minimal test runner for convertTime import React from "react"; import { render, fireEvent } from "@testing-library/react"; import TimezoneComparer from "./timezone-converter"; -// Updated convertTime for new input format and output 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 [, year, month, day, hours, minutes] = match.map(Number); + // Parse as string, not number, to preserve leading zeros for formatting + 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) || @@ -26,28 +30,38 @@ function convertTime(inputTime: string, fromTz: string, toTz: string): string { minutes > 59 ) return "Invalid time format"; - const dateStr = `${year}-${String(month).padStart(2, "0")}-${String(day).padStart( - 2, - "0" - )}T${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}: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(dateStr)) - .map((p) => p.value) - .join("") - ).toISOString() - ); - const dateInFromTz = new Date(utcMillis); + + // Always treat input as wall time in fromTz, and get the UTC time for that wall time + // For UTC, this is a direct UTC date + let date: Date; + if (fromTz === "UTC") { + date = new Date(Date.UTC(year, month - 1, day, hours, minutes, 0)); + } else { + // Create a date object for the input wall time in fromTz + // Get the UTC offset for fromTz at the given wall time + const inputIso = `${yearStr}-${monthStr}-${dayStr}T${hourStr}:${minuteStr}:00`; + // Get the UTC time for the wall time in fromTz + 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); + } + + // Format the date in the target timezone const formatter = new Intl.DateTimeFormat("en-US", { year: "numeric", month: "2-digit", @@ -57,16 +71,16 @@ function convertTime(inputTime: string, fromTz: string, toTz: string): string { hour12: false, timeZone: toTz, }); - const parts = formatter.formatToParts(dateInFromTz); + 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 hourStr = hourPart.value; - if (hourStr === "24") hourStr = "00"; - return `${yearPart.value}:${monthPart.value}:${dayPart.value} ${hourStr}:${minutePart.value}`; + let hourOut = hourPart.value; + if (hourOut === "24") hourOut = "00"; + return `${yearPart.value}:${monthPart.value}:${dayPart.value} ${hourOut}:${minutePart.value}`; } describe("convertTime", () => { diff --git a/pages/utilities/timezone-converter.tsx b/pages/utilities/timezone-converter.tsx index f90f92e..ee63b36 100644 --- a/pages/utilities/timezone-converter.tsx +++ b/pages/utilities/timezone-converter.tsx @@ -22,10 +22,15 @@ const timezones = [ function convertTime(inputTime: string, fromTz: string, toTz: string): string { if (!inputTime) return ""; - // Expect inputTime as "YYYY:MM:DD HH:MM" const match = inputTime.match(/^(\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2})$/); if (!match) return "Invalid time format"; - const [, year, month, day, hours, minutes] = match.map(Number); + // Parse as string, not number, to preserve leading zeros for formatting + 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) || @@ -43,33 +48,37 @@ function convertTime(inputTime: string, fromTz: string, toTz: string): string { ) return "Invalid time format"; - // Create a date string in ISO format for the input date and time - const dateStr = `${year}-${String(month).padStart(2, "0")}-${String(day).padStart( - 2, - "0" - )}T${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}:00`; - - // Parse the date as if it's in fromTz, then get the equivalent UTC time - 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(dateStr)) - .map((p) => p.value) - .join("") - ).toISOString() - ); - - const dateInFromTz = new Date(utcMillis); + // Always treat input as wall time in fromTz, and get the UTC time for that wall time + // For UTC, this is a direct UTC date + let date: Date; + if (fromTz === "UTC") { + date = new Date(Date.UTC(year, month - 1, day, hours, minutes, 0)); + } else { + // Create a date object for the input wall time in fromTz + // Get the UTC offset for fromTz at the given wall time + const inputIso = `${yearStr}-${monthStr}-${dayStr}T${hourStr}:${minuteStr}:00`; + // Get the UTC time for the wall time in fromTz + 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); + } + // Format the date in the target timezone const formatter = new Intl.DateTimeFormat("en-US", { year: "numeric", month: "2-digit", @@ -79,16 +88,16 @@ function convertTime(inputTime: string, fromTz: string, toTz: string): string { hour12: false, timeZone: toTz, }); - const parts = formatter.formatToParts(dateInFromTz); + 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 hourStr = hourPart.value; - if (hourStr === "24") hourStr = "00"; - return `${yearPart.value}:${monthPart.value}:${dayPart.value} ${hourStr}:${minutePart.value}`; + let hourOut = hourPart.value; + if (hourOut === "24") hourOut = "00"; + return `${yearPart.value}:${monthPart.value}:${dayPart.value} ${hourOut}:${minutePart.value}`; } export default function TimezoneComparer() { @@ -97,7 +106,6 @@ export default function TimezoneComparer() { ? Intl.DateTimeFormat().resolvedOptions().timeZone : "UTC"; - // Get today's date and time in YYYY:MM:DD HH:MM format const getDefaultInputTime = () => { const now = new Date(); const yyyy = now.getFullYear(); From 6bc2df38caf14d2877371df0350a28021f94729a Mon Sep 17 00:00:00 2001 From: Rahul Keshervani Date: Fri, 8 Aug 2025 17:20:20 +0530 Subject: [PATCH 7/7] formatting --- pages/utilities/timezone-converter.test.tsx | 51 +++++++++++++-------- pages/utilities/timezone-converter.tsx | 10 +--- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/pages/utilities/timezone-converter.test.tsx b/pages/utilities/timezone-converter.test.tsx index ff9cdc0..2d72247 100644 --- a/pages/utilities/timezone-converter.test.tsx +++ b/pages/utilities/timezone-converter.test.tsx @@ -1,4 +1,3 @@ - import React from "react"; import { render, fireEvent } from "@testing-library/react"; import TimezoneComparer from "./timezone-converter"; @@ -7,7 +6,6 @@ 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"; - // Parse as string, not number, to preserve leading zeros for formatting const [, yearStr, monthStr, dayStr, hourStr, minuteStr] = match; const year = Number(yearStr); const month = Number(monthStr); @@ -31,16 +29,11 @@ function convertTime(inputTime: string, fromTz: string, toTz: string): string { ) return "Invalid time format"; - // Always treat input as wall time in fromTz, and get the UTC time for that wall time - // For UTC, this is a direct UTC date let date: Date; if (fromTz === "UTC") { date = new Date(Date.UTC(year, month - 1, day, hours, minutes, 0)); } else { - // Create a date object for the input wall time in fromTz - // Get the UTC offset for fromTz at the given wall time const inputIso = `${yearStr}-${monthStr}-${dayStr}T${hourStr}:${minuteStr}:00`; - // Get the UTC time for the wall time in fromTz const utcMillis = Date.parse( new Date( new Intl.DateTimeFormat("en-US", { @@ -61,7 +54,6 @@ function convertTime(inputTime: string, fromTz: string, toTz: string): string { date = new Date(utcMillis); } - // Format the date in the target timezone const formatter = new Intl.DateTimeFormat("en-US", { year: "numeric", month: "2-digit", @@ -77,7 +69,8 @@ function convertTime(inputTime: string, fromTz: string, toTz: string): string { 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"; + 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}`; @@ -85,7 +78,9 @@ function convertTime(inputTime: string, fromTz: string, toTz: string): string { 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"); + expect(convertTime("2024:06:01 12:00", "UTC", "UTC")).toBe( + "2024:06:01 12:00" + ); }); test("UTC to New York, date and time format", () => { @@ -94,21 +89,33 @@ describe("convertTime", () => { }); test("New York to Tokyo, date and time format", () => { - const tokyoTime = convertTime("2024:06:01 08:00", "America/New_York", "Asia/Tokyo"); + 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"); + 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"); + 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"); + 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", () => { @@ -121,15 +128,21 @@ describe("convertTime", () => { }); test("Handles leap year", () => { - expect(convertTime("2024:02:29 12:00", "UTC", "UTC")).toBe("2024:02:29 12:00"); + 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"); + 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("2024-06-01 12:00", "UTC", "UTC")).toBe( + "Invalid time format" + ); expect(convertTime("12:00", "UTC", "UTC")).toBe("Invalid time format"); }); -}); \ No newline at end of file +}); diff --git a/pages/utilities/timezone-converter.tsx b/pages/utilities/timezone-converter.tsx index ee63b36..889c8cf 100644 --- a/pages/utilities/timezone-converter.tsx +++ b/pages/utilities/timezone-converter.tsx @@ -24,7 +24,6 @@ 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"; - // Parse as string, not number, to preserve leading zeros for formatting const [, yearStr, monthStr, dayStr, hourStr, minuteStr] = match; const year = Number(yearStr); const month = Number(monthStr); @@ -48,16 +47,11 @@ function convertTime(inputTime: string, fromTz: string, toTz: string): string { ) return "Invalid time format"; - // Always treat input as wall time in fromTz, and get the UTC time for that wall time - // For UTC, this is a direct UTC date let date: Date; if (fromTz === "UTC") { date = new Date(Date.UTC(year, month - 1, day, hours, minutes, 0)); } else { - // Create a date object for the input wall time in fromTz - // Get the UTC offset for fromTz at the given wall time const inputIso = `${yearStr}-${monthStr}-${dayStr}T${hourStr}:${minuteStr}:00`; - // Get the UTC time for the wall time in fromTz const utcMillis = Date.parse( new Date( new Intl.DateTimeFormat("en-US", { @@ -78,7 +72,6 @@ function convertTime(inputTime: string, fromTz: string, toTz: string): string { date = new Date(utcMillis); } - // Format the date in the target timezone const formatter = new Intl.DateTimeFormat("en-US", { year: "numeric", month: "2-digit", @@ -94,7 +87,8 @@ function convertTime(inputTime: string, fromTz: string, toTz: string): string { 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"; + 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}`;