2013-07-18 49 views
12

Ok hãy để tôi bắt đầu bằng cách nói múi giờ của tôi là CET/CEST. Thời điểm chính xác nó thay đổi từ CEST thành CET (từ DST, là GMT + 2, thành bình thường, mà GMT + 1, do đó) luôn là Chủ nhật cuối cùng của tháng 10 lúc 3 giờ sáng. Vào năm 2010, đây là ngày 31 tháng 10.Lấy đúng múi giờ bù đắp bằng Python sử dụng múi giờ địa phương

Bây giờ lưu ý những điều sau:

>>> import datetime 
>>> import pytz.reference 
>>> local_tnz = pytz.reference.LocalTimezone() 
>>> local_tnz.utcoffset(datetime.datetime(2010, 10, 31, 2, 12, 30)) 
datetime.timedelta(0, 3600) 

Đây là sai như đã giải thích ở trên.

>>> local_tnz.utcoffset(datetime.datetime(2010, 10, 30, 2, 12, 30)) 
datetime.timedelta(0, 7200) 
>>> local_tnz.utcoffset(datetime.datetime(2010, 10, 31, 2, 12, 30)) 
datetime.timedelta(0, 7200) 

Bây giờ nó là đột nhiên đúng:/

Tôi biết có một số câu hỏi về vấn đề này đã có, nhưng các giải pháp đưa ra luôn là "sử dụng Localize", nhưng vấn đề của tôi ở đây là LocalTimezone không cung cấp phương pháp đó.

Thực tế, tôi có một vài dấu thời gian tính bằng mili giây mà tôi cần utcoffset múi giờ địa phương (không chỉ của tôi, mà là của bất kỳ ai sử dụng chương trình). Một trong số này là 1288483950000 hoặc CN ngày 31 tháng 10 năm 2010 02:12:30 GMT + 0200 (CEST) trong múi giờ của tôi.

Hiện nay tôi làm như sau để có được những đối tượng datetime:

datetime.datetime.fromtimestamp(int(int(millis)/1E3)) 

và điều này để có được những UTCOFFSET trong vài phút:

-int(local_tnz.utcoffset(date).total_seconds()/60) 

