ISO 8601 biçimli bir tarihi nasıl ayrıştırabilirim?


642

Python'un türü gibi RFC 3339 dizeleri ayrıştırmak gerekiyor ."2008-09-03T20:56:35.450686Z"datetime

strptimePython standart kütüphanesinde buldum , ama çok uygun değil.

Bunu yapmanın en iyi yolu nedir?




3
Açık olmak gerekirse: ISO 8601 ana standarttır. RFC 3339 , ISO 8601 kurallarını geçersiz kılan bazı geçersiz kılmalar yapan, ISO 8601'in kendi beyan ettiği bir “profilidir” .
Basil Bourque

3
Isoformat () tersine çevirmek için aşağıdaki python3.7 + çözümünü kaçırmayın
Brad M

2
Bu soru bağlantılı yazıya dupe olarak kapatılmamalıdır. Bu bir ISO 8601 zaman dizesini ayrıştırmak istediğinden (3.7'den önceki python tarafından yerel olarak desteklenmemektedir) ve diğeri datetime nesnesini eski bir yöntem kullanarak bir epoch dizesine biçimlendirmektir .
abccd

Yanıtlar:


462

Piton-dateutil paket söz konusu gibi sadece RFC 3339 tarih saat dizeleri ayrıştırmak, ancak aynı zamanda diğer ISO 8601 hiçbir UTC olanlar ofset RFC 3339 uymayan tarih ve saat dizeleri (örneğin, ya da olanları temsil ettiğini yalnızca bir tarih).

>>> 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)

Not dateutil.parser.isoparsedaha hacky daha muhtemelen sıkı olduğunu dateutil.parser.parse, ancak bunların her ikisi de oldukça bağışlayıcı ve siz geçmek olduğunu dizeyi yorumlamaya çalışacaktır. Eğer herhangi bir olsa yanlış olasılığını ortadan kaldırmak için, bunlardan birini daha birşey sıkı kullanmak gerekir fonksiyonlar.

Pypi adı python-dateutildeğil dateutil(teşekkürler code3monk3y ):

pip install python-dateutil

Python 3.7 kullanıyorsanız, bir göz bu cevabı hakkında datetime.datetime.fromisoformat.


75
Tembel için, üzeri yüklü python-dateutildeğil dateutilyani: pip install python-dateutil.
cod3monk3y

29
dateutil.parserKasıtlı olarak hileli olduğu konusunda uyarıda bulunun: formatı tahmin etmeye çalışır ve belirsiz durumlarda kaçınılmaz varsayımlar yapar (sadece elle özelleştirilebilir). Bu nedenle, SADECE bilinmeyen formattaki girdileri ayrıştırmanız gerekiyorsa ve ara sıra yanlışları tolere etmek için uygunsa kullanın.
ivan_pozdeev

2
Kabul. Bir örnek 9999 "tarihini" geçiriyor. Bu tarih ile aynı dönecektir (9999, geçerli ay, geçerli gün). Benim görüşüme göre geçerli bir tarih değil.
timbo

1
@ivan_pozdeev Tahmin etmeyen ayrıştırma için hangi paketi önerirsiniz?
bgusach

2
@ivan_pozdeev, modülde iso8601 tarihlerini okuyan bir güncelleme var: dateutil.readthedocs.io/en/stable/…
theEpsilon

197

Python 3.7+'deki yenilikler


datetimeStandart kütüphane inversini bir işlevi tanıtıldı datetime.isoformat().

classmethod datetime.fromisoformat(date_string):

Bir Dönüş datetimeBir tekabül date_stringyaydığı biçimlerden birinde date.isoformat()ve datetime.isoformat().

Özellikle, bu işlev biçim (ler) deki dizeleri destekler:

YYYY-MM-DD[*HH[:MM[:SS[.mmm[mmm]]]][+HH:MM[:SS[.ffffff]]]]

nerede *herhangi bir tek karakteri eşleşebilir.

Dikkat : Bu, keyfi ISO 8601 dizelerinin ayrıştırılmasını desteklemez - yalnızca ters işlemi olarak tasarlanmıştır datetime.isoformat().

Kullanım örneği:

from datetime import datetime

date = datetime.fromisoformat('2017-01-01T12:30:59.000000')

