PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Bỏ qua hoàn toàn múi giờ trong Rails và PostgreSQL

Postgres có hai kiểu dữ liệu dấu thời gian khác nhau:

  • timestamp with time zone , tên viết tắt: timestamptz
  • timestamp without time zone , tên viết tắt: timestamp

timestamptz ưa thích nhập họ ngày / giờ, theo nghĩa đen. Nó có typispreferred đặt trong pg_type , có thể liên quan:

  • Tạo chuỗi thời gian giữa hai ngày trong PostgreSQL

Bộ nhớ trong và kỷ nguyên

Bên trong, dấu thời gian chiếm 8 byte dung lượng lưu trữ trên đĩa và trong RAM. Nó là một giá trị số nguyên đại diện cho số micro giây từ kỷ nguyên Postgres, 2000-01-01 00:00:00 UTC.

Postgres cũng có kiến ​​thức tích hợp về thời gian UNIX đếm giây thường được sử dụng từ kỷ nguyên UNIX, 1970-01-01 00:00:00 UTC và sử dụng kiến ​​thức đó trong các hàm to_timestamp(double precision) hoặc EXTRACT(EPOCH FROM timestamptz) .

Mã nguồn:

Dấu thời gian
* Timestamps, as well as the h/m/s fields of intervals, are stored as
* int64 values with units of microseconds.  (Once upon a time they were  
* double values with units of seconds.)

Và:

/* Julian-date equivalents of Day 0 in Unix and Postgres reckoning */  
#define UNIX_EPOCH_JDATE        2440588 /* == date2j(1970, 1, 1) */  
#define POSTGRES_EPOCH_JDATE    2451545 /* == date2j(2000, 1, 1) */  

Độ phân giải micro giây chuyển thành tối đa 6 chữ số phân số trong giây.

timestamp

Đối với timestamp không có múi giờ nào được cung cấp rõ ràng. Postgres bỏ qua bất kỳ công cụ sửa đổi múi giờ nào được thêm vào đầu vào theo nghĩa đen do nhầm lẫn!

Không có giờ nào được thay đổi để hiển thị. Với mọi thứ diễn ra trong cùng một múi giờ, điều này là tốt. Đối với một múi giờ khác, ý nghĩa thay đổi, nhưng giá trị hiển thị giữ nguyên.

timestamptz

Xử lý timestamptz là khác nhau một cách tinh tế. Tôi trích dẫn sách hướng dẫn ở đây:

Đối với timestamp with time zone , giá trị được lưu trữ nội bộ luôn ở UTC (Giờ Phối hợp Toàn cầu ...)

Tôi nhấn mạnh đậm. Bản thân múi giờ không bao giờ được lưu trữ . Nó là một công cụ sửa đổi đầu vào được sử dụng để tính toán dấu thời gian theo UTC, được lưu trữ - hoặc và bộ trang trí đầu ra được sử dụng để tính giờ địa phương cho hiển thị - với độ lệch múi giờ được thêm vào. Nếu bạn không thêm phần bù cho timestamptz trên đầu vào, cài đặt múi giờ hiện tại của phiên được giả định. Tất cả các tính toán được thực hiện với các giá trị dấu thời gian UTC. Nếu bạn (có thể) phải xử lý nhiều hơn một múi giờ, hãy sử dụng timestamptz . Nói cách khác:Nếu có thể có bất kỳ nghi ngờ hoặc hiểu lầm nào về múi giờ giả định, hãy chuyển sang timestamptz . Áp dụng trong hầu hết các trường hợp sử dụng.

Các ứng dụng khách như psql hoặc pgAdmin hoặc bất kỳ ứng dụng nào giao tiếp qua libpq (như Ruby với ngọc pg) được hiển thị với dấu thời gian cộng với phần bù cho múi giờ hiện tại hoặc theo một yêu cầu múi giờ (xem bên dưới). Nó luôn luôn là cùng một điểm về thời gian , chỉ có định dạng hiển thị khác nhau. Hoặc, như hướng dẫn sử dụng:

Tất cả các ngày và giờ nhận biết múi giờ được lưu trữ nội bộ trong UTC. Chúng được chuyển đổi thành giờ địa phương trong khu vực được chỉ định bởi TimeZone tham số cấu hình trước khi hiển thị cho máy khách.

Ví dụ trong psql:

db=# SELECT timestamptz '2012-03-05 20:00+03';
      timestamptz
------------------------
 2012-03-05 18:00:00+01

Điều gì đã xảy ra ở đây?
Tôi đã chọn độ lệch múi giờ tùy ý +3 cho chữ đầu vào. Đối với Postgres, đây chỉ là một trong nhiều cách để nhập dấu thời gian UTC 2012-03-05 17:00:00 . Kết quả của truy vấn được hiển thị cho cài đặt múi giờ hiện tại Vienna / Austria trong thử nghiệm của tôi, có phần bù +1 trong mùa đông và +2 trong thời gian mùa hè ("thời gian tiết kiệm ánh sáng ban ngày", DST). Vì vậy, 2012-03-05 18:00:00+01 vì DST chỉ có hiệu lực sau đó.

