2015-12-14 16 views
5

Tôi đang cố gắng phân tích cú pháp nguồn cấp dữ liệu RSS. Mục trong thức ăn chăn nuôi có yếu tố ngày như:Bản địa hóa ngày giờ bằng python/django

<dc:date>2016-09-21T16:00:00+02:00</dc:date> 

Sử dụng feedparser, tôi cố gắng làm:

published_time = datetime.fromtimestamp(mktime(entry.published_parsed)) 

Nhưng vấn đề là tôi dường như nhận được sai thời gian lưu trữ trong cơ sở dữ liệu. Trong trường hợp cụ thể này, ngày giờ được lưu trữ dưới dạng:

2016-09-21 13:00:00 

... khi tôi mong đợi 14:00 - đúng giờ UTC.

tôi giả sử vấn đề là trong cài đặt django của chúng tôi, nơi chúng tôi có:

TIME_ZONE = 'Europe/Berlin' 

Bởi vì khi tôi chuyển sang:

TIME_ZONE = 'UTC' 

... các datatime được lưu giữ như thời gian tính theo giờ UTC đúng:

2016-09-21 14:00:00 

Có cách nào để giữ cài đặt django giống như chúng không, nhưng để phân tích cú pháp và lưu trữ datetime này c orrectly, không có thiết lập múi giờ django ảnh hưởng đến nó?

EDIT: Có thể đó là rõ ràng hơn như thế này ...

print entry.published_parsed 
published_time = datetime.fromtimestamp(mktime(entry.published_parsed)) 
print published_time 
localized_time = pytz.timezone(settings.TIME_ZONE).localize(published_time, is_dst=None) 
print localized_time 

time.struct_time(tm_year=2016, tm_mon=9, tm_mday=21, tm_hour=14, tm_min=0, tm_sec=0, tm_wday=2, tm_yday=265, tm_isdst=0) 
2016-09-21 15:00:00 
2016-09-21 15:00:00+02:00 
+1

Bạn có quan tâm đến việc chuyển đổi múi giờ hoặc bạn sẽ mở để chỉ cần thêm một giờ với một ngày giờ.timedelta hoạt động? – JwM

+0

Cuối cùng, tôi muốn có thời gian chính xác trong UTC. Mất một giờ đi ngay bây giờ (hai giờ trong ngày tiết kiệm thời gian) có thể là một cách để đi. Tôi chưa nhìn vào nó. Tôi đã tự hỏi nếu có một cách khác. Tôi đã thử ví dụ timezone.activate() và timezone.deactivate() dường như thay đổi current_timezone theo đúng cách, nhưng điều đó không khắc phục được sự cố. – apiljic

+0

Bạn có thể thực hiện nhận biết về ngày giờ hoặc thay đổi múi giờ nếu đã biết nhưng sai. –

Trả lời

2

feedparser entry.published_parsed luôn là thời điểm utc tuple bất kể chuỗi thời gian đầu vào là gì. Để có được múi giờ-aware datetime đối tượng:

from datetime import datetime 

utc_time = datetime(*entry.published_parsed[:6], tzinfo=utc) 

nơi utc là một đối tượng tzinfo như datetime.timezone.utc, pytz.utc, hoặc chỉ custom tzinfo (for older python versions) của bạn.

Bạn không nên chuyển thời gian utc sang mktime() dự kiến ​​giờ địa phương. Lỗi tương tự: Have a correct datetime with correct timezone.

Đảm bảo USE_TZ=True để django sử dụng các đối tượng datetime nhận biết ở khắp mọi nơi. Cho một đối tượng datetime nhận biết múi giờ, django nên lưu nó vào db một cách chính xác bất cứ điều gì TIME_ZONE or timezone.get_current_timezone() are của bạn.

+0

Tôi cũng đã thử giải pháp này. Nó cũng hoạt động. Cảm ơn! – apiljic

1

Bạn đã cố gắng sử dụng datetime.utcfromtimestamp() thay vì datetime.fromtimestamp()?

Là giải pháp thứ cấp, bạn có thể lấy dữ liệu chưa được phân tích (tôi tin rằng nó có sẵn là entry.published?) Và chỉ sử dụng python-dateutil để phân tích chuỗi, sau đó chuyển đổi thành múi giờ pytz.utc như thế này.

>>> import pytz 
>>> from dateutil import parser 
>>> dt = parser.parse('2016-09-21T16:00:00+02:00') 
>>> dt 
datetime.datetime(2016, 9, 21, 16, 0, tzinfo=tzoffset(None, 7200)) 
>>> dt.astimezone(pytz.utc) 
datetime.datetime(2016, 9, 21, 14, 0, tzinfo=<UTC>) 
+0

time.struct_time (tm_year = 2016, tm_mon = 9, tm_mday = 21, tm_hour = 14, tm_min = 0, tm_sec = 0, tm_wday = 2, tm_yday = 265, tm_isdst = 0) 2016-09-21 13:00 : 00 2016-09-21 13: 00: 00 + 00: 00 ... Đây là đầu ra của utcfromtimestamp(). Múi giờ được thay đổi, nhưng thời gian vẫn không chính xác. – apiljic

