Câu trả lời:
Sự khác biệt được đề cập trong tài liệu PostgreSQL cho các loại ngày / giờ . Có, việc điều trị TIME
hoặc TIMESTAMP
khác nhau giữa một WITH TIME ZONE
hoặc WITHOUT TIME ZONE
. Nó không ảnh hưởng đến cách các giá trị được lưu trữ; nó ảnh hưởng đến cách họ được giải thích.
Ảnh hưởng của múi giờ đối với các loại dữ liệu này được đề cập cụ thể trong các tài liệu. Sự khác biệt phát sinh từ những gì hệ thống có thể biết một cách hợp lý về giá trị:
Với múi giờ là một phần của giá trị, giá trị có thể được hiển thị dưới dạng thời gian cục bộ trong máy khách.
Không có múi giờ là một phần của giá trị, múi giờ mặc định rõ ràng là UTC, do đó, nó được hiển thị cho múi giờ đó.
Hành vi khác nhau tùy thuộc vào ít nhất ba yếu tố:
WITH TIME ZONE
hoặc WITHOUT TIME ZONE
) của giá trị.Dưới đây là các ví dụ bao gồm sự kết hợp của các yếu tố đó:
foo=> SET TIMEZONE TO 'Japan';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 00:00:00+09
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 06:00:00+09
(1 row)
foo=> SET TIMEZONE TO 'Australia/Melbourne';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 00:00:00+11
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 08:00:00+11
(1 row)
timestamp with time zone
và timestamp without time zone
, trong Postgres không * thực sự lưu trữ thông tin múi giờ. Bạn có thể xác nhận điều này bằng cách lướt qua trang tài liệu loại dữ liệu: Cả hai loại đều chiếm cùng số bát phân và có phạm vi lưu giá trị, do đó không có chỗ để lưu trữ thông tin múi giờ. Văn bản của trang xác nhận điều này. Một cái gì đó của một cách hiểu sai: "không có tz" có nghĩa là "bỏ qua phần bù khi chèn dữ liệu" và "với tz" có nghĩa là "sử dụng phần bù để điều chỉnh theo UTC".
Tôi cố gắng giải thích nó dễ hiểu hơn tài liệu PostgreSQL được giới thiệu.
Cả hai TIMESTAMP
biến thể đều không lưu trữ múi giờ (hoặc phần bù), bất chấp những gì tên gợi ý. Sự khác biệt là trong việc giải thích dữ liệu được lưu trữ (và trong ứng dụng dự định), chứ không phải ở định dạng lưu trữ:
TIMESTAMP WITHOUT TIME ZONE
lưu trữ ngày giờ địa phương (còn gọi là ngày lịch treo tường và giờ đồng hồ treo tường). Múi giờ của nó là không xác định theo như PostgreSQL có thể nói (mặc dù ứng dụng của bạn có thể biết nó là gì). Do đó, PostgreSQL không chuyển đổi liên quan đến múi giờ trên đầu vào hoặc đầu ra. Nếu giá trị được nhập vào cơ sở dữ liệu dưới dạng '2011-07-01 06:30:30'
, thì không có vấn đề gì về múi giờ bạn hiển thị sau đó, nó vẫn sẽ nói năm 2011, tháng 07, ngày 01, 06 giờ, 30 phút và 30 giây (ở một số định dạng). Ngoài ra, bất kỳ phần bù hoặc múi giờ nào bạn chỉ định trong đầu vào đều bị PostgreSQL bỏ qua, do đó '2011-07-01 06:30:30+00'
và '2011-07-01 06:30:30+05'
giống như chỉ '2011-07-01 06:30:30'
. Đối với các nhà phát triển Java: nó tương tự như java.time.LocalDateTime
.
TIMESTAMP WITH TIME ZONE
lưu trữ một điểm trên dòng thời gian UTC. Nó trông như thế nào (bao nhiêu giờ, phút, v.v.) phụ thuộc vào múi giờ của bạn, nhưng nó luôn đề cập đến cùng một "vật lý" tức thời (như khoảnh khắc của một sự kiện vật lý thực tế). Đầu vào được chuyển đổi nội bộ sang UTC và đó là cách nó được lưu trữ. Do đó, phần bù của đầu vào phải được biết, do đó, khi đầu vào không chứa phần bù hoặc múi giờ rõ ràng (như '2011-07-01 06:30:30'
), nó được giả định là nằm trong múi giờ hiện tại của phiên PostgreQuery, nếu không thì sử dụng phần bù hoặc múi giờ được chỉ định rõ ràng (như trong '2011-07-01 06:30:30+05'
). Đầu ra được hiển thị được chuyển đổi thành múi giờ hiện tại của phiên PostgreSQL. Đối với các nhà phát triển Java: Nó tương tự java.time.Instant
(với độ phân giải thấp hơn), nhưng với JDBC và JPA 2.2, bạn phải ánh xạ nó tới java.time.OffsetDateTime
(hoặc đến java.util.Date
hoặcjava.sql.Timestamp
tất nhiên).
Một số người nói rằng cả hai TIMESTAMP
biến thể lưu trữ ngày giờ UTC. Kiểu như vậy, nhưng theo tôi thì thật khó hiểu. TIMESTAMP WITHOUT TIME ZONE
được lưu trữ như một TIMESTAMP WITH TIME ZONE
, được hiển thị với múi giờ UTC xảy ra để cung cấp cùng năm, tháng, ngày, giờ, phút, giây và micro giây khi chúng ở trong thời gian ngày địa phương. Nhưng nó không có nghĩa là đại diện cho điểm trên dòng thời gian mà cách giải thích của UTC nói, đó chỉ là cách các trường ngày giờ địa phương được mã hóa. (Đó là một số dấu chấm trên dòng thời gian, vì múi giờ thực không phải là UTC; chúng tôi không biết đó là gì.)
TIMESTAMP WITH TIME ZONE
là a Instant
. Cả hai đều đại diện cho một điểm trên dòng thời gian trong UTC. Instant
Theo ý kiến của tôi, OffsetDateTime
nó được ưu tiên hơn vì nó là tài liệu tự luận hơn: A TIMESTAMP WITH TIME ZONE
luôn được lấy từ cơ sở dữ liệu dưới dạng UTC và Instant
luôn luôn ở trong UTC sao cho khớp một cách tự nhiên, trong khi đó OffsetDateTime
có thể mang các giá trị khác.
OffsetDateTime
như kiểu Java được ánh xạ. Tôi không chắc chắn nếu Instance
vẫn được hỗ trợ không chính thức ở đâu đó.
'2011-07-01 06:30:30+00'
và '2011-07-01 06:30:30+05'
bị bỏ qua nhưng tôi có thể làm được insert into test_table (date) values ('2018-03-24T00:00:00-05:00'::timestamptz);
và nó sẽ chuyển đổi nó thành utc chính xác. trong đó ngày là dấu thời gian không có múi giờ. Tôi đang cố gắng hiểu giá trị chính của dấu thời gian với múi giờ là gì và gặp sự cố.
::timestamptz
. Cùng với đó, bạn chuyển đổi chuỗi thành TIMESTAMP WITH TIME ZONE
và khi đó sẽ được chuyển đổi thành chuỗi WITHOUT TIME ZONE
đó, sẽ lưu trữ ngày "lịch treo tường" và thời gian đồng hồ treo tường của thời gian đó ngay từ múi giờ phiên của bạn (có thể là UTC). Nó vẫn chỉ là dấu thời gian cục bộ với phần bù không xác định (không có vùng).
Dưới đây là một ví dụ cần giúp đỡ. Nếu bạn có dấu thời gian với múi giờ, bạn có thể chuyển đổi dấu thời gian đó thành bất kỳ múi giờ nào khác. Nếu bạn không có múi giờ cơ sở, nó sẽ không được chuyển đổi chính xác.
SELECT now(),
now()::timestamp,
now() AT TIME ZONE 'CST',
now()::timestamp AT TIME ZONE 'CST'
Đầu ra:
-[ RECORD 1 ]---------------------------
now | 2018-09-15 17:01:36.399357+03
now | 2018-09-15 17:01:36.399357
timezone | 2018-09-15 08:01:36.399357
timezone | 2018-09-16 02:01:36.399357+03
timestamp
và timestamptz
có nghĩa. timestamptz
có nghĩa là một điểm tuyệt đối trong thời gian (UTC) trong khi timestamp
biểu thị những gì đồng hồ hiển thị trong một múi giờ nhất định. Vì vậy, khi chuyển đổi timestamptz
sang múi giờ bạn đang hỏi đồng hồ đã hiển thị gì ở New York vào thời điểm tuyệt đối này? trong khi đó khi "chuyển đổi" a timestamp
, bạn đang hỏi thời điểm tuyệt đối khi đồng hồ ở New York hiển thị x là gì?
AT TIME ZONE
trúc này là một lời trêu ghẹo não của chính nó, ngay cả khi bạn đã hiểu WITH
so với WITHOUT TIME ZONE
các loại. Vì vậy, đó là một lựa chọn tò mò để giải thích chúng. (: ( AT TIME ZONE
chuyển đổi WITH TIME ZONE
dấu thời gian thành WITHOUT TIME ZONE
dấu thời gian và ngược lại ... không chính xác rõ ràng.)
now()::timestamp AT TIME ZONE 'CST'
không có ý nghĩa gì, trừ khi bạn ngay lập tức đồng hồ cho khu vực 'CST' sẽ hiển thị thời gian mà đồng hồ địa phương của bạn hiện đang hiển thị