PostgreSQL 데이터베이스에서 어떤 타임 스탬프 유형을 선택해야합니까?


119

다중 시간대 프로젝트의 맥락에서 Postgres 데이터베이스에 타임 스탬프를 저장하는 모범 사례를 정의하고 싶습니다.

저 할 수 있어요

  1. TIMESTAMP WITHOUT TIME ZONE이 필드에 삽입 할 때 사용 된 시간대를 선택 하고 기억하십시오.
  2. TIMESTAMP WITHOUT TIME ZONE삽입 시간에 사용 된 시간대의 이름을 포함 할 다른 필드를 선택 하고 추가합니다.
  3. TIMESTAMP WITH TIME ZONE그에 따라 타임 스탬프를 선택 하고 삽입합니다.

옵션 3 (시간대 포함 타임 스탬프)을 약간 선호하지만 문제에 대한 교육적인 의견을 받고 싶습니다.

답변:


142

우선 PostgreSQL의 시간 처리 및 산술은 환상적이며 일반적인 경우 옵션 3은 괜찮습니다. 그러나 시간 및 시간대에 대한 불완전한보기이며 보완 할 수 있습니다.

  1. 사용자의 시간대 이름을 사용자 환경 설정으로 저장합니다 (예 : America/Los_Angeles아님 -0700).
  2. 사용자 이벤트 / 시간 데이터를 참조 프레임에 로컬로 제출하도록합니다 (예 : UTC로부터의 오프셋 -0700).
  3. 응용 프로그램에서 시간을 변환하고 열을 UTC사용하여 저장하십시오 TIMESTAMP WITH TIME ZONE.
  4. 반환 시간은 사용자의 시간대 (에서 즉, 변환 로컬 요청 UTCAmerica/Los_Angeles).
  5. 데이터베이스 timezoneUTC.

이 옵션은 사용자의 시간대를 가져 오는 것이 어려울 수 있으므로 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 ZONEA와 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 ZONETIME 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)

데이터베이스에 날짜와 시간대를 입력하는 것은 좋지만 미묘하게 잘못된 결과를 얻기 쉽습니다. 시간 정보를 정확하고 완전하게 저장하려면 최소한의 추가 노력이 필요하지만 이것이 항상 추가 노력이 필요한 것은 아닙니다.


2
사용자의 타임 스탬프가있는 정확한 시간대를 postgresql에 정확하게 알려 주면 postgresql이 뒤에서 무거운 작업을 수행합니다. 직접 변환하는 것은 문제를 차용하는 것입니다.
Seth Robertson

1
@Sean-체크 제약 조건으로 타임 스탬프를 set timezone to 'UTC'어떻게 삽입 합니까? 당신이 알고 있는 모든 시간대 인식 날짜는 UTC 내부에 저장된다 ?

2
검사의 요점은 데이터가 UTC에서 오프셋이 0 인 상태로 저장되었는지 확인하는 것입니다. 정보의 정렬 및 검색과 0이 아닌 오프셋을 사용한 시간 비교는 오류가 발생하기 쉽습니다. 제로 UTC 오프셋을 적용하면 모든 시나리오에서 예측 가능하게 작동하는 거의 제로 위험 방식으로 단일 관점에서 데이터와 일관되게 상호 작용할 수 있습니다. 타임 스탬프가 타임 존의 텍스트 표현을 지원하는 것이 실용적이라면 주제에 대한 제 생각은 다를 것입니다. : ~]
Sean

6
@Sean : 그러나 Jack이 지적했듯이 모든 시간대 인식 타임 스탬프는 기본적으로 내부적으로 UTC로 저장되며 사용시 현지 시간대로 변환됩니다. 사실상 extract (timezone from ...)는 연결의 로컬 시간대가 무엇이든 항상 반환합니다. 타임 스탬프가 "저장된"방식과는 관련이 없습니다. 다르게 말하면 시간대는 유형의 일부가 아니며 저장할 수 없습니다. "시간대 포함"은 다른 유형과 상호 작용할 때 데이터가 변환되는 방식에 대한 속성 일뿐입니다. 따라서 데이터에는 텍스트 또는 기타 시간대가 전혀 표시되지 않습니다.
Jay Freeman -saurik- 2012 년

