Como truncar a hora em um objeto DateTime em Python?

251

Qual é uma maneira elegante de truncar um objeto de data e hora python?

Nesse caso em particular, para o dia. Então, basicamente, defina hora, minuto, segundos e microssegundos para 0.

Gostaria que a saída também fosse um objeto datetime, não uma string.

Kyle Brandt
fonte

Respostas:

376

Eu acho que é isso que você está procurando ...

>>> import datetime
>>> dt = datetime.datetime.now()
>>> dt = dt.replace(hour=0, minute=0, second=0, microsecond=0) # Returns a copy
>>> dt
datetime.datetime(2011, 3, 29, 0, 0)

Mas se você realmente não se importa com o aspecto temporal das coisas, deve realmente passar apenas pelos dateobjetos ...

>>> d_truncated = datetime.date(dt.year, dt.month, dt.day)
>>> d_truncated
datetime.date(2011, 3, 29)
Chris W.
fonte
14
Com um dt com reconhecimento de fuso horário, datetime.datetime (dt.year, dt.month, dt.day) joga fora as informações do tzinfo.
ʇsәɹoɈ
1
Se você está procurando apenas hoje, você também pode fazer datetime.date.today ()
galarant
4
Note que Python 2 e 3 docs estaduais que o replace()método retorna um objeto de data e hora, então o encantamento correto seria:dt = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
Brad M
3
O OP quer datetime, não o dateobjeto (que você pode obter usando a dt.date()chamada (não é necessário usar o construtor explícito)). O .replace()método pode falhar se datetimeadicionar suporte em nanossegundos . Você poderia usar em seu datetime.combine()lugar .
JFS
@chrisw Por que não escrever apenas em uma linha datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)?
3kstc 04/10/19
66

Use um datenão datetimese você não se importa com o tempo.

>>> now = datetime.now()
>>> now.date()
datetime.date(2011, 3, 29)

Você pode atualizar uma data e hora como esta:

>>> now.replace(minute=0, hour=0, second=0, microsecond=0)
datetime.datetime(2011, 3, 29, 0, 0)
Jochen Ritzel
fonte
9
Com os tempos de reconhecimento de fuso horário, now.date () joga fora as informações do tzinfo.
ʇsәɹoɈ
1
@ ʇsәɹoɈ: aqui está como você pode obter a meia-noite fuso horário-aware
jfs
34

Quatro anos depois: outra maneira, evitando replace

Eu sei que a resposta aceita de quatro anos atrás funciona, mas isso parece um pouco mais leve do que usar replace:

dt = datetime.date.today()
dt = datetime.datetime(dt.year, dt.month, dt.day)

Notas

  • Quando você cria um datetime objeto sem passar as propriedades de tempo para o construtor, obtém meia-noite.
  • Como outros observaram, isso pressupõe que você deseja um objeto datetime para uso posterior com timedeltas.
  • É claro que você pode substituir isso pela primeira linha: dt = datetime.datetime.now()
zx81
fonte
20

Você não pode truncar um objeto de data e hora porque é imutável .

No entanto, aqui está uma maneira de construir uma nova data e hora com campos de 0 hora, minuto, segundo e microssegundo, sem descartar a data original ou tzinfo:

newdatetime = now.replace(hour=0, minute=0, second=0, microsecond=0)
ʇsәɹoɈ
fonte
1
+1: se você colocar a replaceopção em primeiro lugar, pois provavelmente é isso que eles querem.
31511 S.Lott
Está incorreto de usar tzinfo=now.tzinfo. A tzinfomeia-noite pode ser diferente, por exemplo, o deslocamento utc às 2012-04-01 00:09:00(9h) no fuso horário da Austrália / Melbourne é, AEST+10:00mas é AEDT+11:00às 2012-04-01 00:00:00(meia-noite) - há uma transição de final de horário de verão nesse dia. Você pode usar o pytzmódulo para corrigi-lo, veja minha resposta.
JFS
13

Para obter uma meia-noite correspondente a um determinado objeto datetime, você pode usar o datetime.combine()método :

>>> from datetime import datetime, time
>>> dt = datetime.utcnow()
>>> dt.date()
datetime.date(2015, 2, 3)
>>> datetime.combine(dt, time.min)
datetime.datetime(2015, 2, 3, 0, 0)

A vantagem em comparação com o .replace()método é que datetime.combine()solução baseada continuará a trabalhar mesmo que datetimeintroduz o módulo de nanossegundos apoiar .

tzinfopode ser preservado se necessário, mas o deslocamento utc pode ser diferente à meia-noite, por exemplo, devido a uma transição do horário de verão e, portanto, uma solução ingênua ( tzinfoatributo de configuração de hora) pode falhar. Consulte Como obtenho o horário UTC de "meia-noite" para um determinado fuso horário?

jfs
fonte
7

Você pode usar pandas para isso (embora possa ser uma sobrecarga para essa tarefa). Você pode usar o redondo , o piso e o teto como nos números comuns e em qualquer frequência de pandas de aliases de deslocamento :

import pandas as pd
import datetime as dt

now = dt.datetime.now()
pd_now = pd.Timestamp(now)

freq = '1d'
pd_round = pd_now.round(freq)
dt_round = pd_round.to_pydatetime()

print(now)
print(dt_round)

"""
2018-06-15 09:33:44.102292
2018-06-15 00:00:00
"""
Anton Protopopov
fonte
4

Você pode usar datetime.strftime para extrair o dia, o mês, o ano ...

Exemplo:

from datetime import datetime
d = datetime.today()

# Retrieves the day and the year
print d.strftime("%d-%Y")

Saída (hoje):

29-2011

Se você apenas deseja recuperar o dia, pode usar o atributo day como:

from datetime import datetime
d = datetime.today()

# Retrieves the day
print d.day

Ouput (por hoje):

29
Sandro Munda
fonte
Bem, a coisa é que eu já faço isso uma vez, para que possa ter mais sobrecarga do que apenas definir os campos hora mín, etc, no objeto datetime.
Kyle Brandt
Heh, isso é uma maneira estranha para fazê-lo, você pode realmente apenas fazer d.dayetc.
Jochen Ritzel
3

Existe uma ótima biblioteca usada para manipular datas: Delorean

import datetime
from delorean import Delorean
now = datetime.datetime.now()
d = Delorean(now, timezone='US/Pacific')

>>> now    
datetime.datetime(2015, 3, 26, 19, 46, 40, 525703)

>>> d.truncate('second')
Delorean(datetime=2015-03-26 19:46:40-07:00, timezone='US/Pacific')

>>> d.truncate('minute')
Delorean(datetime=2015-03-26 19:46:00-07:00, timezone='US/Pacific')

>>> d.truncate('hour')
Delorean(datetime=2015-03-26 19:00:00-07:00, timezone='US/Pacific')

>>> d.truncate('day')
Delorean(datetime=2015-03-26 00:00:00-07:00, timezone='US/Pacific')

>>> d.truncate('month')
Delorean(datetime=2015-03-01 00:00:00-07:00, timezone='US/Pacific')

>>> d.truncate('year')
Delorean(datetime=2015-01-01 00:00:00-07:00, timezone='US/Pacific')

e se você deseja recuperar o valor da data e hora:

>>> d.truncate('year').datetime
datetime.datetime(2015, 1, 1, 0, 0, tzinfo=<DstTzInfo 'US/Pacific' PDT-1 day, 17:00:00 DST>)
DmitrySemenov
fonte
retorna o horário errado (deslocamento incorreto do utc) se o tempo resultante tiver um deslocamento utc diferente, por exemplo, devido a uma transição do horário de verão. Consulte Como obtenho o horário UTC de "meia-noite" para um determinado fuso horário?
JFS
1

Existe um módulo datetime_truncate que manipula isso para você. Apenas chama datetime.replace.

kyrre
fonte
1

6 anos depois ... Encontrei este post e gostei mais da abordagem numpy:

import numpy as np
dates_array = np.array(['2013-01-01', '2013-01-15', '2013-01-30']).astype('datetime64[ns]')
truncated_dates = dates_array.astype('datetime64[D]')

Felicidades

Matias Thayer
fonte
1
>>> import datetime
>>> dt = datetime.datetime.now()
>>> datetime.datetime.date(dt)
datetime.date(2019, 4, 2)
marcação
fonte
1

Você pode apenas usar

datetime.date.today()

É leve e retorna exatamente o que você deseja.

Bordotti
fonte
A mesma resposta foi dada antes, não há nada novo.
slfan 7/01/19
Desculpe @slfan, mas eu não vi nenhum post com seu nome, mesmo usando "search" no navegador.
Bordotti 22/04/19
1
Não por mim, por zx81 há 3 anos. procure por datetime.date.today (). Se uma resposta estiver correta, você deve votar novamente, não responder novamente.
slfan
0

Se você estiver lidando com uma série do tipo DateTime, há uma maneira mais eficiente de truncá-los, especialmente quando o objeto Series tiver muitas linhas.

Você pode usar o chão função de

Por exemplo, se você deseja truncá-lo para horas:

Gere um intervalo de datas

times = pd.Series(pd.date_range(start='1/1/2018 04:00:00', end='1/1/2018 22:00:00', freq='s'))

Podemos verificar comparando o tempo de funcionamento entre as funções de substituição e de piso.

%timeit times.apply(lambda x : x.replace(minute=0, second=0, microsecond=0))
>>> 341 ms ± 18.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit times.dt.floor('h')
>>>>2.26 ms ± 451 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Abraham Simpson
fonte
0

Aqui está outra maneira que se encaixa em uma linha, mas não é particularmente elegante:

dt = datetime.datetime.fromordinal(datetime.date.today().toordinal())
Valentin B.
fonte