Conversione di stringhe in datetime


2185

Ho un enorme elenco di date-time come questo come stringhe:

Jun 1 2005  1:33PM
Aug 28 1999 12:00AM

Li riporterò in campi di data e ora appropriati in un database, quindi ho bisogno di inserirli in oggetti di data e ora reali.

Questo sta attraversando l'ORM di Django, quindi non posso usare SQL per eseguire la conversione in insert.


6
A meno che tu non sia sicuro che un formato gestisca ogni singola data-ora (no '', nessun NaNs, nessun incompleto, nessun disadattamento del formato, nessun carattere finale, fuso orario, timestamp di microsecondi o altro testo ...), l'eccezione-felicità di strptime()ti farà impazzire, a meno che tu non lo avvolga. Vedi la mia risposta, basata su O Weis rispondi a questa
smci

L'approccio più pigro e ampiamente utilizzabile che conosca è dateparser (controlla blog.scrapinghub.com/2015/11/09/… ). Funziona anche con espressioni temporali in linguaggio naturale in diverse lingue. Immagino che possa essere lento però.
Armando,

Risposte:


3462

datetime.strptimeè la routine principale per l'analisi delle stringhe in periodi di dati. Può gestire tutti i tipi di formati, con il formato determinato da una stringa di formato che gli dai:

from datetime import datetime

datetime_object = datetime.strptime('Jun 1 2005  1:33PM', '%b %d %Y %I:%M%p')

L' datetimeoggetto risultante è ingenuo rispetto al fuso orario.

link:

Appunti:

  • strptime = "tempo di analisi stringa"
  • strftime = "ora formato stringa"
  • Pronuncialo ad alta voce oggi e non dovrai cercarlo di nuovo tra 6 mesi.

7
'% b', '% p' ​​potrebbe non riuscire in impostazioni internazionali non inglesi.
jfs,

15
@Utente Dovrai sapere in anticipo per escludere quella parte della stringa di formato, ma se vuoi un dateinvece di un datetime, lo attraversi datetimebene: datetime.strptime('Jun 1 2005', '%b %d %Y').date() == date(2005, 6, 1)
Izkata,

14
Se sai che la stringa rappresenta un datetime in UTC, puoi ottenere un datetimeoggetto sensibile al fuso orario aggiungendo questa riga in Python 3:from datetime import timezone; datetime_object = datetime_object.replace(tzinfo=timezone.utc)
Flimm

111
Stavo cercando"%Y-%m-%d %H:%M:%S"
Martin Thoma il

4
@AminahNuraini Ho risolto un problema simile facendo from datetime import datetimeinvece che semplicemente import datetime.
Max Strater,

831

Utilizzare la libreria di dateutil di terze parti :

from dateutil import parser
parser.parse("Aug 28 1999 12:00AM")  # datetime.datetime(1999, 8, 28, 0, 0)

Può gestire la maggior parte dei formati di data, incluso quello che devi analizzare. È più conveniente di strptimecome può indovinare il formato corretto per la maggior parte del tempo.

È molto utile per scrivere test, in cui la leggibilità è più importante delle prestazioni.

Puoi installarlo con:

pip install python-dateutil

86
Tenere presente che per grandi quantità di dati questo potrebbe non essere il modo migliore per affrontare il problema. Indovinare il formato ogni volta può essere terribilmente lento.
Paweł Polewicz,

14
Questo è carino ma sarebbe bello avere una soluzione integrata anziché dover andare da una terza parte.
Brian Buck,

1
Quando provo ad analizzare "32nd jan", mi restituisce "2032-01-06" .. che non è corretto. c'è un modo per verificare se la stringa è una data valida o no
Kartik Domadiya

6
@Reef: 5 volte più lento secondo il mio benchmark rapido e sporco. Non così orribilmente lento come mi sarei aspettato.
Antony Hatchkins,

2
Ha i suoi problemi - come, ad esempio, eliminare silenziosamente le informazioni sul fuso orario dai tempi: prova parser.parse ('15: 55EST ') e confronta con parser.parse ('15 .55CST') come esempio
F1Rumors

490

