feat: add timezone support on date fields (#10896)

Adds support for timezone selection on date fields.

### Summary

New `admin.timezones` config:

```ts
{
  // ...
  admin: {
    // ...
    timezones: {
      supportedTimezones: ({ defaultTimezones }) => [
        ...defaultTimezones,
        { label: '(GMT-6) Monterrey, Nuevo Leon', value: 'America/Monterrey' },
      ],
      defaultTimezone: 'America/Monterrey',
    },
  }
}
```

New `timezone` property on date fields:

```ts
{
  type: 'date',
  name: 'date',
  timezone: true,
}
```

### Configuration

All date fields now accept `timezone: true` to enable this feature,
which will inject a new field into the configuration using the date
field's name to construct the name for the timezone column. So
`publishingDate` will have `publishingDate_tz` as an accompanying
column. This new field is inserted during config sanitisation.

Dates continue to be stored in UTC, this will help maintain dates
without needing a migration and it makes it easier for data to be
manipulated as needed. Mongodb also has a restriction around storing
dates only as UTC.

All timezones are stored by their IANA names so it's compatible with
browser APIs. There is a newly generated type for `SupportedTimezones`
which is reused across fields.

We handle timezone calculations via a new package `@date-fns/tz` which
we will be using in the future for handling timezone aware scheduled
publishing/unpublishing and more.

### UI

Dark mode

![image](https://github.com/user-attachments/assets/fcebdb7f-be01-4382-a1ce-3369f72b4309)

Light mode

![image](https://github.com/user-attachments/assets/dee2f1c6-4d0c-49e9-b6c8-a51a83a5e864)
This commit is contained in:
Paul
2025-02-10 20:02:53 +00:00
committed by GitHub
parent 3415ba81ac
commit 430ebd42ff
65 changed files with 1213 additions and 47 deletions

View File

@@ -6,6 +6,60 @@
* and re-run `payload generate:types` to regenerate this file.
*/
/**
* Supported timezones in IANA format.
*
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "supportedTimezones".
*/
export type SupportedTimezones =
| 'Pacific/Midway'
| 'Pacific/Niue'
| 'Pacific/Honolulu'
| 'Pacific/Rarotonga'
| 'America/Anchorage'
| 'Pacific/Gambier'
| 'America/Los_Angeles'
| 'America/Tijuana'
| 'America/Denver'
| 'America/Phoenix'
| 'America/Chicago'
| 'America/Guatemala'
| 'America/New_York'
| 'America/Bogota'
| 'America/Caracas'
| 'America/Santiago'
| 'America/Buenos_Aires'
| 'America/Sao_Paulo'
| 'Atlantic/South_Georgia'
| 'Atlantic/Azores'
| 'Atlantic/Cape_Verde'
| 'Europe/London'
| 'Europe/Berlin'
| 'Africa/Lagos'
| 'Europe/Athens'
| 'Africa/Cairo'
| 'Europe/Moscow'
| 'Asia/Riyadh'
| 'Asia/Dubai'
| 'Asia/Baku'
| 'Asia/Karachi'
| 'Asia/Tashkent'
| 'Asia/Calcutta'
| 'Asia/Dhaka'
| 'Asia/Almaty'
| 'Asia/Jakarta'
| 'Asia/Bangkok'
| 'Asia/Shanghai'
| 'Asia/Singapore'
| 'Asia/Tokyo'
| 'Asia/Seoul'
| 'Australia/Sydney'
| 'Pacific/Guam'
| 'Pacific/Noumea'
| 'Pacific/Auckland'
| 'Pacific/Fiji'
| 'America/Monterrey';
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "BlockColumns".
@@ -1081,6 +1135,29 @@ export interface DateField {
dayOnly?: string | null;
dayAndTime?: string | null;
monthOnly?: string | null;
defaultWithTimezone?: string | null;
defaultWithTimezone_tz?: SupportedTimezones;
/**
* This date here should be required.
*/
dayAndTimeWithTimezone: string;
dayAndTimeWithTimezone_tz: SupportedTimezones;
timezoneBlocks?:
| {
dayAndTime?: string | null;
dayAndTime_tz?: SupportedTimezones;
id?: string | null;
blockName?: string | null;
blockType: 'dateBlock';
}[]
| null;
timezoneArray?:
| {
dayAndTime?: string | null;
dayAndTime_tz?: SupportedTimezones;
id?: string | null;
}[]
| null;
updatedAt: string;
createdAt: string;
}
@@ -2745,6 +2822,29 @@ export interface DateFieldsSelect<T extends boolean = true> {
dayOnly?: T;
dayAndTime?: T;
monthOnly?: T;
defaultWithTimezone?: T;
defaultWithTimezone_tz?: T;
dayAndTimeWithTimezone?: T;
dayAndTimeWithTimezone_tz?: T;
timezoneBlocks?:
| T
| {
dateBlock?:
| T
| {
dayAndTime?: T;
dayAndTime_tz?: T;
id?: T;
blockName?: T;
};
};
timezoneArray?:
| T
| {
dayAndTime?: T;
dayAndTime_tz?: T;
id?: T;
};
updatedAt?: T;
createdAt?: T;
}