2015-12-14 3 views
5

Я пытаюсь разобрать RSS-канал. Записи в сырье имеют дату элементы, такие как:Локализация даты и времени с python/django

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

Использование feedparser, я пытаюсь сделать:

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

Но проблема в том, что я, кажется, получают неправильное время хранится в базе данных. В этом конкретном случае время datetime хранится как:

2016-09-21 13:00:00 

... когда я ожидаю, что 14:00 - правильное время UTC.

Я полагаю, что проблема в наших настройках Джанго, где мы имеем:

TIME_ZONE = 'Europe/Berlin' 

Потому что, когда я переключаюсь на:

TIME_ZONE = 'UTC' 

... datatime хранится в правильное время UTC:

2016-09-21 14:00:00 

есть ли способ, чтобы сохранить настройки Джанго, как они есть, но разобрать и хранить этот DATETIME гр правильно, без установки часового пояса django, влияющих на него?

EDIT: Может быть, это более ясно, как это ...

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

Вы заинтересованы в преобразовании часового пояса или вы сможете просто добавить час с датой.timedelta операция? – JwM

+0

В конечном счете, я хотел бы иметь правильное время в UTC. Взятие часа сейчас (два часа в дневной сберегательный период) может быть способом. Хотя я еще не посмотрел на него. Мне было интересно, есть ли другой способ. Я пробовал, например, timezone.activate() и timezone.deactivate(), которые, похоже, правильно изменяли current_timezone, но это не помогло решить проблему. – apiljic

+0

Вы можете сообщить дату и время или изменить часовой пояс, если это уже известно, но неправильно. –

ответ

2

feedparser's entry.published_parsed всегда является временным кортежем utc независимо от строки ввода времени. Для того, чтобы получить часовой пояс-Aware datetime объект:

from datetime import datetime 

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

где utc является объектом tzinfo, такие как datetime.timezone.utc, pytz.utc, или просто ваш custom tzinfo (for older python versions).

Вы должны пройти мимо utc до mktime(), который вы рассчитываете по местному времени. Такая же ошибка: Have a correct datetime with correct timezone.

Убедитесь, что USE_TZ=True так, что django использует известные объекты datetime повсюду. Учитывая объект datetime, поддерживающий часовой пояс, django должен сохранить его в db правильно, независимо от вашего TIME_ZONE or timezone.get_current_timezone() are.

+0

Я также пробовал это решение. Он тоже работает. Благодаря! – apiljic

1

Вы пробовали использовать datetime.utcfromtimestamp() вместо datetime.fromtimestamp()?

В качестве вторичного решения, вы можете получить неразобранные данные (я считаю, что это доступно, как entry.published?) И просто использовать питон-dateutil разобрать строку, а затем преобразовать его в pytz.utc часового пояса, как это.

>>> 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 ... Это результат utcfromtimestamp(). Часовой пояс изменен, но время все еще не правильно. – apiljic

+0

Второе решение может работать. Моя единственная проблема заключается в том, что существует много разных форматов дат. Из того, с чем мы столкнулись, у feedparser нет проблем с любым из них. Мне интересно, правильно ли работает парсер, который вы предлагаете. Используете ли вы его для разных форматов даты? – apiljic

+1

@apiljic: используйте feedparser для анализа строк времени ввода (атрибуты '_parsed'). 'dateutil' принимает слишком много форматов входных данных и поэтому может безрезультатно возвращать неправильный результат. – jfs

1

Использование

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

Feedparser может анализировать широкий спектр форматов даты, вы можете найти их here.

Как вы можете видеть в feedparser/feedparser/datetimes/__init__.py, встроенная функция от Feedparser _parse_date выполняет следующие действия:

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

Это означает, что в parsed_entry.published_parsed у вас есть time.struct_time объект в часовом поясе.

При преобразовании его в datetime объекта с помощью

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

проблема заключается в том, что mktime предполагает, что переданный кортеж в по местному времени, который не является, это GMT/UTC! Кроме того, вы не правильно локализуете объект datetime в конце преобразования.

Необходимо заменить это преобразование следующим, помня, что Feedparser возвращает GMT struct_time и локализует это с помощью часового пояса, который вам нравится (UTC для простоты).

  • используется calendar.timegm, что дает количество секунд между эпохой и датой, переданной в качестве параметра, при условии, что переданный объект находится в UTC/GMT (мы знаем из Feedparser это)
  • используется utcfromtimestamp для получения наивного объекта datetime (который, как мы знаем, представляет дату-время в UTC, но Python в данный момент нет)
  • С pytz.utc.localize вы правильно локализуете в UTC объект datetime.

Пример:

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

Пока вы последовательны, это не имеет значения, если вы используете fromtimestamp или utcfromtimestamp. Если вы используете fromtimestamp, вам нужно сообщить Python, что созданный вами объект datetime имеет локальный часовой пояс. Предположим, вы находитесь в Европе/Берлин, это тоже хорошо:

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

Были parsed_entry.published_parsed также в местном часовом поясе, mktime должен использоваться вместо calendar.timegm.

В качестве альтернативы вы можете разобрать себя строку данных вы получите от Feedparser parsed_entry['published']

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

Вы можете проверить, что следующие возвращается True:

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

Установка Django TIME_ZONE фактически не потому что он используется только для целей визуализации или для автоматического преобразования наивных дат.

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.

Важно, чтобы всегда правильно использовать локализованные даты, независимо от того, какой часовой пояс используется. Пока они не в наивном формате, они будут надлежащим образом обработаны Django.

+0

это не нужно. Вот [более простое решение] (http://stackoverflow.com/a/34292796/4279) – jfs

+0

Я согласен, вам нужно это усложнение, когда вам нужно рассмотреть флаг dst, который имеет место для локального времени (то есть вы используете mktime), а не для UTC, а это не так. –

+0

если время не UTC, то код не просто сложный; это просто неправильно. – jfs

Смежные вопросы