Saya perlu mengurai string RFC 3339 seperti "2008-09-03T20:56:35.450686Z"
ke datetime
tipe Python .
Saya telah menemukan strptime
di perpustakaan standar Python, tetapi tidak terlalu nyaman.
Apa cara terbaik untuk melakukan ini?
Saya perlu mengurai string RFC 3339 seperti "2008-09-03T20:56:35.450686Z"
ke datetime
tipe Python .
Saya telah menemukan strptime
di perpustakaan standar Python, tetapi tidak terlalu nyaman.
Apa cara terbaik untuk melakukan ini?
Jawaban:
Paket python-dateutil dapat mengurai tidak hanya string datetime RFC 3339 seperti yang ada dalam pertanyaan, tetapi juga string tanggal dan waktu ISO 8601 lainnya yang tidak mematuhi RFC 3339 (seperti yang tanpa offset UTC, atau yang mewakili hanya kencan).
>>> 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)
Catatan yang dateutil.parser.isoparse
mungkin lebih ketat daripada yang lebih rumit dateutil.parser.parse
, tetapi keduanya cukup memaafkan dan akan berusaha menafsirkan string yang Anda lewati. Jika Anda ingin menghilangkan kemungkinan salah baca, Anda perlu menggunakan sesuatu yang lebih ketat daripada salah satu dari ini. fungsi.
Nama Pypi adalah python-dateutil
, bukan dateutil
(terima kasih code3monk3y ):
pip install python-dateutil
Jika Anda menggunakan Python 3.7, lihat jawaban ini tentang datetime.datetime.fromisoformat
.
python-dateutil
tidak dateutil
, sehingga: pip install python-dateutil
.
dateutil.parser
ini sengaja diretas: ia mencoba menebak format dan membuat asumsi yang tak terhindarkan (hanya dapat disesuaikan dengan tangan) dalam kasus yang ambigu. Jadi HANYA menggunakannya jika Anda perlu mem-parsing input dari format yang tidak dikenal dan boleh saja mentolerir kesalahan membaca sesekali.
The datetime
perpustakaan standar diperkenalkan fungsi untuk pembalik datetime.isoformat()
.
classmethod
datetime.fromisoformat(date_string)
:Kembalikan yang
datetime
sesuai kedate_string
dalam salah satu format yang dipancarkan olehdate.isoformat()
dandatetime.isoformat()
.Secara khusus, fungsi ini mendukung string dalam format:
YYYY-MM-DD[*HH[:MM[:SS[.mmm[mmm]]]][+HH:MM[:SS[.ffffff]]]]
di mana
*
bisa cocok dengan satu karakter.Perhatian : Ini tidak mendukung parsing string ISO 8601 yang sewenang-wenang - ini hanya dimaksudkan sebagai operasi terbalik dari
datetime.isoformat()
.
Contoh penggunaan:
from datetime import datetime
date = datetime.fromisoformat('2017-01-01T12:30:59.000000')
datetime
dapat berisi a tzinfo
, dan dengan demikian menampilkan zona waktu, tetapi datetime.fromisoformat()
tidak menguraikan tzinfo? sepertinya bug ..
isoformat
. Itu tidak menerima contoh dalam pertanyaan "2008-09-03T20:56:35.450686Z"
karena trailing Z
, tetapi itu menerima "2008-09-03T20:56:35.450686"
.
Z
skrip input dengan benar dapat dimodifikasi date_string.replace("Z", "+00:00")
.
Catatan dalam Python 2.6+ dan Py3K, karakter% f menangkap microseconds.
>>> datetime.datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
Lihat masalah di sini
strptime
sebenarnya tidak mungkin.
datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f')
jadi ini triknya
Beberapa jawaban di sini menyarankandatetime.datetime.strptime
untuk menggunakan parse data RFC 3339 atau ISO 8601 dengan zona waktu, seperti yang diperlihatkan dalam pertanyaan:
2008-09-03T20:56:35.450686Z
Ini ide yang buruk.
Dengan asumsi bahwa Anda ingin mendukung format RFC 3339 lengkap, termasuk dukungan untuk offset UTC selain nol, maka kode yang disarankan oleh jawaban ini tidak berfungsi. Memang, itu tidak bisa bekerja, karena parsing RFC 3339 menggunakan sintaks strptime
tidak mungkin. String format yang digunakan oleh modul datetime Python tidak mampu menggambarkan sintaksis RFC 3339.
Masalahnya adalah offset UTC. The RFC 3339 Tanggal Internet / Waktu Format mengharuskan setiap tanggal-waktu termasuk UTC offset, dan bahwa mereka offset baik dapat Z
(singkatan dari "waktu Zulu") atau dalam +HH:MM
atau -HH:MM
Format, seperti +05:00
atau -10:30
.
Oleh karena itu, ini semua data RFC 3339 valid:
2008-09-03T20:56:35.450686Z
2008-09-03T20:56:35.450686+05:00
2008-09-03T20:56:35.450686-10:30
Sayangnya, string format digunakan oleh strptime
dan strftime
tidak memiliki arahan yang sesuai dengan offset UTC dalam format RFC 3339. Daftar lengkap arahan yang mereka dukung dapat ditemukan di https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior , dan satu-satunya arahan offset UTC yang termasuk dalam daftar adalah %z
:
% z
UTC diimbangi dalam bentuk + HHMM atau -HHMM (string kosong jika objeknya naif).
Contoh: (kosong), +0000, -0400, +1030
Ini tidak cocok dengan format offset RFC 3339, dan memang jika kita mencoba menggunakan %z
string format dan menguraikan tanggal RFC 3339, kita akan gagal:
>>> 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'
(Sebenarnya, di atas hanya apa yang akan Anda lihat di Python 3. Dalam Python 2 kita akan gagal karena alasan yang lebih sederhana, yaitu bahwa strptime
tidak menerapkan %z
arahan sama sekali di Python 2. )
Beberapa jawaban di sini yang merekomendasikan strptime
semua bekerja di sekitar ini dengan memasukkan literal Z
dalam string format mereka, yang cocok dengan Z
dari string datetime contoh penanya pertanyaan (dan membuangnya, menghasilkan datetime
objek tanpa zona waktu):
>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
Karena ini membuang informasi zona waktu yang termasuk dalam string datetime asli, patut dipertanyakan apakah kita harus menganggap bahkan hasil ini sebagai benar. Tetapi yang lebih penting, karena pendekatan ini melibatkan pengodean keras offset UTC tertentu ke dalam format string , itu akan mencekik saat mencoba mem-parse setiap waktu RFC 3339 dengan offset UTC yang berbeda:
>>> 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'
Kecuali Anda yakin bahwa Anda hanya perlu mendukung data RFC 3339 dalam waktu Zulu, dan bukan yang dengan offset zona waktu lainnya, jangan gunakan strptime
. Gunakan salah satu dari banyak pendekatan lain yang dijelaskan dalam jawaban di sini sebagai gantinya.
strptime()
dalam Python 3.7 sekarang mendukung semua yang digambarkan sebagai tidak mungkin dalam jawaban ini ('Z' literal dan ':' dalam offset zona waktu). Sayangnya, ada kasus sudut lain yang membuat RFC 3339 pada dasarnya tidak kompatibel dengan ISO 8601, yaitu, yang pertama memungkinkan offset zona waktu negatif negatif -00: 00 dan kemudian tidak.
Coba modul iso8601 ; itu persis seperti ini.
Ada beberapa pilihan lain disebutkan pada WorkingWithTime halaman pada python.org wiki.
iso8601.parse_date("2008-09-03T20:56:35.450686Z")
impor ulang, datetime s = "2008-09-03T20: 56: 35.450686Z" d = datetime.datetime (* peta (int, re.split ('[^ \ d]', s) [: - 1]))
datetime.datetime(*map(int, re.findall('\d+', s))
Apa kesalahan sebenarnya yang Anda dapatkan? Apakah ini seperti yang berikut ini?
>>> 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
Jika ya, Anda dapat membagi string input Anda pada ".", Dan kemudian menambahkan mikrodetik ke datetime yang Anda dapatkan.
Coba ini:
>>> 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)
""
atau "Z"
, maka itu harus diimbangi dalam jam / menit, yang dapat langsung ditambahkan ke / dikurangi dari objek datetime. Anda bisa membuat subclass tzinfo untuk menanganinya, tapi itu mungkin tidak disarankan.
Mulai dari Python 3.7, strptime mendukung pembatas titik dua dalam offset UTC ( sumber ). Jadi Anda dapat menggunakan:
import datetime
datetime.datetime.strptime('2018-01-31T09:24:31.488670+00:00', '%Y-%m-%dT%H:%M:%S.%f%z')
EDIT:
Seperti yang ditunjukkan oleh Martijn, jika Anda membuat objek datetime menggunakan isoformat (), Anda bisa menggunakan datetime.fromisoformat ()
datetime.fromisoformat()
yang menangani string seperti masukan Anda secara otomatis: datetime.datetime.isoformat('2018-01-31T09:24:31.488670+00:00')
.
datetime.fromisoformat()
dandatetime.isoformat()
Di hari-hari ini, Arrow juga dapat digunakan sebagai solusi pihak ketiga:
>>> import arrow
>>> date = arrow.get("2008-09-03T20:56:35.450686Z")
>>> date.datetime
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())
Cukup gunakan python-dateutil
modul:
>>> 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
(diperiksa di epochconverter.com ) ,,, kecuali saya kehilangan sesuatu?
Jika Anda tidak ingin menggunakan dateutil, Anda dapat mencoba fungsi ini:
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)
Uji:
from_utc("2007-03-04T21:08:12.123Z")
Hasil:
datetime.datetime(2007, 3, 4, 21, 8, 12, 123000)
strptime
. Ini adalah ide yang buruk karena itu akan gagal untuk mem-parsing setiap datetime dengan offset UTC yang berbeda dan menimbulkan pengecualian. Lihat jawaban saya yang menjelaskan bagaimana penguraian RFC 3339 dengan strptime sebenarnya tidak mungkin.
toISOString
metode JavaScript . Tetapi tidak disebutkan batasan tanggal waktu Zulu dalam jawaban ini, tidak juga pertanyaan menunjukkan bahwa hanya itu yang diperlukan, dan hanya menggunakan dateutil
biasanya sama nyaman dan tidak terlalu sempit dalam apa yang dapat diuraikan.
Jika Anda bekerja dengan Django, ia menyediakan modul dateparse yang menerima banyak format yang mirip dengan format ISO, termasuk zona waktu.
Jika Anda tidak menggunakan Django dan Anda tidak ingin menggunakan salah satu perpustakaan lain yang disebutkan di sini, Anda mungkin bisa mengadaptasi kode sumber Django untuk dateparse ke proyek Anda.
DateTimeField
menggunakan ini ketika Anda menetapkan nilai string.
Saya telah menemukan ciso8601 sebagai cara tercepat untuk mengurai cap waktu ISO 8601. Seperti namanya, ini diimplementasikan dalam C.
import ciso8601
ciso8601.parse_datetime('2014-01-09T21:48:00.921000+05:30')
The GitHub Repo README menunjukkan mereka> 10x speedup terhadap semua perpustakaan lainnya yang tercantum dalam jawaban lainnya.
Proyek pribadi saya melibatkan banyak penguraian ISO 8601. Senang rasanya bisa hanya beralih panggilan dan pergi 10x lebih cepat. :)
Sunting: Saya sejak itu menjadi pengelola ciso8601. Sekarang lebih cepat dari sebelumnya!
datetime.strptime()
adalah solusi tercepat berikutnya. Terima kasih telah mengumpulkan semua info itu!
datetime.strptime()
ini bukan parsing library ISO 8601 penuh. Jika Anda menggunakan Python 3.7, Anda bisa menggunakan datetime.fromisoformat()
metode ini, yang sedikit lebih fleksibel. Anda mungkin tertarik pada daftar parser yang lebih lengkap ini yang harus segera digabung ke dalam ciso8601 README.
Ini berfungsi untuk stdlib pada Python 3.2 dan seterusnya (dengan anggapan semua cap waktu adalah UTC):
from datetime import datetime, timezone, timedelta
datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%fZ").replace(
tzinfo=timezone(timedelta(0)))
Sebagai contoh,
>>> datetime.utcnow().replace(tzinfo=timezone(timedelta(0)))
... datetime.datetime(2015, 3, 11, 6, 2, 47, 879129, tzinfo=datetime.timezone.utc)
strptime
. Ini adalah ide yang buruk karena itu akan gagal untuk mem-parsing setiap datetime dengan offset UTC yang berbeda dan menimbulkan pengecualian. Lihat jawaban saya yang menjelaskan bagaimana penguraian RFC 3339 dengan strptime sebenarnya tidak mungkin.
timezone.utc
bukan timezone(timedelta(0))
. Juga, kode ini bekerja di Python 2.6+ (setidaknya) jika Anda menyediakan utc
objek tzinfo
%Z
zona waktu untuk di versi Python terbaru.
Salah satu cara mudah untuk mengubah string tanggal mirip ISO 8601 ke stempel waktu UNIX atau datetime.datetime
objek di semua versi Python yang didukung tanpa menginstal modul pihak ketiga adalah dengan menggunakan pengurai tanggal 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)
Keluaran:
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
Saya telah membuat kode parser untuk standar ISO 8601 dan menaruhnya di GitHub: https://github.com/boxed/iso8601 . Implementasi ini mendukung semua yang ada dalam spesifikasi kecuali untuk durasi, interval, interval periodik, dan tanggal di luar rentang tanggal yang didukung dari modul datetime Python.
Tes sudah termasuk! : P
Fungsi Django parse_datetime () mendukung tanggal dengan offset UTC:
parse_datetime('2016-08-09T15:12:03.65478Z') =
datetime.datetime(2016, 8, 9, 15, 12, 3, 654780, tzinfo=<UTC>)
Jadi itu dapat digunakan untuk parsing tanggal ISO 8601 di bidang dalam seluruh proyek:
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')
Karena ISO 8601 memungkinkan banyak variasi titik dua dan garis opsional hadir, pada dasarnya CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]
. Jika Anda ingin menggunakan strptime, Anda harus menghapus variasi itu terlebih dahulu.
Tujuannya adalah untuk menghasilkan objek datetime utc.
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
atau 2008-09-03T20:56:35.450686+05:00
gunakan yang berikut ini. Ini akan mengkonversi semua variasi menjadi sesuatu tanpa pembatas variabel seperti 20080903T205635.450686+0500
membuatnya lebih konsisten / mudah diurai.
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
arahan strptime (Anda melihat sesuatu seperti ValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z'
) maka Anda perlu secara manual mengimbangi waktu dari Z
(UTC). Catatan %z
mungkin tidak berfungsi pada sistem Anda dalam versi python <3 karena bergantung pada dukungan pustaka c yang bervariasi di seluruh tipe build sistem / python (yaitu Jython, Cython, dll.).
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
Untuk sesuatu yang berfungsi dengan pustaka 2.X standar coba:
calendar.timegm(time.strptime(date.split(".")[0]+"UTC", "%Y-%m-%dT%H:%M:%S%Z"))
calendar.timegm adalah versi timemkm gm yang hilang.
Python-dateutil akan memunculkan eksepsi jika mem-parsing string tanggal tidak valid, jadi Anda mungkin ingin menangkap pengecualian.
from dateutil import parser
ds = '2012-60-31'
try:
dt = parser.parse(ds)
except ValueError, e:
print '"%s" is an invalid date' % ds
Saat ini ada Maya: Datetimes for Humans ™ , dari penulis Permintaan populer: paket 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>)
Cara lain adalah dengan menggunakan parser khusus untuk ISO-8601 adalah dengan menggunakan isoparse fungsi parser dateutil:
from dateutil import parser
date = parser.isoparse("2008-09-03T20:56:35.450686+01:00")
print(date)
Keluaran:
2008-09-03 20:56:35.450686+01:00
Fungsi ini juga disebutkan dalam dokumentasi untuk fungsi Python standar datetime.fromisoformat :
Parser ISO 8601 berfitur lengkap, dateutil.parser.isoparse tersedia di dateutil paket pihak ketiga.
Terima kasih atas jawaban Mark Amery yang luar biasa. Saya merancang fungsi untuk memperhitungkan semua format ISO mungkin pada waktu:
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)
Perhatikan bahwa kita harus melihat jika string tidak diakhiri dengan Z
, kita dapat menguraikan penggunaan %z
.
Awalnya saya mencoba dengan:
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)
Tetapi itu tidak berhasil pada zona waktu negatif. Namun ini saya bekerja dengan baik, dengan 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)
Beberapa tes, perhatikan bahwa keluaran hanya berbeda dengan ketepatan mikrodetik. Hingga 6 digit presisi pada mesin saya, tetapi 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(('+', '-'))
? Tidakkah seharusnya tuple normal seperti ('+', '-')
dapat melakukan hal yang sama?