6
Bu tuhaf. Çünkü a bir datetimeiçerebilir tzinfove bu nedenle bir saat dilimi çıktısı alabilir, ancak datetime.fromisoformat()tzinfo'yu ayrıştırmaz mı? bir böcek gibi görünüyor ..
Hendy Irawan

20
Belgelerde bu notu kaçırmayın, bu geçerli ISO 8601 dizelerinin tümünü kabul etmez , sadece tarafından oluşturulanları kabul eder isoformat. Söz konusu örnek kabul etmez "2008-09-03T20:56:35.450686Z"çünkü Römorkun Zama kabul etmez "2008-09-03T20:56:35.450686".
Flimm

26
ZGiriş betiğini uygun şekilde desteklemek için ile değiştirilebilir date_string.replace("Z", "+00:00").
jox

7
Saniyeler için yalnızca tam olarak 0, 3 veya 6 ondalık basamak işleyebileceğini unutmayın. Giriş verilerinde 1, 2, 4, 5, 7 veya daha fazla ondalık basamak varsa ayrıştırma başarısız olur!
Felk

1
@JDOaktown Bu örnek, dateutil'in ayrıştırıcısını değil, yerel Python'un datetime kütüphanesini kullanır. Bu yaklaşımla ondalık basamaklar 0, 3 veya 6 değilse aslında başarısız olur.
abccd

174

Python 2.6+ ve Py3K'da% f karakteri mikrosaniye yakalar.

>>> datetime.datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")

Soruna buradan bakın


4
Not - Naif tarihler kullanıyorsanız - Bence hiç TZ almıyorsunuz - Z hiçbir şeyle uyuşmayabilir.
Danny Staple

24
Bu yanıt (geçerli, düzenlenmiş biçiminde) belirli bir UTC ofsetini (+00: 00 anlamına gelen "Z") kod dizesine sabit olarak kodlamaya dayanır. Bu kötü bir fikir çünkü herhangi bir tarih saatini farklı bir UTC ofsetiyle ayrıştıramayacak ve bir istisna oluşturamayacaktır. RFC 3339 ile ayrıştırmanın aslında imkansız olduğunu açıklayan cevabımı görün strptime.
Mark Amery

1
benim durumumda% f Z yerine mikrosaniye yakaladı, datetime.datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.%f') bu hile yaptı
ashim888

Py3K, Python 3000 anlamına mı geliyor?
Robino

2
@Robino IIRC, "Python 3000" şimdi Python 3 olarak bilinen için eski bir isim.
Throw Away Account

161

Buradaki birkaç cevap, RFC 3339 veya ISO 8601'i zaman zamanlarında, örneğin soruda gösterilenler gibi ayrıştırmayı kullanmayı önermektedir : datetime.datetime.strptime

2008-09-03T20:56:35.450686Z

Bu kötü bir fikir.

Sıfır dışında UTC ofsetleri için destek de dahil olmak üzere tam RFC 3339 biçimini desteklemek istediğinizi varsayarsak, bu yanıtların önerdiği kod çalışmaz. Gerçekten de, yapamam kullanarak RFC 3339 sözdizimi ayrıştırma, çünkü çalışma strptimeimkansızdır. Python'un datetime modülü tarafından kullanılan biçim dizeleri, RFC 3339 sözdizimini açıklayamaz.

Sorun UTC ofsetleridir. RFC 3339 Internet Tarih / Saat Biçimi her tarih-saat, bir UTC ayarı içerdiğini gerektirir ve bu uzaklıklar ya olabileceğini Z(kısa "Zulu zamanı") veya +HH:MMveya -HH:MMformat gibi +05:00ya-10:30 .

Sonuç olarak, bunların hepsi geçerli RFC 3339 tarihleridir:

  • 2008-09-03T20:56:35.450686Z
  • 2008-09-03T20:56:35.450686+05:00
  • 2008-09-03T20:56:35.450686-10:30

Ne yazık ki, tarafından kullanılan biçim dizeleri strptimeve strftimeRFC 3339 biçimindeki UTC ofsetlerine karşılık gelen bir yönergesi yoktur. Destekledikleri direktiflerin tam bir listesini https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior adresinde bulabilirsiniz ve listedeki tek UTC ofset yönergesi %z:

% z

+ HHMM veya -HHMM (nesnenin saf olması durumunda boş dize) biçimindeki UTC uzaklığı.

