From 360d18d8008108accbd28472a736d21a438f22ab Mon Sep 17 00:00:00 2001 From: aniket866 Date: Sun, 1 Mar 2026 00:23:58 +0530 Subject: [PATCH 1/6] timezone-fix --- frontend/src/components/InvoicePreview.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/components/InvoicePreview.jsx b/frontend/src/components/InvoicePreview.jsx index 3e7e895d..862be2bc 100644 --- a/frontend/src/components/InvoicePreview.jsx +++ b/frontend/src/components/InvoicePreview.jsx @@ -297,6 +297,7 @@ const InvoicePreview = ({ year: "numeric", month: "short", day: "numeric", + timeZone: "UTC", // Fix: Forces the date to render in absolute UTC })} @@ -309,6 +310,7 @@ const InvoicePreview = ({ year: "numeric", month: "short", day: "numeric", + timeZone: "UTC", // Fix: Forces the date to render in absolute UTC })} From 354a57699c217992eb134103532a606f730567f5 Mon Sep 17 00:00:00 2001 From: aniket866 Date: Sun, 22 Mar 2026 21:31:27 +0530 Subject: [PATCH 2/6] fixing-timezone --- frontend/src/components/InvoicePreview.jsx | 15 ++---- frontend/src/page/BatchPayment.jsx | 13 ++--- frontend/src/page/ReceivedInvoice.jsx | 18 ++----- frontend/src/page/SentInvoice.jsx | 13 ++--- frontend/src/utils/formatDate.js | 55 ++++++++++++++++++++++ frontend/src/utils/generateInvoicePDF.js | 14 ++---- 6 files changed, 74 insertions(+), 54 deletions(-) create mode 100644 frontend/src/utils/formatDate.js diff --git a/frontend/src/components/InvoicePreview.jsx b/frontend/src/components/InvoicePreview.jsx index 862be2bc..62b1a9f6 100644 --- a/frontend/src/components/InvoicePreview.jsx +++ b/frontend/src/components/InvoicePreview.jsx @@ -38,6 +38,7 @@ import PaidIcon from "@mui/icons-material/CheckCircle"; import UnpaidIcon from "@mui/icons-material/Pending"; import CancelIcon from "@mui/icons-material/Cancel"; import CurrencyExchangeIcon from "@mui/icons-material/CurrencyExchange"; +import { formatInvoiceDate } from "../utils/formatDate"; const InvoicePreview = ({ invoice, @@ -293,12 +294,7 @@ const InvoicePreview = ({ Issued: - {new Date(invoice.issueDate).toLocaleDateString("en-US", { - year: "numeric", - month: "short", - day: "numeric", - timeZone: "UTC", // Fix: Forces the date to render in absolute UTC - })} + {formatInvoiceDate(invoice.issueDate)}
@@ -306,12 +302,7 @@ const InvoicePreview = ({ Due: - {new Date(invoice.dueDate).toLocaleDateString("en-US", { - year: "numeric", - month: "short", - day: "numeric", - timeZone: "UTC", // Fix: Forces the date to render in absolute UTC - })} + {formatInvoiceDate(invoice.dueDate)}
diff --git a/frontend/src/page/BatchPayment.jsx b/frontend/src/page/BatchPayment.jsx index 6982df09..e4d47a9b 100644 --- a/frontend/src/page/BatchPayment.jsx +++ b/frontend/src/page/BatchPayment.jsx @@ -8,6 +8,7 @@ import html2canvas from "html2canvas"; import { LitNodeClient } from "@lit-protocol/lit-node-client"; import { decryptToString } from "@lit-protocol/encryption/src/lib/encryption.js"; import { LIT_ABILITY, LIT_NETWORK } from "@lit-protocol/constants"; +import { formatInvoiceDate, formatDateTime } from "../utils/formatDate"; import { createSiweMessageWithRecaps, generateAuthSig, @@ -1186,7 +1187,7 @@ function BatchPayment() { )} - {formatDate(invoice.issueDate)} + {formatDateTime(invoice.issueDate)}
@@ -1482,16 +1483,10 @@ function BatchPayment() {
- Issued:{" "} - {new Date( - drawerState.selectedInvoice.issueDate - ).toLocaleDateString()} + Issued: {formatInvoiceDate(drawerState.selectedInvoice.issueDate)} - Due:{" "} - {new Date( - drawerState.selectedInvoice.dueDate - ).toLocaleDateString()} + Due: {formatInvoiceDate(drawerState.selectedInvoice.dueDate)}
diff --git a/frontend/src/page/ReceivedInvoice.jsx b/frontend/src/page/ReceivedInvoice.jsx index 1bac242f..f7b9a720 100644 --- a/frontend/src/page/ReceivedInvoice.jsx +++ b/frontend/src/page/ReceivedInvoice.jsx @@ -57,6 +57,7 @@ import CloseIcon from "@mui/icons-material/Close"; import ErrorIcon from "@mui/icons-material/Error"; import { useTokenList } from "@/hooks/useTokenList"; import WalletConnectionAlert from "@/components/WalletConnectionAlert"; +import { formatInvoiceDate, formatDateTime } from "@/utils/formatDate"; const columns = [ { id: "select", label: "", minWidth: 50 }, @@ -883,11 +884,6 @@ function ReceivedInvoice() { )}`; }; - const formatDate = (issueDate) => { - const date = new Date(issueDate); - return date.toLocaleString(); - }; - const unpaidInvoices = receivedInvoices.filter( (inv) => !inv.isPaid && !inv.isCancelled ); @@ -1404,7 +1400,7 @@ function ReceivedInvoice() { - {formatDate(invoice.issueDate)} + {formatDateTime(invoice.issueDate)} @@ -1780,16 +1776,10 @@ function ReceivedInvoice() {
- Issued:{" "} - {new Date( - drawerState.selectedInvoice.issueDate - ).toLocaleDateString()} + Issued: {formatInvoiceDate(drawerState.selectedInvoice.issueDate)} - Due:{" "} - {new Date( - drawerState.selectedInvoice.dueDate - ).toLocaleDateString()} + Due: {formatInvoiceDate(drawerState.selectedInvoice.dueDate)}
diff --git a/frontend/src/page/SentInvoice.jsx b/frontend/src/page/SentInvoice.jsx index b953713f..4919dc1e 100644 --- a/frontend/src/page/SentInvoice.jsx +++ b/frontend/src/page/SentInvoice.jsx @@ -47,6 +47,7 @@ import CancelIcon from "@mui/icons-material/Cancel"; import CurrencyExchangeIcon from "@mui/icons-material/CurrencyExchange"; import { useTokenList } from "@/hooks/useTokenList"; import WalletConnectionAlert from "@/components/WalletConnectionAlert"; +import { formatInvoiceDate, formatDateTime } from "@/utils/formatDate"; const columns = [ { id: "fname", label: "Client", minWidth: 120 }, @@ -613,7 +614,7 @@ function SentInvoice() { {/* Date Column */} - {formatDate(invoice.issueDate)} + {formatDateTime(invoice.issueDate)} @@ -922,16 +923,10 @@ function SentInvoice() {
- Issued:{" "} - {new Date( - drawerState.selectedInvoice.issueDate - ).toLocaleDateString()} + Issued: {formatInvoiceDate(drawerState.selectedInvoice.issueDate)} - Due:{" "} - {new Date( - drawerState.selectedInvoice.dueDate - ).toLocaleDateString()} + Due: {formatInvoiceDate(drawerState.selectedInvoice.dueDate)}
diff --git a/frontend/src/utils/formatDate.js b/frontend/src/utils/formatDate.js new file mode 100644 index 00000000..783b6929 --- /dev/null +++ b/frontend/src/utils/formatDate.js @@ -0,0 +1,55 @@ +/** + * Returns the user's local UTC offset string, e.g. "UTC+5:30" or "UTC-8:00" + */ +function getUTCOffset() { + const offset = -new Date().getTimezoneOffset(); // minutes, sign flipped + const sign = offset >= 0 ? "+" : "-"; + const abs = Math.abs(offset); + const hours = Math.floor(abs / 60); + const minutes = abs % 60; + return `UTC${sign}${hours}:${minutes.toString().padStart(2, "0")}`; +} + +/** + * Formats invoice issue/due dates (date only, no time). + * Output: "Feb 20, 2026 (UTC+5:30)" + * + * Why: new Date(isoString).toLocaleDateString() silently shifts the date + * across midnight for users in negative UTC offsets. Showing the offset + * makes the displayed date unambiguous for financial documents. + * + * @param {string|Date} dateStr + * @returns {string} + */ +export function formatInvoiceDate(dateStr) { + if (!dateStr) return "N/A"; + const date = new Date(dateStr); + if (isNaN(date.getTime())) return "Invalid date"; + const formatted = date.toLocaleDateString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + }); + return `${formatted} (${getUTCOffset()})`; +} + +/** + * Formats a full timestamp (date + time) for table rows. + * Output: "Feb 20, 2026, 3:45 PM (UTC+5:30)" + * + * @param {string|Date} dateStr + * @returns {string} + */ +export function formatDateTime(dateStr) { + if (!dateStr) return "N/A"; + const date = new Date(dateStr); + if (isNaN(date.getTime())) return "Invalid date"; + const formatted = date.toLocaleString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + }); + return `${formatted} (${getUTCOffset()})`; +} diff --git a/frontend/src/utils/generateInvoicePDF.js b/frontend/src/utils/generateInvoicePDF.js index 52a0a14d..c32b78a0 100644 --- a/frontend/src/utils/generateInvoicePDF.js +++ b/frontend/src/utils/generateInvoicePDF.js @@ -1,6 +1,8 @@ import jsPDF from "jspdf"; import { ethers } from "ethers"; import { getWagmiChainName, getWagmiChainInfo } from "./wagmiChainHelpers"; +import { formatInvoiceDate } from "./formatDate"; + /** * Load logo image with multiple fallback methods @@ -369,16 +371,8 @@ export const generateInvoicePDF = async (invoice, fee = 0) => { pdf.setTextColor(...darkGray); pdf.setFontSize(9); pdf.setFont("helvetica", "normal"); - const issueDate = new Date(invoice.issueDate).toLocaleDateString("en-US", { - year: "numeric", - month: "short", - day: "numeric", - }); - const dueDate = new Date(invoice.dueDate).toLocaleDateString("en-US", { - year: "numeric", - month: "short", - day: "numeric", - }); + const issueDate = formatInvoiceDate(invoice.issueDate); + const dueDate = formatInvoiceDate(invoice.dueDate); pdf.text(`Issued: ${issueDate}`, 25, yPos + 5.5); pdf.text(`Due: ${dueDate}`, 160, yPos + 5.5); From 564731761cb0eac215770c672c5613b23259710f Mon Sep 17 00:00:00 2001 From: aniket866 Date: Sun, 22 Mar 2026 21:54:10 +0530 Subject: [PATCH 3/6] fixing-timezone --- frontend/src/page/BatchPayment.jsx | 5 ----- frontend/src/page/SentInvoice.jsx | 5 ----- frontend/src/utils/formatDate.js | 22 ++++++++++++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/frontend/src/page/BatchPayment.jsx b/frontend/src/page/BatchPayment.jsx index e4d47a9b..42d667b8 100644 --- a/frontend/src/page/BatchPayment.jsx +++ b/frontend/src/page/BatchPayment.jsx @@ -823,11 +823,6 @@ function BatchPayment() { )}`; }; - const formatDate = (issueDate) => { - const date = new Date(issueDate); - return date.toLocaleString(); - }; - const unpaidInvoices = receivedInvoices.filter( (inv) => !inv.isPaid && !inv.isCancelled ); diff --git a/frontend/src/page/SentInvoice.jsx b/frontend/src/page/SentInvoice.jsx index 4919dc1e..9698354a 100644 --- a/frontend/src/page/SentInvoice.jsx +++ b/frontend/src/page/SentInvoice.jsx @@ -418,11 +418,6 @@ function SentInvoice() { )}`; }; - const formatDate = (issueDate) => { - const date = new Date(issueDate); - return date.toLocaleString(); - }; - return ( <>
diff --git a/frontend/src/utils/formatDate.js b/frontend/src/utils/formatDate.js index 783b6929..4d34b965 100644 --- a/frontend/src/utils/formatDate.js +++ b/frontend/src/utils/formatDate.js @@ -20,18 +20,28 @@ function getUTCOffset() { * * @param {string|Date} dateStr * @returns {string} - */ -export function formatInvoiceDate(dateStr) { - if (!dateStr) return "N/A"; +*/ + // Strip time component to avoid UTC→local midnight shift. + // "2026-02-20T..." or "2026-02-20" → ["2026","02","20"] + const parts = dateStr.split("T")[0].split("-"); + if (parts.length === 3) { + const [year, month, day] = parts.map(Number); + const date = new Date(year, month - 1, day); // local date, no TZ conversion + if (isNaN(date.getTime())) return "Invalid date"; + return date.toLocaleDateString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + }); + } + // Fallback for non-ISO formats const date = new Date(dateStr); if (isNaN(date.getTime())) return "Invalid date"; - const formatted = date.toLocaleDateString("en-US", { + return date.toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric", }); - return `${formatted} (${getUTCOffset()})`; -} /** * Formats a full timestamp (date + time) for table rows. From 4491c46920dca567e2c39faba4cd2993107d7675 Mon Sep 17 00:00:00 2001 From: Aniket Date: Sun, 22 Mar 2026 22:08:33 +0530 Subject: [PATCH 4/6] Code rabbit follow up Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- frontend/src/page/BatchPayment.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/page/BatchPayment.jsx b/frontend/src/page/BatchPayment.jsx index 42d667b8..8326dc19 100644 --- a/frontend/src/page/BatchPayment.jsx +++ b/frontend/src/page/BatchPayment.jsx @@ -1182,7 +1182,7 @@ function BatchPayment() { )} - {formatDateTime(invoice.issueDate)} + {formatInvoiceDate(invoice.issueDate)}
From 52c776609efca70cae05baf951b6384767df98f8 Mon Sep 17 00:00:00 2001 From: Aniket Date: Sun, 22 Mar 2026 22:09:06 +0530 Subject: [PATCH 5/6] Code rabbit follow up Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- frontend/src/page/SentInvoice.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/page/SentInvoice.jsx b/frontend/src/page/SentInvoice.jsx index 9698354a..a104d485 100644 --- a/frontend/src/page/SentInvoice.jsx +++ b/frontend/src/page/SentInvoice.jsx @@ -609,7 +609,7 @@ function SentInvoice() { {/* Date Column */} - {formatDateTime(invoice.issueDate)} + {formatInvoiceDate(invoice.issueDate)} From 9b9c74e75ae1c90d249928a266c92fb7cb0c2153 Mon Sep 17 00:00:00 2001 From: aniket866 Date: Sun, 22 Mar 2026 22:12:30 +0530 Subject: [PATCH 6/6] code-rabbit-fix --- frontend/src/utils/formatDate.js | 51 +++++++++++++++++--------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/frontend/src/utils/formatDate.js b/frontend/src/utils/formatDate.js index 4d34b965..b4ce1cc2 100644 --- a/frontend/src/utils/formatDate.js +++ b/frontend/src/utils/formatDate.js @@ -15,15 +15,17 @@ function getUTCOffset() { * Output: "Feb 20, 2026 (UTC+5:30)" * * Why: new Date(isoString).toLocaleDateString() silently shifts the date - * across midnight for users in negative UTC offsets. Showing the offset - * makes the displayed date unambiguous for financial documents. + * across midnight for users in negative UTC offsets. Rendering the calendar + * date only keeps financial document dates stable across viewers. * * @param {string|Date} dateStr * @returns {string} -*/ - // Strip time component to avoid UTC→local midnight shift. ++ */ +export function formatInvoiceDate(dateStr) { + if (!dateStr) return "N/A"; + // Strip time component to avoid UTC→local midnight shift // "2026-02-20T..." or "2026-02-20" → ["2026","02","20"] - const parts = dateStr.split("T")[0].split("-"); + const parts = String(dateStr).split("T")[0].split("-"); if (parts.length === 3) { const [year, month, day] = parts.map(Number); const date = new Date(year, month - 1, day); // local date, no TZ conversion @@ -43,23 +45,24 @@ function getUTCOffset() { day: "numeric", }); -/** - * Formats a full timestamp (date + time) for table rows. - * Output: "Feb 20, 2026, 3:45 PM (UTC+5:30)" - * - * @param {string|Date} dateStr - * @returns {string} - */ -export function formatDateTime(dateStr) { - if (!dateStr) return "N/A"; - const date = new Date(dateStr); - if (isNaN(date.getTime())) return "Invalid date"; - const formatted = date.toLocaleString("en-US", { - year: "numeric", - month: "short", - day: "numeric", - hour: "2-digit", - minute: "2-digit", - }); - return `${formatted} (${getUTCOffset()})`; + /** + * Formats a full timestamp (date + time) for table rows. + * * Output: "Feb 20, 2026" + * + * @param {string|Date} dateStr + * @returns {string} + */ + export function formatDateTime(dateStr) { + if (!dateStr) return "N/A"; + const date = new Date(dateStr); + if (isNaN(date.getTime())) return "Invalid date"; + const formatted = date.toLocaleString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + }); + return `${formatted} (${getUTCOffset()})`; + } }