Date & Time

Core React supports JS Date and time in Zulu relative format, the style ending with Z e.g. "2021-01-01T00:00:00.000Z" is 12AM the start of a new day in London; the start of a new day in Los Angeles PST would be "2021-01-01T08:00:00.000Z". You can see your computer's local time in Zulu relative format by running new Date().toISOString().

Core uses date-fns, date-fns-tz, and the brower's Intl APIs. In order to support IE11, client applications will require a polyfill to enable time zone features.

The JavaScript community discourages instantiating Date objects and Date.parse with a string, for example Date.new('2018-12-07T03:24:00'), due to inconsistent and poorly documented browser behavior. For consistent and well documented parsing of date time strings to Date objects, we recommend using the parse method in date-fns. We don't use moment.js, but toDate() should work. The date constructor also accepts numbers from year -> milliseconds.

When dealing with time zones, the implementor's dates can be stored in relation to a time zone and Core React will adjust accordingly. Anything inside a DateTimeProvider with a timeZone set, will act as if it was in that time zone, on display and on select will shift the date time. When displaying time, a time zone identifier is always necessary. While some providers typically have only one instance near the top of the tree like I18nProvider, a client could have several DateTimeProviders throughout. Below are two options of composition, pick a path that makes most sense for the data backing a component.

Components that are on the 'no shift' path and display a time will always display the time zone identifier of the current computer's location.

Inside - out

Outside - in

A DateTimeProvider resets a parent provider, setting things undefined again. All components and hooks should be aware of time zone shifting opt in and opt out:

  • Calendar
  • DateSelect
  • useDateTime
Time Zone Console Logging
Enable time zone console logging by setting the environment variable TZ_LOG to true. This can be used during development to compare inputs and outputs of date shifting.

Polyfill

Time zone support is partially unavailable for IE 11, which can be restored with date-time-format-timezone polyfill. Each client (not library) must determine if the application needs to support IE 11, if yes include the polyfill. Please note that this polyfill has an extremely large bundle size and may make your application slower, so use with caution and serve to a user's device only a single time. If opting for a different polyfill form the one list here, be sure to add support for more than the en locale.

For library packages:

  • list "date-time-format-timezone" as a peerDependency
  • do not bundle the polyfill
  • document instructing clients to add "date-time-format-timezone" as a dependency

To add the polyfill on a client applcation, you should:

  1. Add the following dependency to your project.
  1. Add the below line to your list of imports once

Displaying Time Zone Dates

Working with date-fns-tz utcToZonedTime

This function does object shifts, get a date/time in the local time of any time zone from UTC time. Read more from documentation or continue to read below as the browser can do this automatically when needing to display a date with Intl.DateTimeFormat.

Working with Intl.DateTimeFormat

Each instance is a single format to display date and/or time.

Providing a timeZone to Intl.DateTimeFormat will time zone shift. Time zone shifting sounds complex but the concept is how we reference time at locations day-to-day. Given a date time that starts at midnight which took place in a specific time zone:

  • Without a time zone shift:
    • time is direct to the current user's time zone
    • time is midnight (in UTC)
    • time zone label is the current user's time zone
  • With a time zone shift:
    • time is relative to the current user's time zone
    • when midnight at current user's location, what time is it the in other time zone (in UTC)
    • time zone label is the destination's time zone

Midnight time is centered around UTC, both paths will have an hour/date offset from UTC. Dates are if they happened in the destination time zone, with that time zone's time relative to UTC. Only a destination and user in London would see a time of all zeros to represent midnight if time zones are respected.

Intl Examples

Examples in California Standard Time Zone
Static code samples using new Date were ran in California during winter when clocks 'fallback' to standard time PST -8, not daylight savings time PDT -7. If you run new Date() locally, it will be relative to your time zone.
timeZoneName: 'short'

Now always display the time zone name, per Core Design System spec.

You can negate and avoid time zone shifting by passing undefined (by adding a new DateTimeProvider). If the time zone is undefined, it will not shift but warning: if displaying the time, the label of the time zone will be the current user's location which could be incorrect.

Working with useDateTime

Building off the Intl API and knowning a I18nProvider and DateTimeProvider context, this hook gives names to preset configurations.

Some variations go beyond the browser defaults to create custom formats.

Read more on useDateTime.

Selecting Time Zone Dates

Working with date-fns-tz zonedTimeToUtc

JS new Date() is the current computers location. The zonedTimeToUtc function makes it seem like the computer was in that time zone. Given a date and any time zone, returns a Date with the equivalent UTC time.

A picker's on select time zone shift is like flying your computer to the destination and calling new Date().

Working with DateSelect

Knowing that JS new Date() calls relate to the current computers location, given a wider time zone range this can result in off by 1 days:

  • a project is in Sydney
  • a professional using Procore is in Los Angeles
  • Sydney is 19 hours ahead of Los Angeles PST

In the demo below, first notice how the same new Date at noon UTC displays different values. Australia's day starts earlier than England's, so dates seem like they are 'eager'. Next select the same day and notice the stored values are different. The person in Los Angeles clicks Friday in local California time. This time happened 19 hours ago in the project time zone. In the demo, the component applys a time shift and saves the value with the shift (in UTC time) but displays as the original date clicked.

DateSelect Demo

Calendar Demo

The current day is different too, today in the calendar will be today in the time zone.

Locales

Display Formats

Locale: en

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


Locale: en-CA

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


Locale: fr-CA

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


Locale: fr-FR

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


Locale: es

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


Locale: en-AE

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


Locale: en-AU

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


Locale: en-GB

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


Locale: en-SG

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


Locale: ko

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


Locale: th-TH

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


Locale: pt

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


Locale: is-IS

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


Locale: de-DE

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


Locale: pseudo

abbr-weekday-abbr-date:
abbr-weekday-abbr-date-at-time:
date:
date-at-time:
numeric-date:
numeric-date-at-time:
time:
time-on-abbr-weekday-abbr-date:
time-on-weekday-date:
time-on-date:
time-on-numeric-date:
weekday-date:
weekday-date-at-time:
none:


DateSelect Formats

Locale: en

mm
dd
yyyy

Locale: en-CA

mm
dd
yyyy

Locale: fr-CA

jj
mm
aaaa

Locale: fr-FR

jj
mm
aaaa

Locale: es

dd
mm
aaaa

Locale: en-AE

dd
mm
yyyy

Locale: en-AU

dd
mm
yyyy

Locale: en-GB

dd
mm
yyyy

Locale: en-SG

dd
mm
yyyy

Locale: ko

yyyy
mm
dd

Locale: th-TH

dd
mm
yyyy

Locale: pt

dd
mm
aaaa

Locale: is-IS

dd
mm
áááá

Locale: de-DE

tt
MM
jjjj

Locale: pseudo

dd
mm
yyyy


Calendar Formats

Locale: en

Nov
2022
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10

Locale: en-CA

Nov
2022
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10

Locale: fr-CA

nov.
2022
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11

Locale: fr-FR

nov.
2022
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11

Locale: es

nov
2022
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11

Locale: en-AE

Nov
2022
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9

Locale: en-AU

Nov
2022
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10

Locale: en-GB

Nov
2022
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11

Locale: en-SG

Nov
2022
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10

Locale: ko

11월
2022
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10

Locale: th-TH

พ.ย.
2022
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10

Locale: pt

nov
2022
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10

Locale: is-IS

nóv.
2022
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11

Locale: de-DE

Nov.
2022
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11

Locale: pseudo

Nov
2022
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10