Dai un'occhiata a strptime nel modulo orario . È l'inverso dello strftime .

$ python
>>> import time
>>> my_time = time.strptime('Jun 1 2005  1:33PM', '%b %d %Y %I:%M%p')
time.struct_time(tm_year=2005, tm_mon=6, tm_mday=1,
                 tm_hour=13, tm_min=33, tm_sec=0,
                 tm_wday=2, tm_yday=152, tm_isdst=-1)

timestamp = time.mktime(my_time)
# convert time object to datetime
from datetime import datetime
my_datetime = datetime.fromtimestamp(timestamp)
# convert time object to date
from datetime import date
my_date = date.fromtimestamp(timestamp)

16
Da quello che ho capito, questa risposta produce solo oggetti temporali, non oggetti datetime - motivo per cui la risposta sarebbe sepolta rispetto alla risposta di Patrick.
Alexander Bird,

C'è un modo per impostare il formato datetime predefinito di DateTimeField?
Kingpin

3
Come ha detto Alexander, questo restituisce uno struct_time, non un datetime. Ovviamente puoi convertirlo in un datetime, ma la risposta di Patrick è più semplice se vuoi un oggetto datetime alla fine.
Leandro Alves,

Non c'è niente come strtotime nella libreria standard di Python, ma dateutil ha un parser che riconosce molti dei formati di data migliori.
Geoff Gerrietts,

1
@BenBlank: '% b', '% p' ​​potrebbe non riuscire in impostazioni internazionali non inglesi.
jfs,

113

Ho messo insieme un progetto in grado di convertire alcune espressioni davvero pulite. Dai un'occhiata al timestring .

Ecco alcuni esempi di seguito:

pip install timestring
>>> import timestring
>>> timestring.Date('monday, aug 15th 2015 at 8:40 pm')
<timestring.Date 2015-08-15 20:40:00 4491909392>
>>> timestring.Date('monday, aug 15th 2015 at 8:40 pm').date
datetime.datetime(2015, 8, 15, 20, 40)
>>> timestring.Range('next week')
<timestring.Range From 03/10/14 00:00:00 to 03/03/14 00:00:00 4496004880>
>>> (timestring.Range('next week').start.date, timestring.Range('next week').end.date)
(datetime.datetime(2014, 3, 10, 0, 0), datetime.datetime(2014, 3, 14, 0, 0))

2
Wow. Wow. Wow. Wow. È così facile Ho una stringa datetime e voglio solo tirare fuori l'anno. Semplice come: import timestring timestring.Date('27 Mar 2014 12:32:29 GMT').yearquesta libreria l'ha resa COSÌ FACILE! Grazie.
brandonjp,

Sei il benvenuto. Mi piacerebbe i tuoi commenti e idee su come migliorare questo pacchetto. Fammi sapere, usa i problemi di github. Grazie!
Steve Peak,

Ciao steve, il modulo è fantastico. Sarebbe bello avere anche un attributo stringa nei giorni feriali. Altrimenti non sono sicuro se inizi da lunedì o domenica
Anake,

1
Non converte correttamente come '5 febbraio 2017' e '5 febbraio 2017' (che sono formati popolari in alcune cerchie, e IMO alcuni dei migliori formati di data per chiarezza e leggibilità). Li memorizza come 2017-02-01. Lo stesso per il 5 febbraio 2017 (funziona correttamente comunque il 5 febbraio 2017); nessuno di questi ultimi due sono formati che ho mai visto abituati alle mie conoscenze, ma ho pensato di segnalarlo comunque.
Brōtsyorfuzthrāx il

2
ATTENZIONE: Questo pacchetto non sembra essere stato mantenuto o migliorato in nessun momento negli ultimi 5 anni e analizza regolarmente date ovviamente errate. Ad esempio, l'istanza in Date("20180912")qualche modo analizza un valore di 2018-11-21. Utilizzare a proprio rischio.
bsplosion,

54

Ricorda questo e non hai dovuto confonderti di nuovo nella conversione del datetime.

String to datetime object = strptime

oggetto datetime in altri formati = strftime

Jun 1 2005 1:33PM

è uguale a

%b %d %Y %I:%M%p