@ JayFreeman-saurik- : 당신은 절대적으로 맞습니다. ``CHECK ()``는 의심스러운 코드로부터 보호하기위한 풋 슈팅 방지 조치입니다. 쓰기시 데이터가 UTC인지 확인하면 코드가 충분히 고려되었거나 실행 환경이 올바르게 설정되었음을 보증 할 수 있습니다.
Sean

58

Sean의 대답은 지나치게 복잡하고 오해의 소지가 있습니다.

사실 "WITH TIME ZONE"과 "WITHOUT TIME ZONE"은 값을 유닉스와 같은 절대 UTC 타임 스탬프로 저장합니다. 차이점은 모두 타임 스탬프가 표시되는 방식입니다. "WITH time zone"인 경우 표시되는 값은 사용자의 영역으로 변환 된 UTC 저장 값입니다. "WITHOUT time zone"인 경우 UTC 저장 값은 사용자가 설정 한 영역에 관계없이 동일한 시계 문자판을 표시하도록 왜곡됩니다.

"WITHOUT 시간대"를 사용할 수있는 유일한 상황은 실제 구역에 관계없이 시계 문자판 값이 적용되는 경우입니다. 예를 들어 타임 스탬프가 투표소가 닫힐 수있는시기를 나타내는 경우 (예 : 사람의 시간대에 관계없이 20:00에 닫음).

선택 3을 사용하십시오. 특별한 이유가없는 한 항상 "WITH time zone"을 사용하십시오.


10
Postgres의 주요 전문가 인 David E. Wheeler는 자신의 게시물 인 Always Use TIMESTAMP WITH TIME ZONE 에 따라 귀하의 평가에 동의 할 것 입니다.
Basil Bourque 2014

2
브라우저가 UTC 타임 스탬프를 현지 시간대로 변환하게하려면 어떻게해야합니까? 따라서 db는 변환을 수행하지 않으며 UTC 만 포함합니다. "시간대 없음"이 허용됩니까?
dman

5

Postgres는 시간대와 관련된 타임 스탬프를 재 계산하는 작업을 모두 수행 할 수있는 반면 다른 두 가지 작업은 직접 수행해야하기 때문에 옵션 3을 선호합니다. 타임 존과 함께 타임 스탬프를 저장하는 추가 스토리지 오버 헤드는 수백만 개의 레코드에 대해 이야기하지 않는 한 실제로 무시할 수있는 수준입니다.이 경우 어쨌든 이미 상당한 스토리지 요구 사항이있을 것입니다.


19
틀 렸습니다. 오버 헤드가 없습니다. Postgres는 시간대를 저장 하지 않습니다 ( '오프셋'은 시간대 가 아니라 올바른 용어입니다). TIMESTAMP WITH TIME ZONE이름은 잘못된 것입니다. 실제로 "삽입 / 업데이트 할 때 지정된 오프셋에주의를 기울이고 해당 오프셋을 사용하여 날짜-시간을 UTC로 조정"한다는 의미입니다. TIMESTAMP WITHOUT TIME ZONE이름 수단 "이라 함은, 즉 삽입 / 업데이트 중에 존재할 수있는 오프셋 조정 필요없이 UTC 인 것으로 날짜 및 시간 부분을 고려 무시 '. 문서를 주의 깊게 읽으십시오 .
Basil Bourque 2014

1
@BasilBourque이 정보에 감사드립니다. 매우 유용합니다. 이것을 읽는 다른 사람들을 위해 문서의 줄은 "시간대가없는 타임 스탬프로 결정된 리터럴에서 PostgreSQL은 모든 시간대 표시를 자동으로 무시합니다. 즉, 결과 값은의 날짜 / 시간 필드에서 파생됩니다. 입력 값이며 시간대에 맞게 조정되지 않습니다. "
Aidan Rosswood
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.