타임스탬프 타입
타임스탬프 타입
타임스탬프는 시간상의 한 지점을 나타내요. 그래서 DATE와 TIME 정보를 결합합니다. 타입 이름 뒤에 ISO 8601 형식 YYYY-MM-DD hh:mm:ss[.zzzzzzzzz][+-TT[:tt]]에 따라 형식화된 문자열을 붙여 만들 수 있으며, 이 형식은 이 문서에서도 사용하는 형식이에요. 지원 정밀도를 넘는 소수 자리는 무시됩니다.
출처: 문서
본문
타임스탬프 타입
| 이름 | 별칭 | 설명 |
|---|---|---|
TIMESTAMP_NS |
나노초 정밀도의 naive 타임스탬프 | |
TIMESTAMP |
DATETIME, TIMESTAMP WITHOUT TIME ZONE |
마이크로초 정밀도의 naive 타임스탬프 |
TIMESTAMP_MS |
밀리초 정밀도의 naive 타임스탬프 | |
TIMESTAMP_S |
초 정밀도의 naive 타임스탬프 | |
TIMESTAMPTZ |
TIMESTAMP WITH TIME ZONE |
마이크로초 정밀도의 시간대 인식 타임스탬프 |
경고 — 현재
TIMESTAMP_NS WITH TIME ZONE데이터 타입이 없으므로, 나노초 정밀도와WITH TIME ZONE의미를 가진 외부 컬럼(예:isAdjustedToUTC=true인 Parquet 타임스탬프 컬럼)은TIMESTAMP WITH TIME ZONE으로 변환되어 DuckDB로 읽을 때 정밀도를 잃어요.
SELECT TIMESTAMP_NS '1992-09-20 11:30:00.123456789';
1992-09-20 11:30:00.123456789
SELECT TIMESTAMP '1992-09-20 11:30:00.123456789';
1992-09-20 11:30:00.123456
SELECT TIMESTAMP_MS '1992-09-20 11:30:00.123456789';
1992-09-20 11:30:00.123
SELECT TIMESTAMP_S '1992-09-20 11:30:00.123456789';
1992-09-20 11:30:00
SELECT TIMESTAMPTZ '1992-09-20 11:30:00.123456789';
1992-09-20 11:30:00.123456+00
SELECT TIMESTAMPTZ '1992-09-20 12:30:00.123456789+01:00';
1992-09-20 11:30:00.123456+00
DuckDB는 WITHOUT TIME ZONE과 WITH TIME ZONE(현재 유일한 대표자는 TIMESTAMP WITH TIME ZONE) 타임스탬프를 구분해요.
이름과 달리, TIMESTAMP WITH TIME ZONE은 시간대 정보를 저장하지 않아요. 대신 Unix epoch 1970-01-01 00:00:00+00 이후의 윤초를 제외한 INT64 마이크로초 수만 저장하며, 따라서 절대 시간의 한 지점, 즉 instant를 모호함 없이 식별합니다. 시간대 인식과 WITH TIME ZONE이라는 라벨이 붙은 이유는 이 타입에 대한 타임스탬프 산술, 빈닝, 문자열 형식화가 구성된 시간대에서 수행되기 때문이에요. 이는 기본적으로 시스템 시간대로, 위 예시들에서는 그냥 UTC+00:00일 뿐이에요.
대응하는 TIMESTAMP WITHOUT TIME ZONE은 같은 INT64를 저장하지만, 산술·빈닝·문자열 형식화는 오프셋이나 시간대 없이 협정 세계시(UTC)의 간단한 규칙을 따릅니다. 따라서 TIMESTAMP는 UTC 타임스탬프로 해석될 수 있지만, 더 흔하게는 지정되지 않은 시간대에서 기록된 시간의 로컬 관측을 나타내는 데 사용되며, 이 타입에 대한 연산은 명목상 시간적 논리를 따라 튜플 필드를 조작하는 것으로 해석될 수 있어요.
이런 관측을 시간대 사양이나 UTC 오프셋 없이 원시 문자열로 저장했을 수 있는데, 이를 모호함 없는 TIMESTAMP WITH TIME ZONE instant로 명확히 하는 것은 흔한 데이터 정리 문제예요. 한 가지 해결책은 문자열에 UTC 오프셋을 추가한 뒤 명시적으로 TIMESTAMP WITH TIME ZONE으로 캐스팅하는 것이고, 다른 방법은 먼저 TIMESTAMP WITHOUT TIME ZONE을 만든 다음 시간대 사양과 결합해 시간대 인식 TIMESTAMP WITH TIME ZONE을 얻는 것이에요.
문자열과 Naive/시간대 인식 타임스탬프 간 변환
UTC 오프셋이나 IANA 시간대 이름 없는 문자열과 WITHOUT TIME ZONE 타입 간의 변환은 모호함이 없고 간단해요. UTC 오프셋이나 시간대 이름 있는 문자열과 WITH TIME ZONE 타입 간의 변환도 모호함이 없지만, 시간대 이름을 처리하려면 ICU 확장이 필요해요.
UTC 오프셋이나 시간대 이름 없는 문자열이 WITH TIME ZONE 타입으로 변환되면, 문자열은 구성된 시간대에서 해석돼요. UTC 오프셋이 있는 문자열이 WITHOUT TIME ZONE 타입으로 전달되면, 오프셋이나 시간대 사양은 무시돼요. UTC 외의 시간대 이름이 있는 문자열이 WITHOUT TIME ZONE 타입으로 전달되면 오류가 발생해요.
마지막으로, WITH TIME ZONE과 WITHOUT TIME ZONE 타입이 명시적/암묵적 캐스트로 서로 변환될 때, 번역은 구성된 시간대를 사용해요. 대체 시간대를 쓰려면 ICU 확장이 제공하는 timezone 함수를 사용할 수 있어요.
SELECT
timezone('America/Denver', TIMESTAMP '2001-02-16 20:38:40') AS aware1,
timezone('America/Denver', TIMESTAMPTZ '2001-02-16 04:38:40') AS naive1,
timezone('UTC', TIMESTAMP '2001-02-16 20:38:40+00:00') AS aware2,
timezone('UTC', TIMESTAMPTZ '2001-02-16 04:38:40 Europe/Berlin') AS naive2;
| aware1 | naive1 | aware2 | naive2 |
|---|---|---|---|
| 2001-02-17 04:38:40+01 | 2001-02-15 20:38:40 | 2001-02-16 21:38:40+01 | 2001-02-16 03:38:40 |
TIMESTAMP는 결과에서 시간대 사양 없이 표시(지역 시간에 대한 ISO 8601 규칙)되는 반면, 시간대 인식 TIMESTAMPTZ는 구성된 시간대(예시에서는 'Europe/Berlin')의 UTC 오프셋과 함께 표시된다는 점에 주목하세요. 관련된 모든 instant에서 'America/Denver'와 'Europe/Berlin'의 UTC 오프셋은 각각 -07:00과 +01:00이에요.
특수 값
타임스탬프를 만드는 데 세 가지 특수 문자열을 쓸 수 있어요.
| 입력 문자열 | 설명 |
|---|---|
epoch |
1970-01-01 00:00:00[+00] (Unix 시스템 시간 0) |
infinity |
다른 모든 타임스탬프보다 이후 |
-infinity |
다른 모든 타임스탬프보다 이전 |
infinity와 -infinity 값은 특수 처리되어 변경 없이 표시되는 반면, epoch 값은 단순히 읽을 때 해당 타임스탬프 값으로 변환되는 표기 축약 표현이에요.
SELECT '-infinity'::TIMESTAMP, 'epoch'::TIMESTAMP, 'infinity'::TIMESTAMP;
| Negative | Epoch | Positive |
|---|---|---|
| -infinity | 1970-01-01 00:00:00 | infinity |
함수
타임스탬프 함수 문서를 참고해 주세요.
시간대
시간대와 WITH TIME ZONE 타입을 이해하려면 두 가지 개념에서 시작하는 것이 도움이 돼요: instant와 시간적 빈닝(temporal binning).
Instants
instant는 절대 시간의 한 지점으로, 보통 고정 시점(epoch이라고 함)으로부터의 어떤 시간 증분의 개수로 주어져요. 이것은 지구 표면의 위치를 적도와 그리니치 자오선을 기준으로 위도·경도로 나타내는 것과 비슷해요. DuckDB에서 고정 시점은 Unix epoch 1970-01-01 00:00:00+00:00이고, 증분은 특정 데이터 타입에 따라 초, 밀리초, 마이크로초 또는 나노초예요.
시간적 빈닝
빈닝은 연속 데이터에서 흔한 관행이에요. 가능한 값의 범위를 인접한 부분집합으로 나누고, 빈닝 연산이 실제 값을 그 값이 속하는 *빈(bin)*에 매핑합니다. 시간적 빈닝은 이 관행을 instant에 적용한 것으로, 예를 들어 instants를 년, 월, 일로 빈닝하는 것이에요.
시간적 빈닝 규칙은 복잡하며, 일반적으로 시간대와 달력 두 세트로 나뉘어요. 대부분의 작업에서 달력은 널리 쓰이는 그레고리안 달력이겠지만, 시간대는 로케일별 규칙을 적용하며 매우 다양할 수 있어요. 예를 들어, 'America/Los_Angeles' 시간대에 대한 빈닝이 epoch 근처에서 어떻게 보이는지가 바로 그런 예시예요.
가장 흔한 시간적 빈닝 문제는 일광 절약 시간제 변경 때 발생해요. 아래 예시에는 "hour" 빈이 두 시간 길이인 일광 절약 시간제 변경이 포함돼요. 두 시간을 구분하려면 UTC로부터의 오프셋을 포함한 또 다른 빈 범위가 필요해요.
시간대 지원
TIMESTAMPTZ 타입은 적절한 확장을 사용해 달력과 시계 빈으로 빈닝할 수 있어요. 내장된 ICU 확장은 International Components for Unicode의 시간대·달력 함수를 사용해 모든 빈닝·산술 함수를 구현합니다.
사용할 시간대를 설정하려면 먼저 ICU 확장을 로드하세요. ICU 확장은 Python, R, JDBC, ODBC를 포함한 여러 DuckDB 클라이언트에 미리 번들되어 있으므로 이런 경우에는 이 단계를 건너뛸 수 있어요. 다른 경우에는 ICU 확장을 먼저 설치·로드해야 할 수도 있어요.
INSTALL icu;
LOAD icu;
그다음 SET TimeZone 명령어를 사용하세요.
SET TimeZone = 'America/Los_Angeles';
TIMESTAMPTZ에 대한 시간 빈닝 연산은 그다음부터 주어진 시간대를 사용해 구현돼요.
사용 가능한 시간대 목록은 pg_timezone_names() 테이블 함수에서 가져올 수 있어요.
SELECT
name,
abbrev,
utc_offset
FROM pg_timezone_names()
ORDER BY
name;
사용 가능한 시간대의 참조 테이블도 찾을 수 있어요.
달력 지원
ICU 확장은 SET Calendar 명령어를 사용해 비-그레고리안 달력도 지원해요. INSTALL과 LOAD 단계는 DuckDB 클라이언트가 ICU 확장을 번들하지 않을 때만 필요하다는 점에 주의하세요.
INSTALL icu;
LOAD icu;
SET Calendar = 'japanese';
TIMESTAMPTZ에 대한 시간 빈닝 연산은 그다음부터 주어진 달력을 사용해 구현돼요. 이 예시에서 era 부분은 이제 일본 황실 연호 번호를 보고해요.
사용 가능한 달력 목록은 icu_calendar_names() 테이블 함수에서 가져올 수 있어요.
SELECT name
FROM icu_calendar_names()
ORDER BY 1;
설정
TimeZone과 Calendar 설정의 현재 값은 ICU가 시작될 때 결정돼요. duckdb_settings() 테이블 함수에서 조회할 수 있어요.
SELECT *
FROM duckdb_settings()
WHERE name = 'TimeZone';
| name | value | description | input_type |
|---|---|---|---|
| TimeZone | Europe/Amsterdam | The current time zone | VARCHAR |
SELECT *
FROM duckdb_settings()
WHERE name = 'Calendar';
| name | value | description | input_type |
|---|---|---|---|
| Calendar | gregorian | The current calendar | VARCHAR |
빈닝 연산이 기대대로 동작하지 않는다면
TimeZone과Calendar값을 확인하고 필요하면 조정해 주세요.
더 알아보기 (Learn more)
- 타임스탬프 함수는
sql/functions/timestamp, 시간대 목록은sql/data_types/timezones문서를 참고해 주세요.