% b Mese come nome abbreviato della locale (giu)

% d Giorno del mese come numero decimale con riempimento zero (1)

% Anno Y con secolo come un numero decimale (2015)

% I Hour (orologio a 12 ore) come numero decimale con spaziatura zero (01)

% M Minuto come numero decimale con spaziatura zero (33)

% p equivalente di Locale di AM o PM (PM)

quindi hai bisogno di strptime, ovvero conversione stringa

>>> dates = []
>>> dates.append('Jun 1 2005  1:33PM')
>>> dates.append('Aug 28 1999 12:00AM')
>>> from datetime import datetime
>>> for d in dates:
...     date = datetime.strptime(d, '%b %d %Y %I:%M%p')
...     print type(date)
...     print date
... 

Produzione

<type 'datetime.datetime'>
2005-06-01 13:33:00
<type 'datetime.datetime'>
1999-08-28 00:00:00

E se hai un diverso formato di date puoi usare panda o dateutil.parse

>>> import dateutil
>>> dates = []
>>> dates.append('12 1 2017')
>>> dates.append('1 1 2017')
>>> dates.append('1 12 2017')
>>> dates.append('June 1 2017 1:30:00AM')
>>> [parser.parse(x) for x in dates]

Produzione

[datetime.datetime(2017, 12, 1, 0, 0), datetime.datetime(2017, 1, 1, 0, 0), datetime.datetime(2017, 1, 12, 0, 0), datetime.datetime(2017, 6, 1, 1, 30)]

% S per i secondi come decimale
ottimista

1
Non si %bromperà se analizzi una data inglese su una macchina che non ha una lingua inglese?
bfontaine,

48

In Python> = 3.7.0,

per convertire la stringa AAAA-MM-GG in oggetto datetime , è datetime.fromisoformatpossibile utilizzare.

>>> from datetime import datetime

>>> date_string = "2012-12-12 10:10:10"
>>> print (datetime.fromisoformat(date_string))
>>> 2012-12-12 10:10:10

32

Molti timestamp hanno un fuso orario implicito. Per garantire che il codice funzioni in ogni fuso orario, è necessario utilizzare UTC internamente e collegare un fuso orario ogni volta che un oggetto estraneo entra nel sistema.

Python 3.2+:

>>> datetime.datetime.strptime(
...     "March 5, 2014, 20:13:50", "%B %d, %Y, %H:%M:%S"
... ).replace(tzinfo=datetime.timezone(datetime.timedelta(hours=-3)))

3
Perché tieni il brutto e talvolta sbagliato ( mktime()durante le transizioni DST) se conosci il 2o metodo ( datetime.strptime())? Se vuoi evitare un'eccezione durante un secondo bisestile (il secondo metodo fallisce), puoi calendar.timegminvece utilizzare :(datetime(1970,1,1)+timedelta(seconds=timegm(time.strptime(..)))).replace(tzinfo=timezone(timedelta(-3)))
jfs

29

Ecco due soluzioni che utilizzano Panda per convertire le date formattate come stringhe in oggetti datetime.date.

import pandas as pd

dates = ['2015-12-25', '2015-12-26']

# 1) Use a list comprehension.
>>> [d.date() for d in pd.to_datetime(dates)]
[datetime.date(2015, 12, 25), datetime.date(2015, 12, 26)]

# 2) Convert the dates to a DatetimeIndex and extract the python dates.
>>> pd.DatetimeIndex(dates).date.tolist()
[datetime.date(2015, 12, 25), datetime.date(2015, 12, 26)]

Tempi

dates = pd.DatetimeIndex(start='2000-1-1', end='2010-1-1', freq='d').date.tolist()

>>> %timeit [d.date() for d in pd.to_datetime(dates)]
# 100 loops, best of 3: 3.11 ms per loop

>>> %timeit pd.DatetimeIndex(dates).date.tolist()
# 100 loops, best of 3: 6.85 ms per loop

Ed ecco come convertire gli esempi di data e ora originali del PO:

datetimes = ['Jun 1 2005  1:33PM', 'Aug 28 1999 12:00AM']

