2011-09-19 43 views
9

Tôi đang phân tích cú pháp nguồn cấp cảnh báo của Dịch vụ thời tiết quốc gia thành ứng dụng web. Tôi muốn thanh lọc các cảnh báo khi họ đạt đến thời gian hết hạn. Tôi cũng muốn hiển thị thời gian hết hạn theo định dạng giờ địa phương cho khu vực địa lý mà chúng liên quan đến.Chuyển đổi chuỗi ngày nhận biết múi giờ thành UTC và quay lại bằng Python

Các cảnh báo bao gồm toàn bộ Hoa Kỳ, vì vậy tôi nghĩ cách tốt nhất là lưu trữ và so sánh thời gian trong dấu thời gian UTC. Thời gian hết hạn đến trong nguồn cấp dữ liệu dưới dạng chuỗi như sau: 2011-09-09T22:12:00-04:00.

Tôi đang sử dụng Labix dateutils gói để phân tích chuỗi một cách múi giờ-aware:

>>> from dateutil.parser import parse 
>>> d = parse("2011-09-18T15:52:00-04:00") 
>>> d 
datetime.datetime(2011, 9, 18, 15, 52, tzinfo=tzoffset(None, -14400)) 

Tôi cũng có thể nắm bắt được tính theo giờ UTC bù đắp trong giờ:

>>> offset_hours = (d.utcoffset().days * 86400 + d.utcoffset().seconds)/3600 
>>> offset_hours 
-4 

Sử dụng phương thức datetime.utctimetuple()time.mktime(), tôi có thể chuyển đổi ngày được phân tích thành dấu thời gian UTC:

>>> import time 
>>> expiration_utc_ts = time.mktime(d.utctimetuple()) 
>>> expiration_utc_ts 
1316393520.0 

Tại thời điểm này, tôi cảm thấy khá tốt rằng tôi có thể chuyển đổi các chuỗi thô thành một dấu thời gian biểu thị thời gian hết hạn trong UTC. Tôi có thể so sánh thời điểm hiện tại như một UTC dấu thời gian để kết thúc thời hạn và xác định xem nó cần phải được thanh lọc:

>>> now_utc_ts = time.mktime(time.gmtime()) 
>>> now_utc_ts 
1316398744.0 
>>> now_utc_ts >= expiration_tc_ts 
True 

Khó khăn tôi đang gặp đang cố gắng để chuyển đổi lưu trữ UTC timestamp của tôi trở lại với bản gốc định dạng bản địa hóa. Tôi có những giờ bù đắp được lưu trữ từ việc chuyển đổi ban đầu và một chuỗi Tôi phân tích cú pháp để lưu trữ các nhãn múi giờ:

>>> print offset_hours 
-4 
>>> print timezone 
EDT 

Tôi muốn chuyển đổi UTC timestamp trở lại trong một thời gian định dạng tại địa phương, nhưng chuyển đổi nó trở lại một datetime dường như không có tác dụng:

>>> import datetime 
>>> datetime.datetime.fromtimestamp(expiration_utc_ts) + datetime.timedelta(hours=offset_hours) 
datetime.datetime(2011, 9, 18, 16, 52) # The hour is 16 but it should be 15 

dường như đó là tắt bởi một tiếng đồng hồ. Tôi không chắc chắn nơi lỗi được giới thiệu? Tôi đặt cùng một thử nghiệm khác và có kết quả tương tự:

>>> # Running this at 21:29pm EDT 
>>> utc_now = datetime.datetime.utcnow() 
>>> utc_now_ts = time.mktime(right_now.utctimetuple()) 
>>> datetime.datetime.fromtimestamp(utc_now_ts) 
datetime.datetime(2011, 9, 18, 22, 29, 47) # Off by 1 hour 

Ai đó có thể giúp tôi tìm thấy lỗi của mình không? Tôi không chắc đó có phải là vấn đề tiết kiệm ánh sáng ban ngày không? Tôi bắt gặp một số thứ khiến tôi tin rằng nó có thể đang cố gắng bản địa hoá ngày tháng và thời gian của tôi nhưng vào thời điểm này tôi khá là bối rối. Tôi đã hy vọng làm tất cả những tính toán/so sánh này theo cách thức bất khả tri theo thời gian.

+0

