¿Cómo truncar el tiempo en un objeto DateTime en Python?

251

¿Cuál es una forma elegante de truncar un objeto de fecha y hora de Python?

En este caso particular, al día. Básicamente, establece la hora, los minutos, los segundos y los microsegundos en 0.

Me gustaría que la salida también sea un objeto de fecha y hora, no una cadena.

Kyle Brandt
fuente

Respuestas:

376

Creo que esto es lo que estás buscando ...

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

Pero si realmente no te importa el aspecto temporal de las cosas, entonces solo deberías pasar dateobjetos ...

>>> d_truncated = datetime.date(dt.year, dt.month, dt.day)
>>> d_truncated
datetime.date(2011, 3, 29)
Chris W.
fuente
14
Con un dt que reconoce la zona horaria, datetime.datetime (dt.year, dt.month, dt.day) arroja la información de tzinfo.
ʇsәɹoɈ
1
si está buscando solo hoy, también puede hacer datetime.date.today ()
galarant
44
Tenga en cuenta que Python 2 y Python 3 documentos estatales que el replace()método devuelve un objeto de fecha y hora, por lo que el encantamiento correcto sería:dt = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
Brad M
3
OP quiere datetime, no dateobjeto (que podrías obtener usando dt.date()call (no es necesario usar el constructor explícito)). El .replace()método puede fallar si datetimeagrega soporte de nanosegundos . Podrías usar datetime.combine()en su lugar .
jfs 28/03/2015
@chrisw ¿Por qué no solo escribirlo en una línea datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)?
3kstc
66

Use un dateno a datetimesi no le importa el tiempo.

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

Puede actualizar una fecha y hora como esta:

>>> now.replace(minute=0, hour=0, second=0, microsecond=0)
datetime.datetime(2011, 3, 29, 0, 0)
Jochen Ritzel
fuente
99
Con las fechas y horas con reconocimiento de la zona horaria, now.date () arroja la información de tzinfo.
ɈsәɹoɈ
1
@ ʇsәɹoɈ: así es como puedes obtener la medianoche consciente de la zona horaria
jfs
34

Cuatro años después: otra forma, evitando replace

Sé que la respuesta aceptada de hace cuatro años funciona, pero esto parece un poco más ligero que usar replace:

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

Notas

  • Cuando crea un datetimeobjeto sin pasar propiedades de tiempo al constructor, obtiene la medianoche.
  • Como otros han señalado, esto supone que desea un objeto de fecha y hora para su uso posterior con timedeltas.
  • Por supuesto, puede sustituir esto por la primera línea: dt = datetime.datetime.now()
zx81
fuente
20

No puede truncar un objeto datetime porque es inmutable .

Sin embargo, aquí hay una forma de construir una nueva fecha y hora con campos de 0 horas, minutos, segundos y microsegundos, sin tirar la fecha original o tzinfo:

newdatetime = now.replace(hour=0, minute=0, second=0, microsecond=0)
ʇsәɹoɈ
fuente
1
+1: si pones la replaceopción primero, ya que eso es probablemente lo que quieren.
S.Lott
Es incorrecto de usar tzinfo=now.tzinfo. A la tzinfomedianoche puede ser diferente, por ejemplo, el desplazamiento utc a las 2012-04-01 00:09:00(9 a.m.) en Australia / Melbourne es la zona horaria, AEST+10:00pero es AEDT+11:00a las 2012-04-01 00:00:00(medianoche): hay una transición al final del horario de verano en ese día. Podrías usar el pytzmódulo para arreglarlo, mira mi respuesta.
jfs
13

Para obtener una medianoche correspondiente a un objeto de fecha y hora dado, puede usar el 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)

La ventaja en comparación con el .replace()método es que la datetime.combine()solución basada continuará funcionando incluso si el datetimemódulo introduce el soporte de nanosegundos .

tzinfopuede conservarse si es necesario, pero el desplazamiento utc puede ser diferente a la medianoche, por ejemplo, debido a una transición DST y, por lo tanto, una solución ingenua ( tzinfoatributo de tiempo de configuración ) puede fallar. Consulte ¿Cómo obtengo la hora UTC de "medianoche" para una zona horaria determinada?

jfs
fuente
7

Podría usar pandas para eso (aunque podría ser una sobrecarga para esa tarea). Puede usar redondo , piso y techo como para los números habituales y cualquier frecuencia de pandas de alias offset :

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

Puede usar datetime.strftime para extraer el día, el mes, el año ...

Ejemplo:

from datetime import datetime
d = datetime.today()

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

Salida (para hoy):

29-2011

Si solo desea recuperar el día, puede usar el atributo day como:

from datetime import datetime
d = datetime.today()

# Retrieves the day
print d.day

Ouput (por hoy):

29
Sandro Munda
fuente
Bueno, la cosa es que ya hago esto una vez, por lo que podría tener más sobrecarga que simplemente configurar los campos de hora, min, etc. en el objeto de fecha y hora.
Kyle Brandt
Je, eso es una extraña manera de hacerlo, en realidad se puede hacer simplemente d.dayetc.
Jochen Ritzel
3

Hay una gran biblioteca utilizada para manipular fechas: 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')

y si desea recuperar el valor de fecha y hora:

>>> d.truncate('year').datetime
datetime.datetime(2015, 1, 1, 0, 0, tzinfo=<DstTzInfo 'US/Pacific' PDT-1 day, 17:00:00 DST>)
DmitrySemenov
fuente
devuelve el tiempo incorrecto (desplazamiento de utc incorrecto) si el tiempo de resultado tiene un desplazamiento de utc diferente, por ejemplo, debido a una transición DST. Consulte ¿Cómo obtengo la hora UTC de "medianoche" para una zona horaria determinada?
jfs
1

Hay un módulo datetime_truncate que maneja esto para usted. Simplemente llama a datetime.replace.

kyrre
fuente
1

6 años después ... encontré esta publicación y me gustó más el enfoque 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]')

salud

Matias Thayer
fuente
1
>>> import datetime
>>> dt = datetime.datetime.now()
>>> datetime.datetime.date(dt)
datetime.date(2019, 4, 2)
Markling
fuente
1

Puedes usar

datetime.date.today()

Es ligero y devuelve exactamente lo que quieres.

Bordotti
fuente
La misma respuesta se ha dado antes, no hay nada nuevo.
slfan
Lo siento @slfan pero no vi ninguna publicación con tu nombre, incluso usando "buscar" desde el navegador.
Bordotti
1
No por mí, por zx81 hace 3 años. busque datetime.date.today (). Si una respuesta es correcta, debe votar a favor, no responder de nuevo.
slfan
0

Si se trata de una serie de tipo DateTime, hay una forma más eficiente de truncarlas, especialmente cuando el objeto de la serie tiene muchas filas.

Puedes usar la función de piso

Por ejemplo, si desea truncarlo a horas:

Genera un rango de fechas

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

Podemos verificarlo comparando el tiempo de ejecución entre las funciones de reemplazo y 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
fuente
0

Aquí hay otra forma que encaja en una línea pero que no es particularmente elegante:

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