2009-08-11 31 views
8

Tôi có một truy vấn dựa trên thời gian dựa trên đường ray có một số hành vi nhạy cảm múi giờ lẻ, mặc dù theo tôi biết tôi đang sử dụng UTC. Tóm lại, các truy vấn này đưa ra câu trả lời khác nhau:Tại sao truy vấn đường ray này hoạt động khác nhau tùy thuộc vào múi giờ?

>> Model.find(:all,:conditions=>['created_at<=?',(Time.now-1.hours).gmtime]).length 
=> 279 
>> Model.find(:all,:conditions=>['created_at<=?',(Time.now-1.hours)]).length 
=> 280 

đâu DB thực sự có chứa một mô hình tạo ra trong giờ cuối cùng, và tổng số các mô hình là 280. Vì vậy, chỉ truy vấn đầu tiên là đúng.

Tuy nhiên, trong environment.rb tôi có:

config.time_zone = 'UTC' 

Các múi giờ hệ thống (theo báo cáo của 'date') là BST (đó là GMT + 1) - vì vậy bằng cách nào đó này gió lên bị coi là UTC và truy vấn vi phạm.

Điều này đang gây ra cho tôi tất cả các vấn đề khi tôi cần tham số truy vấn vào các thời điểm khác nhau cho một hành động (sau đó được chuyển đổi bằng Time.parse()) và mặc dù tôi gửi thời gian UTC, 'này tắt bởi một giờ 'DST vấn đề cây trồng rất nhiều. Ngay cả khi sử dụng '.gmtime()' không phải lúc nào cũng sửa chữa nó.

Rõ ràng sự khác biệt được gây ra bằng cách nào đó bởi một chuyển đổi tiềm ẩn ở đâu đó dẫn đến BST được xử lý không chính xác như UTC, nhưng tại sao? Không đường ray lưu trữ dấu thời gian trong UTC? Không phải là múi giờ của Lớp thời gian nhận thức? Tôi đang sử dụng Rails 2.2.2

Vậy điều gì đang xảy ra ở đây - và cách an toàn để lập trình xung quanh nó là gì?

chỉnh sửa, một số thông tin bổ sung để hiển thị những gì DB và lớp Thời gian đang làm:

>> Model.find(:last).created_at 
=> Tue, 11 Aug 2009 20:31:07 UTC +00:00 
>> Time.now 
=> Tue Aug 11 22:00:18 +0100 2009 
>> Time.now.gmtime 
=> Tue Aug 11 21:00:22 UTC 2009 

Trả lời

13

Lớp Time không phải là trực tiếp nhận thức múi giờ cấu hình của bạn. Rails 2.1 đã thêm một loạt hỗ trợ múi giờ, nhưng Time sẽ vẫn hoạt động theo múi giờ địa phương của bạn. Đây là lý do tại sao Time.now trả về thời gian BST.

Điều bạn có thể muốn là tương tác với Time.zone. Bạn có thể gọi các phương thức trên này giống như bạn sẽ là lớp Time nhưng nó sẽ trả về nó trong múi giờ đã chỉ định.

Time.zone.now # => Tue, 11 Aug 2009 21:31:45 UTC +00:00 
Time.zone.parse("2:30 PM Aug 23, 2009") # => Sun, 23 Aug 2009 14:30:00 UTC +00:00 

Một điều bạn phải cẩn thận với là nếu bạn đã từng làm các truy vấn trên cơ sở dữ liệu mà bạn đang so sánh lần, nhưng chắc chắn để sử dụng thời gian UTC (thậm chí nếu bạn có một múi giờ khác nhau quy định) vì Rails luôn luôn lưu trữ UTC trong cơ sở dữ liệu.

Item.all(:conditions => ["published_at <= ?", Time.now.utc]) 

Ngoài ra, thay vì Time.now-1.hour do 1.hour.ago. Nó dễ đọc hơn và Rails sẽ tự động sử dụng múi giờ đã cấu hình.

+0

Thú vị ... là Time.utc một từ đồng nghĩa với Time.gmtime? Tôi cũng nhận được >> Time.zone => # >. Tôi nghĩ rằng vấn đề là theo cách suy nghĩ của tôi "Thứ Ba ngày 11 tháng 8 22:00:18 +0100 2009" và "Thứ Ba ngày 11 tháng 8 21:00:22 UTC 2009" tham khảo cùng một thời gian hợp lý. Tôi đoán rằng đường ray/ruby ​​chỉ đơn giản là bỏ qua sự bù đắp khi xây dựng SQL. – frankodwyer

+1

Tôi tin rằng Time # utc chỉ là bí danh đối với Time # gmtime.Ngoài ra, bù trừ múi giờ được bỏ qua khi giao dịch với thời gian bình thường (Time.now) nhưng được tính đến khi sử dụng Time.zone.now. Vì vậy, tốt nhất để luôn luôn sử dụng Time.zone sau đó bạn không cần phải gọi "utc" trên đó. – ryanb

+0

cảm ơn, điều đó giúp ích rất nhiều. nó không rõ ràng với tôi rằng nó hoạt động theo cách đó nhưng ít nhất tôi hiểu những gì đang xảy ra bây giờ. Tôi đã thay đổi mã cho phù hợp và có vẻ như đã xóa vấn đề. Một điều mà đã ném mã của tôi là 1. năm không chia hết cho 1.weeks như tôi mong đợi nó được ... Tôi đã làm một vòng lặp và nhận được một lỗi không liên quan nhưng tương tự vì điều đó. – frankodwyer

0

Các TimeZone bạn cần phải thiết lập là Vương quốc Anh, điều này sẽ tự động xử lý BST

Time.zone = 'UK' 
Time.zone.now 
=> Sun, 17 Oct 2010 02:09:54 BST +01:00 
+0

Tôi tin rằng điều này bây giờ sẽ là Time.zone = "London" – simonmorley

+0

Có điều này bây giờ sẽ là London – MatthewFord

0
start_date_format = DateTime.strptime(@start_date, date_format) 
start_date_format_with_hour = 
DateTime.strptime((start_date_format.to_i + timezone_offset*60*60).to_s,'%s').strftime(date_format) 

end_date_format = DateTime.strptime(@end_date, date_format) 
end_date_format_with_hour = DateTime.strptime((end_date_format.to_i + timezone_offset*60*60).to_s,'%s').strftime(date_format) 

@filters_date = "invoices.created_at >= ? AND invoices.created_at < ?", start_date_format_with_hour, end_date_format_with_hour 
+0

Chỉ cần thả một đoạn mã hiếm khi hữu ích. Vui lòng giải thích một chút về mã. – lexicore

Các vấn đề liên quan