thể trùng lặp của [Issue với python/pytz Chuyển đổi từ múi giờ địa phương để UTC sau đó trở lại] (http://stackoverflow.com/questions/7465181/issue-with-python-pytz-converting- từ-local-timezone-to-utc-then-back) – agf

+0

Tôi đã nghĩ bạn có thể làm tất cả điều này với các đối tượng datetime, và không chuyển đổi thành tuple hoặc timestamps. Nếu bạn thực hiện đối tượng datetime "now" nhận biết múi giờ thì chỉ cần thực hiện "if x

+0

Tôi hy vọng như vậy, nhưng PostgreSQL vẫn muốn bản địa hoá giá trị datetime. (Tôi chắc chắn có một cách xung quanh đó, nhưng tôi thấy nó sẽ được dễ dàng hơn ngay bây giờ để chỉ lưu trữ thời gian UTC) – Scott

Trả lời

3

Vấn đề là thời gian tiết kiệm ánh sáng ban ngày đang được áp dụng hai lần.

Một ví dụ nhỏ:

>>> time_tuple = datetime(2011,3,13,2,1,1).utctimetuple() 
time.struct_time(tm_year=2011, tm_mon=3, tm_mday=13, tm_hour=2, tm_min=1, tm_sec=1, tm_wday=6, tm_yday=72, tm_isdst=0) 
>>> datetime.fromtimestamp(time.mktime(time_tuple)) 
datetime.datetime(2011, 3, 13, 3, 1, 1) 

Tôi khá chắc chắn rằng lỗi nằm trong time.mktime(). Như đã nói trong số documentation:

Đây là hàm nghịch đảo của giờ địa phương(). Đối số của nó là struct_time hoặc toàn bộ 9-tuple (vì cờ dst là cần thiết; sử dụng -1 làm cờ dst nếu không xác định) hiển thị thời gian theo giờ địa phương chứ không phải UTC.Nó trả về một số dấu phẩy động, cho số tương thích với thời gian(). Nếu giá trị đầu vào không thể được biểu diễn là một khoảng thời gian hợp lệ, hoặc OverflowError hoặc ValueError sẽ được nâng lên (trong đó phụ thuộc vào giá trị không hợp lệ được Python hoặc các thư viện C cơ bản bắt giữ). Ngày sớm nhất có thể tạo thời gian phụ thuộc vào nền tảng.

Khi bạn vượt qua một tuple thời gian để time.mktime(), nó hy vọng một lá cờ vào việc thời gian là trong ánh sáng ban ngày tiết kiệm thời gian hay không. Như bạn thấy ở trên, utctimetuple() trả về một tuple với cờ đánh dấu 0, vì nó nói nó sẽ làm gì trong nó documentation:

Nếu datetime dụ d là ngây thơ, điều này cũng giống như d.timetuple() ngoại trừ tm_isdst bị buộc phải 0 bất kể những gì d.dst() trả về. DST không bao giờ có hiệu lực trong thời gian UTC.

Nếu d nhận thức được, d được chuẩn hóa theo thời gian UTC, bằng cách trừ d.utcoffset() và time.struct_time cho thời gian chuẩn hóa là được trả về. tm_isdst bị buộc phải 0. Lưu ý rằng kết quả tm_year thành viên có thể là MINYEAR-1 hoặc MAXYEAR + 1, nếu d.year là MINYEAR hoặc MAXYEAR và điều chỉnh UTC tràn qua ranh giới một năm.

Vì bạn đã nói với time.mktime() rằng thời gian của bạn không phải là DST, và công việc của mình là để chuyển đổi tất cả các lần vào giờ địa phương, và nó hiện là ánh sáng ban ngày tiết kiệm thời gian trong khu vực của bạn, nó thêm một giờ để làm cho nó thời gian tiết kiệm ánh sáng ban ngày. Do đó kết quả.


Trong khi tôi không có bài viết có ích, tôi đi qua một phương pháp một vài ngày trước để chuyển đổi datetimes múi giờ-aware vào những ngây thơ theo giờ địa phương của bạn. Điều này có thể làm việc tốt hơn cho các ứng dụng của bạn hơn những gì bạn đang làm (sử dụng mô-đun pytz xuất sắc):

import pytz 
def convert_to_local_time(dt_aware): 
    tz = pytz.timezone('America/Los_Angeles') # Replace this with your time zone string 
    dt_my_tz = dt_aware.astimezone(tz) 
    dt_naive = dt_my_tz.replace(tzinfo=None) 
    return dt_naive 

Thay thế 'Mỹ/Los Angeles' với chuỗi múi giờ của riêng bạn, mà bạn có thể tìm thấy ở đâu đó trong pytz.all_timezones.

+0

Nó không chính xác. 'mktime()' mong đợi * local time * trong khi OP chuyển UTC time. Nó không thành công nếu múi giờ địa phương không phải là UTC. Không sử dụng 'naive_datetime_object.utctimetuple()' - nó không chuyển đổi nó thành UTC (OP sử dụng 'know_datetime.object.utctimetuple()' hoạt động như mong đợi). Bạn có thể cần 'tz.normalize()' gọi sau '.astimezone()' nếu 'aware_dt' không có trong UTC. – jfs

0

datetime.fromtimestamp() là phương pháp chính xác để nhận giờ địa phương từ dấu thời gian POSIX. Vấn đề trong câu hỏi của bạn là bạn chuyển đổi một đối tượng ngày giờ nhận biết sang dấu thời gian POSIX bằng cách sử dụng time.mktime() không chính xác. Dưới đây là one of correct ways to do it:

expiration_utc_ts = (d - datetime(1970, 1, 1, tzinfo=utc)).total_seconds() 
local_dt = datetime.fromtimestamp(expiration_utc_ts) 
Các vấn đề liên quan