Skip to main content

Timezone 설정에 대한 가이드라인

글로벌 서비스를 만들면 시간에 대해서 잘 고려해서 다루어야 한다.

  • 시간에 대한 절대값 개념
  • 시간에 대한 상대적 표현 개념
  • 시간에 대한 해석

위 3가지가 주요 컨셉이다.

1.시간 객체에 대해서 유닉스 타임 스템프가 고정되어 있는가?

📌 시간의 절대값은 유닉스 타임 스템프이다.

  • 유닉스 타임 스탬프 : 1970년 1월 1일 00:00:00 협정 세계시(UTC)부터 현재까지 경과된 시간을 초(second) 단위로 나타낸 정수
  • 유닉스 타임 스템프 고정 전과 후로 나누어서 생각하면 편하다.
  • 절대값이 고정되면 로컬 브라우저의 타임존에 맞게 표기하는 문제로 넘어갈 수 있다.

📌 예) 대만 서비스를 진행하는 상황을 가정.

절대값 : 2025-10-14 07:00 (GMT+0)은 고정된 상태의 시간 객체를 다룬다고 생각해보자.

1.사용자-facing 화면

  • 당연히 대만 사용자에게는 본인의 시간대인 UTC+8 기준으로 표기한다.
    • → 예: 2025-10-14 15:00 (GMT+8)

2.운영자-facing 화면 (Admin, Dashboard 등)

문제는 운영자 화면에서 어떻게 표기 할 것인가?

선택지 1. 내부 운영 효율을 위해 대만 사용자가 보는 그대로 표기한다.

  • 브라우저의 로컬 시간대는 UTC+9이지만, 강제로 UTC+8로(사업하는 곳의 현지시간) 변경하여 처리한다.

선택지 2. 접속한 사용자의 브라우저의 시간대에 맞추어서 표기한다.

  • 장점은 KST 로컬 사용자의 입장에서 시간대를 고려해서 표기가 가능하다.
  • 예를들어 대만에서는 아침 12시부터 가게가 오픈하는데, 한국시간 기준으로 보면 13시인 상황이다.
  • 사용자 경험에서 현지 사업장 기준으로 생각하니, 대만 지점 영업 시작: 12:00 (NST). (13:00 KST) 으로 2개 표기도 방법 이겠다.

선택지 3. 타임존 변경 옵션을 주기

  • 보통 에이전시 등 해외에서 현지의 사업을 관리한다면 기본적으로 로컬 타임존으로 표기하되, 변경 옵션을 주는것도 좋겠다.
  • 표기는 현지 시간 표기 + 로컬 브라우저 타임존 시간 표기 2가지 진행 ( Localization + Clarity 충족 )

2.각 모듈별로 타임존 설정 어떻게 하는지 코드 수준에서 필요.

1.현재 타임을 생성하는 로직

  • 현재 시간을 생성하면 시간의 절대값은 모두 동일하다, 어떤 타임존으로 표기 의 문제

2.타임을 해석(생성)하는 로직

  • 매우 중요하다.
  • 특히나 타임존 정보가 없는 시간정보를 해석할때 무엇으로 해석하느냐에 따라서 시간 버그가 발생할 수 있다.
  • 2.1 타임존이 있는 경우
    • '2025-10-23T14:30:06.117Z' 처럼 타임존 정보가 포함된 문자열은 알아서 처리 된다.
  • 2.2 타임존이 없는 경우
    • '2025-10-23 14:30:06' 처럼 타임존 정보가 없다면 이를 UTC+9로 해석할지 설정을 해야 한다.
    • 자주 발생하는 버그케이스
      • 서버에서 UTC+0으로 저장된 문자열 '2025-10-23 14:30:06'을 브라우저에서 받고, 이를 UTC+0이 아닌, 로컬 타임존으로 해석하는 경우.

3.현재 타임을 표기하는 로직

  • 기본 : 로컬 브라우저의 타임존에 맞게 표기.
import dayjs, { type Dayjs } from 'dayjs';

import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
import customParseFormat from 'dayjs/plugin/customParseFormat';

