`Calendar.TimeZoneDatabase`
Calendar.TimeZoneDatabase
타임존 데이터를 제공하기 위한 behaviour를 정의하는 모듈이에요.
IANA(Internet Assigned Numbers Authority)는 각 타임존의 UTC 오프셋과 표준 오프셋에 대한 데이터를 포함한 타임존 데이터를 제공합니다.
본문
타임존은 "이 지역의 현재 시각이 UTC와 얼마나 다른가"를 규정하는 규칙의 묶음이에요. 그런데 이 규칙은 계절에 따라 바뀌기도 하고(일광 절약 시간), 역사적으로도 여러 번 조정되어서 단순한 상수로 표현할 수 없어요. IANA가 배포하는 타임존 데이터는 바로 이런 복잡한 규칙을 정리한 것입니다. Calendar.TimeZoneDatabase는 이 데이터를 Elixir의 Calendar가 이해하는 형태로 제공하기 위한 계약(behaviour)의 정의예요.
이 behaviour의 핵심 타입은 time_zone_period예요. 특정 기간 동안 어떤 UTC 오프셋, 표준 오프셋, 존 약어(zone abbreviation) 조합이 유효한지를 담은 값입니다.
@type time_zone_period() :: %{
optional(any()) => any(),
utc_offset: Calendar.utc_offset(),
std_offset: Calendar.std_offset(),
zone_abbr: Calendar.zone_abbr()
}
예를 들어 Europe/London 타임존의 2018년 여름은 일광 절약 시간(summer time)이 적용되는 기간이에요. 봄부터 가을까지는 여름 기간이라 std_offset이 특정 값이고, 가을이 되면 std_offset과 zone_abbr이 함께 바뀌면서 겨울에는 또 다른 period가 필요해지죠. 이런 식으로 한 타임존 안에서도 여러 period가 번갈아 나타납니다.
time_zone_period_limit은 period가 시작되거나 끝나는 시점을 나타내는 타입이에요.
@type time_zone_period_limit() :: Calendar.naive_datetime()
시작 시점은 포함되고, 종료 시점은 포함되지 않아요. 예를 들어 2015-03-29 01:00:00부터 2015-10-25 01:00:00까지인 period는 2015-03-29 01:00:00의 시작을 포함해 그 시점부터, 2015-10-25 01:00:00 직전까지 지속됩니다. 일부 period의 시작이나 끝은 무한할 수도 있어요. DST가 없고 변경 계획도 없는 타임존의 최신 period가 그런 경우죠. 다만 이 behaviour의 목적상, 이 무한한 한계는 벽시계(wall time)의 갭(gap)에서 필요한 특정 시점으로만 사용됩니다.
이 behaviour는 두 가지 콜백(callback)을 정의해요. 먼저 time_zone_period_from_utc_iso_days/2입니다.
@callback time_zone_period_from_utc_iso_days(Calendar.iso_days(), Calendar.time_zone()) ::
{:ok, time_zone_period()}
| {:error, :time_zone_not_found | :utc_only_time_zone_database}
이 콜백은 특정 타임존에서 특정 UTC 시점에 해당하는 타임존 period를 돌려줍니다. 타임존 이름과 UTC의 어떤 시점을 받아서, 그 시점의 time_zone_period를 반환하죠. (v1.8.0부터 제공)
두 번째 콜백은 time_zone_periods_from_wall_datetime/2예요.
@callback time_zone_periods_from_wall_datetime(
Calendar.naive_datetime(),
Calendar.time_zone()
) ::
{:ok, time_zone_period()}
| {:ambiguous, time_zone_period(), time_zone_period()}
| {:gap, {time_zone_period(), time_zone_period_limit()},
{time_zone_period(), time_zone_period_limit()}}
| {:error, :time_zone_not_found | :utc_only_time_zone_database}
이 콜백은 특정 타임존과 벽시계(wall clock) 날짜·시간에 대해 가능한 타임존 period들을 돌려줍니다. 주어진 naive datetime이 모호(ambiguous)할 때, 즉 두 개의 가능한 해석이 있을 때는 :ambiguous와 함께 두 period를 튜플로 돌려주는데, 이때 첫 요소가 더 먼저 시작하는 period여야 해요.
주어진 naive datetime이 갭(gap)에 있는 경우도 있어요. 겨울에서 여름으로 넘어가는 "spring forward" 시점처럼 실제로 존재하지 않는 시각이 생길 때죠. 이때는 :gap과 함께 두 period와 각각의 한계를 중첩 튜플로 돌려줍니다. 첫 번째 중첩 2-튜플은 갭 이전의 period와 그 period가 끝나는 시점(wall time)이 되고, 두 번째 중첩 2-튜플은 갭 직후의 period와 그 period가 시작되는 시점(wall time)입니다.
주어진 datetime에 가능한 period가 하나뿐이라면 :ok와 함께 그 time_zone_period를 돌려줍니다.
즉 Calendar.TimeZoneDatabase는 "Elixir의 Calendar가 타임존을 제대로 처리하려면 어떤 함수를 어떻게 구현해야 하는가"를 규정하는 틀이에요. 이를 구현해 두면 Calendar가 타임존 변환, 모호한 시각의 해석, DST 갭 처리 등을 일관된 방식으로 수행할 수 있게 됩니다.
더 알아보기
- 실제로 이 behaviour를 공식적으로 구현한
Calendar.UTCOnlyTimeZoneDatabase예시를 참고하세요. - 사용할 타임존 데이터베이스를 지정하는 방법은
Calendar모듈 문서에서 다룹니다. - IANA 타임존 데이터의 형식과 규칙은 tz database 공식 문서에서 더 자세히 확인할 수 있어요.