Những điều chính cần hiểu
timestamp without time zone AT TIME ZONE
diễn giải lại một timestamp
khi ở trong múi giờ đó với mục đích chuyển đổi nó sang UTC .
timestamp with time zone AT TIME ZONE
người chuyển đổi một timestamptz
vào một timestamp
ở múi giờ được chỉ định.
PostgreSQL sử dụng múi giờ ISO-8601, múi giờ này chỉ định rằng phía đông Greenwich là dương ... trừ khi bạn sử dụng công cụ xác định múi giờ POSIX, trong trường hợp đó nó tuân theo POSIX. Sự điên rồ xảy ra sau đó.
Tại sao kết quả đầu tiên lại cho kết quả không mong muốn
Dấu thời gian và múi giờ trong SQL thật kinh khủng. Cái này:
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';
bí mật về chữ '2011-12-30 00:30:00'
được gõ không xác định dưới dạng timestamp without time zone
, mà Pg giả định là trong TimeZone cục bộ trừ khi được thông báo khác. Khi bạn sử dụng AT TIME ZONE
, nó được (theo thông số kỹ thuật) được diễn giải lại dưới dạng timestamp with time zone
trong múi giờ EST5EDT
sau đó được lưu trữ dưới dạng thời gian tuyệt đối theo UTC - vì vậy nó được chuyển đổi từ EST5EDT
đến UTC, tức là chênh lệch múi giờ bị trừ đi . x - (-5)
là x + 5
.
Dấu thời gian này, được điều chỉnh thành bộ nhớ UTC, sau đó được điều chỉnh cho máy chủ của bạn TimeZone
cài đặt cho màn hình để nó được hiển thị theo giờ địa phương.
Thay vào đó, nếu bạn muốn nói "Tôi có dấu thời gian này theo giờ UTC và muốn xem giờ địa phương tương đương trong EST5EDT là bao nhiêu", nếu bạn muốn độc lập với cài đặt TimeZone của máy chủ, bạn cần viết một cái gì đó như:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE 'EST5EDT';
Điều này cho biết "Đã cung cấp dấu thời gian 2011-12-30 00:30:00, hãy coi nó như một dấu thời gian trong UTC khi chuyển đổi sang dấu thời gian, sau đó chuyển đổi dấu thời gian đó thành giờ địa phương trong EST5EDT".
Kinh khủng phải không? Tôi muốn trao đổi với công ty bất cứ ai đã quyết định về ngữ nghĩa điên rồ của AT TIME ZONE
- nó thực sự phải là một cái gì đó giống như timestamp CONVERT FROM TIME ZONE '-5'
và timestamptz CONVERT TO TIME ZONE '+5'
. Ngoài ra, timestamp with time zone
thực sự phải mang theo múi giờ của nó, không được lưu trữ trong UTC và tự động chuyển đổi thành giờ địa phương.
Tại sao thứ hai hoạt động (miễn là TimeZone =UTC)
Phiên bản "tác phẩm" ban đầu của bạn:
select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';
sẽ chỉ đúng nếu TimeZone được đặt thành UTC, bởi vì truyền văn bản thành thời gian sẽ giả định TimeZone khi một thời gian không được chỉ định.
Tại sao cái thứ ba hoạt động
Hai vấn đề triệt tiêu lẫn nhau.
Phiên bản khác dường như hoạt động là TimeZone độc lập, nhưng nó chỉ hoạt động vì hai vấn đề tự hủy bỏ. Đầu tiên, như đã giải thích ở trên, timestamp without time zone AT TIME ZONE
diễn giải lại dấu thời gian ở trong múi giờ đó để chuyển đổi sang dấu thời gian UTC; điều này có hiệu quả trừ đi chênh lệch múi giờ.
Tuy nhiên, vì những lý do tôi vượt quá giới hạn của mình, PostgreSQL sử dụng dấu thời gian với dấu hiệu ngược lại với những gì tôi thường thấy ở hầu hết các nơi. Xem tài liệu:
Một vấn đề khác cần lưu ý là trong tên múi giờ POSIX, hiệu số dương được sử dụng cho các vị trí phía tây Greenwich. Ở mọi nơi khác, PostgreSQL tuân theo quy ước ISO-8601 rằng chênh lệch múi giờ dương ở phía đông Greenwich.
Điều này có nghĩa là EST5EDT
giống với +5
, không phải -5
. Đó là lý do tại sao nó hoạt động:bởi vì bạn đang trừ phần bù tz không phải cộng nó, nhưng bạn đang trừ phần bù bị phủ định!
Thay vào đó, những gì bạn cần sửa lại là:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE '+5';