gitlab-ce/doc/development/fe_guide/date_and_time.md

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`.