>>> pd.to_datetime(datetimes).to_pydatetime().tolist()
[datetime.datetime(2005, 6, 1, 13, 33), 
 datetime.datetime(1999, 8, 28, 0, 0)]

Esistono molte opzioni per la conversione dalle stringhe in Timasamp di Panda usando to_datetime, quindi controlla i documenti se hai bisogno di qualcosa di speciale.

Allo stesso modo, i timestamp hanno molte proprietà e metodi a cui è possibile accedere oltre.date


26

Personalmente mi piace la soluzione usando il parsermodulo, che è la seconda risposta a questa domanda ed è bellissimo, dato che non devi costruire letterali di stringa per farlo funzionare. MA , un aspetto negativo è che è più lento del 90% rispetto alla risposta accettata strptime.

from dateutil import parser
from datetime import datetime
import timeit

def dt():
    dt = parser.parse("Jun 1 2005  1:33PM")
def strptime():
    datetime_object = datetime.strptime('Jun 1 2005  1:33PM', '%b %d %Y %I:%M%p')

print(timeit.timeit(stmt=dt, number=10**5))
print(timeit.timeit(stmt=strptime, number=10**5))
>10.70296801342902
>1.3627995655316933

Finché non lo farai più di un milione di volte, penso ancora che il parsermetodo sia più conveniente e gestirà automaticamente la maggior parte dei formati temporali.


24

Qualcosa che non è menzionato qui ed è utile: aggiungere un suffisso alla giornata. Ho disaccoppiato la logica del suffisso in modo da poterla utilizzare per qualsiasi numero che ti piace, non solo per le date.

import time

def num_suffix(n):
    '''
    Returns the suffix for any given int
    '''
    suf = ('th','st', 'nd', 'rd')
    n = abs(n) # wise guy
    tens = int(str(n)[-2:])
    units = n % 10
    if tens > 10 and tens < 20:
        return suf[0] # teens with 'th'
    elif units <= 3:
        return suf[units]
    else:
        return suf[0] # 'th'

def day_suffix(t):
    '''
    Returns the suffix of the given struct_time day
    '''
    return num_suffix(t.tm_mday)

# Examples
print num_suffix(123)
print num_suffix(3431)
print num_suffix(1234)
print ''
print day_suffix(time.strptime("1 Dec 00", "%d %b %y"))
print day_suffix(time.strptime("2 Nov 01", "%d %b %y"))
print day_suffix(time.strptime("3 Oct 02", "%d %b %y"))
print day_suffix(time.strptime("4 Sep 03", "%d %b %y"))
print day_suffix(time.strptime("13 Nov 90", "%d %b %y"))
print day_suffix(time.strptime("14 Oct 10", "%d %b %y"))​​​​​​​

17
In [34]: import datetime

In [35]: _now = datetime.datetime.now()

In [36]: _now
Out[36]: datetime.datetime(2016, 1, 19, 9, 47, 0, 432000)

In [37]: print _now
2016-01-19 09:47:00.432000

In [38]: _parsed = datetime.datetime.strptime(str(_now),"%Y-%m-%d %H:%M:%S.%f")

In [39]: _parsed
Out[39]: datetime.datetime(2016, 1, 19, 9, 47, 0, 432000)

In [40]: assert _now == _parsed

16

Esempio di oggetto datetime consapevole di Django Timezone.

import datetime
from django.utils.timezone import get_current_timezone
tz = get_current_timezone()

format = '%b %d %Y %I:%M%p'
date_object = datetime.datetime.strptime('Jun 1 2005  1:33PM', format)
date_obj = tz.localize(date_object)

Questa conversione è molto importante per Django e Python quando hai USE_TZ = True:

RuntimeWarning: DateTimeField MyModel.created received a naive datetime (2016-03-04 00:00:00) while time zone support is active.

12

Crea una piccola funzione di utilità come:

def date(datestr="", format="%Y-%m-%d"):
    from datetime import datetime
    if not datestr:
        return datetime.today().date()
    return datetime.strptime(datestr, format).date()

Questo è abbastanza versatile:

  • Se non passi alcun argomento restituirà la data odierna.
  • C'è un formato data come predefinito che puoi sovrascrivere.
  • Puoi facilmente modificarlo per restituire un datetime.

