Fix date format inconsistency in Task detail screen (#956)
* Fix date format inconsistency in Task detail screen (#938) Replace browser-dependent toLocaleDateString() with explicit country-based date formatting to ensure consistent date formats based on user's timezone. Problem: - User with English language + Greek timezone saw MM/DD/YYYY format - Expected DD/MM/YYYY format based on timezone/country - Browser's Intl.DateTimeFormat had incomplete locale support for combined locales like "en-GR" Solution: - Add country-to-format mapping in dateUtils.ts (60+ countries) - New formatDateByCountry() for dates (DD/MM/YYYY, MM/DD/YYYY, YYYY/MM/DD) - New formatDateTimeByCountry() for datetimes with 24h time - Update TaskDueDateCard and TaskDeferUntilCard to use new functions - Uses date-fns for consistent cross-browser formatting Testing: - Added 40 comprehensive test cases covering all format types - Verified with Greece (DD/MM), US (MM/DD), Japan (YYYY/MM/DD) - All tests passing Fixes #938 * chore: remove unused import in dateUtils.ts
This commit is contained in:
parent
84d30b5230
commit
2444e36f47
4 changed files with 410 additions and 27 deletions
|
|
@ -1,9 +1,13 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ClockIcon } from '@heroicons/react/24/outline';
|
||||
import TaskDeferUntilSection from '../TaskForm/TaskDeferUntilSection';
|
||||
import { Task } from '../../../entities/Task';
|
||||
import { resolveUserLocale } from '../../../utils/localeUtils';
|
||||
import {
|
||||
formatDateTimeByCountry,
|
||||
getUserTimezone,
|
||||
} from '../../../utils/dateUtils';
|
||||
import { getCountryFromTimezone } from '../../../utils/localeUtils';
|
||||
|
||||
interface TaskDeferUntilCardProps {
|
||||
task: Task;
|
||||
|
|
@ -24,23 +28,16 @@ const TaskDeferUntilCard: React.FC<TaskDeferUntilCardProps> = ({
|
|||
onSave,
|
||||
onCancel,
|
||||
}) => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const displayLocale = useMemo(
|
||||
() => resolveUserLocale(i18n.language),
|
||||
[i18n.language]
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const getDeferUntilDisplay = (deferUntil: string) => {
|
||||
const date = new Date(deferUntil);
|
||||
if (Number.isNaN(date.getTime())) return null;
|
||||
|
||||
const formattedDateTime = date.toLocaleString(displayLocale, {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
// Format datetime based on user's timezone-derived country
|
||||
const timezone = getUserTimezone();
|
||||
const country = getCountryFromTimezone(timezone);
|
||||
const formattedDateTime = formatDateTimeByCountry(date, country);
|
||||
|
||||
const now = new Date();
|
||||
const diffMs = date.getTime() - now.getTime();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
CalendarIcon,
|
||||
|
|
@ -6,8 +6,12 @@ import {
|
|||
} from '@heroicons/react/24/outline';
|
||||
import TaskDueDateSection from '../TaskForm/TaskDueDateSection';
|
||||
import { Task } from '../../../entities/Task';
|
||||
import { parseDateString } from '../../../utils/dateUtils';
|
||||
import { resolveUserLocale } from '../../../utils/localeUtils';
|
||||
import {
|
||||
parseDateString,
|
||||
formatDateByCountry,
|
||||
getUserTimezone,
|
||||
} from '../../../utils/dateUtils';
|
||||
import { getCountryFromTimezone } from '../../../utils/localeUtils';
|
||||
|
||||
interface TaskDueDateCardProps {
|
||||
task: Task;
|
||||
|
|
@ -28,21 +32,16 @@ const TaskDueDateCard: React.FC<TaskDueDateCardProps> = ({
|
|||
onSave,
|
||||
onCancel,
|
||||
}) => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const displayLocale = useMemo(
|
||||
() => resolveUserLocale(i18n.language),
|
||||
[i18n.language]
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const getDueDateDisplay = (dueDate: string) => {
|
||||
const date = parseDateString(dueDate);
|
||||
if (!date) return null;
|
||||
|
||||
const formattedDate = date.toLocaleDateString(displayLocale, {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
});
|
||||
// Format date based on user's timezone-derived country
|
||||
const timezone = getUserTimezone();
|
||||
const country = getCountryFromTimezone(timezone);
|
||||
const formattedDate = formatDateByCountry(date, country);
|
||||
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
|
|
|||
249
frontend/utils/dateUtils.test.ts
Normal file
249
frontend/utils/dateUtils.test.ts
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
import { formatDateByCountry, formatDateTimeByCountry } from './dateUtils';
|
||||
|
||||
describe('formatDateByCountry', () => {
|
||||
// Test date: March 15, 2026 at 10:30 AM
|
||||
const testDate = new Date('2026-03-15T10:30:00');
|
||||
|
||||
describe('DD/MM/YYYY format (European and most global)', () => {
|
||||
it('formats dates in DD/MM/YYYY for Greece', () => {
|
||||
expect(formatDateByCountry(testDate, 'GR')).toBe('15/03/2026');
|
||||
});
|
||||
|
||||
it('formats dates in DD/MM/YYYY for United Kingdom', () => {
|
||||
expect(formatDateByCountry(testDate, 'GB')).toBe('15/03/2026');
|
||||
});
|
||||
|
||||
it('formats dates in DD/MM/YYYY for France', () => {
|
||||
expect(formatDateByCountry(testDate, 'FR')).toBe('15/03/2026');
|
||||
});
|
||||
|
||||
it('formats dates in DD/MM/YYYY for Germany', () => {
|
||||
expect(formatDateByCountry(testDate, 'DE')).toBe('15/03/2026');
|
||||
});
|
||||
|
||||
it('formats dates in DD/MM/YYYY for Italy', () => {
|
||||
expect(formatDateByCountry(testDate, 'IT')).toBe('15/03/2026');
|
||||
});
|
||||
|
||||
it('formats dates in DD/MM/YYYY for Spain', () => {
|
||||
expect(formatDateByCountry(testDate, 'ES')).toBe('15/03/2026');
|
||||
});
|
||||
|
||||
it('formats dates in DD/MM/YYYY for Australia', () => {
|
||||
expect(formatDateByCountry(testDate, 'AU')).toBe('15/03/2026');
|
||||
});
|
||||
|
||||
it('formats dates in DD/MM/YYYY for India', () => {
|
||||
expect(formatDateByCountry(testDate, 'IN')).toBe('15/03/2026');
|
||||
});
|
||||
|
||||
it('formats dates in DD/MM/YYYY for Brazil', () => {
|
||||
expect(formatDateByCountry(testDate, 'BR')).toBe('15/03/2026');
|
||||
});
|
||||
|
||||
it('formats dates in DD/MM/YYYY for South Africa', () => {
|
||||
expect(formatDateByCountry(testDate, 'ZA')).toBe('15/03/2026');
|
||||
});
|
||||
});
|
||||
|
||||
describe('MM/DD/YYYY format (North America)', () => {
|
||||
it('formats dates in MM/DD/YYYY for United States', () => {
|
||||
expect(formatDateByCountry(testDate, 'US')).toBe('03/15/2026');
|
||||
});
|
||||
|
||||
it('formats dates in MM/DD/YYYY for Canada', () => {
|
||||
expect(formatDateByCountry(testDate, 'CA')).toBe('03/15/2026');
|
||||
});
|
||||
|
||||
it('formats dates in MM/DD/YYYY for Philippines', () => {
|
||||
expect(formatDateByCountry(testDate, 'PH')).toBe('03/15/2026');
|
||||
});
|
||||
});
|
||||
|
||||
describe('YYYY/MM/DD format (East Asia)', () => {
|
||||
it('formats dates in YYYY/MM/DD for Japan', () => {
|
||||
expect(formatDateByCountry(testDate, 'JP')).toBe('2026/03/15');
|
||||
});
|
||||
|
||||
it('formats dates in YYYY/MM/DD for South Korea', () => {
|
||||
expect(formatDateByCountry(testDate, 'KR')).toBe('2026/03/15');
|
||||
});
|
||||
|
||||
it('formats dates in YYYY/MM/DD for China', () => {
|
||||
expect(formatDateByCountry(testDate, 'CN')).toBe('2026/03/15');
|
||||
});
|
||||
|
||||
it('formats dates in YYYY/MM/DD for Taiwan', () => {
|
||||
expect(formatDateByCountry(testDate, 'TW')).toBe('2026/03/15');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Default and edge cases', () => {
|
||||
it('uses DD/MM/YYYY as default format when country is null', () => {
|
||||
expect(formatDateByCountry(testDate, null)).toBe('15/03/2026');
|
||||
});
|
||||
|
||||
it('uses DD/MM/YYYY as default format when country is undefined', () => {
|
||||
expect(formatDateByCountry(testDate, undefined)).toBe('15/03/2026');
|
||||
});
|
||||
|
||||
it('uses DD/MM/YYYY as default format when country is unknown', () => {
|
||||
expect(formatDateByCountry(testDate, 'UNKNOWN')).toBe('15/03/2026');
|
||||
});
|
||||
|
||||
it('uses DD/MM/YYYY as default format for empty string', () => {
|
||||
expect(formatDateByCountry(testDate, '')).toBe('15/03/2026');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Date boundaries', () => {
|
||||
it('formats dates with single-digit day correctly', () => {
|
||||
const date = new Date('2026-03-05T10:00:00');
|
||||
expect(formatDateByCountry(date, 'GR')).toBe('05/03/2026');
|
||||
expect(formatDateByCountry(date, 'US')).toBe('03/05/2026');
|
||||
expect(formatDateByCountry(date, 'JP')).toBe('2026/03/05');
|
||||
});
|
||||
|
||||
it('formats dates with single-digit month correctly', () => {
|
||||
const date = new Date('2026-01-15T10:00:00');
|
||||
expect(formatDateByCountry(date, 'GR')).toBe('15/01/2026');
|
||||
expect(formatDateByCountry(date, 'US')).toBe('01/15/2026');
|
||||
expect(formatDateByCountry(date, 'JP')).toBe('2026/01/15');
|
||||
});
|
||||
|
||||
it('formats end of month dates correctly', () => {
|
||||
const date = new Date('2026-12-31T10:00:00');
|
||||
expect(formatDateByCountry(date, 'GR')).toBe('31/12/2026');
|
||||
expect(formatDateByCountry(date, 'US')).toBe('12/31/2026');
|
||||
expect(formatDateByCountry(date, 'JP')).toBe('2026/12/31');
|
||||
});
|
||||
|
||||
it('formats start of year dates correctly', () => {
|
||||
const date = new Date('2026-01-01T10:00:00');
|
||||
expect(formatDateByCountry(date, 'GR')).toBe('01/01/2026');
|
||||
expect(formatDateByCountry(date, 'US')).toBe('01/01/2026');
|
||||
expect(formatDateByCountry(date, 'JP')).toBe('2026/01/01');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatDateTimeByCountry', () => {
|
||||
// Test datetime: March 15, 2026 at 14:30 (2:30 PM)
|
||||
const testDate = new Date('2026-03-15T14:30:00');
|
||||
|
||||
describe('DD/MM/YYYY HH:MM format (European and most global)', () => {
|
||||
it('formats datetime in DD/MM/YYYY HH:MM for Greece', () => {
|
||||
expect(formatDateTimeByCountry(testDate, 'GR')).toBe(
|
||||
'15/03/2026 14:30'
|
||||
);
|
||||
});
|
||||
|
||||
it('formats datetime in DD/MM/YYYY HH:MM for United Kingdom', () => {
|
||||
expect(formatDateTimeByCountry(testDate, 'GB')).toBe(
|
||||
'15/03/2026 14:30'
|
||||
);
|
||||
});
|
||||
|
||||
it('formats datetime in DD/MM/YYYY HH:MM for Germany', () => {
|
||||
expect(formatDateTimeByCountry(testDate, 'DE')).toBe(
|
||||
'15/03/2026 14:30'
|
||||
);
|
||||
});
|
||||
|
||||
it('formats datetime in DD/MM/YYYY HH:MM for Australia', () => {
|
||||
expect(formatDateTimeByCountry(testDate, 'AU')).toBe(
|
||||
'15/03/2026 14:30'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('MM/DD/YYYY HH:MM format (North America)', () => {
|
||||
it('formats datetime in MM/DD/YYYY HH:MM for United States', () => {
|
||||
expect(formatDateTimeByCountry(testDate, 'US')).toBe(
|
||||
'03/15/2026 14:30'
|
||||
);
|
||||
});
|
||||
|
||||
it('formats datetime in MM/DD/YYYY HH:MM for Canada', () => {
|
||||
expect(formatDateTimeByCountry(testDate, 'CA')).toBe(
|
||||
'03/15/2026 14:30'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('YYYY/MM/DD HH:MM format (East Asia)', () => {
|
||||
it('formats datetime in YYYY/MM/DD HH:MM for Japan', () => {
|
||||
expect(formatDateTimeByCountry(testDate, 'JP')).toBe(
|
||||
'2026/03/15 14:30'
|
||||
);
|
||||
});
|
||||
|
||||
it('formats datetime in YYYY/MM/DD HH:MM for South Korea', () => {
|
||||
expect(formatDateTimeByCountry(testDate, 'KR')).toBe(
|
||||
'2026/03/15 14:30'
|
||||
);
|
||||
});
|
||||
|
||||
it('formats datetime in YYYY/MM/DD HH:MM for China', () => {
|
||||
expect(formatDateTimeByCountry(testDate, 'CN')).toBe(
|
||||
'2026/03/15 14:30'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Time formatting', () => {
|
||||
it('uses 24-hour format for all countries', () => {
|
||||
const morning = new Date('2026-03-15T09:15:00');
|
||||
const afternoon = new Date('2026-03-15T15:45:00');
|
||||
const midnight = new Date('2026-03-15T00:00:00');
|
||||
const noon = new Date('2026-03-15T12:00:00');
|
||||
|
||||
expect(formatDateTimeByCountry(morning, 'US')).toBe(
|
||||
'03/15/2026 09:15'
|
||||
);
|
||||
expect(formatDateTimeByCountry(afternoon, 'US')).toBe(
|
||||
'03/15/2026 15:45'
|
||||
);
|
||||
expect(formatDateTimeByCountry(midnight, 'US')).toBe(
|
||||
'03/15/2026 00:00'
|
||||
);
|
||||
expect(formatDateTimeByCountry(noon, 'US')).toBe(
|
||||
'03/15/2026 12:00'
|
||||
);
|
||||
});
|
||||
|
||||
it('formats minutes with leading zero', () => {
|
||||
const date = new Date('2026-03-15T14:05:00');
|
||||
expect(formatDateTimeByCountry(date, 'GR')).toBe(
|
||||
'15/03/2026 14:05'
|
||||
);
|
||||
});
|
||||
|
||||
it('formats hours with leading zero for single digits', () => {
|
||||
const date = new Date('2026-03-15T08:30:00');
|
||||
expect(formatDateTimeByCountry(date, 'GR')).toBe(
|
||||
'15/03/2026 08:30'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Default and edge cases', () => {
|
||||
it('uses DD/MM/YYYY HH:MM as default format when country is null', () => {
|
||||
expect(formatDateTimeByCountry(testDate, null)).toBe(
|
||||
'15/03/2026 14:30'
|
||||
);
|
||||
});
|
||||
|
||||
it('uses DD/MM/YYYY HH:MM as default format when country is undefined', () => {
|
||||
expect(formatDateTimeByCountry(testDate, undefined)).toBe(
|
||||
'15/03/2026 14:30'
|
||||
);
|
||||
});
|
||||
|
||||
it('uses DD/MM/YYYY HH:MM as default format when country is unknown', () => {
|
||||
expect(formatDateTimeByCountry(testDate, 'UNKNOWN')).toBe(
|
||||
'15/03/2026 14:30'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -293,3 +293,141 @@ export const isTaskOverdueInTodayPlan = (task: {
|
|||
// are likely to have been sitting there for a while
|
||||
return createdDate.getTime() < yesterday.getTime();
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps ISO 3166-1 country codes to date format patterns.
|
||||
* Used to ensure consistent date formatting based on regional conventions,
|
||||
* independent of browser locale support.
|
||||
*
|
||||
* @returns Object mapping country code to date-fns format string
|
||||
*/
|
||||
const getCountryDateFormats = (): Record<string, string> => ({
|
||||
// DD/MM/YYYY format (most common globally - European standard)
|
||||
AT: 'dd/MM/yyyy', // Austria
|
||||
BE: 'dd/MM/yyyy', // Belgium
|
||||
BG: 'dd/MM/yyyy', // Bulgaria
|
||||
CH: 'dd/MM/yyyy', // Switzerland
|
||||
CY: 'dd/MM/yyyy', // Cyprus
|
||||
CZ: 'dd/MM/yyyy', // Czech Republic
|
||||
DE: 'dd/MM/yyyy', // Germany
|
||||
DK: 'dd/MM/yyyy', // Denmark
|
||||
EE: 'dd/MM/yyyy', // Estonia
|
||||
ES: 'dd/MM/yyyy', // Spain
|
||||
FI: 'dd/MM/yyyy', // Finland
|
||||
FR: 'dd/MM/yyyy', // France
|
||||
GB: 'dd/MM/yyyy', // United Kingdom
|
||||
GR: 'dd/MM/yyyy', // Greece
|
||||
HR: 'dd/MM/yyyy', // Croatia
|
||||
IE: 'dd/MM/yyyy', // Ireland
|
||||
IS: 'dd/MM/yyyy', // Iceland
|
||||
IT: 'dd/MM/yyyy', // Italy
|
||||
LT: 'dd/MM/yyyy', // Lithuania
|
||||
LU: 'dd/MM/yyyy', // Luxembourg
|
||||
LV: 'dd/MM/yyyy', // Latvia
|
||||
MT: 'dd/MM/yyyy', // Malta
|
||||
NL: 'dd/MM/yyyy', // Netherlands
|
||||
NO: 'dd/MM/yyyy', // Norway
|
||||
PL: 'dd/MM/yyyy', // Poland
|
||||
PT: 'dd/MM/yyyy', // Portugal
|
||||
RO: 'dd/MM/yyyy', // Romania
|
||||
SE: 'dd/MM/yyyy', // Sweden
|
||||
SI: 'dd/MM/yyyy', // Slovenia
|
||||
SK: 'dd/MM/yyyy', // Slovakia
|
||||
TR: 'dd/MM/yyyy', // Turkey
|
||||
RU: 'dd/MM/yyyy', // Russia
|
||||
|
||||
// DD/MM/YYYY format (Africa)
|
||||
DZ: 'dd/MM/yyyy', // Algeria
|
||||
EG: 'dd/MM/yyyy', // Egypt
|
||||
GH: 'dd/MM/yyyy', // Ghana
|
||||
KE: 'dd/MM/yyyy', // Kenya
|
||||
MA: 'dd/MM/yyyy', // Morocco
|
||||
NG: 'dd/MM/yyyy', // Nigeria
|
||||
TN: 'dd/MM/yyyy', // Tunisia
|
||||
ZA: 'dd/MM/yyyy', // South Africa
|
||||
|
||||
// DD/MM/YYYY format (Asia/Pacific)
|
||||
AU: 'dd/MM/yyyy', // Australia
|
||||
BD: 'dd/MM/yyyy', // Bangladesh
|
||||
FJ: 'dd/MM/yyyy', // Fiji
|
||||
GU: 'dd/MM/yyyy', // Guam
|
||||
HK: 'dd/MM/yyyy', // Hong Kong
|
||||
ID: 'dd/MM/yyyy', // Indonesia
|
||||
IN: 'dd/MM/yyyy', // India
|
||||
MY: 'dd/MM/yyyy', // Malaysia
|
||||
NZ: 'dd/MM/yyyy', // New Zealand
|
||||
PK: 'dd/MM/yyyy', // Pakistan
|
||||
SG: 'dd/MM/yyyy', // Singapore
|
||||
TH: 'dd/MM/yyyy', // Thailand
|
||||
|
||||
// DD/MM/YYYY format (Middle East)
|
||||
AE: 'dd/MM/yyyy', // United Arab Emirates
|
||||
IL: 'dd/MM/yyyy', // Israel
|
||||
IR: 'dd/MM/yyyy', // Iran
|
||||
SA: 'dd/MM/yyyy', // Saudi Arabia
|
||||
|
||||
// DD/MM/YYYY format (Americas - except US/CA)
|
||||
AR: 'dd/MM/yyyy', // Argentina
|
||||
BR: 'dd/MM/yyyy', // Brazil
|
||||
CL: 'dd/MM/yyyy', // Chile
|
||||
CO: 'dd/MM/yyyy', // Colombia
|
||||
MX: 'dd/MM/yyyy', // Mexico
|
||||
PE: 'dd/MM/yyyy', // Peru
|
||||
VE: 'dd/MM/yyyy', // Venezuela
|
||||
|
||||
// MM/DD/YYYY format (North America and some others)
|
||||
US: 'MM/dd/yyyy', // United States
|
||||
CA: 'MM/dd/yyyy', // Canada
|
||||
PH: 'MM/dd/yyyy', // Philippines
|
||||
|
||||
// YYYY/MM/DD format (East Asia - ISO-like format)
|
||||
CN: 'yyyy/MM/dd', // China
|
||||
JP: 'yyyy/MM/dd', // Japan
|
||||
KR: 'yyyy/MM/dd', // South Korea
|
||||
TW: 'yyyy/MM/dd', // Taiwan
|
||||
});
|
||||
|
||||
/**
|
||||
* Formats a date using country-specific format pattern.
|
||||
* This provides consistent date formatting independent of browser locale support.
|
||||
*
|
||||
* @param date - Date to format
|
||||
* @param country - ISO 3166-1 country code (optional)
|
||||
* @returns Formatted date string (e.g., "15/03/2026" for Greece, "03/15/2026" for US)
|
||||
*/
|
||||
export const formatDateByCountry = (
|
||||
date: Date,
|
||||
country?: string | null
|
||||
): string => {
|
||||
const formats = getCountryDateFormats();
|
||||
|
||||
// Use country-specific format if available, otherwise default to DD/MM/YYYY
|
||||
const formatPattern =
|
||||
country && formats[country] ? formats[country] : 'dd/MM/yyyy';
|
||||
|
||||
return format(date, formatPattern);
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats a datetime using country-specific date format + 24-hour time.
|
||||
* This provides consistent datetime formatting independent of browser locale support.
|
||||
*
|
||||
* @param date - Date to format
|
||||
* @param country - ISO 3166-1 country code (optional)
|
||||
* @returns Formatted datetime string (e.g., "15/03/2026 14:30" for Greece)
|
||||
*/
|
||||
export const formatDateTimeByCountry = (
|
||||
date: Date,
|
||||
country?: string | null
|
||||
): string => {
|
||||
const formats = getCountryDateFormats();
|
||||
|
||||
// Use country-specific format if available, otherwise default to DD/MM/YYYY
|
||||
const datePattern =
|
||||
country && formats[country] ? formats[country] : 'dd/MM/yyyy';
|
||||
|
||||
// Combine date pattern with 24-hour time format
|
||||
const datetimePattern = `${datePattern} HH:mm`;
|
||||
|
||||
return format(date, datetimePattern);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue