Convertendo string em data e hora

2183

Eu tenho uma lista enorme de datas e horários como este:

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

Vou colocá-los de volta nos campos de data e hora apropriados em um banco de dados, então eu preciso incluí-los em objetos de data e hora reais.

Isso está passando pelo ORM do Django, então não posso usar o SQL para fazer a conversão na inserção.

Oli
fonte
6
A menos que você tenha certeza de que um formato lida com cada data / hora (não '', NaNs, incompletos, sem diferenças de formato, sem caracteres finais, fusos horários, timestamps de microssegundos ou outro texto ...), a felicidade da exceção de strptime()vai deixá-lo louco, a menos que você o enrole. Veja minha resposta, com base na resposta da Or Weis a isto
smci
A abordagem mais preguiçosa e mais utilizável que conheço é o comparador de datas (consulte blog.scrapinghub.com/2015/11/09/… ). Ele funciona mesmo com expressões de horário do idioma natural em vários idiomas prontos para uso. Eu acho que pode ser lento embora.
Armando #
Há um link útil aqui: stackabuse.com/converting-strings-to-datetime-in-python
GoingMyWay

Respostas:

3461

datetime.strptimeé a principal rotina para analisar seqüências de caracteres em tempos de data. Ele pode lidar com todos os tipos de formatos, com o formato determinado por uma string de formato fornecida:

from datetime import datetime

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

O datetimeobjeto resultante é ingênuo ao fuso horário.

Ligações:

Notas:

  • strptime = "tempo de análise da string"
  • strftime = "hora do formato da string"
  • Pronuncie em voz alta hoje e não será necessário procurá-lo novamente em 6 meses.
Patrick Harrington
fonte
7
'% b', '% p' ​​podem falhar no código de idioma que não está em inglês.
JFS
15
@User Você vai ter que saber de antemão para excluir essa parte da cadeia de formato, mas se você quiser um date, em vez de um datetime, passando datetimelida com isso muito bem: datetime.strptime('Jun 1 2005', '%b %d %Y').date() == date(2005, 6, 1)
Izkata
14
Se você sabe que a sequência representa uma data e hora no UTC, é possível obter um datetimeobjeto com reconhecimento de fuso horário adicionando esta linha no Python 3:from datetime import timezone; datetime_object = datetime_object.replace(tzinfo=timezone.utc)
Flimm
111
Eu estava procurando"%Y-%m-%d %H:%M:%S"
Martin Thoma
4
@AminahNuraini Eu resolvi um problema semelhante fazendo, em from datetime import datetimevez de apenas import datetime.
Max Strater
831

Use a biblioteca dateutil de terceiros :

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

Ele pode lidar com a maioria dos formatos de data, incluindo o que você precisa analisar. É mais conveniente do strptimeque pode imaginar o formato correto na maioria das vezes.

É muito útil para escrever testes, onde a legibilidade é mais importante que o desempenho.

Você pode instalá-lo com:

pip install python-dateutil
Simon Willison
fonte
86
Esteja ciente de que, para grandes quantidades de dados, essa pode não ser a melhor maneira de abordar o problema. Adivinhar o formato todas as vezes pode ser terrivelmente lento.
Paweł Polewicz
14
Isso é bom, mas seria bom ter uma solução integrada, em vez de ter que ir para terceiros.
Brian Buck
1
Quando tento analisar "32 de janeiro", ele me retorna "2032-01-06" .. o que está incorreto. Existe alguma maneira de verificar se a string é uma data válida ou não
Kartik Domadiya
6
@ Recife: 5 vezes mais lento de acordo com a minha referência rápida e suja. Não é tão horrivelmente lento como eu esperaria.
Antony Hatchkins
2
Tem seus próprios problemas - como, por exemplo, eliminar silenciosamente as informações de fuso horário dos horários: tente parser.parse ('15: 55EST ') e compare com parser.parse ('15 .55CST') como exemplo
F1Rumors
490

Confira o tempo de execução no módulo de tempo . É o inverso do tempo de strft .

$ 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)
florim
fonte
16
Pelo que entendi, essa resposta gera apenas objetos de tempo, não objetos de data e hora - e é por isso que a resposta seria enterrada em comparação com a resposta de Patrick.
Alexander Bird
Existe uma maneira de definir o formato de data e hora padrão do DateTimeField?
kingpin
3
Como Alexander disse, isso retorna um struct_time, não um datetime. É claro que você pode convertê-lo em um datetime, mas a resposta de Patrick é mais direta se você quiser um objeto datetime no final.
Leandro Alves
Não há nada como strtotime na biblioteca python padrão, mas o dateutil possui um analisador que reconhece muitos dos melhores formatos de data de esforço.
Geoff Gerrietts
1
@BenBlank: '% b', '% p' ​​podem falhar na localidade que não está em inglês.
JFS
113

Eu montei um projeto que pode converter algumas expressões realmente legais. Confira timestring .