đó, không may, là sai trong nhiều dịp :(.

Bất kỳ ý tưởng nào?

Lưu ý: Tôi đang sử dụng python3.2.4, không phải vậy nó sẽ quan trọng trong trường hợp này.

EDIT:

Tìm thấy giải pháp nhờ @JamesHolderness:

def datetimeFromMillis(millis): 
    return pytz.utc.localize(datetime.datetime.utcfromtimestamp(int(int(millis)/1E3))) 

def getTimezoneOffset(date): 
    return -int(date.astimezone(local_tz).utcoffset().total_seconds()/60) 

Với local_tz bằng tzlocal.get_localzone() từ các mô-đun tzlocal.

+0

Sử dụng Python 2.7.5 và pytz 2013b, tôi nhận được các kết quả khác nhau và nhất quán: 'datetime.timedelta (0, 3600)'. 'datetime.timedelta (0, 7200)', và 'datetime.timedelta (0, 3600)' tương ứng. Tôi đặt múi giờ của mình thành CET trước khi chạy thử nghiệm. – martineau

+1

Cố gắng tự đi qua các dòng trong hàm utcoffset và _isdst trên một cli, xem mã tại đây: http://bazaar.launchpad.net/~stub/pytz/devel/view/head:/src/pytz/reference .py Xem bạn có thể thấy điều gì sai không. – ojs

+0

liên quan: [Nhận bù đắp UTC của máy tính bằng Python] (http://stackoverflow.com/q/3168096/4279) – jfs

Trả lời

18

Theo Wikipedia, quá trình chuyển đổi đến và từ Giờ mùa hè diễn ra lúc 01:00 UTC.

  • Lúc 00:12 UTC bạn vẫn đang ở Giờ mùa hè Trung Âu (tức là UTC + 02: 00), vì vậy giờ địa phương là 02:12.

  • Lúc 01:12 UTC bạn trở lại giờ chuẩn Trung Âu (tức là UTC + 01: 00), vì vậy giờ địa phương là 02:12.

Khi thay đổi từ giờ mùa hè trở lại giờ chuẩn, giờ địa phương đi từ 02:59 trở lại 02:00 và giờ tự lặp lại. Vì vậy, khi yêu cầu bù đắp UTC của 02:12 (giờ địa phương), câu trả lời có thể trung thực là +01: 00 hoặc +02: 00 - nó phụ thuộc phiên bản nào của 02:12 mà bạn đang nói đến.

Khi điều tra thêm về thư viện pytz, tôi nghĩ rằng vấn đề của bạn có thể là bạn không nên sử dụng triển khai thực hiện pytz.reference, điều này có thể không giải quyết được những sự mơ hồ này.Trích dẫn từ các nhận xét trong mã nguồn:

Triển khai tzinfo tham chiếu từ tài liệu Python. Được sử dụng để thử nghiệm vì chúng chỉ đúng trong các năm 1987 đến 2006. Không sử dụng các mã này cho mã thực.

Làm việc với thời gian mơ hồ trong pytz

Những gì bạn cần phải làm là xây dựng một múi giờ đối tượng cho các múi giờ thích hợp:

import pytz 
cet = pytz.timezone('CET') 

Sau đó, bạn có thể sử dụng UTCOFFSET để tính toán độ lệch UTC của ngày/giờ trong múi giờ đó.

dt = datetime.datetime(2010, 10, 31, 2, 12, 30) 
offset = cet.utcoffset(dt) 

Lưu ý, rằng ví dụ trên sẽ ném một AmbiguousTimeError ngoại lệ, bởi vì nó không thể nói đó của hai phiên bản của bạn có nghĩa là 02:12:30. May mắn thay, pytz sẽ cho phép bạn xác định xem bạn muốn phiên bản dst hay phiên bản tiêu chuẩn bằng cách đặt tham số is_dst. Ví dụ:

offset = cet.utcoffset(dt, is_dst = True) 

Lưu ý rằng nó không gây hại để thiết lập thông số này trên tất cả các cuộc gọi đến UTCOFFSET, ngay cả khi thời gian sẽ không được mơ hồ. Theo tài liệu, nó chỉ được sử dụng trong các giai đoạn chuyển tiếp không rõ ràng của DST để giải quyết sự mơ hồ đó.

Làm thế nào để đối phó với timestamps

Như để đối phó với timestamps, tốt nhất là bạn lưu trữ chúng như các giá trị tính theo giờ UTC càng lâu càng tốt, nếu không bạn có khả năng kết thúc ném đi những thông tin có giá trị. Vì vậy, trước tiên hãy chuyển đổi thành ngày giờ UTC với phương thức datetime.utcfromtimestamp.

dt = datetime.datetime.utcfromtimestamp(1288483950) 

Sau đó sử dụng pytz để bản địa hóa thời gian là UTC, do đó múi giờ được gắn với đối tượng ngày giờ.

dt = pytz.utc.localize(dt) 

Cuối cùng bạn có thể chuyển đổi đó UTC datetime vào múi giờ địa phương của bạn, và có được múi giờ bù đắp như thế này:

offset = dt.astimezone(cet).utcoffset() 

Lưu ý rằng bộ này tính toán sẽ tạo ra các hiệu chính xác cho cả hai 1288483950 và 1288487550 , mặc dù cả hai dấu thời gian được biểu thị trước 02:12:30 theo múi giờ CET.

Xác định múi giờ địa phương

Nếu bạn cần phải sử dụng các múi giờ địa phương của máy tính của bạn chứ không phải là một múi giờ cố định, bạn không thể làm điều đó từ pytz trực tiếp. Bạn cũng không thể chỉ cần tạo đối tượng pytz.timezone bằng tên múi giờ từ time.tzname, vì tên sẽ không luôn được nhận dạng bởi pytz.

Giải pháp là sử dụng tzlocal module - mục đích duy nhất của nó là cung cấp chức năng thiếu này trong pytz. Bạn sử dụng nó như thế này:

import tzlocal 
local_tz = tzlocal.get_localzone() 

Các get_localzone() hàm trả về một đối tượng pytz.timezone, vì vậy bạn sẽ có thể sử dụng giá trị rằng trong tất cả những nơi tôi đã sử dụng cet biến trong các ví dụ trên.

+0

Tôi đã sử dụng phiên bản mới nhất và kết quả cũng sai cho 01:12 CE (S) T trên cùng ngày, do đó, nó không có gì để làm với sự mơ hồ của ngày. Ngoài ra, nó thường là quy ước để giả định DST trong các giai đoạn chuyển tiếp này, JavaScript thực hiện theo cách này. –

+1

Tôi tò mò điều gì khiến bạn nghĩ rằng có bất kỳ quy ước nào liên quan đến những giai đoạn chuyển tiếp này. Trên máy tính của tôi, việc triển khai javascript khác với một trình duyệt sang trình duyệt tiếp theo. Hãy thử nhập 'ngày mới (2010, 9, 31, 2, 12, 30, 0) .getTime()' vào bảng điều khiển trình duyệt của bạn. Trên Chrome, tôi nhận được 1288487550000 (tức là 01:12 UTC). Trên Firefox, tôi nhận được 1288483950000 (tức là 00:12 UTC). –

+0

Hãy thử với ngày mới (1288483950000), cung cấp cho "CN ngày 31 tháng 10 năm 2010 02:12:30 GMT + 0200" trên Firefox và Chrome cho tôi. –

6

Cho một dấu thời gian trong mili giây bạn có thể nhận được utc bù đắp cho các múi giờ địa phương chỉ sử dụng stdlib:

#!/usr/bin/env python 
from datetime import datetime 

millis = 1288483950000 
ts = millis * 1e-3 
# local time == (utc time + utc offset) 
utc_offset = datetime.fromtimestamp(ts) - datetime.utcfromtimestamp(ts) 

Nếu chúng ta bỏ qua thời gian xung quanh giây nhuận thì không có sự mơ hồ hoặc thời gian không tồn tại.

Hỗ trợ DST và thay đổi bù trừ utc vì các lý do khác nếu OS duy trì múi giờ lịch sử db, nó hoạt động trên Ubuntu cho bất kỳ ngày nào trong quá khứ/hiện tại nhưng có thể bị hỏng trên Windows.

Dưới đây là cùng sử dụng tzlocal mô-đun mà nên làm việc trên các hệ thống * nix và Win32:

#!/usr/bin/env python 
from datetime import datetime 
from tzlocal import get_localzone # pip install tzlocal 

millis = 1288483950000 
ts = millis * 1e-3 
local_dt = datetime.fromtimestamp(ts, get_localzone()) 
utc_offset = local_dt.utcoffset() 

Xem How to convert a python utc datetime to a local datetime using only python standard library?

Để các utc bù đắp trong vài phút (Python 3.2+):

from datetime import timedelta 

minutes = utc_offset/timedelta(minutes=1) 

Không sử dụng pytz.reference.LocalTimezone(), it is only for tests.

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