Örnek: (boş), +0000, -0400, +1030

Bu bir RFC 3339 ofsetinin biçimiyle eşleşmiyor ve gerçekten %zde biçim dizesinde kullanmaya ve bir RFC 3339 tarihini ayrıştırmaya çalışırsak başarısız oluruz:

>>> 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'

(Aslında, yukarıdaki sadece Python 3'te göreceğiniz şeydir. Python 2'de daha basit bir nedenden dolayı başarısız olacağız, bu da Python 2'de direktifi strptimeuygulamaz.%z .)

Burada , soru sorucusunun örnek datetime dizesiyle eşleşen (ve atar, saat dilimi olmayan bir nesne üreterek) biçim dizesine strptimebir değişmez değer ekleyerek bu sorunu çözen tüm yanıtlar :ZZdatetime

>>> datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)

Bu, orijinal datetime dizesine dahil olan saat dilimi bilgilerini attığından, bu sonucu bile doğru olarak kabul edip etmeyeceğimiz sorgulanabilir. Ancak daha da önemlisi, bu yaklaşım belirli bir UTC ofsetini format dizesine kodlamayı gerektirdiğinden , herhangi bir RFC 3339 tarih saatini farklı bir UTC ofseti ile ayrıştırmaya çalıştığı anı boğacaktır:

>>> 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'

Vermenize sürece belirli yalnızca RFC diğer zaman dilimi uzaklıklar ile 3339 Zulu sürede tarihsaat değil, olanları desteklemek gerektiğini, kullanmayın strptime. Bunun yerine burada yanıtlarda açıklanan diğer birçok yaklaşımdan birini kullanın.


79
Strptime'ın ISO biçiminde saat dilimi bilgileri için neden bir yönergesi olmadığı ve neden ayrıştırılamadığı akılda kalıcıdır. İnanılmaz.
Csaba Toth

2
@CsabaToth Tamamen kabul etti - öldürmek için biraz zamanım varsa, belki de dile eklemeye çalışacağım. Ya da bunu yapabilirdiniz, eğer bu kadar eğimli olsaydınız - benden farklı bir C deneyiminiz olduğunu görüyorum.
Mark Amery

1
@CsabaToth - Neden inanılmaz? Çoğu insan için yeterince iyi çalışır veya yeterince kolay bir çözüm bulmuşlardır. Özelliğe ihtiyacınız varsa, açık kaynaktır ve ekleyebilirsiniz. Ya da sizin için yapması için birine ödeme yapın. Neden birisi kendi özel problemlerini çözmek için kendi boş zamanını gönüllü olarak kabul etsin? Kaynak seninle olsun.
Peter M. - Monica