Aqui estão alguns exemplos abaixo:

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))
Steve Peak
fonte
2
Uau. Uau. Uau. Uau. Isto é tão fácil. Eu tenho uma string de data e hora e só quero sair o ano. Tão simples quanto: import timestring timestring.Date('27 Mar 2014 12:32:29 GMT').yearEsta biblioteca tornou TÃO FÁCIL! Obrigado.
precisa saber é o seguinte
Você é muito bem-vindo. Eu adoraria seus comentários e idéias sobre como melhorar este pacote. Deixe-me saber, use os problemas do github. Obrigado!
Steve Peak
Oi Steve, o módulo é ótimo. Seria bom ter um atributo de seqüência de dias da semana também. Caso contrário, não tenho certeza se você começa a partir de segunda ou domingo
Anake
1
Ele não converte corretamente '5 de fevereiro de 2017' e '5 de fevereiro de 2017' (formatos populares em alguns círculos e IMO, alguns dos melhores formatos de data para maior clareza e legibilidade). Ele os armazena como 2017-02-01. O mesmo para 5 / fev / 2017 (no entanto, fev / 5/2017 corretamente); nenhum dos dois últimos são formatos que eu já vi acostumados com o meu conhecimento, mas pensei em apontar isso de qualquer maneira.
Brōtsyorfuzthrāx
2
AVISO: Este pacote não parece ter sido mantido ou aprimorado em nenhum momento nos últimos 5 anos e analisa rotineiramente datas obviamente incorretas. Por exemplo, a instanciação de Date("20180912")alguma forma analisa um valor de 2018-11-21. Use por sua conta e risco.
bsplosion
54

Lembre-se disso e você não precisou se confundir na conversão de data e hora novamente.

String para objeto datetime = strptime

objeto datetime para outros formatos = strftime

Jun 1 2005 1:33PM

é igual a

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

% b Mês como nome abreviado da localidade (junho)

% d Dia do mês como um número decimal preenchido com zero (1)

% Y Ano com o século como um número decimal (2015)

% I hora (relógio de 12 horas) como um número decimal preenchido com zero (01)

% M Minuto como um número decimal preenchido com zero (33)

% p O equivalente da localidade de AM ou PM (PM)

então você precisa de tempo de atividade, ou seja, a conversão stringpara

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

Resultado

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

E se você tiver um formato diferente de datas, poderá usar panda ou 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]

Resultado

[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)]
Rizwan Mumtaz
fonte
% S para segundos como decimal
otimista
1
Não será %binterrompido se você analisar uma data em inglês em uma máquina que não possui um código de idioma em inglês?
Bfontaine # 8/18
48

No Python> = 3.7.0,

para converter a seqüência AAAA-MM-DD em objeto de data e hora , datetime.fromisoformatpode ser usado.

>>> from datetime import datetime

>>> date_string = "2012-12-12 10:10:10"
>>> print (datetime.fromisoformat(date_string))
>>> 2012-12-12 10:10:10
Super Nova
fonte
32

Muitos carimbos de hora têm um fuso horário implícito. Para garantir que seu código funcione em todos os fusos horários, você deve usar o UTC internamente e anexar um fuso horário sempre que um objeto estranho entrar no sistema.

Python 3.2 ou superior:

>>> datetime.datetime.strptime(
...     "March 5, 2014, 20:13:50", "%B %d, %Y, %H:%M:%S"
... ).replace(tzinfo=datetime.timezone(datetime.timedelta(hours=-3)))
Janus Troelsen
fonte
3
Por que você mantém o mktime()1º método feio e às vezes errado ( durante as transições de horário de verão) se conhece o 2º método ( datetime.strptime())? Se você quiser evitar uma exceção durante um segundo bissexto (o segundo método falhar), poderá usar calendar.timegm:(datetime(1970,1,1)+timedelta(seconds=timegm(time.strptime(..)))).replace(tzinfo=timezone(timedelta(-3)))
jfs
29

Aqui estão duas soluções usando o Pandas para converter datas formatadas como seqüências de caracteres em objetos 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)]

Horários

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

E aqui está como converter os exemplos de data e hora originais do OP:

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

Existem muitas opções para converter as seqüências de caracteres em carimbos de data / hora do Pandas to_datetime, portanto verifique os documentos se precisar de algo especial.

Da mesma forma, os carimbos de data e hora têm muitas propriedades e métodos que podem ser acessados, além de.date

Alexander
fonte
26

Pessoalmente, gosto da solução usando o parsermódulo, que é a segunda resposta a esta pergunta e é bonito, pois você não precisa construir nenhum literal de string para fazê-lo funcionar. MAS , uma desvantagem é que é 90% mais lenta que a resposta aceita 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

Contanto que você não faça isso um milhão de vezes sem parar, ainda acho que o parsermétodo é mais conveniente e manipulará a maioria dos formatos de hora automaticamente.

user1767754
fonte
24

Algo que não é mencionado aqui e é útil: adicionar um sufixo ao dia. Decotei a lógica do sufixo para que você possa usá-lo para qualquer número que desejar, não apenas para datas.

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"))​​​​​​​
Aram Kocharyan
fonte
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
guneysus
fonte
16

