IT박스

Rails 및 PostgreSQL에서 시간대를 모두 무시

itboxs 2020. 6. 7. 10:50
반응형

Rails 및 PostgreSQL에서 시간대를 모두 무시


Rails와 Postgres의 날짜와 시간을 다루고 있으며이 문제가 발생합니다.

데이터베이스는 UTC입니다.

사용자는 Rails 앱에서 시간대를 선택하지만 사용자가 시간을 비교하기 위해 현지 시간을 가질 때만 사용됩니다.

사용자는 시간을 저장합니다 (예 : 2012 년 3 월 17 일 오후 7시). 시간대 변환이나 시간대를 저장하고 싶지 않습니다. 날짜와 시간을 절약하고 싶습니다. 이렇게하면 사용자가 시간대를 변경 한 경우 여전히 2012 년 3 월 17 일 오후 7 시가 표시됩니다.

사용자가 지정한 시간대 만 사용하여 사용자 현지 시간대의 현재 시간을 '이전'또는 '이후'로 가져옵니다.

현재 '표준 시간대가없는 타임 스탬프'를 사용하고 있지만 레코드를 검색하면 레일 (?)이 앱의 표준 시간대로 변환되어 원하지 않습니다.

Appointment.first.time
 => Fri, 02 Mar 2012 19:00:00 UTC +00:00 

데이터베이스의 레코드가 UTC로 나오는 것처럼 보이기 때문에 현재 시간을 내고 'Date.strptime (str, "% m / % d / % Y")'로 시간대를 제거한 다음 내 작업을 수행해야합니다. 그와 함께 쿼리 :

.where("time >= ?", date_start)

모든 시간대를 무시하는 쉬운 방법이 있어야합니다. 어떤 아이디어?


데이터 유형 timestamp은의 짧은 이름입니다 timestamp without time zone.
다른 옵션 timestamptz은 짧습니다 timestamp with time zone.

timestamptz는 IS 원하는 날짜 / 시간 가족 유형은 그대로. 그것은 한 typispreferred설정 pg_type관련 수있는 :

시대

내부적 으로 타임 스탬프는 신기원 의 카운트로 저장됩니다. Postgres는 2000 년 첫날의 첫 번째 순간 인 UTC (2000-01-01T00 : 00 : 00Z)를 사용합니다. 카운트 번호를 저장하는 데8 개의 옥텟 이 사용됩니다. 컴파일 시간 옵션에 따라 해당 숫자는 다음 중 하나입니다.

  • 8 바이트 정수 분수 초 0-6 자리 (기본),
  • 소수 자릿수 초 (0-10 자리)의 부동 소수점 숫자 (더 이상 사용되지 않음). 여기서는 에포크에서 멀어 질수록 정밀도가 빠르게 저하됩니다.
    최신 Postgres 설치는 8 바이트 정수를 사용합니다.

Postgres는 Unix 시간을 사용 하지 않습니다 . Postgres의 시대는 Unix의 1970-01-01이 아니라 2000-01-01의 첫 순간입니다. 유닉스 시간은 초 단위의 해상도를 가지고 있지만 Postgres는 몇 초를 유지합니다.

timestamp

당신은 데이터 유형 정의하면 당신은 포스트 그레스를 말하고있다 : "나는 명시 적으로 시간대를 제공하고 있지 않다을 대신 현재 시간대를 가정 포스트 그레스는 타임 스탬프를 저장합니다. 그대로 - 무시 시간대 수정을 하나를 추가해야하는 경우!timestamp [without time zone]

나중에이 표시 timestamp하면 문자 그대로 입력 한 내용이 다시 표시됩니다 . 동일한 시간대 설정으로 모든 것이 정상입니다. 세션의 시간대 설정이 변경되면 timestamp-의 의미도 변경 됩니다. 은 동일하게 유지됩니다.

timestamptz

의 취급은 timestamp with time zone미묘하게 다르다. 나는 여기에 매뉴얼을 인용한다 :

의 경우 timestamp with time zone내부 저장된 값은 항상 UTC (Universal Coordinated Time ...)입니다.

대담한 강조 광산. 시간대 자체는 저장되지 않습니다 . UTC 타임 스탬프를 계산하는 데 사용되는 입력 수정 자이며, 추가 된 표준 시간대 오프셋과 함께 저장되는 로컬 시간을 계산하는 데 사용되는 출력 수정 자입니다. timestamptz입력시 오프셋을 추가하지 않으면 세션의 현재 시간대 설정이 가정됩니다. 모든 계산은 UTC 타임 스탬프 값으로 수행됩니다. 둘 이상의 시간대를 처리해야하거나해야 할 경우을 사용하십시오 timestamptz.

psql 또는 pgAdmin과 같은 클라이언트 또는 libpq 를 통해 통신하는 모든 응용 프로그램 (예 : pg gem이있는 Ruby)에는 현재 시간대에 대한 타임 스탬프와 오프셋이 표시 되거나 요청 된 시간대 에 따라 표시됩니다 (아래 참조). 항상 같은 시점이며 표시 형식 만 다릅니다. 또는 매뉴얼에 따르면 :