2
formatè una parola riservata in Python e non deve essere utilizzata come nome di variabile.
triturazione del

12

Sarebbe utile per convertire la stringa in datetime e anche con il fuso orario

def convert_string_to_time(date_string, timezone):
    from datetime import datetime
    import pytz
    date_time_obj = datetime.strptime(date_string[:26], '%Y-%m-%d %H:%M:%S.%f')
    date_time_obj_timezone = pytz.timezone(timezone).localize(date_time_obj)

    return date_time_obj_timezone

date = '2018-08-14 13:09:24.543953+00:00'
TIME_ZONE = 'UTC'
date_time_obj_timezone = convert_string_to_time(date, TIME_ZONE)

9

freccia offre molte funzioni utili per date e orari. Questo bit di codice fornisce una risposta alla domanda e mostra che la freccia è anche in grado di formattare facilmente le date e visualizzare informazioni per altre localizzazioni.

>>> import arrow
>>> dateStrings = [ 'Jun 1  2005 1:33PM', 'Aug 28 1999 12:00AM' ]
>>> for dateString in dateStrings:
...     dateString
...     arrow.get(dateString.replace('  ',' '), 'MMM D YYYY H:mmA').datetime
...     arrow.get(dateString.replace('  ',' '), 'MMM D YYYY H:mmA').format('ddd, Do MMM YYYY HH:mm')
...     arrow.get(dateString.replace('  ',' '), 'MMM D YYYY H:mmA').humanize(locale='de')
...
'Jun 1  2005 1:33PM'
datetime.datetime(2005, 6, 1, 13, 33, tzinfo=tzutc())
'Wed, 1st Jun 2005 13:33'
'vor 11 Jahren'
'Aug 28 1999 12:00AM'
datetime.datetime(1999, 8, 28, 0, 0, tzinfo=tzutc())
'Sat, 28th Aug 1999 00:00'
'vor 17 Jahren'

Vedi http://arrow.readthedocs.io/en/latest/ per ulteriori informazioni.


6

Puoi usare easy_date per semplificare:

import date_converter
converted_date = date_converter.string_to_datetime('Jun 1 2005  1:33PM', '%b %d %Y %I:%M%p')

4

Se vuoi solo il formato della data, puoi convertirlo manualmente passando i tuoi campi individuali come:

>>> import datetime
>>> date = datetime.date(int('2017'),int('12'),int('21'))
>>> date
datetime.date(2017, 12, 21)
>>> type(date)
<type 'datetime.date'>

Puoi passare i valori della stringa divisa per convertirla nel tipo di data come:

selected_month_rec = '2017-09-01'
date_formate = datetime.date(int(selected_month_rec.split('-')[0]),int(selected_month_rec.split('-')[1]),int(selected_month_rec.split('-')[2]))

Otterrai il valore risultante nel formato data.


2

Puoi anche dare un'occhiata dateparser

dateparser fornisce moduli per analizzare facilmente date localizzate in quasi tutti i formati di stringa che si trovano comunemente nelle pagine Web.

Installare:

$ pip install dateparser

Questo è, credo, il modo più semplice per analizzare le date.

Il modo più semplice è utilizzare la dateparser.parsefunzione, che avvolge la maggior parte delle funzionalità del modulo.

Codice di esempio:

import dateparser

t1 = 'Jun 1 2005  1:33PM'
t2 = 'Aug 28 1999 12:00AM'

dt1 = dateparser.parse(t1)
dt2 = dateparser.parse(t2)

print(dt1)
print(dt2)

Produzione:

2005-06-01 13:33:00
1999-08-28 00:00:00

1

Vedere la mia risposta .

Nei dati del mondo reale questo è un vero problema: formati di data multipli, non corrispondenti, incompleti, incoerenti e multilingua / regione, spesso mescolati liberamente in un unico set di dati. Non va bene che il codice di produzione fallisca, figuriamoci che fa eccezione, come una volpe.

