Comment tronquer l'heure sur un objet DateTime en Python?

251

Quelle est une manière élégante de tronquer un objet datetime python?

Dans ce cas particulier, au jour le jour. Donc, en gros, régler l'heure, les minutes, les secondes et les microsecondes à 0.

Je voudrais que la sortie soit également un objet datetime, pas une chaîne.

Kyle Brandt
la source

Réponses:

376

Je pense que c'est ce que vous cherchez ...

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

Mais si vous ne vous souciez vraiment pas de l'aspect temporel des choses, alors vous ne devriez vraiment contourner que des dateobjets ...

>>> d_truncated = datetime.date(dt.year, dt.month, dt.day)
>>> d_truncated
datetime.date(2011, 3, 29)
Chris W.
la source
14
Avec un dt sensible au fuseau horaire, datetime.datetime (dt.year, dt.month, dt.day) jette les informations tzinfo.
ʇsәɹoɈ
1
si vous cherchez juste aujourd'hui, vous pouvez aussi faire datetime.date.today ()
galarant
4
Notez que python 2 et Python 3 docs tous deux que la replace()méthode retourne un objet datetime, de sorte que le incantatoire correcte serait:dt = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
Brad M
3
OP veut datetime, pas dateobjet (que vous pourriez obtenir en utilisant dt.date()call (pas besoin d'utiliser le constructeur explicite)). La .replace()méthode peut échouer si elle datetimeajoute un support nanoseconde . Vous pouvez utiliser à la datetime.combine()place .
jfs
@chrisw Pourquoi ne pas simplement l'écrire sur une seule ligne datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)?
3kstc
66

Utilisez un datepas datetimesi vous ne vous souciez pas du temps.

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

Vous pouvez mettre à jour un datetime comme ceci:

>>> now.replace(minute=0, hour=0, second=0, microsecond=0)
datetime.datetime(2011, 3, 29, 0, 0)
Jochen Ritzel
la source
9
Avec les datetimes sensibles au fuseau horaire, now.date () supprime les informations tzinfo.
ʇsәɹoɈ
1
@ ʇsәɹoɈ: voici comment vous pouvez obtenir le minuit en fonction du fuseau horaire
jfs
35

Quatre ans plus tard: une autre façon, en évitant replace

Je sais que la réponse acceptée d'il y a quatre ans fonctionne, mais cela semble un peu plus léger que l'utilisation replace:

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

Remarques

  • Lorsque vous créez un datetimeobjet sans transmettre les propriétés de temps au constructeur, vous obtenez minuit.
  • Comme d'autres l'ont noté, cela suppose que vous souhaitez un objet datetime pour une utilisation ultérieure avec timedeltas.
  • Vous pouvez bien sûr substituer ceci à la première ligne: dt = datetime.datetime.now()
zx81
la source
20

Vous ne pouvez pas tronquer un objet datetime car il est immuable .

Cependant, voici une façon de construire un nouveau datetime avec des champs de 0 heure, minute, seconde et microseconde, sans jeter la date d'origine ou tzinfo:

newdatetime = now.replace(hour=0, minute=0, second=0, microsecond=0)
ʇsәɹoɈ
la source
1
+1: si vous mettez l' replaceoption en premier, car c'est probablement ce qu'ils veulent.
S.Lott
Il est incorrect à utiliser tzinfo=now.tzinfo. Le tzinfominuit peut être différent, par exemple, le décalage utc à 2012-04-01 00:09:00(9h) en Australie / Melbourne est le fuseau horaire AEST+10:00mais il est AEDT+11:00à 2012-04-01 00:00:00(minuit) - il y a une transition de fin de l'heure d'été ce jour-là. Vous pouvez utiliser le pytzmodule pour le réparer, voir ma réponse.
jfs
13

Pour obtenir un minuit correspondant à un objet datetime donné, vous pouvez utiliser la datetime.combine()méthode :

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

L'avantage par rapport à la .replace()méthode est que la datetime.combine()solution basée sur continuera à fonctionner même si le datetimemodule introduit le support des nanosecondes .

tzinfopeut être conservé si nécessaire mais le décalage utc peut être différent à minuit, par exemple, en raison d'une transition DST et donc une solution naïve (définition de l' tzinfoattribut time) peut échouer. Voir Comment puis-je obtenir l'heure UTC de «minuit» pour un fuseau horaire donné?

jfs
la source
7

Vous pouvez utiliser des pandas pour cela (bien que cela puisse être une surcharge pour cette tâche). Vous pouvez utiliser le rond , le sol et le plafond comme pour les nombres habituels et toute fréquence de pandas provenant d' alias décalés :

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
la source
4

Vous pouvez utiliser datetime.strftime pour extraire le jour, le mois, l'année ...

Exemple :

from datetime import datetime
d = datetime.today()

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

Sortie (pour aujourd'hui):

29-2011

Si vous voulez simplement récupérer le jour, vous pouvez utiliser l'attribut day comme:

from datetime import datetime
d = datetime.today()

# Retrieves the day
print d.day

Ouput (pour aujourd'hui):

29
Sandro Munda
la source
Eh bien, le fait est que je le fais déjà une fois, de sorte que cela pourrait avoir plus de surcharge que de simplement définir les champs heure min, etc. dans l'objet datetime.
Kyle Brandt
Hé, c'est une façon étrange de le faire, vous pouvez simplement faire d.dayetc.
Jochen Ritzel
3

Il existe une grande bibliothèque utilisée pour manipuler les dates: 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')

et si vous souhaitez récupérer la valeur datetime:

>>> d.truncate('year').datetime
datetime.datetime(2015, 1, 1, 0, 0, tzinfo=<DstTzInfo 'US/Pacific' PDT-1 day, 17:00:00 DST>)
DmitrySemenov
la source
il renvoie le mauvais moment (décalage utc incorrect) si le temps de résultat a un décalage utc différent, par exemple, en raison d'une transition DST. Voir Comment puis-je obtenir l'heure UTC de «minuit» pour un fuseau horaire donné?
jfs
1

Il existe un module datetime_truncate qui gère cela pour vous. Il appelle simplement datetime.replace.

kyrre
la source
1

6 ans plus tard ... J'ai trouvé ce post et j'ai plus aimé l'approche 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]')

à votre santé

Matias Thayer
la source
1
>>> import datetime
>>> dt = datetime.datetime.now()
>>> datetime.datetime.date(dt)
datetime.date(2019, 4, 2)
marquage
la source
1

Vous pouvez simplement utiliser

datetime.date.today()

Il est léger et renvoie exactement ce que vous voulez.

Bordotti
la source
La même réponse a déjà été donnée, il n'y a rien de nouveau.
slfan
Désolé @slfan, mais je n'ai vu aucun message avec votre nom, même en utilisant la «recherche» du navigateur.
Bordotti
1
Pas par moi, par zx81 il y a 3 ans. recherchez datetime.date.today (). Si une réponse est correcte, vous devriez voter, pas répondre à nouveau.
slfan
0

Si vous traitez une série de type DateTime, il existe un moyen plus efficace de les tronquer, spécialement lorsque l'objet Series a beaucoup de lignes.

Vous pouvez utiliser la fonction étage

Par exemple, si vous souhaitez le tronquer en heures:

Générer une plage de dates

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

Nous pouvons le vérifier en comparant le temps de fonctionnement entre les fonctions de remplacement et de plancher.

%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
la source
0

Voici encore une autre manière qui tient dans une ligne mais n'est pas particulièrement élégante:

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