모든 시간대 인식 날짜 및 시간은 내부적으로 UTC로 저장됩니다. 클라이언트에 표시되기 전에 TimeZone 구성 매개 변수로 지정된 영역에서 현지 시간으로 변환 됩니다.

이 간단한 예제 (psql)를 고려하십시오.

db = # SELECT 타임 스탬프 '2012-03-05 20:00 +03 ';
      타임 스탬프
------------------------
 2012-03-05 18:00:00 +01

대담한 강조 광산. 여기에 무슨 일이 벌어 졌었 나? 입력 리터럴에 대해
임의의 시간대 오프셋 +3선택했습니다 . Postgres에게 이것은 UTC 타임 스탬프를 입력하는 많은 방법 중 하나 일뿐 2012-03-05 17:00:00입니다. 내 테스트에서 현재 시간대 설정 Vienna / Austria대해 쿼리 결과가 표시됩니다.테스트는 겨울철과 여름철에 상쇄 됩니다. 겨울철 에 빠지기 때문입니다.+1+22012-03-05 18:00:00+01

Postgres는 이미이 값을 입력 한 방법을 잊었습니다. 기억하는 것은 가치와 데이터 유형뿐입니다. 십진수와 마찬가지로. numeric '003.4', numeric '3.40'또는 numeric '+3.4'- 동일한 내부 값의 모든 결과.

AT TIME ZONE

As soon as you get a grasp on this logic, you can do anything you want. All that's missing now, is a tool to interpret or represent timestamp literals according to a specific time zone. That's where the AT TIME ZONE construct comes in. There are two different use cases. timestamptz is converted to timestamp and vice versa.

To enter the UTC timestamptz 2012-03-05 17:00:00+0:

SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC'

... which is equivalent to:

SELECT timestamptz '2012-03-05 17:00:00 UTC'

To display the same point in time as EST timestamp (Eastern Standard Time):

SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST'

That's right, AT TIME ZONE 'UTC' twice. The first one interprets the timestamp value as (given) UTC timestamp returning the type timestamptz. The second one converts the timestamptz to the timestamp in the given time zone 'EST' - what a clock in the time zone EST displays at this unique point in time.

Examples

SELECT ts AT TIME ZONE 'UTC'
FROM  (
   VALUES
      (1, timestamptz '2012-03-05 17:00:00+0')
    , (2, timestamptz '2012-03-05 18:00:00+1')
    , (3, timestamptz '2012-03-05 17:00:00 UTC')
    , (4, timestamp   '2012-03-05 11:00:00'  AT TIME ZONE '+6') 
    , (5, timestamp   '2012-03-05 17:00:00'  AT TIME ZONE 'UTC') 
    , (6, timestamp   '2012-03-05 07:00:00'  AT TIME ZONE 'US/Hawaii')  -- ①
    , (7, timestamptz '2012-03-05 07:00:00 US/Hawaii')                  -- ①
    , (8, timestamp   '2012-03-05 07:00:00'  AT TIME ZONE 'HST')        -- ①
    
  
   , (9, timestamp   '2012-03-05 18:00:00+1')
    -- ② loaded footgun!
      ) t(id, ts);

Returns 8 (or 9) identical rows with a timestamptz columns holding the same UTC timestamp 2012-03-05 17:00:00. The 9th row sort of happens to work in my time zone, but is an evil trap. See below.

① Rows 6 - 8 with time zone name and time zone abbreviation for Hawaii time are subject to DST (daylight saving time) and might differ, though not currently. A time zone name like 'US/Hawaii' is aware of DST rules and all historic shifts automatically, while an abbreviation like HST is just a dumb code for a fixed offset. You may need to append a different abbreviation for summer / standard time. The name correctly interprets any timestamp at the given time zone. An abbreviation is cheap, but needs to be the right one for the given timestamp:

Daylight Saving Time is not among the brightest ideas humanity ever came up with.

② Row 9, marked as loaded footgun works for me, but only by coincidence. If you explicitly cast a literal to timestamp [without time zone], any time zone offset is ignored! Only the bare timestamp is used. The value is then automatically coerced to timestamptz in the example to match the column type. For this step, the timezone setting of the current session is assumed, which happens to be the same time zone +1 in my case (Europe/Vienna). But probably not in your case - which will result in a different value. In short: Don't cast timestamptz literals to timestamp or you lose the time zone offset.

Your questions

User stores a time, say March 17, 2012, 7pm. I don't want timezone conversions or the timezone to be stored.

Time zone itself is never stored. Use one of the methods above to enter a UTC timestamp.

I only use the users specified time zone to get records 'before' or 'after' the current time in the users local time zone.

You can use one query for all clients in different time zones.
For absolute global time:

SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time

For time according to the local clock:

SELECT * FROM tbl WHERE time_col > now()::time

Not tired of background information, yet? There is more in the manual.


If you want to deal in UTC by default:

In config/application.rb, add:

config.time_zone = 'UTC'

Then, if you store the current user timezone name is current_user.timezone you can say.

post.created_at.in_time_zone(current_user.timezone)

current_user.timezone should be a valid timezone name, otherwise you will get ArgumentError: Invalid Timezone, see full list.

참고URL : https://stackoverflow.com/questions/9571392/ignoring-time-zones-altogether-in-rails-and-postgresql

반응형