tududi/frontend/utils/dateUtils.test.ts
Chris 2444e36f47
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
2026-03-21 18:47:33 +02:00

249 lines
9.2 KiB
TypeScript

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'
);
});
});
});