Exemplo de objeto de data e hora do 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)

Essa conversão é muito importante para Django e Python quando você tem USE_TZ = True:

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

Crie uma pequena função utilitária como:

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

Isso é bastante versátil:

  • Se você não passar nenhum argumento, ele retornará a data de hoje.
  • Há um formato de data como padrão que você pode substituir.
  • Você pode modificá-lo facilmente para retornar um datetime.
Mackraken
fonte
2
formaté uma palavra reservada em python e não deve ser usada como um nome de variável.
triturando
12

Seria útil para converter string em data e hora e também com fuso horário

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)
Kanish Mathew
fonte
9

A seta oferece muitas funções úteis para datas e horas. Esse pedaço de código fornece uma resposta para a pergunta e mostra que a seta também é capaz de formatar datas com facilidade e exibir informações para outros locais.

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

Veja http://arrow.readthedocs.io/en/latest/ para mais informações.

Bill Bell
fonte
6

Você pode usar easy_date para facilitar:

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

Se você quiser apenas o formato da data, poderá convertê-lo manualmente passando seus campos individuais, como:

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

Você pode passar seus valores de sequência dividida para convertê-lo em tipo de data como:

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

Você receberá o valor resultante no formato da data.

Javed
fonte
2

Você também pode conferir dateparser

dateparser fornece módulos para analisar facilmente datas localizadas em quase todos os formatos de string comumente encontrados em páginas da web.

Instalar:

$ pip install dateparser

Acho que é a maneira mais fácil de analisar datas.

A maneira mais direta é usar a dateparser.parsefunção, que envolve a maior parte das funcionalidades do módulo.

Código de amostra:

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)

Resultado:

2005-06-01 13:33:00
1999-08-28 00:00:00
Bilesh Ganguly
fonte
1

Veja minha resposta .

Em dados do mundo real, esse é um problema real: formatos de data múltiplos, incompatíveis, incompletos, inconsistentes e em vários idiomas / região, geralmente misturados livremente em um conjunto de dados. Não é aceitável que o código de produção falhe, muito menos seja feliz como uma raposa.

Precisamos tentar ... capturar vários formatos de data e hora fmt1, fmt2, ..., fmtn e suprimir / manipular as exceções (de strptime()) para todos aqueles que não correspondem (e, em particular, evitam a necessidade de uma escada de tentativa recuada e profunda yukky cláusulas de captura). Da minha solução

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
smci
fonte
A pergunta não dizia nada sobre "formatos de data múltiplos, incompatíveis, incompletos, inconsistentes e com vários idiomas / região" etc. Isso pode ser um problema real, mas não relevante aqui.
RoG
1
@RoG: Nunca disse que não eram, e isso implicava que eram: "lista enorme ... banco de dados" . Na maioria dos bancos de dados / arquivos de log em que trabalhei (mesmo em tamanho pequeno), havia vários formatos de data, identificadores de fuso horário, MM-DD etc. ele não obtém o formato esperado (mesmo retornando Nenhum ou '' é mais aceitável). Daí a necessidade de múltiplos formatos. Portanto, isso aborda a pergunta feita, e passei um pouco de tempo descobrindo a maneira mais pitônica de lidar com erros de vários formatos.
smci
"lista enorme ... banco de dados" simplesmente implica que existem muitos deles, não que sejam todos formatos diferentes. É totalmente aceitável escrever código que leia um único formato, se você souber que existe um único formato na entrada. Nesse caso, ele deve travar se for passado algo que não está no formato correto.
RoG
@RoG: é inaceitável escrever código de produção que trava em formato incorreto / desconfigurado / Unicode / truncado / ausente / dados, NaNs, formato M / D / Y vs D / M / Y, AA vs AAAA, etc. Especialmente se esses exceções podem ser evitadas com uma solução de sete linhas, como mostrei. A maioria dos "grandes bancos de dados" do mundo real é assim. Só porque o OP não disse explicitamente que isso não significa que não seja o contexto típico. Eu não vou brigar com você. Em que tipo de conjunto de dados você trabalha e por que você acha que essas suposições são razoáveis? A menos que estejamos falando apenas de código de brinquedo que requer intervenção constante.
smci
1
Parece um pouco tolo supor com total certeza que o OP deve ter dados que nunca apresentam inconsistências. Sim, é possível ter dados assim, mas não, não podemos assumir que é o caso aqui. Achei que essa resposta foi útil, certamente para mim, cuja busca por respostas semelhantes para uma pergunta muito semelhante, onde inconsistências são definitivamente um problema.
Paul Miller
1
emp = pd.read_csv("C:\\py\\programs\\pandas_2\\pandas\\employees.csv")
emp.info()

mostra a coluna "Data da hora de início" e "Hora do último login" são "object = strings" no quadro de dados

<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

Ao usar a parse_datesopção read_csvmencionada, você pode converter sua string datetime no formato pandas datetime.

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
Riz.Khan
fonte