날짜 및 시간
GitLab v19.4요약
디자인 가이드라인인 Pajamas는 다음과 같이 안내합니다. 사용자의 위치를 기준으로 현지화된 시간 및 날짜 형식을 표시하거나, ISO 8601 표준을 따르는 비현지화 형식을 사용할 수 있습니다. UI에 표시할 날짜의 형식을 지정할 때는 localeDateFormat 싱글턴을 사용합니다.
포맷 지정#
디자인 가이드라인인 Pajamas는 다음과 같이 안내합니다.
사용자의 위치를 기준으로 현지화된 시간 및 날짜 형식을 표시하거나, ISO 8601 표준을 따르는 비현지화 형식을 사용할 수 있습니다.
UI에 표시할 날짜의 형식을 지정할 때는 localeDateFormat 싱글턴을 사용합니다. 사용자의 로케일 설정에 맞춰 날짜를 현지화하기 때문입니다.
로케일을 가져오는 로직은 app/assets/javascripts/locale/index.js의 getPreferredLocales 함수에 있습니다.
formatDate 및 dateFormat 날짜 유틸리티 함수는 날짜를 현지화된 형식으로 지정하지 않으므로 사용을 피합니다.
// good
const formattedDate = localeDateFormat.asDate.format(date);
// bad
const formattedDate = formatDate(date);
const formattedDate = dateFormat(date);
주의 사항#
날짜를 다루다 보면 예상하지 못한 동작을 만날 수 있습니다.
Date-only 버그#
yyyy-mm-dd 형식의 문자열을 Date 생성자에 전달할 때 발생하는 버그가 있습니다.
MDN Date 페이지의 설명은 다음과 같습니다.
타임존 오프셋이 없으면 date-only 형식은 UTC 시간으로, date-time 형식은 로컬 시간으로 해석됩니다. 이는 ISO 8601과 일치하지 않는 과거 명세의 오류에서 비롯되었으나, 웹 호환성 때문에 변경하지 못했습니다.
new Date('2020-02-02')를 실행하면 로컬 시간 기준으로 Sun Feb 02 2020 00:00:00 같은 날짜가 생성될 것으로 예상할 수 있습니다.
그러나 이 date-only 버그 때문에 new Date('2020-02-02')는 UTC로 해석됩니다.
예를 들어 타임존이 UTC-8 이라면, 로컬 UTC-8 타임존이 아니라 UTC 기준(Sun Feb 02 2020 00:00:00 UTC)으로 날짜 객체가 생성된 뒤 로컬 UTC-8 타임존(Sat Feb 01 2020 16:00:00 GMT-0800 (Pacific Standard Time))으로 변환됩니다.
UTC보다 뒤처진 타임존에서는 파싱된 날짜가 하루 이전이 되어 예상하지 못한 버그로 이어집니다.
date-only 문자열을 같은 날짜로 유지하면서 변환하는 방법은 다음과 같습니다.
- 이 버그를 피하기 위해 만들어진
newDate함수를 사용합니다.Date생성자를 감싼 래퍼입니다. - 문자열에 시간 구성 요소를 포함합니다.
(year, month, day)생성자를 사용합니다.
가능하면 Date 객체를 만들 때 newDate 함수를 사용해 이 버그를 신경 쓰지 않아도 되게 합니다.
// 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)
날짜 선택기#
GlDatepicker 컴포넌트는 로컬 시간 자정을 가리키는 Date 객체를 반환합니다.
UTC보다 앞선 타임존에서는 이 때문에 문제가 생길 수 있으며, GraphQL 뮤테이션이 그런 예입니다.
예를 들어 UTC+8에서는 다음과 같이 동작합니다.
- 날짜 선택기에서
2020-02-02를 선택합니다. - 반환되는
Date객체는 로컬 시간Sun Feb 02 2020 00:00:00 GMT+0800 (China Standard Time)입니다. - GraphQL로 전송되면 하루 이전인 UTC 문자열
2020-02-01T16:00:00.000Z로 변환됩니다.
날짜를 보존하려면 toISODateFormat으로 Date 객체를 date-only 문자열로 변환합니다.
const dateString = toISODateFormat(dateObject); // "2020-02-02"
테스트#
수동 테스트#
머지 리퀘스트를 리뷰할 때처럼 날짜를 수동으로 테스트할 때는 UTC-8, UTC, UTC+8처럼 UTC보다 뒤처진 타임존과 앞선 타임존에서 함께 테스트해 잠재적 버그를 찾습니다.
macOS에서 타임존을 변경하는 방법은 다음과 같습니다.
- System Settings > General > Date & Time으로 이동합니다.
- Set time zone automatically using your current location 체크박스를 해제합니다.
- Closest city를 Sacramento, London, Beijing처럼 다른 타임존의 도시로 변경합니다.
Jest#
Jest 테스트는 결정성을 위해 2020-07-06으로 모킹된 날짜로 실행되며, useFakeDate 함수로 재정의할 수 있습니다.
이에 대한 로직은 spec/frontend/__helpers__/fake_date/fake_date.js에 있습니다.