dayjs.extend(utc);
dayjs.extend(timezone);
// 타임존 없는 문자열을 특정 포맷으로 해석(parse)하기 위해 필요합니다.
dayjs.extend(customParseFormat);

// 현지 사업장 타임존 설정 (예시: Asia/Taipei)
const TIMEZONE = 'Asia/Taipei';

// dayjs는 여러 포맷을 배열로 받으면 순서대로 파싱을 시도합니다.
const DATE_FORMATS = [
'YYYY-MM-DD HH:mm:ss', // "2020-12-20 15:15:12"
'YYYY-MM-DD', // "2020-12-20"
];

type DayjsInput = string | number | Date | Dayjs | null | undefined;

/**
* 마켓 설정에 따른 타임존을 적용한 Dayjs 객체를 반환합니다.
* 타임존 정보가 없는 문자열은 'Asia/Taipei'를 기준으로 해석합니다.
*
* @param date - dayjs 생성자에 전달할 인자 (날짜/시간 문자열, Date 객체, Unix timestamp 등)
* @returns 타임존이 적용된 Dayjs 객체
*/
export function dayjsTz(date?: DayjsInput): Dayjs {
const timezone = TIMEZONE as string | undefined;

// 타임존 설정이 없는 경우 (로컬 또는 기본 설정 사용)
if (!timezone) {
return dayjs(date);
}

// 1) 인자 없음 또는 null/undefined: 현재 시각을 마켓 타임존으로 표시
if (date === undefined || date === null) {
return dayjs().tz(timezone);
}

// 2) 문자열 인자 처리
if (typeof date === 'string') {
// 타임존 정보가 포함된 문자열인지 확인 (Z, GMT, +09:00 등이 포함된 ISO/GMT 문자열)
// 이 경우, dayjs는 문자열을 UTC로 해석 후 tz()를 통해 TIMEZONE으로 변환합니다.
if (/(Z|[+-]\d{2}(:?\d{2})?|GMT)/i.test(date)) {
return dayjs.tz(date, timezone);
}

// 타임존 정보가 없는 문자열 ("2020-12-20", "2020-12-20 15:15:12" 등)
// - dayjs.tz(string, formats, timezone)를 사용하여 해당 타임존 기준으로 해석(parse)합니다.
// - customParseFormat 플러그인이 필요합니다.
return dayjs.tz(date, DATE_FORMATS, timezone);
}

// 3) Date 객체, Dayjs 객체, 숫자 (timestamp) 등의 인자 처리
// - dayjs로 생성 후 .tz()를 적용하여 해당 타임존으로 변환합니다.
return dayjs(date).tz(timezone);
}



/**
* UTC 기준 시간 문자열을 지정된 타임존으로 변환합니다.
* @param {string|Date} createdAt - UTC 기준 시간 (ISO 문자열 또는 Date 객체)
* @param {string} timeZone - 변환할 타임존 (기본값: 'Asia/Seoul')
* @returns {dayjs.Dayjs} - 타임존이 적용된 Day.js 객체
*/
function interpretAsUTC(createdAt, timeZone = 'Asia/Seoul') {
return dayjs.utc(createdAt).tz(timeZone);
}


/**
* 로컬 타임존 기준 시간 문자열을 지정된 타임존으로 변환합니다.
* @param {string|Date} createdAt - 로컬 타임존 기준 시간 (ISO 문자열 또는 Date 객체)
* @param {string} timeZone - 변환할 타임존 (기본값: 'Asia/Seoul')
* @returns {dayjs.Dayjs} - 타임존이 적용된 Day.js 객체
*/
function interpretAsTimezone(createdAt, timeZone = 'Asia/Seoul') {
return dayjs(createdAt).tz(timeZone);
}

추가 고려 사항

커스텀 Timezone 처리

  • 커스텀 처리 할 Timezone 변수를 데이터 패칭 이후 External Store Sync에 대한 타이밍 이슈가 굉장히 중요.

캘린더 정책

  • AntD 는 moment객체를 사용하는데 moment.tz()의 타임존에 따라서 표기되는 캘린더의 UI가 달라진다.
  • 사용자 표기 타임존에 대한 정책도 필요하다.