Как сделать неосознанный часовой пояс datetime в python

что нужно делать

у меня есть объект datetime с часовым поясом, к которому мне нужно добавить часовой пояс, чтобы иметь возможность сравнить его с другими объектами datetime с часовым поясом. Я не хочу конвертировать все мое приложение в часовой пояс, не зная об этом одном устаревшем случае.

что я пробовал

во-первых, чтобы продемонстрировать проблему:

Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> import pytz
>>> unaware = datetime.datetime(2011,8,15,8,15,12,0)
>>> unaware
datetime.datetime(2011, 8, 15, 8, 15, 12)
>>> aware = datetime.datetime(2011,8,15,8,15,12,0,pytz.UTC)
>>> aware
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> aware == unaware
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes

во-первых, я попытался astimezone:

>>> unaware.astimezone(pytz.UTC)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: astimezone() cannot be applied to a naive datetime
>>>

это не удивительно, что это не удалось, так как на самом деле пытается сделать преобразование. Замена казалась лучшим выбором (согласно Python: как получить значение datetime.сегодня () это"часовой пояс"?):

>>> unaware.replace(tzinfo=pytz.UTC)
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> unaware == aware
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
>>> 

но, как вы можете видеть, replace, похоже, устанавливает tzinfo, но не делает объект осведомленным. Я готовлюсь вернуться к обработке входной строки, чтобы иметь часовой пояс перед ее разбором (я использую dateutil для парсинга, если это имеет значение), но это кажется невероятно запутано.

кроме того, я пробовал это как в python 2.6, так и в python 2.7 с одинаковыми результатами.

контекст

Я пишу парсер для некоторых файлов данных. Существует старый формат, который мне нужно поддерживать, где строка даты не имеет индикатора часового пояса. Я уже исправил источник данных, но мне все еще нужно поддерживать устаревший формат данных. Однократное преобразование устаревших данных не вариант по различным причинам бизнеса BS. Хотя в целом мне не нравится идея жесткого кодирования часового пояса по умолчанию, в этом случае это кажется лучшим вариантом. Я знаю с уверенностью, что все старые данные в UTC, так что я готов принять риск дефолта в этом случае.

9 ответов


В общем, чтобы сделать наивный часовой пояс datetime, используйте локализовать способ:

import datetime
import pytz

unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
aware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0, pytz.UTC)

now_aware = pytz.utc.localize(unaware)
assert aware == now_aware

для часового пояса UTC использовать localize поскольку нет расчета летнего времени для обработки:

now_aware = unaware.replace(tzinfo=pytz.UTC)

строительство. (.replace возвращает новое datetime; он не изменяет unaware.)


все эти примеры используют внешний модуль, но вы можете достичь того же результата, используя только модуль datetime.

from datetime import datetime
from datetime import timezone

dt = datetime.now()
dt.replace(tzinfo=timezone.utc)

print(dt.replace(tzinfo=timezone.utc).isoformat())
'2017-01-12T22:11:31+00:00'

меньше зависимостей и нет проблем с pytz.

Примечание: Если вы хотите использовать это с python3 и python2, вы можете использовать это также для импорта часового пояса (жестко для UTC):

try:
    from datetime import timezone
    utc = timezone.utc
except ImportError:
    #Hi there python2 user
    class UTC(tzinfo):
        def utcoffset(self, dt):
            return timedelta(0)
        def tzname(self, dt):
            return "UTC"
        def dst(self, dt):
            return timedelta(0)
utc = UTC()

я использовал от dt_aware до dt_unware

dt_unaware = dt_aware.replace(tzinfo=None)

и dt_unware для dt_aware

from pytz import timezone
localtz = timezone('Europe/Lisbon')
dt_aware = localtz.localize(dt_unware)

но ответ раньше также является хорошим решением.


Я использую этот оператор в Django для преобразования неосознанного времени в осознанное:

from django.utils import timezone

dt_aware = timezone.make_aware(dt_unaware, timezone.get_current_timezone())

это закрепляет @Сержиу и @unutbu это ответы. Он будет "просто работать" с


Я согласен с предыдущими ответами и в порядке, если вы в порядке, чтобы начать в UTC. Но я думаю, что это также общий сценарий для людей, чтобы работать с значением TZ, которое имеет datetime, который имеет не локальный часовой пояс UTC.

Если бы вы просто пошли по имени, можно было бы сделать вывод, что replace () будет применим и создаст правильный объект datetime aware. Это не так.

замена( tzinfo=... ) кажется случайным в своей поведение. Поэтому она бесполезна. Не используйте это!

localize-правильная функция для использования. Пример:

localdatetime_aware = tz.localize(datetime_nonaware)

или более полный пример:

import pytz
from datetime import datetime
pytz.timezone('Australia/Melbourne').localize(datetime.now())

дает мне часовой пояс, знающий значение datetime текущего локального времени:

datetime.datetime(2017, 11, 3, 7, 44, 51, 908574, tzinfo=<DstTzInfo 'Australia/Melbourne' AEDT+11:00:00 DST>)

для добавления локального часового пояса; (используя dateutil)

from dateutil import tz
import datetime

dt_unaware = datetime.datetime(2017, 6, 24, 12, 24, 36)

dt_aware = dt_unaware.replace(tzinfo=tz.tzlocal())

два метода я знаю чисто

from datetime import datetime
import pytz

naive = datetime.now()
aware = naive.replace(tzinfo=pytz.UTC)

или

aware = pytz.UTC.localize(naive)

конечно, вы можете использовать любой часовой пояс вместо UTC,


в формате ответа unutbu; я сделал служебный модуль, который обрабатывает такие вещи, с более понятным синтаксисом. Может быть установлен с помощью pip.

import datetime
import saturn

unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
now_aware = saturn.fix_naive(unaware)

now_aware_madrid = saturn.fix_naive(unaware, 'Europe/Madrid')