Dobbiamo provare ... catturare più formati datetime fmt1, fmt2, ..., fmtn e sopprimere / gestire le eccezioni (da strptime()) per tutti quelli che non corrispondono (e in particolare, evitare di aver bisogno di una scala di prova con rientro yukky n-deep ..catch clauses). Dalla mia soluzione

def try_strptime(s, fmts=['%d-%b-%y','%m/%d/%Y']):
    for fmt in fmts:
        try:
            return datetime.strptime(s, fmt)
        except:
            continue

    return None # or reraise the ValueError if no format matched, if you prefer

La domanda non diceva nulla riguardo a "formati di data multipli, non corrispondenti, incompleti, incoerenti e multilingua / regionali" ecc. Questo può essere un problema reale, ma non pertinente qui.
RoG

1
@RoG: Non ha mai detto di no, e implicava che fossero: "elenco enorme ... database" . Nella maggior parte di tutti i database / file di log su cui ho lavorato (anche di piccole dimensioni), c'erano diversi formati di data, identificatori di fuso orario, MM-DD ecc. In produzione è inaccettabile scrivere codice fragile che codifica in formati e si blocca con l'eccezione quando non ottiene il formato previsto (anche la restituzione di None o "" è più accettabile). Da qui la necessità di più formati. Quindi questo risponde alla domanda posta, e ho trascorso un po 'di tempo a capire il modo più Pythonic per gestire gli errori da più formati.
smci,

"enorme elenco ... database" implica semplicemente che ce ne sono molti, non che siano tutti formati diversi. È assolutamente accettabile scrivere codice che legge un singolo formato, se si sa che c'è un solo formato nell'input. In questo caso dovrebbe bloccarsi se viene passato qualcosa che non è nel formato giusto.
RoG

@RoG: è inaccettabile scrivere codice di produzione che si arresta in modo anomalo in formato errato / Unicode alterato / troncato / mancante / dati, NaN, formato M / D / Y vs D / M / Y, AA vs AAAA, ecc. Soprattutto se quelli le eccezioni possono essere evitate con una soluzione a sette linee come ho mostrato. La maggior parte dei "database enormi" del mondo reale sono così. Solo perché l'OP non ha detto esplicitamente che ciò non significa che non sia il contesto tipico. Non ho intenzione di litigare con te. Su quale tipo di set di dati lavori e perché ritieni che tali presupposti siano ragionevoli? A meno che non stiamo parlando solo di un codice giocattolo che richiede un intervento costante.
smci,

1
Sembra un po 'sciocco presumere con assoluta certezza che l'OP deve avere dati che non hanno mai avuto incoerenze. Sì, è possibile avere dati del genere, ma no non possiamo supporre che sia il caso qui. Ho pensato che questa risposta fosse utile, certamente per me la cui ricerca di risposte simili a una domanda molto simile, in cui le incoerenze sono sicuramente un problema.
Paul Miller,

1
emp = pd.read_csv("C:\\py\\programs\\pandas_2\\pandas\\employees.csv")
emp.info()

mostra la colonna "Ora data inizio" e "Ora ultimo accesso" sono entrambi "oggetto = stringhe" nel frame di dati

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 8 columns):
First Name           933 non-null object
Gender               855 non-null object
Start Date           1000 non-null object

Last Login Time      1000 non-null object
Salary               1000 non-null int64
Bonus %              1000 non-null float64
Senior Management    933 non-null object
Team                 957 non-null object
dtypes: float64(1), int64(1), object(6)
memory usage: 62.6+ KB

Usando l' parse_datesopzione read_csvmenzionata puoi convertire il tuo datetime di stringa nel formato datetime di Panda.

emp = pd.read_csv("C:\\py\\programs\\pandas_2\\pandas\\employees.csv", parse_dates=["Start Date", "Last Login Time"])
emp.info()


<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 8 columns):
First Name           933 non-null object
Gender               855 non-null object
Start Date           1000 non-null datetime64[ns]
Last Login Time      1000 non-null datetime64[ns]
Salary               1000 non-null int64
Bonus %              1000 non-null float64
Senior Management    933 non-null object
Team                 957 non-null object
dtypes: datetime64[ns](2), float64(1), int64(1), object(4)
memory usage: 62.6+ KB
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.