2
@PeterMasiar İnanılmaz çünkü genellikle biri pitondaki şeylerin düşünceli ve tam olarak uygulandığını keşfeder. Bu dikkatle detaylara dikkatimizi çektik ve bu yüzden “unpythonic” dilde bir şeyle karşılaştığımızda, oyuncaklarımızı çocuk arabasına atıyoruz, şu anda yapmak üzereyim. Whaaaaaaaaaa whaa wahaaaaa :-(
Robino

2
strptime()Python 3.7'de artık bu cevapta imkansız olarak tanımlanan her şeyi destekliyor ('Z' değişmez değeri ve ':' saat dilimi dengesinde). Ne yazık ki, RFC 3339'u ISO 8601 ile temel olarak uyumsuz hale getiren başka bir köşe durumu var, birincisi, negatif bir boş saat dilimi -00: 00 ve daha sonra değil.
SergiyKolesnikov

75

İso8601 modülünü deneyin ; tam olarak bunu yapıyor.

Python.org wiki'sindeki WorkingWithTime sayfasında belirtilen birkaç seçenek daha vardır .


Basit olarakiso8601.parse_date("2008-09-03T20:56:35.450686Z")
Pakman

3
Soru "ISO 8601 tarihlerini nasıl ayrıştırırım" değil, "bu tam tarih biçimini nasıl ayrıştırırım" idi.
Nicholas Riley

3
@tiktak OP, "X gibi dizeleri ayrıştırmam gerekiyor" sorusunu sordu ve her iki kütüphaneyi de denediğimde yanıtım başka bir tane kullanmaktır, çünkü iso8601'in hala önemli olan önemli sorunları vardır. Böyle bir projeye katılımım veya eksikliğim, cevapla tamamen ilgisiz.
Tobia

2
İso8601'in pip versiyonunun 2007'den beri güncellenmediğini ve olağanüstü bazı ciddi hatalara sahip olduğunu unutmayın. Yamaları biraz eleştirmenizi veya zaten yapmış olan birçok github çatalından birini bulmanızı öneririm github.com/keithhackbarth/pyiso8601-strict
keithhackbarth

6
iso8601 , diğer pyiso8601 , Şubat 2014 kadar yakın zamanda güncellenmiştir. En son sürüm çok daha geniş bir ISO 8601 dizesini destekler. Bazı projelerimde iyi bir etki yaratmaya çalışıyorum.
Dave Hein

34
import re, datetime
s = "2008-09-03T20: 56: 35.450686Z"
d = datetime.datetime (* harita (int, re.split ('[^ \ d]', s) [: - 1]))

73
Kabul etmiyorum, bu pratik olarak okunamıyor ve anlayabildiğim kadarıyla, zaman dilimi verileri sağlanmış olsa bile bu datetime naif yapan Zulu (Z) dikkate almıyor.
umbrae

14
Oldukça okunabilir buluyorum. Aslında, ek paketler yüklemeden dönüştürmeyi yapmanın muhtemelen en kolay ve en performanslı yolu.
Tobia

2
Sanırım d = datetime.datetime (* map (int, re.split ('\ D', s) [: - 1])) ile eşdeğerdir.
Xuan

4
bir varyasyon:datetime.datetime(*map(int, re.findall('\d+', s))
jfs

3
Bu, saat dilimi olmadan saf bir tarih-saat nesnesiyle sonuçlanır, değil mi? Çeviride UTC biti kayboluyor mu?
w00t

32

Aldığınız tam hata nedir? Aşağıdaki gibi mi?

>>> 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

Cevabınız evetse, giriş dizginizi "." Üzerine bölebilir ve sonra mikrosaniyeyi aldığınız tarih saatine ekleyebilirsiniz.

Bunu dene:

>>> 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)

10
.Z'yi sadece şeritleyemezsiniz çünkü saat dilimi anlamına gelir ve farklı olabilir. Tarihi UTC saat dilimine dönüştürmem gerekiyor.
Alexander Artemenko

Düz bir datetime nesnesinin saat dilimi kavramı yoktur. Tüm zamanlarınız "Z" ile bitiyorsa, aldığınız tüm tarihler UTC'dir (Zulu saati).
tzot

saat dilimi ""veya dışında "Z"bir şeyse, saat / dakika cinsinden bir uzaklık olmalıdır; bu dattime nesnesine doğrudan eklenebilir / bu nesneden çıkarılabilir. Eğer olabilir idare etmek alt sınıf tzinfo bir oluşturmak, ama muhtemelen kurumlarda değil.
SingleNegationElimination

8
Ayrıca, "% f" mikrosaniye belirtecidir, bu nedenle (zaman dilimi-naif) bir strptime dizesi şöyle görünür: "% Y-% m-% dT% H:% M:% S.% f".
quodlibetor

1
Belirtilen datetime dizesi "Z" dışında bir UTC ofsetine sahipse bu bir istisna oluşturur. Tüm RFC 3339 formatını desteklemez ve UTC ofsetlerini düzgün işleyen diğerlerine göre daha düşük bir cevaptır.
Mark Amery

25

Python 3.7'den başlayarak, strptime, UTC ofsetlerindeki ( kaynak ) kolon sınırlayıcılarını destekler . Böylece şunları kullanabilirsiniz:

import datetime
datetime.datetime.strptime('2018-01-31T09:24:31.488670+00:00', '%Y-%m-%dT%H:%M:%S.%f%z')

DÜZENLE:

Martijn tarafından belirtildiği gibi, datetime nesnesini isoformat () kullanarak oluşturduysanız, datetime.fromisoformat () yöntemini kullanabilirsiniz.


4
Ama 3.7 'de, sen de sahip datetime.fromisoformat()otomatik girişi gibi hangi kolları dizeleri: datetime.datetime.isoformat('2018-01-31T09:24:31.488670+00:00').
Martijn Pieters

2
İyi bir nokta. Kabul ediyorum, kullanmanızı tavsiye ederim datetime.fromisoformat()vedatetime.isoformat()
Andreas Profous

19

Bu günlerde Arrow ayrıca üçüncü taraf bir çözüm olarak da kullanılabilir:

>>> import arrow
>>> date = arrow.get("2008-09-03T20:56:35.450686Z")
>>> date.datetime
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())

