Date field format display (#11384)

## Introduction

This PR enables functionality discussed in [Layout Date
Formatting](https://github.com/twentyhq/core-team-issues/issues/97).

### TLDR;
It enables greater control of date formatting at the object's field
level by upgrading all DATE and DATE_TIME fields' settings from:

```ts
{
    displayAsRelativeDate: boolean
}
```

to:

```ts

type FieldDateDisplayFormat = 'full_date' | 'relative_date' | 'date' | 'time' | 'year' | 'custom'

{
    displayFormat: FieldDateDisplayFormat
}
```

PR also includes an upgrade command that will update any existing DATE
and DATE_TIME fields to the new settings value

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
oliver
2025-04-18 01:00:02 -06:00
committed by GitHub
parent dd0ea2366f
commit 53042cc9dc
33 changed files with 690 additions and 250 deletions

View File

@ -0,0 +1,50 @@
import { formatDateISOStringToCustomUnicodeFormat } from '@/localization/utils/formatDateISOStringToCustomUnicodeFormat';
import { formatInTimeZone } from 'date-fns-tz';
jest.mock('date-fns-tz');
describe('formatDateISOStringToCustomUnicodeFormat', () => {
const mockDate = '2023-08-15T10:30:00Z';
const mockTimeZone = 'America/New_York';
const mockTimeFormat = 'HH:mm';
beforeEach(() => {
jest.resetAllMocks();
});
it('should use provided timezone', () => {
formatInTimeZone.mockReturnValue('06:30');
const result = formatDateISOStringToCustomUnicodeFormat(
mockDate,
mockTimeZone,
mockTimeFormat,
);
expect(formatInTimeZone).toHaveBeenCalledWith(
new Date(mockDate),
mockTimeZone,
mockTimeFormat,
);
expect(result).toBe('06:30');
});
it('should gracefully handle errors', () => {
formatInTimeZone.mockImplementation(() => {
throw new Error();
});
const result = formatDateISOStringToCustomUnicodeFormat(
mockDate,
mockTimeZone,
'f',
);
expect(formatInTimeZone).toHaveBeenCalledWith(
new Date(mockDate),
mockTimeZone,
'f',
);
expect(result).toBe('Invalid format string');
});
});

View File

@ -0,0 +1,13 @@
import { formatInTimeZone } from 'date-fns-tz';
export const formatDateISOStringToCustomUnicodeFormat = (
date: string,
timeZone: string,
dateFormat: string,
) => {
try {
return formatInTimeZone(new Date(date), timeZone, dateFormat);
} catch (e) {
return 'Invalid format string';
}
};

View File

@ -6,5 +6,5 @@ export const formatDateISOStringToDate = (
timeZone: string,
dateFormat: DateFormat,
) => {
return formatInTimeZone(new Date(date), timeZone, `${dateFormat}`);
return formatInTimeZone(new Date(date), timeZone, dateFormat);
};

View File

@ -0,0 +1,10 @@
import { format } from 'date-fns';
export const validateCustomDateFormat = (formatString: string): boolean => {
try {
format(new Date(), formatString);
return true;
} catch {
return false;
}
};