우선 PostgreSQL의 시간 처리 및 산술은 환상적이며 일반적인 경우 옵션 3은 괜찮습니다. 그러나 시간 및 시간대에 대한 불완전한보기이며 보완 할 수 있습니다.
- 사용자의 시간대 이름을 사용자 환경 설정으로 저장합니다 (예 :
America/Los_Angeles
아님 -0700
).
- 사용자 이벤트 / 시간 데이터를 참조 프레임에 로컬로 제출하도록합니다 (예 : UTC로부터의 오프셋
-0700
).
- 응용 프로그램에서 시간을 변환하고 열을
UTC
사용하여 저장하십시오 TIMESTAMP WITH TIME ZONE
.
- 반환 시간은 사용자의 시간대 (에서 즉, 변환 로컬 요청
UTC
에 America/Los_Angeles
).
- 데이터베이스
timezone
를 UTC
.
이 옵션은 사용자의 시간대를 가져 오는 것이 어려울 수 있으므로 TIMESTAMP WITH TIME ZONE
경량 애플리케이션 에 사용할 헤지 조언을 제공하기 때문에 항상 작동하지는 않습니다 . 즉,이 옵션 4의 배경 측면을 좀 더 자세히 설명하겠습니다.
옵션 3과 마찬가지로 그 이유 WITH TIME ZONE
는 어떤 일이 발생한 시간이 절대적인 순간 이기 때문 입니다. 상대 시간대를 WITHOUT TIME ZONE
산출합니다 . 절대 및 상대 TIMESTAMP를 절대 혼합하지 마십시오.
프로그래밍 및 일관성 관점에서 UTC를 시간대로 사용하여 모든 계산이 이루어 지도록합니다. 이것은 PostgreSQL 요구 사항은 아니지만 다른 프로그래밍 언어 또는 환경과 통합 할 때 도움이됩니다. CHECK
타임 스탬프 열에 대한 쓰기가 시간대 오프셋을 0
갖도록 열에 a 를 설정하는 것은 몇 가지 버그 클래스를 방지하는 방어적인 위치입니다 (예 : 스크립트는 데이터를 파일로 덤프하고 다른 것은 a를 사용하여 시간 데이터를 정렬합니다. 어휘 정렬). 다시 말하지만, PostgreSQL은 날짜 계산을 올바르게 수행하거나 시간대간에 변환하기 위해 이것이 필요하지 않습니다 (즉, PostgreSQL은 임의의 두 시간대 사이의 시간 변환에 매우 능숙합니다). 데이터베이스로 들어가는 데이터가 오프셋 0으로 저장되도록하려면 다음을 수행하십시오.
CREATE TABLE my_tbl (
my_timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
CHECK(EXTRACT(TIMEZONE FROM my_timestamp) = '0')
);
test=> SET timezone = 'America/Los_Angeles';
SET
test=> INSERT INTO my_tbl (my_timestamp) VALUES (NOW());
ERROR: new row for relation "my_tbl" violates check constraint "my_tbl_my_timestamp_check"
test=> SET timezone = 'UTC';
SET
test=> INSERT INTO my_tbl (my_timestamp) VALUES (NOW());
INSERT 0 1
100 % 완벽하지는 않지만 데이터가 이미 UTC로 변환되었는지 확인하는 충분히 강력한 풋 슈팅 방지 조치를 제공합니다. 이를 수행하는 방법에 대한 많은 의견이 있지만 이것이 내 경험으로 볼 때 실제로 가장 좋은 것 같습니다.
데이터베이스 시간대 처리에 대한 비판은 대체로 정당화되지만 (이를 처리 할 수있는 데이터베이스는 매우 무능함) PostgreSQL의 타임 스탬프 및 시간대 처리는 꽤 훌륭합니다 (여기 저기 몇 가지 "기능"에도 불구하고). 예를 들어 다음과 같은 기능이 있습니다.
-- Make sure we're all working off of the same local time zone
test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT NOW();
now
-------------------------------
2011-05-27 15:47:58.138995-07
(1 row)
test=> SELECT NOW() AT TIME ZONE 'UTC';
timezone
----------------------------
2011-05-27 22:48:02.235541
(1 row)
AT TIME ZONE 'UTC'
시간대 정보 를 제거하고 TIMESTAMP WITHOUT TIME ZONE
대상의 참조 프레임 ( UTC
)을 사용하여 친척 을 만듭니다 .
불완전한에서 변환 할 때 TIMESTAMP WITHOUT TIME ZONE
A와 TIMESTAMP WITH TIME ZONE
누락 된 시간대는 연결에서 상속됩니다 :
test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM NOW());
date_part
-----------
-7
(1 row)
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2011-05-27 22:48:02.235541');
date_part
-----------
-7
(1 row)
-- Now change to UTC
test=> SET timezone = 'UTC';
SET
-- Create an absolute time with timezone offset:
test=> SELECT NOW();
now
-------------------------------
2011-05-27 22:48:40.540119+00
(1 row)
-- Creates a relative time in a given frame of reference (i.e. no offset)
test=> SELECT NOW() AT TIME ZONE 'UTC';
timezone
----------------------------
2011-05-27 22:48:49.444446
(1 row)
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM NOW());
date_part
-----------
0
(1 row)
test=> SELECT EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP WITH TIME ZONE '2011-05-27 22:48:02.235541');
date_part
-----------
0
(1 row)
결론:
- (예를 들어 명명 된 레이블로 사용자의 시간대를 저장
America/Los_Angeles
)과는 UTC로부터의 오프셋 (offset)하지 (예 -0700
)
- 0이 아닌 오프셋을 저장해야하는 설득력있는 이유가없는 한 모든 것에 UTC를 사용하십시오.
- 0이 아닌 모든 UTC 시간을 입력 오류로 처리
- 상대 및 절대 타임 스탬프를 혼합 및 일치시키지 마십시오.
- 또한 사용
UTC
은 AS timezone
가능하면 데이터베이스에
임의 프로그래밍 언어 참고 : Python의 datetime
데이터 유형은 절대 시간과 상대 시간을 구분하는 데 매우 유용합니다 (처음에는 PyTZ 와 같은 라이브러리로 보완 할 때까지 실망 스럽지만 ).
편집하다
상대와 절대의 차이를 좀 더 설명하겠습니다.
이벤트를 기록하는 데 절대 시간이 사용됩니다. 예 : "사용자 123이 로그인했습니다."또는 "졸업식이 태평양 표준시 기준 2011-05-28 오후 2시에 시작됩니다." 현지 시간대에 관계없이 이벤트가 발생한 위치로 순간 이동할 수 있다면 이벤트가 발생하는 것을 목격 할 수 있습니다. 데이터베이스에있는 대부분의 시간 데이터는 절대적입니다 (따라서 TIMESTAMP WITH TIME ZONE
오프셋이 아닌 특정 시간대를 제어하는 규칙을 나타내는 텍스트 레이블과 +0 오프셋이있는 것이 이상적입니다).
상대적인 이벤트는 아직 결정되지 않은 시간대의 관점에서 무언가의 시간을 기록하거나 예약하는 것입니다. 예 : "우리 회사의 문은 오전 8시에 문을 열고 오후 9시에 문을 닫습니다.", "매주 월요일 오전 7시에 만나서 매주 아침 모임"또는 "매일 할로윈 오후 8시에" 일반적으로 상대 시간은 이벤트에 대한 템플릿이나 팩토리에서 사용되며 다른 거의 모든 경우에는 절대 시간이 사용됩니다. 상대적인 시간의 가치를 설명 할 가치가있는 드문 예외가 하나 있습니다. 무언가가 발생할 수있는 절대 시간에 대한 불확실성이있을 수있는 미래에 충분히 먼 미래 이벤트의 경우 상대 타임 스탬프를 사용하십시오. 다음은 실제 사례입니다.
2004 년이고 2008 년 10 월 31 일 오후 1시에 미국 서부 해안에서 배달을 예약해야한다고 가정합니다 (예 : America/Los_Angeles
/ PST8PDT
). 를 사용하여 절대 시간을 사용하여 저장 한 경우 ’2008-10-31 21:00:00.000000+00’::TIMESTAMP WITH TIME ZONE
미국 정부가 일광 절약 시간을 관리하는 규칙을 변경 한 2005 년 에너지 정책 법을 통과했기 때문에 배달이 오후 2시에 표시되었을 것 입니다. 배송이 예정된 2004 년에 날짜 10-31-2008
는 태평양 표준시 ( +8000
) 였지만 2005 년부터는 10-31-2008
태평양 일광 절약 시간 (+0700
). 상대적인 타임 스탬프를 표준 시간대와 함께 저장하면 상대적인 타임 스탬프가 의회의 부당한 정보 조작에 영향을받지 않기 때문에 정확한 배송 일정을 만들 수 있습니다. 일정을 예약하기 위해 상대적인 시간과 절대적인 시간을 사용하는 것 사이의 컷오프가 모호한 선이지만, 경험상의 규칙은 3 ~ 6 개월 이상의 미래에 대한 일정은 상대적인 타임 스탬프를 사용해야한다는 것입니다 (예정 됨 = 절대적 vs 계획 됨 = 상대 ???).
다른 / 마지막 유형의 상대 시간은 INTERVAL
. 예 : "사용자가 로그인 한 후 20 분 후에 세션이 시간 초과됩니다." 이 INTERVAL
중 절대 타임 스탬프 (올바르게 이용 될 수있다 TIMESTAMP WITH TIME ZONE
) 또는 상대적 타임 스탬프 ( TIMESTAMP WITHOUT TIME ZONE
). "사용자 세션은 로그인 성공 (login_utc + session_duration) 후 20 분 후에 만료됩니다."또는 "조식 회의는 60 분만 지속될 수 있습니다 (recurring_start_time + meeting_length)"라고 말하는 것도 똑같이 정확합니다.
혼란의 마지막 비트 : DATE
, TIME
, TIME WITHOUT TIME ZONE
및 TIME WITH TIME ZONE
모든 관련 데이터 유형입니다. 예 : '2011-05-28'::DATE
자정을 식별하는 데 사용할 수있는 시간대 정보가 없기 때문에 상대 날짜를 나타냅니다. 마찬가지로, '23:23:59'::TIME
시간대 나 시간으로 DATE
표시되는 것을 모르기 때문에 상대적 입니다. 를 사용하더라도이 '23:59:59-07'::TIME WITH TIME ZONE
무엇인지 알 수 없습니다 DATE
. 마지막으로 DATE
시간대는 실제로 a DATE
가 아니라 TIMESTAMP WITH TIME ZONE
다음과 같습니다.
test=> SET timezone = 'America/Los_Angeles';
SET
test=> SELECT '2011-05-11'::DATE AT TIME ZONE 'UTC';
timezone
---------------------
2011-05-11 07:00:00
(1 row)
test=> SET timezone = 'UTC';
SET
test=> SELECT '2011-05-11'::DATE AT TIME ZONE 'UTC';
timezone
---------------------
2011-05-11 00:00:00
(1 row)
데이터베이스에 날짜와 시간대를 입력하는 것은 좋지만 미묘하게 잘못된 결과를 얻기 쉽습니다. 시간 정보를 정확하고 완전하게 저장하려면 최소한의 추가 노력이 필요하지만 이것이 항상 추가 노력이 필요한 것은 아닙니다.