6
Arrow ISO8601'i düzgün bir şekilde desteklemiyor: github.com/crsmithdev/arrow/issues/291
kutulu

1
Sadece python-dateutil kullanın - ok python-dateutil gerektirir.
danizen

Arrow şimdi ISO8601'i destekliyor. Başvurulan sorunlar artık kapandı.
Altus

18

Sadece python-dateutilmodülü kullanın :

>>> 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())

belgeleme


1
Bu tam olarak @Flimms yanıtı değil mi?
leo

1
Saniyeler içinde nerede ayrıldığını görüyorsun? Bu makaleyi çığır açmaya çalışarak buldum, bu yüzden başka birinin de olacağını düşündüm.
Blairg23

1
Bu benim sistemimde UTC değil . Aksine, saniye cinsinden çıktı, tarih benim yerel saat dilimimdeymiş gibi unix dönemidir.
Elliot

1
Bu cevap arabasıdır ve kabul edilmemelidir. Muhtemelen tüm soru stackoverflow.com/questions/11743019/…
üçlü

@tripleee Aslında ben sadece kod kontrol ve doğru cevabı dönmek için görünmüyor: 455051100(kontrol epochconverter.com ben bir şey eksik sürece ,,,)?
Blairg23

13

Dateutil kullanmak istemiyorsanız, bu işlevi deneyebilirsiniz:

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)

Ölçek:

from_utc("2007-03-04T21:08:12.123Z")

Sonuç:

datetime.datetime(2007, 3, 4, 21, 8, 12, 123000)

5
Bu yanıt, belirli bir UTC ofsetinin (+00: 00 anlamına gelen "Z") geçtiği format dizesine sabit kodlanmasına dayanır strptime. Bu kötü bir fikir çünkü herhangi bir tarih saatini farklı bir UTC ofsetiyle ayrıştıramayacak ve bir istisna oluşturamayacaktır. RFC 3339'u strptime ile ayrıştırmanın aslında imkansız olduğunu açıklayan cevabımı görün .
Mark Amery

1
Sabit kodludur, ancak yalnızca zulu ayrıştırmanız gerektiğinde durum için yeterlidir.
Sasha

1
@alexander yes - örneğin, tarih dizenizin JavaScript'in toISOStringyöntemiyle oluşturulduğunu biliyorsanız durum böyle olabilir . Ancak bu cevapta Zulu zaman tarihlerinin kısıtlılığından bahsetmiyoruz ya da soru, tüm gereken bu değil ve sadece kullanmanın dateutilgenellikle eşit derecede uygun ve ayrıştırılabileceği şeyde daha az dar olduğunu belirtmedi.
Mark Amery


11

ISO 8601 zaman damgalarını ayrıştırmanın en hızlı yolu olarak ciso8601'i buldum . Adından da anlaşılacağı gibi, C dilinde uygulanır.

import ciso8601
ciso8601.parse_datetime('2014-01-09T21:48:00.921000+05:30')

GitHub Repo README diğer yanıtlar listelenen diğer kütüphanelerin tüm karşı kendi> 10x hıza gösterir.

Kişisel projem birçok ISO 8601 ayrıştırma işini içeriyordu. Sadece aramayı değiştirmek ve 10 kat daha hızlı gitmek güzeldi. :)

Düzenleme: O zamandan beri ciso8601'in bir koruyucusu oldum. Şimdi her zamankinden daha hızlı!


Bu harika bir kütüphane gibi görünüyor! Google App Engine'de ISO8601 ayrıştırmasını optimize etmek isteyenler için, ne yazık ki, bir C kütüphanesi olduğu için kullanamayız, ancak ölçütleriniz yerelin bir datetime.strptime()sonraki en hızlı çözüm olduğunu göstermek için kavrayışa sahipti . Tüm bu bilgileri bir araya getirdiğiniz için teşekkürler!
hamx0r

