Мне нужно разобрать строки RFC 3339, как "2008-09-03T20:56:35.450686Z"
в datetime
тип Python .
Я нашел strptime
в стандартной библиотеке Python, но это не очень удобно.
Каков наилучший способ сделать это?
Мне нужно разобрать строки RFC 3339, как "2008-09-03T20:56:35.450686Z"
в datetime
тип Python .
Я нашел strptime
в стандартной библиотеке Python, но это не очень удобно.
Каков наилучший способ сделать это?
Ответы:
Пакет python-dateutil может анализировать не только строки даты и времени RFC 3339, как в вопросе, но также и другие строки даты и времени ISO 8601, которые не соответствуют RFC 3339 (например, те, которые не имеют смещения UTC, или те, которые представляют только свидание).
>>> import dateutil.parser
>>> dateutil.parser.isoparse('2008-09-03T20:56:35.450686Z') # RFC 3339 format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())
>>> dateutil.parser.isoparse('2008-09-03T20:56:35.450686') # ISO 8601 extended format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> dateutil.parser.isoparse('20080903T205635.450686') # ISO 8601 basic format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> dateutil.parser.isoparse('20080903') # ISO 8601 basic format, date only
datetime.datetime(2008, 9, 3, 0, 0)
Обратите внимание, что dateutil.parser.isoparse
предположительно строже, чем более хакерскийdateutil.parser.parse
, но оба они довольно просты и попытаются интерпретировать передаваемую вами строку. Если вы хотите исключить возможность каких-либо неправильных прочтений, вам нужно использовать что-то более строгое, чем любой из этих функции.
Имя Pypi python-dateutil
не dateutil
(спасибо code3monk3y ):
pip install python-dateutil
Если вы используете Python 3.7, взглянуть на этот ответ о datetime.datetime.fromisoformat
.
python-dateutil
не dateutil
так: pip install python-dateutil
.
dateutil.parser
он намеренно взломан: он пытается угадать формат и делает неизбежные предположения (настраиваемые только вручную) в неоднозначных случаях. Так что используйте его ТОЛЬКО, если вам нужно разобрать ввод неизвестного формата, и вы можете терпеть случайные неправильные чтения.
В datetime
стандартной библиотеке появилась функция инвертирования datetime.isoformat()
.
classmethod
datetime.fromisoformat(date_string)
:Вернуть
datetime
соответствующий adate_string
в одном из форматовdate.isoformat()
иdatetime.isoformat()
.В частности, эта функция поддерживает строки в формате (ах):
YYYY-MM-DD[*HH[:MM[:SS[.mmm[mmm]]]][+HH:MM[:SS[.ffffff]]]]
где
*
может соответствовать любой отдельный символ.Внимание : это не поддерживает разбор произвольных строк ISO 8601 - оно предназначено только как обратная операция
datetime.isoformat()
.
Пример использования:
from datetime import datetime
date = datetime.fromisoformat('2017-01-01T12:30:59.000000')
datetime
может содержать a tzinfo
и, следовательно, выводить часовой пояс, но datetime.fromisoformat()
не анализирует tzinfo? похоже на ошибку ..
isoformat
. Он не принимает пример в вопросе "2008-09-03T20:56:35.450686Z"
из-за трейлинга Z
, но он принимает "2008-09-03T20:56:35.450686"
.
Z
входной скрипт можно изменить с помощью date_string.replace("Z", "+00:00")
.
Обратите внимание, что в Python 2.6+ и Py3K символ% f перехватывает микросекунды.
>>> datetime.datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
Смотрите проблему здесь
strptime
на самом деле невозможно выполнить разбор RFC 3339 .
datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f')
так что
Несколько ответов здесь предлагают использовать datetime.datetime.strptime
для анализа времени RFC 3339 или ISO 8601 с часовыми поясами, как показано в вопросе:
2008-09-03T20:56:35.450686Z
Это плохая идея.
Предполагая, что вы хотите поддерживать полный формат RFC 3339, включая поддержку смещений UTC, отличных от нуля, код, предлагаемый этими ответами, не работает. Действительно, это не может работать, потому что синтаксический анализ RFC 3339 с использованиемstrptime
невозможен. Строки формата, используемые модулем datetime в Python, не могут описать синтаксис RFC 3339.
Проблема в смещениях UTC. RFC 3339 Интернет - формат даты / времени требует , чтобы каждая дата-время включает в себя UTC смещение, и что эти смещения могут быть либо Z
(сокращенно «Зулу времени») или в +HH:MM
или -HH:MM
формате, как +05:00
и -10:30
.
Следовательно, все они являются действительными датами времени RFC 3339:
2008-09-03T20:56:35.450686Z
2008-09-03T20:56:35.450686+05:00
2008-09-03T20:56:35.450686-10:30
Увы, строки формата используются strptime
и не strftime
имеют директив, соответствующих смещениям UTC в формате RFC 3339. Полный список директив, которые они поддерживают, можно найти по адресу https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior , и единственная директива смещения UTC, включенная в список %z
:
% г
Смещение UTC в форме + ЧЧММ или -ЧЧММ (пустая строка, если объект наивный).
Пример: (пусто), +0000, -0400, +1030
Это не соответствует формату смещения RFC 3339, и действительно, если мы попытаемся использовать %z
в строке формата и проанализировать дату RFC 3339, у нас не получится:
>>> from datetime import datetime
>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
(data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686Z' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
>>> datetime.strptime("2008-09-03T20:56:35.450686+05:00", "%Y-%m-%dT%H:%M:%S.%f%z")
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
(data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686+05:00' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
(На самом деле, вышесказанное - это то, что вы увидите в Python 3. В Python 2 мы потерпим неудачу по еще более простой причине, заключающейся в том, что в Python 2 директива strptime
вообще не реализуется.%z
)
Несколько ответов здесь, которые рекомендуют strptime
всем обойти это, путем включения литерала Z
в их строку формата, которая совпадает Z
со строкой даты и времени из примера автора вопроса (и отбрасывает ее, создаваяdatetime
объект без часового пояса):
>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
Поскольку при этом отбрасывается информация о часовом поясе, которая была включена в исходную строку даты и времени, сомнительно, должны ли мы рассматривать даже этот результат как правильный. Но что более важно, потому что этот подход включает в себя жесткое кодирование определенного смещения UTC в строку формата , он будет подавлен в тот момент, когда попытается проанализировать дату / время RFC 3339 с другим смещением UTC:
>>> datetime.strptime("2008-09-03T20:56:35.450686+05:00", "%Y-%m-%dT%H:%M:%S.%fZ")
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.4/_strptime.py", line 500, in _strptime_datetime
tt, fraction = _strptime(data_string, format)
File "/usr/lib/python3.4/_strptime.py", line 337, in _strptime
(data_string, format))
ValueError: time data '2008-09-03T20:56:35.450686+05:00' does not match format '%Y-%m-%dT%H:%M:%S.%fZ'
Если вы не уверены, что вам нужно только поддерживать время RFC 3339 по времени Зулу, а не время с другими смещениями часового пояса, не используйте strptime
. Вместо этого используйте один из многих других подходов, описанных в ответах.
strptime()
в Python 3.7 теперь поддерживает все, что описано как невозможное в этом ответе (буквально 'Z' и ':' в смещении часового пояса). К сожалению, есть еще один угловой случай, который делает RFC 3339 принципиально несовместимым с ISO 8601, а именно первый допускает отрицательное нулевое смещение часового пояса -00: 00, а последний нет.
Попробуйте модуль iso8601 ; это делает именно это.
Есть несколько других вариантов , упомянутых на WorkingWithTime странице на python.org вики.
iso8601.parse_date("2008-09-03T20:56:35.450686Z")
импорт ре, дата и время s = "2008-09-03T20: 56: 35.450686Z" d = datetime.datetime (* map (int, re.split ('[^ \ d]', s) [: - 1]))
datetime.datetime(*map(int, re.findall('\d+', s))
Какую именно ошибку вы получаете? Это похоже на следующее?
>>> datetime.datetime.strptime("2008-08-12T12:20:30.656234Z", "%Y-%m-%dT%H:%M:%S.Z")
ValueError: time data did not match format: data=2008-08-12T12:20:30.656234Z fmt=%Y-%m-%dT%H:%M:%S.Z
Если да, вы можете разделить вашу входную строку на «.», А затем добавить микросекунды к полученному времени.
Попробуй это:
>>> def gt(dt_str):
dt, _, us= dt_str.partition(".")
dt= datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S")
us= int(us.rstrip("Z"), 10)
return dt + datetime.timedelta(microseconds=us)
>>> gt("2008-08-12T12:20:30.656234Z")
datetime.datetime(2008, 8, 12, 12, 20, 30, 656234)
""
или "Z"
, то это должно быть смещение в часах / минутах, которое может быть непосредственно добавлено / вычтено из объекта datetime. Вы могли бы создать подкласс tzinfo, чтобы справиться с этим, но это, вероятно, не рекомендуется.
Начиная с Python 3.7, strptime поддерживает разделители двоеточий в смещениях UTC ( источник ). Таким образом, вы можете использовать:
import datetime
datetime.datetime.strptime('2018-01-31T09:24:31.488670+00:00', '%Y-%m-%dT%H:%M:%S.%f%z')
РЕДАКТИРОВАТЬ:
Как отметил Мартин, если вы создали объект datetime с помощью isoformat (), вы можете просто использовать datetime.fromisoformat ()
datetime.fromisoformat()
какие ручки строки , как автоматически ваш вход: datetime.datetime.isoformat('2018-01-31T09:24:31.488670+00:00')
.
datetime.fromisoformat()
иdatetime.isoformat()
В наши дни Arrow также можно использовать как стороннее решение:
>>> import arrow
>>> date = arrow.get("2008-09-03T20:56:35.450686Z")
>>> date.datetime
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())
Просто используйте python-dateutil
модуль:
>>> import dateutil.parser as dp
>>> t = '1984-06-02T19:05:00.000Z'
>>> parsed_t = dp.parse(t)
>>> print(parsed_t)
datetime.datetime(1984, 6, 2, 19, 5, tzinfo=tzutc())
455051100
(проверено на epochconverter.com ) ,,, разве я что-то упустил?
Если вы не хотите использовать dateutil, вы можете попробовать эту функцию:
def from_utc(utcTime,fmt="%Y-%m-%dT%H:%M:%S.%fZ"):
"""
Convert UTC time string to time.struct_time
"""
# change datetime.datetime to time, return time.struct_time type
return datetime.datetime.strptime(utcTime, fmt)
Тестовое задание:
from_utc("2007-03-04T21:08:12.123Z")
Результат:
datetime.datetime(2007, 3, 4, 21, 8, 12, 123000)
strptime
. Это плохая идея, потому что она не сможет проанализировать любое время и дату с другим смещением UTC и вызвать исключение. Посмотрите мой ответ, который описывает, как на самом деле невозможно проанализировать RFC 3339 с помощью strptime.
toISOString
методом JavaScript . Но в этом ответе нет упоминания об ограничении дат времени зулусов, и при этом вопрос не указывает, что это все, что нужно, и простое использование dateutil
обычно одинаково удобно и менее узко в том, что он может анализировать.
Если вы работаете с Django, он предоставляет модуль dateparse, который принимает множество форматов, похожих на формат ISO, включая часовой пояс.
Если вы не используете Django и не хотите использовать одну из других библиотек, упомянутых здесь, вы, вероятно, можете адаптировать исходный код Django для dateparse для вашего проекта.
DateTimeField
использует это, когда вы устанавливаете строковое значение.
Я обнаружил, что ciso8601 - это самый быстрый способ анализа временных меток ISO 8601. Как следует из названия, он реализован на C.
import ciso8601
ciso8601.parse_datetime('2014-01-09T21:48:00.921000+05:30')
GitHub Repo README показывает их> 10x ускорение по отношению ко всем другим библиотекам , перечисленных в других ответах.
Мой личный проект включал много разбора ISO 8601. Было приятно иметь возможность просто переключать вызов и идти в 10 раз быстрее. :)
Изменить: с тех пор я стал сопровождающим ciso8601. Теперь быстрее, чем когда-либо!
datetime.strptime()
является следующим самым быстрым решением. Спасибо, что собрали всю эту информацию!
datetime.strptime()
это не полная библиотека синтаксического анализа ISO 8601. Если вы используете Python 3.7, вы можете использовать datetime.fromisoformat()
метод, который немного более гибкий. Возможно, вас заинтересует этот более полный список парсеров, который вскоре должен быть объединен с README ciso8601.
Это работает для stdlib на Python 3.2 и более поздних версиях (при условии, что все метки времени указаны в формате UTC):
from datetime import datetime, timezone, timedelta
datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%fZ").replace(
tzinfo=timezone(timedelta(0)))
Например,
>>> datetime.utcnow().replace(tzinfo=timezone(timedelta(0)))
... datetime.datetime(2015, 3, 11, 6, 2, 47, 879129, tzinfo=datetime.timezone.utc)
strptime
. Это плохая идея, потому что она не сможет проанализировать любое время и дату с другим смещением UTC и вызвать исключение. Посмотрите мой ответ, который описывает, как на самом деле невозможно проанализировать RFC 3339 с помощью strptime.
timezone.utc
вместо timezone(timedelta(0))
. Кроме того, код работает в Python 2.6+ (по крайней мере), если вы предоставляете utc
объект tzinfo
%Z
для часового пояса в самых последних версиях Python.
Одним простым способом преобразования строки даты, подобной ISO 8601, в метку времени UNIX или datetime.datetime
объект во всех поддерживаемых версиях Python без установки сторонних модулей является использование анализатора даты SQLite .
#!/usr/bin/env python
from __future__ import with_statement, division, print_function
import sqlite3
import datetime
testtimes = [
"2016-08-25T16:01:26.123456Z",
"2016-08-25T16:01:29",
]
db = sqlite3.connect(":memory:")
c = db.cursor()
for timestring in testtimes:
c.execute("SELECT strftime('%s', ?)", (timestring,))
converted = c.fetchone()[0]
print("%s is %s after epoch" % (timestring, converted))
dt = datetime.datetime.fromtimestamp(int(converted))
print("datetime is %s" % dt)
Вывод:
2016-08-25T16:01:26.123456Z is 1472140886 after epoch
datetime is 2016-08-25 12:01:26
2016-08-25T16:01:29 is 1472140889 after epoch
datetime is 2016-08-25 12:01:29
Я написал парсер для стандарта ISO 8601 и разместил его на GitHub: https://github.com/boxed/iso8601 . Эта реализация поддерживает все в спецификации, кроме длительностей, интервалов, периодических интервалов и дат вне поддерживаемого диапазона дат модуля Python datetime.
Тесты включены! :П
Функция parse_datetime () в Django поддерживает даты со смещением UTC:
parse_datetime('2016-08-09T15:12:03.65478Z') =
datetime.datetime(2016, 8, 9, 15, 12, 3, 654780, tzinfo=<UTC>)
Таким образом, его можно использовать для анализа дат ISO 8601 в полях всего проекта:
from django.utils import formats
from django.forms.fields import DateTimeField
from django.utils.dateparse import parse_datetime
class DateTimeFieldFixed(DateTimeField):
def strptime(self, value, format):
if format == 'iso-8601':
return parse_datetime(value)
return super().strptime(value, format)
DateTimeField.strptime = DateTimeFieldFixed.strptime
formats.ISO_INPUT_FORMATS['DATETIME_INPUT_FORMATS'].insert(0, 'iso-8601')
Потому что ISO 8601 допускает множество вариаций необязательных двоеточий и тире CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]
. Если вы хотите использовать strptime, вы должны сначала удалить эти варианты.
Цель состоит в том, чтобы сгенерировать объект utc datetime.
2016-06-29T19:36:29.3453Z
:
datetime.datetime.strptime(timestamp.translate(None, ':-'), "%Y%m%dT%H%M%S.%fZ")
2016-06-29T19:36:29.3453-0400
или 2008-09-03T20:56:35.450686+05:00
используйте следующее. Они преобразуют все варианты во что-то без разделителей переменных, например, 20080903T205635.450686+0500
делая его более согласованным / более простым для анализа.
import re
# this regex removes all colons and all
# dashes EXCEPT for the dash indicating + or - utc offset for the timezone
conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', timestamp)
datetime.datetime.strptime(conformed_timestamp, "%Y%m%dT%H%M%S.%f%z" )
%z
директиву strptime (вы видите что-то подобное ValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z'
), вам нужно вручную сместить время от Z
(UTC). Примечание %z
может не работать в вашей системе в версиях Python <3, поскольку это зависит от поддержки библиотеки c, которая варьируется в зависимости от типа сборки системы / python (например, Jython, Cython и т. Д.).
import re
import datetime
# this regex removes all colons and all
# dashes EXCEPT for the dash indicating + or - utc offset for the timezone
conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', timestamp)
# split on the offset to remove it. use a capture group to keep the delimiter
split_timestamp = re.split(r"[+|-]",conformed_timestamp)
main_timestamp = split_timestamp[0]
if len(split_timestamp) == 3:
sign = split_timestamp[1]
offset = split_timestamp[2]
else:
sign = None
offset = None
# generate the datetime object without the offset at UTC time
output_datetime = datetime.datetime.strptime(main_timestamp +"Z", "%Y%m%dT%H%M%S.%fZ" )
if offset:
# create timedelta based on offset
offset_delta = datetime.timedelta(hours=int(sign+offset[:-2]), minutes=int(sign+offset[-2:]))
# offset datetime with timedelta
output_datetime = output_datetime + offset_delta
Для чего-то, что работает со стандартной библиотекой 2.X, попробуйте:
calendar.timegm(time.strptime(date.split(".")[0]+"UTC", "%Y-%m-%dT%H:%M:%S%Z"))
calendar.timegm - это отсутствующая версия gm time.mktime.
Python-dateutil будет генерировать исключение при разборе недопустимых строк даты, поэтому вы можете захотеть перехватить исключение.
from dateutil import parser
ds = '2012-60-31'
try:
dt = parser.parse(ds)
except ValueError, e:
print '"%s" is an invalid date' % ds
В настоящее время существует Maya: Datetimes for Humans ™ , от автора популярного пакета Requests: HTTP for Humans ™:
>>> import maya
>>> str = '2008-09-03T20:56:35.450686Z'
>>> maya.MayaDT.from_rfc3339(str).datetime()
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=<UTC>)
Другой способ заключается в использовании специализированного парсер для ISO-8601 является использование isoparse функции dateutil парсер:
from dateutil import parser
date = parser.isoparse("2008-09-03T20:56:35.450686+01:00")
print(date)
Вывод:
2008-09-03 20:56:35.450686+01:00
Эта функция также упоминается в документации для стандартной функции Python datetime.fromisoformat :
Более полнофункциональный анализатор ISO 8601, dateutil.parser.isoparse доступен в пакете dateutil стороннего производителя.
Благодаря ответу великого Марка Эмери я разработал функцию учета всех возможных форматов ISO даты и времени:
class FixedOffset(tzinfo):
"""Fixed offset in minutes: `time = utc_time + utc_offset`."""
def __init__(self, offset):
self.__offset = timedelta(minutes=offset)
hours, minutes = divmod(offset, 60)
#NOTE: the last part is to remind about deprecated POSIX GMT+h timezones
# that have the opposite sign in the name;
# the corresponding numeric value is not used e.g., no minutes
self.__name = '<%+03d%02d>%+d' % (hours, minutes, -hours)
def utcoffset(self, dt=None):
return self.__offset
def tzname(self, dt=None):
return self.__name
def dst(self, dt=None):
return timedelta(0)
def __repr__(self):
return 'FixedOffset(%d)' % (self.utcoffset().total_seconds() / 60)
def __getinitargs__(self):
return (self.__offset.total_seconds()/60,)
def parse_isoformat_datetime(isodatetime):
try:
return datetime.strptime(isodatetime, '%Y-%m-%dT%H:%M:%S.%f')
except ValueError:
pass
try:
return datetime.strptime(isodatetime, '%Y-%m-%dT%H:%M:%S')
except ValueError:
pass
pat = r'(.*?[+-]\d{2}):(\d{2})'
temp = re.sub(pat, r'\1\2', isodatetime)
naive_date_str = temp[:-5]
offset_str = temp[-5:]
naive_dt = datetime.strptime(naive_date_str, '%Y-%m-%dT%H:%M:%S.%f')
offset = int(offset_str[-4:-2])*60 + int(offset_str[-2:])
if offset_str[0] == "-":
offset = -offset
return naive_dt.replace(tzinfo=FixedOffset(offset))
def parseISO8601DateTime(datetimeStr):
import time
from datetime import datetime, timedelta
def log_date_string(when):
gmt = time.gmtime(when)
if time.daylight and gmt[8]:
tz = time.altzone
else:
tz = time.timezone
if tz > 0:
neg = 1
else:
neg = 0
tz = -tz
h, rem = divmod(tz, 3600)
m, rem = divmod(rem, 60)
if neg:
offset = '-%02d%02d' % (h, m)
else:
offset = '+%02d%02d' % (h, m)
return time.strftime('%d/%b/%Y:%H:%M:%S ', gmt) + offset
dt = datetime.strptime(datetimeStr, '%Y-%m-%dT%H:%M:%S.%fZ')
timestamp = dt.timestamp()
return dt + timedelta(hours=dt.hour-time.gmtime(timestamp).tm_hour)
Обратите внимание, что мы должны посмотреть, если строка не заканчивается Z
, мы могли бы проанализировать с помощью %z
.
Первоначально я пытался с:
from operator import neg, pos
from time import strptime, mktime
from datetime import datetime, tzinfo, timedelta
class MyUTCOffsetTimezone(tzinfo):
@staticmethod
def with_offset(offset_no_signal, signal): # type: (str, str) -> MyUTCOffsetTimezone
return MyUTCOffsetTimezone((pos if signal == '+' else neg)(
(datetime.strptime(offset_no_signal, '%H:%M') - datetime(1900, 1, 1))
.total_seconds()))
def __init__(self, offset, name=None):
self.offset = timedelta(seconds=offset)
self.name = name or self.__class__.__name__
def utcoffset(self, dt):
return self.offset
def tzname(self, dt):
return self.name
def dst(self, dt):
return timedelta(0)
def to_datetime_tz(dt): # type: (str) -> datetime
fmt = '%Y-%m-%dT%H:%M:%S.%f'
if dt[-6] in frozenset(('+', '-')):
dt, sign, offset = strptime(dt[:-6], fmt), dt[-6], dt[-5:]
return datetime.fromtimestamp(mktime(dt),
tz=MyUTCOffsetTimezone.with_offset(offset, sign))
elif dt[-1] == 'Z':
return datetime.strptime(dt, fmt + 'Z')
return datetime.strptime(dt, fmt)
Но это не сработало на отрицательных часовых поясах. Это, однако, я работал нормально, в Python 3.7.3:
from datetime import datetime
def to_datetime_tz(dt): # type: (str) -> datetime
fmt = '%Y-%m-%dT%H:%M:%S.%f'
if dt[-6] in frozenset(('+', '-')):
return datetime.strptime(dt, fmt + '%z')
elif dt[-1] == 'Z':
return datetime.strptime(dt, fmt + 'Z')
return datetime.strptime(dt, fmt)
Некоторые тесты отмечают, что выход отличается только точностью микросекунд. Получил 6 цифр точности на моей машине, но YMMV:
for dt_in, dt_out in (
('2019-03-11T08:00:00.000Z', '2019-03-11T08:00:00'),
('2019-03-11T08:00:00.000+11:00', '2019-03-11T08:00:00+11:00'),
('2019-03-11T08:00:00.000-11:00', '2019-03-11T08:00:00-11:00')
):
isoformat = to_datetime_tz(dt_in).isoformat()
assert isoformat == dt_out, '{} != {}'.format(isoformat, dt_out)
frozenset(('+', '-'))
? Разве обычный кортеж не ('+', '-')
должен быть в состоянии выполнить то же самое?