fix - timezone fix
This commit is contained in:
@@ -55,52 +55,109 @@ export function toUTCDate(date: Date): Date {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------- formatUTCDateStringToLocal ----------
|
||||||
/**
|
/**
|
||||||
* Converts a stored UTC date string (e.g. from DB) into a Date object
|
* If `dateStr` is:
|
||||||
* and formats it as local yyyy-MM-dd string for UI use.
|
* - date-only "YYYY-MM-DD" -> returns it unchanged
|
||||||
|
* - ISO instant/string -> returns the LOCAL calendar date "YYYY-MM-DD" of that instant
|
||||||
*/
|
*/
|
||||||
export function formatUTCDateStringToLocal(dateStr: string): string {
|
export function formatUTCDateStringToLocal(dateStr: string): string {
|
||||||
const localDate = parseLocalDate(dateStr); // will strip the time part
|
if (!dateStr) return "";
|
||||||
return formatLocalDate(localDate); // e.g., "2025-07-15"
|
if (isDateOnlyString(dateStr)) return dateStr;
|
||||||
|
|
||||||
|
const d = new Date(dateStr);
|
||||||
|
if (isNaN(d.getTime())) {
|
||||||
|
// fallback: strip time part and treat as local date
|
||||||
|
try {
|
||||||
|
const maybe = parseLocalDate(dateStr);
|
||||||
|
return formatLocalDate(maybe);
|
||||||
|
} catch {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return formatLocalDate(d); // uses local fields of the instant
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure any date (Date|string) is formatted to ISO string for consistent backend storage.
|
* Frontend-only normalizer.
|
||||||
* If it's already a string, pass through. If it's a Date, convert to ISO.
|
* - Returns "YYYY-MM-DD" string representing the calendar date the user expects.
|
||||||
|
* - This avoids producing ISO instants that confuse frontend display.
|
||||||
|
*
|
||||||
|
* Use this for UI display or for sending date-only values to backend if backend accepts date-only.
|
||||||
*/
|
*/
|
||||||
export function normalizeToISOString(date: Date | string): string {
|
export function normalizeToISOString(date: Date | string): string {
|
||||||
const parsed = parseLocalDate(date);
|
const parsed = parseLocalDate(date); // returns local-midnight-based Date
|
||||||
return parsed.toISOString(); // ensures it always starts from local midnight
|
return formatLocalDate(parsed); // "YYYY-MM-DD"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ---------- helpers ----------
|
||||||
* Formats a date string or Date object into a human-readable "DD Mon YYYY" string.
|
const MONTH_SHORT = [
|
||||||
* Examples: "22 Jul 2025"
|
"Jan",
|
||||||
*
|
"Feb",
|
||||||
* @param dateInput The date as a string (e.g., ISO, YYYY-MM-DD) or a Date object.
|
"Mar",
|
||||||
* @returns A formatted date string.
|
"Apr",
|
||||||
*/
|
"May",
|
||||||
export const formatDateToHumanReadable = (
|
"Jun",
|
||||||
dateInput?: string | Date
|
"Jul",
|
||||||
): string => {
|
"Aug",
|
||||||
if (!dateInput) return "N/A";
|
"Sep",
|
||||||
// Create a Date object from the input.
|
"Oct",
|
||||||
// The Date constructor is quite flexible with various string formats.
|
"Nov",
|
||||||
const date = new Date(dateInput);
|
"Dec",
|
||||||
|
];
|
||||||
|
|
||||||
// Check if the date is valid. If new Date() fails to parse, it returns "Invalid Date".
|
function isDateOnlyString(s: string): boolean {
|
||||||
if (isNaN(date.getTime())) {
|
return /^\d{4}-\d{2}-\d{2}$/.test(s);
|
||||||
console.error("Invalid date input provided:", dateInput);
|
}
|
||||||
return "Invalid Date"; // Or handle this error in a way that suits your UI
|
|
||||||
|
// ---------- formatDateToHumanReadable ----------
|
||||||
|
/**
|
||||||
|
* Frontend-safe: never lets timezone shift the displayed calendar day.
|
||||||
|
* - "YYYY-MM-DD" strings are shown exactly.
|
||||||
|
* - Date objects are shown using their calendar fields (getFullYear/getMonth/getDate).
|
||||||
|
* - ISO/timestamp strings are parsed and shown using UTC date component so they do not flip on client TZ.
|
||||||
|
*/
|
||||||
|
export function formatDateToHumanReadable(dateInput?: string | Date): string {
|
||||||
|
if (!dateInput) return "N/A";
|
||||||
|
|
||||||
|
// date-only string -> show as-is
|
||||||
|
if (typeof dateInput === "string" && isDateOnlyString(dateInput)) {
|
||||||
|
const parts = dateInput.split("-");
|
||||||
|
const [y, m, d] = parts;
|
||||||
|
|
||||||
|
// Defensive check so TypeScript and runtime are both happy
|
||||||
|
if (!y || !m || !d) {
|
||||||
|
console.error("Invalid date-only string:", dateInput);
|
||||||
|
return "Invalid Date";
|
||||||
|
}
|
||||||
|
|
||||||
|
const day = d.padStart(2, "0");
|
||||||
|
const monthIndex = parseInt(m, 10) - 1;
|
||||||
|
const monthName = MONTH_SHORT[monthIndex] ?? "Invalid";
|
||||||
|
|
||||||
|
return `${day} ${monthName} ${y}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use Intl.DateTimeFormat for locale-aware, human-readable formatting.
|
// Date object -> use its calendar fields (no TZ conversion)
|
||||||
return new Intl.DateTimeFormat("en-US", {
|
if (dateInput instanceof Date) {
|
||||||
day: "2-digit", // e.g., "01", "22"
|
if (isNaN(dateInput.getTime())) return "Invalid Date";
|
||||||
month: "short", // e.g., "Jan", "Jul"
|
const dd = String(dateInput.getDate()).padStart(2, "0");
|
||||||
year: "numeric", // e.g., "2023", "2025"
|
const mm = MONTH_SHORT[dateInput.getMonth()];
|
||||||
}).format(date);
|
const yy = dateInput.getFullYear();
|
||||||
};
|
return `${dd} ${mm} ${yy}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise (ISO/timestamp string) -> parse and use UTC date components
|
||||||
|
const parsed = new Date(dateInput);
|
||||||
|
if (isNaN(parsed.getTime())) {
|
||||||
|
console.error("Invalid date input provided:", dateInput);
|
||||||
|
return "Invalid Date";
|
||||||
|
}
|
||||||
|
const dd = String(parsed.getUTCDate()).padStart(2, "0");
|
||||||
|
const mm = MONTH_SHORT[parsed.getUTCMonth()];
|
||||||
|
const yy = parsed.getUTCFullYear();
|
||||||
|
return `${dd} ${mm} ${yy}`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert any OCR numeric-ish value into a number.
|
* Convert any OCR numeric-ish value into a number.
|
||||||
@@ -125,7 +182,9 @@ export function toStr(val: string | number | null | undefined): string {
|
|||||||
* Convert OCR date strings like "070825" (MMDDYY) into a JS Date object.
|
* Convert OCR date strings like "070825" (MMDDYY) into a JS Date object.
|
||||||
* Example: "070825" → 2025-08-07.
|
* Example: "070825" → 2025-08-07.
|
||||||
*/
|
*/
|
||||||
export function convertOCRDate(input: string | number | null | undefined): Date {
|
export function convertOCRDate(
|
||||||
|
input: string | number | null | undefined
|
||||||
|
): Date {
|
||||||
const raw = toStr(input);
|
const raw = toStr(input);
|
||||||
|
|
||||||
if (!/^\d{6}$/.test(raw)) {
|
if (!/^\d{6}$/.test(raw)) {
|
||||||
@@ -140,7 +199,6 @@ export function convertOCRDate(input: string | number | null | undefined): Date
|
|||||||
return new Date(year, month, day);
|
return new Date(year, month, day);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a Date or date string into "HH:mm" (24-hour) string.
|
* Format a Date or date string into "HH:mm" (24-hour) string.
|
||||||
*
|
*
|
||||||
@@ -194,4 +252,3 @@ export function formatLocalTime(
|
|||||||
|
|
||||||
return `${pad2(hours)}:${pad2(minutes)}`;
|
return `${pad2(hours)}:${pad2(minutes)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user