Мені потрібно проаналізувати рядки 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
not dateutil
, так що : pip install python-dateutil
.
dateutil.parser
це навмисно хакі: він намагається відгадати формат і робить неминучі припущення (налаштовуються лише вручну) у неоднозначних випадках. Тож використовуйте ТОЛЬКІ, якщо вам потрібно розібрати вхід невідомого формату і нормально терпіти випадкові неправильні прочитання.
datetime
Стандартна бібліотека представила функцію перекидання datetime.isoformat()
.
classmethod
datetime.fromisoformat(date_string)
:Поверніть
datetime
відповідне аdate_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
насправді неможливо.
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
неможливий. Рядки формату, використовувані модулем дат 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#strWeather-and-strptime-behavior , і єдиною директивою щодо зміщення UTC, включеною у цей список, є %z
:
% z
Зсув UTC у вигляді + HHMM або -HHMM (порожній рядок, якщо об’єкт є наївним).
Приклад: (порожній), +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 ми вийдемо з ладу з ще простішої причини. Це те, що strptime
взагалі не реалізує %z
директиву в Python 2 )
Тут є декілька відповідей, які рекомендують 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")
імпортування re, datetime s = "2008-09-03T20: 56: 35.450686Z" d = datetime.datetime (* карта (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')
Редагувати:
Як вказував Мартійн, якщо ви створили об’єкт дати за допомогою 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, він надає модуль dataparse, який приймає купу форматів, схожих на формат ISO, включаючи часовий пояс.
Якщо ви не використовуєте Django і не хочете використовувати одну з інших бібліотек, згаданих тут, ви, ймовірно, зможете адаптувати вихідний код Django для аналізу часу у вашому проекті.
DateTimeField
використовує це під час встановлення рядкового значення.
Я знайшов ciso8601 як найшвидший спосіб розбору часових позначок ISO 8601. Як випливає з назви, вона реалізована в С.
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()
методом, який є трохи більш гнучким. Можливо, вас зацікавить цей більш повний список парсерів, які незабаром повинні бути об'єднані в ciso8601 README.
Це працює для 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
об'єкт
%Z
часовий пояс в останніх версіях Python.
Один простий спосіб конвертувати рядок дат, подібних ISO 8601, у часову позначку або datetime.datetime
об'єкт UNIX у всіх підтримуваних версіях 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.
Тести включені! : P
Функція 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
На сьогоднішній день є Майя: Дата для Humans ™ від автора популярного пакету Запити: 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 - це використовувати ізопарну функцію аналізатора датування:
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(('+', '-'))
? Чи не повинен нормальний кортеж, як-от, ('+', '-')
змогти здійснити те ж саме?