+0

Giải pháp thứ hai có thể hoạt động. Mối quan tâm duy nhất của tôi là có nhiều định dạng ngày khác nhau. Từ những gì chúng tôi đã gặp cho đến nay, feedparser đã không có một vấn đề với bất kỳ người trong số họ. Tôi tự hỏi nếu các phân tích cú pháp bạn đề nghị hoạt động tốt như nhau. Bạn có sử dụng nó cho nhiều định dạng ngày khác nhau không? – apiljic

+1

@apiljic: sử dụng feedparser để phân tích chuỗi thời gian đầu vào (thuộc tính '_parsed'). 'dateutil' chấp nhận quá nhiều định dạng thời gian đầu vào và do đó có thể trả về một kết quả sai một cách âm thầm. – jfs

1

Sử dụng

published_time = pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed))) 

Feedparser thể phân tích một phạm vi rộng lớn các định dạng ngày, bạn có thể tìm thấy chúng here.

Như bạn có thể thấy trong feedparser/feedparser/datetimes/__init__.py, được xây dựng trong chức năng từ Feedparser _parse_date nào sau đây:

Parses a variety of date formats into a 9-tuple in GMT

Điều này có nghĩa rằng trong parsed_entry.published_parsed bạn có một đối tượng time.struct_time trong giờ múi giờ.

Khi bạn chuyển nó sang một đối tượng sử dụng datetime

published_time = datetime.fromtimestamp(mktime(parsed_entry.published_parsed)) 

vấn đề là mktime giả định rằng các tuple qua là trong giờ địa phương, mà không phải là, nó GMT/UTC! Ngoài ra, bạn không bản địa hóa đối tượng datetime ở cuối chuyển đổi.

Bạn cần phải thay thế chuyển đổi đó bằng những điều sau, nhớ rằng Feedparser trả về GMT struct_time và bản địa hóa múi giờ đó theo múi giờ bạn thích (UTC vì mục đích đơn giản).

  • Bạn sử dụng calendar.timegm, mang đến cho số giây giữa thời đại và ngày trôi qua như một tham số, giả định rằng các đối tượng thông qua là trong UTC/GMT (chúng ta đã biết từ Feedparser nó là)
  • Bạn sử dụng utcfromtimestamp để có được một đối tượng ngây thơ datetime (mà chúng ta biết là đại diện cho một datetime trong UTC, nhưng Python không có tại thời điểm này)
  • Với pytz.utc.localize bạn đúng địa phương hóa trong UTC các đối tượng datetime.

Ví dụ:

import calendar 
from datetime import datetime 
import pytz 
localized_dt = pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed))) 

Chừng nào bạn là phù hợp, nó không quan trọng nếu bạn sử dụng fromtimestamp hoặc utcfromtimestamp. Nếu bạn sử dụng fromtimestamp, bạn cần cho Python biết rằng đối tượng datetime bạn đã tạo có múi giờ địa phương. Giả sử bạn đang ở Châu Âu/Berlin, đây là cũng tốt:

pytz.timezone('Europe/Berlin').localize(datetime.fromtimestamp(calendar.timegm(parsed_entry.published_parsed))) 

Were parsed_entry.published_parsed còn ở múi giờ địa phương, mktime phải được sử dụng ở vị trí của calendar.timegm.

Là một thay thế, bạn có thể phân tích cho mình chuỗi dữ liệu bạn nhận được từ Feedparser parsed_entry['published']

from dateutil import parser 
localized_dt = parser.parse(parsed_entry['published']) 

Bạn có thể kiểm tra xem lợi nhuận sau True:

parser.parse(parsed_entry['published']) == pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed))) 

Thiết lập Django TIME_ZONE không thực sự vấn đề, bởi vì nó chỉ được sử dụng cho các mục đích hình dung hoặc để tự động chuyển đổi các datetimes ngây thơ.

When USE_TZ is True, this is the default time zone that Django will use to display datetimes in templates and to interpret datetimes entered in forms.

Điều quan trọng là luôn sử dụng đúng giờ địa phương, bất kể múi giờ nào được sử dụng. Miễn là chúng không có định dạng ngây thơ, chúng sẽ được Django xử lý đúng cách.

+0

nó là không cần thiết phức tạp. Đây là một [giải pháp đơn giản] (http://stackoverflow.com/a/34292796/4279) – jfs

+0

Tôi đồng ý, bạn cần sự phức tạp này khi bạn cần xem xét cờ dst, đó là trường hợp của một giờ địa phương (đó là nơi bạn sử dụng mktime) và không phải cho UTC, mà không có nó. –

+0

nếu thời gian không phải là UTC thì mã không chỉ phức tạp; nó chỉ là sai. – jfs

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