Python datetime strptime() и strftime(): как сохранить информацию о часовом поясе

См. следующий код:

import datetime
import pytz

fmt = '%Y-%m-%d %H:%M:%S %Z'

d = datetime.datetime.now(pytz.timezone("America/New_York"))
d_string = d.strftime(fmt)
d2 = datetime.datetime.strptime(d_string, fmt)
print d_string 
print d2.strftime(fmt)

вывод

2013-02-07 17:42:31 EST
2013-02-07 17:42:31 

Информация о часовом поясе просто потерялась при переводе.

Если я переключу «%Z» на «%z», я получу

ValueError: 'z' is a bad directive in format '%Y-%m-%d %H:%M:%S %z'

Я знаю, что могу использовать python-dateutil, но мне просто показалось странным, что я не могу реализовать эту простую функцию в datetime и должен вводить больше зависимостей?


person Vendetta    schedule 07.02.2013    source источник


Ответы (4)


Часть проблемы здесь заключается в том, что строки, обычно используемые для представления часовых поясов, на самом деле не уникальны. «EST» означает «Америка/Нью-Йорк» только для жителей Северной Америки. Это ограничение в C time API, и решение Python состоит в том, чтобы… добавить полные функции tz в какую-нибудь будущую версию в любой день, если кто-то захочет написать PEP.

Вы можете форматировать и анализировать часовой пояс как смещение, но при этом теряется информация о переходе на летнее/летнее время (например, вы не можете отличить "Америку/Феникс" от "Америка/Лос-Анджелес" летом) . Вы можете отформатировать часовой пояс как трехбуквенную аббревиатуру, но вы не можете проанализировать его обратно.

Если вам нужно что-то нечеткое и неоднозначное, но обычно то, что вам нужно, вам нужна сторонняя библиотека, такая как dateutil.

Если вам нужно что-то действительно однозначное, просто добавьте фактическое имя tz к локальной строке даты и времени и разделите его на другом конце:

d = datetime.datetime.now(pytz.timezone("America/New_York"))
dtz_string = d.strftime(fmt) + ' ' + "America/New_York"

d_string, tz_string = dtz_string.rsplit(' ', 1)
d2 = datetime.datetime.strptime(d_string, fmt)
tz2 = pytz.timezone(tz_string)

print dtz_string 
print d2.strftime(fmt) + ' ' + tz_string

Или… на полпути между этими двумя, вы уже используете библиотеку pytz, которая может анализировать (в соответствии с некоторыми произвольными, но четко определенными правилами устранения неоднозначности) такие форматы, как «EST». Итак, если вы действительно хотите, вы можете оставить %Z на стороне форматирования, затем вытащить его и проанализировать с помощью pytz.timezone(), прежде чем передать остальное в strptime.

person abarnert    schedule 07.02.2013
comment
Я думаю, что замена EST на America/New_York не лучшая идея: это элементы из разных наборов. America/New_York принадлежит набору, содержащему среди прочего Europe/Rome и Africa/Dakar, а EST имеет братьев и сестер EDT, CET, UTC, а также EST и CEST. Более того, означает ли Europe/Rome CET (UTC+1) или CEST (UTC+2) зависит от календарного дня и политических решений, которые могут меняться в течение года. Я бы последовал ответу @Johnny. - person mariotomo; 22.12.2017

К сожалению, strptime() может обрабатывать только часовой пояс, настроенный вашей ОС, и то только как смещение времени. Из документации:

Поддержка директивы %Z основана на значениях, содержащихся в tzname, и на том, верно ли daylight. Из-за этого он зависит от платформы, за исключением распознавания UTC и GMT, которые всегда известны (и считаются часовыми поясами без перехода на летнее время).

strftime() официально не поддерживает %z.

Боюсь, вы застряли с python-dateutil для поддержки анализа часовых поясов.

person Martijn Pieters    schedule 07.02.2013
comment
Фактически, %z поддерживается в большинстве систем POSIX в соответствии с правилом Дополнительные директивы могут поддерживаться на определенных платформах (один абзац с конца вашей ссылки). См. также http://docs.python.org/2/library/time.html#id2 внизу страницы. Но он не задокументирован для работы в какой-либо конкретной системе (на практике он работает только в том случае, если ваша платформа POSIX и встроенная функция libc поддерживает его), а также не задокументировано, что произойдет, если вы используете его на система, которая не может справиться с этим, и обычно это так же плохо, как и не работает. - person abarnert; 08.02.2013
comment
Верно; изменено на «официально». Поддержка часовых поясов в стандартной библиотеке отсутствует. Спасибо за объяснение! - person Martijn Pieters; 08.02.2013
comment
Я считаю, что предложение добавить полные функции tz в какую-то будущую версию в любой день снова живо. На этот раз идея просто включить tzdb в Python для Windows и других платформ, на которых его изначально нет, не вызывает немедленного смеха, потому что увеличение Python на 131 КБ больше не так уж страшно. Но есть целая проблема поддержания базы данных в актуальном состоянии… - person abarnert; 08.02.2013
comment
Я всегда думал, что этот последний пункт является причиной того, что в stdlib нет хорошей поддержки TZ… - person Martijn Pieters; 08.02.2013
comment
Что ж, IIRC, причина отсутствия поддержки в 1.6 или 2.0 или всякий раз, когда это было впервые предложено, на самом деле больше касалась 131 КБ… и того факта, что какая-то платформа (SGI?) действительно поставлялась с zoneinfo, но она была сломана. Но да, теперь, когда мы преодолели эти две проблемы, мысль о том, что в Windows версии 3.4.0 и 2.7.4 (или что-то еще) будут навсегда заморожены в определениях 2013 года, довольно пугающая, учитывая, сколько людей все еще используют 2.7. .2 или даже 2.6.x сегодня… - person abarnert; 08.02.2013

Вот мой ответ в Python 2.7

Распечатать текущее время с часовым поясом

from datetime import datetime
import tzlocal  # pip install tzlocal

print datetime.now(tzlocal.get_localzone()).strftime("%Y-%m-%d %H:%M:%S %z")

Распечатать текущее время с указанием часового пояса

from datetime import datetime
import pytz # pip install pytz

print datetime.now(pytz.timezone('Asia/Taipei')).strftime("%Y-%m-%d %H:%M:%S %z")

Он напечатает что-то вроде

2017-08-10 20:46:24 +0800
person Johnny    schedule 10.08.2017
comment
Суть вопроса strptime(). Кроме того, полностью упускается из виду отсутствие глобальных стандартных имен часовых поясов. - person jennykwan; 28.03.2018

Попробуй это:

import pytz
import datetime

fmt = '%Y-%m-%d %H:%M:%S %Z'

d = datetime.datetime.now(pytz.timezone("America/New_York"))
d_string = d.strftime(fmt)
d2 = pytz.timezone('America/New_York').localize(d.strptime(d_string,fmt), is_dst=None)
print(d_string)
print(d2.strftime(fmt))
person patcou    schedule 13.01.2021