112 lines
4.7 KiB
Markdown
112 lines
4.7 KiB
Markdown
---
|
|
stage: none
|
|
group: unassigned
|
|
info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/development/development_processes/#development-guidelines-review.
|
|
title: Date and time
|
|
---
|
|
|
|
## Formatting
|
|
|
|
Our design guidelines, [Pajamas, states](https://design.gitlab.com/content/date-and-time):
|
|
|
|
> We can either display a localized time and date format based on the user's location or use a non-localized format following the ISO 8601 standard.
|
|
|
|
When formatting dates for the UI, use the `localeDateFormat` singleton as this localizes dates based on the user's locale preferences.
|
|
The logic for getting the locale is in the `getPreferredLocales` function in `app/assets/javascripts/locale/index.js`.
|
|
|
|
Avoid using the `formatDate` and `dateFormat` date utility functions as they do not format dates in a localized way.
|
|
|
|
```javascript
|
|
// good
|
|
const formattedDate = localeDateFormat.asDate.format(date);
|
|
|
|
// bad
|
|
const formattedDate = formatDate(date);
|
|
const formattedDate = dateFormat(date);
|
|
```
|
|
|
|
## Gotchas
|
|
|
|
When working with dates, you might encounter unexpected behavior.
|
|
|
|
### Date-only bug
|
|
|
|
There is a bug when passing a string of the format `yyyy-mm-dd` to the `Date` constructor.
|
|
|
|
From the [MDN Date page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date):
|
|
|
|
> When the time zone offset is absent, **date-only forms are interpreted as a UTC time and date-time forms are interpreted as local time**.
|
|
This is due to a historical spec error that was not consistent with ISO 8601 but could not be changed due to web compatibility.
|
|
|
|
When doing `new Date('2020-02-02')`, you might expect this to create a date like `Sun Feb 02 2020 00:00:00` in your local time.
|
|
However, due to this date-only bug, `new Date('2020-02-02')` is interpreted as UTC.
|
|
For example, if your time zone is UTC-8, this creates the date object at UTC (`Sun Feb 02 2020 00:00:00 UTC`) instead of local UTC-8 timezone, and is then converted to local UTC-8 timezone (`Sat Feb 01 2020 16:00:00 GMT-0800 (Pacific Standard Time)`).
|
|
When in a time zone behind UTC, this causes the parsed date to become a day behind, resulting in unexpected bugs.
|
|
|
|
There are a few ways to convert a date-only string to keep the same date:
|
|
|
|
- Use the `newDate` function, created specifically to avoid this bug, which is a wrapper around the `Date` constructor.
|
|
- Include a time component in the string.
|
|
- Use the `(year, month, day)` constructor.
|
|
|
|
Ideally, use the `newDate` function when creating a `Date` object so you don't have to worry about this bug.
|
|
|
|
```javascript
|
|
// good
|
|
|
|
// use the newDate function
|
|
import { newDate } from '~/lib/utils/datetime_utility';
|
|
newDate('2020-02-02') // Sun Feb 02 2020 00:00:00 GMT-0800 (Pacific Standard Time)
|
|
|
|
// add a time component
|
|
new Date('2020-02-02T00:00') // Sun Feb 02 2020 00:00:00 GMT-0800 (Pacific Standard Time)
|
|
|
|
// use the (year, month, day) constructor - month is 0-indexed (another source of possible bugs, yay!)
|
|
new Date(2020, 1, 2) // Sun Feb 02 2020 00:00:00 GMT-0800 (Pacific Standard Time)
|
|
|
|
// bad
|
|
|
|
// date-only string
|
|
new Date('2020-02-02') // Sat Feb 01 2020 16:00:00 GMT-0800 (Pacific Standard Time)
|
|
|
|
// using the static parse method with a date-only string
|
|
new Date(Date.parse('2020-02-02')) // Sat Feb 01 2020 16:00:00 GMT-0800 (Pacific Standard Time)
|
|
|
|
// using the static UTC method
|
|
new Date(Date.UTC(2020, 1, 2)) // Sat Feb 01 2020 16:00:00 GMT-0800 (Pacific Standard Time)
|
|
```
|
|
|
|
### Date picker
|
|
|
|
The `GlDatepicker` component returns a `Date` object at midnight local time.
|
|
This can cause issues in time zones ahead of UTC, for example with GraphQL mutations.
|
|
|
|
For example, in UTC+8:
|
|
|
|
1. You select `2020-02-02` in the date picker.
|
|
1. The `Date` object returned is `Sun Feb 02 2020 00:00:00 GMT+0800 (China Standard Time)` local time.
|
|
1. When sent to GraphQL, it's converted to the UTC string `2020-02-01T16:00:00.000Z`, which is a day behind.
|
|
|
|
To preserve the date, use `toISODateFormat` to convert the `Date` object to a date-only string:
|
|
|
|
```javascript
|
|
const dateString = toISODateFormat(dateObject); // "2020-02-02"
|
|
```
|
|
|
|
## Testing
|
|
|
|
### Manual testing
|
|
|
|
When performing manual testing of dates, such as when reviewing merge requests, test with time zones behind and ahead of UTC, such as UTC-8, UTC, and UTC+8, to spot potential bugs.
|
|
|
|
To change the time zone on macOS:
|
|
|
|
1. Go to **System Settings > General > Date & Time**.
|
|
1. Clear the **Set time zone automatically using your current location** checkbox.
|
|
1. Change **Closest city** to a city in another time zone, such as Sacramento, London, or Beijing.
|
|
|
|
### Jest
|
|
|
|
Our Jest tests are run with a mocked date of 2020-07-06 for determinism, which can be overridden using the `useFakeDate` function.
|
|
The logic for this is in `spec/frontend/__helpers__/fake_date/fake_date.js`.
|