Postgres quên nghĩa đen đầu vào ngay lập tức. Tất cả những gì nó ghi nhớ là giá trị cho kiểu dữ liệu. Cũng giống như với một số thập phân. numeric '003.4' hoặc numeric '+3.4' - cả hai đều dẫn đến cùng một giá trị nội bộ.

AT TIME ZONE

Tất cả những gì còn thiếu bây giờ, là một công cụ để giải thích hoặc biểu diễn các ký tự dấu thời gian theo một múi giờ cụ thể. Đó là nơi AT TIME ZONE có hai trường hợp sử dụng khác nhau. timestamptz được chuyển đổi thành timestamp và ngược lại.

Để nhập UTC timestamptz 2012-03-05 17:00:00+0 :

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

... tương đương với:

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

Để hiển thị cùng một thời điểm với EST timestamp (Giờ chuẩn miền Đông):

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

Đúng vậy, AT TIME ZONE 'UTC' hai lần . Đầu tiên diễn giải timestamp giá trị như (đã cho) dấu thời gian UTC trả về loại timestamptz . Thứ hai chuyển đổi timestamptz đến timestamp trong múi giờ nhất định 'EST' - đồng hồ treo tường hiển thị ở múi giờ EST tại thời điểm này.

Ví dụ

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);

Trả về 8 (hoặc 9) giống hệt nhau các hàng có cột dấu thời gian chứa cùng dấu thời gian UTC 2012-03-05 17:00:00 . Loại hàng thứ 9 tình cờ hoạt động theo múi giờ của tôi, nhưng là một cái bẫy xấu xa. Xem bên dưới.

① Hàng 6 - 8 với múi giờ tên và múi giờ chữ viết tắt đối với giờ Hawaii tuân theo DST (giờ tiết kiệm ánh sáng ban ngày) và có thể khác, mặc dù hiện tại không phải. Tên múi giờ như 'US/Hawaii' biết các quy tắc DST và tất cả các thay đổi lịch sử tự động, trong khi viết tắt như HST chỉ là một mã câm cho một phần bù cố định. Bạn có thể cần thêm một từ viết tắt khác cho mùa hè / giờ chuẩn. Tên diễn giải chính xác bất kỳ dấu thời gian tại múi giờ nhất định. Một chữ viết tắt rẻ, nhưng cần phải là sản phẩm phù hợp với dấu thời gian nhất định:

  • Tên múi giờ có các thuộc tính giống hệt nhau mang lại kết quả khác khi được áp dụng cho dấu thời gian

Giờ tiết kiệm ánh sáng ban ngày không phải là một trong những ý tưởng sáng suốt nhất mà nhân loại từng nghĩ ra.

② Hàng 9, được đánh dấu là khẩu súng ngắn đã nạp đạn hoạt động cho tôi , nhưng chỉ do trùng hợp. Nếu bạn truyền một cách rõ ràng một ký tự sang timestamp [without time zone] , bất kỳ chênh lệch múi giờ nào đều bị bỏ qua! Chỉ có dấu thời gian trống được sử dụng. Sau đó, giá trị sẽ tự động được ép buộc thành timestamptz trong ví dụ để phù hợp với loại cột. Đối với bước này, timezone cài đặt của phiên hiện tại được giả định, có cùng múi giờ +1 trong trường hợp của tôi (Châu Âu / Viên). Nhưng có lẽ không phải trong trường hợp của bạn - điều này sẽ dẫn đến một giá trị khác. Tóm lại:Không truyền timestamptz chữ thành timestamp hoặc bạn mất độ lệch múi giờ.

Câu hỏi của bạn

Người dùng lưu trữ thời gian, giả sử 7 giờ tối ngày 17 tháng 3 năm 2012. Tôi không muốn chuyển đổi múi giờ hoặc múi giờ được lưu trữ.

Múi giờ chính nó không bao giờ được lưu trữ. Sử dụng một trong các phương pháp ở trên để nhập dấu thời gian UTC.

Tôi chỉ sử dụng múi giờ do người dùng chỉ định để lấy bản ghi 'trước' hoặc 'sau' giờ hiện tại trong múi giờ địa phương của người dùng.

Bạn có thể sử dụng một truy vấn cho tất cả các máy khách ở các múi giờ khác nhau.
Đối với giờ toàn cầu tuyệt đối:

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

Đối với thời gian theo đồng hồ địa phương:

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

Chưa mệt mỏi với thông tin cơ bản? Có nhiều hơn trong sách hướng dẫn.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Có cách nào để tắt cập nhật / xóa nhưng vẫn cho phép trình kích hoạt thực hiện chúng không?

  2. Không thể CHÈN:LỖI:giá trị mảng phải bắt đầu bằng {hoặc thông tin về thứ nguyên

  3. Làm cách nào để tạo chỉ mục trên trường JSON trong Postgres?

  4. Làm cách nào để chọn giá trị trước đó một cách hiệu quả?

  5. Cách kết nối với máy chủ PostgreSQL từ máy ảo vagrant