3
@ hamx0r, datetime.strptime()tam bir ISO 8601 ayrıştırma kütüphanesi olmadığını unutmayın. Python 3.7 üzerindeyseniz, datetime.fromisoformat()biraz daha esnek olan yöntemi kullanabilirsiniz . Yakında ciso8601 README ile birleştirilmesi gereken ayrıştırıcıların bu tam listesi ile ilgilenebilirsiniz .
movermeyer

ciso8601 oldukça güzel çalışıyor, ama bir "pip install pytz" yapmak zorunda, çünkü biri pytz bağımlılığı olmadan zaman dilimi bilgileri ile bir zaman damgası ayrıştırılamıyor. Örnek şuna benzer: dob = ciso8601.parse_datetime (sonuç ['dob'] ['tarih'])
Dirk

2
@Dirk, sadece Python 2'de . Ancak bu bile bir sonraki sürümde kaldırılmalıdır .
movermeyer

8

Bu, Python 3.2'den sonraki stdlib için çalışır (tüm zaman damgalarının UTC olduğu varsayılarak):

from datetime import datetime, timezone, timedelta
datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%fZ").replace(
    tzinfo=timezone(timedelta(0)))

Örneğin,

>>> datetime.utcnow().replace(tzinfo=timezone(timedelta(0)))
... datetime.datetime(2015, 3, 11, 6, 2, 47, 879129, tzinfo=datetime.timezone.utc)

2
Bu yanıt, belirli bir UTC ofsetinin (+00: 00 anlamına gelen "Z") geçtiği format dizesine sabit kodlanmasına dayanır strptime. Bu kötü bir fikir çünkü herhangi bir tarih saatini farklı bir UTC ofsetiyle ayrıştıramayacak ve bir istisna oluşturamayacaktır. RFC 3339'u strptime ile ayrıştırmanın aslında imkansız olduğunu açıklayan cevabımı görün .
Mark Amery

1
Teorik olarak, evet, bu başarısız olur. Uygulamada, Zulu zamanında olmayan ISO 8601 formatlı bir tarihle hiç karşılaşmadım. Çok nadiren ihtiyacım için, bu harika çalışıyor ve bazı harici kütüphanelere bağlı değil.
Benjamin Riggs

4
timezone.utcyerine kullanabilirsiniz timezone(timedelta(0)). Ayrıca, Python kod çalışır 2.6+ eğer (en azından) tedarik utctzinfo nesne
jfs

Karşılaştığınız önemli değil, spesifikasyonla eşleşmiyor.
öğretmen

Sen kullanabilirsiniz %ZPython en son sürümlerinde Zaman dilimi için.
sventechie

7

İso8601 utils yazarıyım. Bu bulunabilir GitHub üzerinde veya üzerinde PyPI . Örneğinizi nasıl ayrıştırabileceğiniz aşağıda açıklanmıştır:

>>> from iso8601utils import parsers
>>> parsers.datetime('2008-09-03T20:56:35.450686Z')
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)

6

ISO 8601 benzeri bir tarih dizesini, datetime.datetimeüçüncü taraf modülleri yüklemeden desteklenen tüm Python sürümlerinde UNIX zaman damgasına veya nesneye dönüştürmenin basit bir yolu , SQLite'ın tarih ayrıştırıcısını kullanmaktır .

#!/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)

Çıktı:

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

11
Teşekkürler. Bu iğrenç. Onu seviyorum.
wchargin

1
Ne inanılmaz, harika, güzel bir hack! Teşekkürler!
Havok

6

ISO 8601 standardı için bir ayrıştırıcı kodladım ve GitHub'a koydum: https://github.com/boxed/iso8601 . Bu uygulama, Python'un datetime modülünün desteklenen tarih aralığının dışındaki süreler, aralıklar, periyodik aralıklar ve tarihler hariç spesifikasyondaki her şeyi destekler.

Testler dahildir! : P


2
Genel olarak, bir araca veya kütüphaneye bağlantılara kullanım notları, bağlı kaynağın soruna nasıl uygulanabileceğine dair özel bir açıklama veya bazı örnek kodlar veya mümkünse yukarıdakilerin tümü eşlik etmelidir .
Samuel Liew

6

Django'nun parse_datetime () işlevi UTC ötelemeli tarihleri ​​destekler:

parse_datetime('2016-08-09T15:12:03.65478Z') =
datetime.datetime(2016, 8, 9, 15, 12, 3, 654780, tzinfo=<UTC>)

Bu nedenle, tüm proje içindeki alanlarda ISO 8601 tarihlerini ayrıştırmak için kullanılabilir:

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')

4

Çünkü ISO 8601, temel olarak isteğe bağlı iki nokta üst üste ve tire çeşitlerinin mevcut olmasına izin verir CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]. Eğer strptime kullanmak istiyorsanız, önce bu varyasyonları çıkarmanız gerekir.

Amaç bir utc datetime nesnesi oluşturmaktır.


Sadece Z son eki ile UTC için çalışan temel bir durum istiyorsanız 2016-06-29T19:36:29.3453Z:

datetime.datetime.strptime(timestamp.translate(None, ':-'), "%Y%m%dT%H%M%S.%fZ")


Aşağıdaki gibi saat dilimi ofsetlerini işlemek 2016-06-29T19:36:29.3453-0400veya 2008-09-03T20:56:35.450686+05:00kullanmak istiyorsanız . Bunlar, tüm varyasyonları 20080903T205635.450686+0500, ayrıştırmayı daha tutarlı / kolay hale getirmek gibi değişken sınırlayıcılar olmadan bir şeye dönüştürecektir .

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" )


Sisteminiz %zstrptime yönergesini desteklemiyorsa ( şuna benzer bir şey görürsünüz ValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z') saati Z(UTC) ile manuel olarak dengelemeniz gerekir . Not %z, sistem / python oluşturma türüne (yani Jython, Cython vb.) Göre değişen c kütüphane desteğine bağlı olarak <3 python sürümlerinde sisteminizde çalışmayabilir.

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

2.X standart kitaplığıyla çalışan bir şey için şunu deneyin:

calendar.timegm(time.strptime(date.split(".")[0]+"UTC", "%Y-%m-%dT%H:%M:%S%Z"))

calendar.timegm, time.mktime öğesinin eksik gm sürümüdür.


1
Bu sadece saat dilimini görmezden gelir '2013-01-28T14: 01: 01.335612-08: 00' -> PDT değil UTC olarak ayrıştırıldı
gatoatigrado

2

Geçersiz tarih dizeleri ayrıştırılırsa python-dateutil bir istisna atar, bu nedenle istisnayı yakalamak isteyebilirsiniz.

from dateutil import parser
ds = '2012-60-31'
try:
  dt = parser.parse(ds)
except ValueError, e:
  print '"%s" is an invalid date' % ds

2

Günümüzde popüler İstekler: HTTP for Humans ™ paketinin yazarından Maya: Datetimes for Humans ™ var :

>>> 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>)

2

Başka bir yol, ISO-8601 için özel ayrıştırıcı kullanmak , dateutil ayrıştırıcının izoparse işlevini kullanmaktır :

from dateutil import parser

date = parser.isoparse("2008-09-03T20:56:35.450686+01:00")
print(date)

Çıktı:

2008-09-03 20:56:35.450686+01:00

Bu işlev, standart Python işlevi datetime.fromisoformat ile ilgili belgelerde de belirtilmiştir :

Daha kapsamlı bir ISO 8601 ayrıştırıcısı olan dateutil.parser.isoparse, üçüncü taraf paket dateutil'de mevcuttur.


1

Harika Mark Amery'nin cevabı sayesinde , tarih saatinin tüm olası ISO formatlarını hesaba katmak için fonksiyon tasarladım:

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))

0
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)

Dizenin bitip bitmediğine bakmamız gerektiğini Z, kullanarak ayrıştırabileceğimizi unutmayın %z.


0

Başlangıçta ile denedim:

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)

Ama bu olumsuz zaman dilimlerinde işe yaramadı. Ancak bu Python 3.7.3 iyi çalıştım:

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)

Bazı testler, çıkışın sadece mikrosaniyelerin hassasiyetinden farklı olduğunu unutmayın. Makinemde 6 basamak hassasiyet var, ancak 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)

Neden yaptın diye sorabilir miyim frozenset(('+', '-'))? Normal bir grup ('+', '-')aynı şeyi başaramaz mı?
Prahlad Yeri

Tabii, ama mükemmel bir karma arama yerine doğrusal bir tarama değil mi?
AT
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.