Jak skrócić czas na obiekcie DateTime w Pythonie?

251

Jaki jest elegancki sposób na obcięcie obiektu Python datetime?

W tym konkretnym przypadku do dnia. Więc w zasadzie ustaw godzinę, minutę, sekundy i mikrosekundy na 0.

Chciałbym, aby wynik był również obiektem typu data-godzina, a nie ciągiem.

Kyle Brandt
źródło

Odpowiedzi:

376

Myślę, że tego właśnie szukasz ...

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

Ale jeśli tak naprawdę nie zależy ci na aspekcie czasowym rzeczy, powinieneś naprawdę tylko omijać dateprzedmioty ...

>>> d_truncated = datetime.date(dt.year, dt.month, dt.day)
>>> d_truncated
datetime.date(2011, 3, 29)
Chris W.
źródło
14
W przypadku dt uwzględniającego strefę czasową datetime.datetime (dt.rok, dt.miesiąc, dt.dzień) odrzuca informacje tzinfo.
ʇsәɹoɈ
1
jeśli szukasz właśnie dziś, możesz także wykonać datetime.date.today ()
galarant
4
Zauważ, że zarówno dokumenty Python 2, jak i Python 3 stwierdzają, że replace()metoda zwraca obiekt daty / godziny, więc poprawna inkantacja to:dt = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
Brad M
3
OP chce datetime, a nie dateobiekt (że można uzyskać za pomocą dt.date()wywołania (nie trzeba używać jawnego konstruktora)). .replace()Metoda może zawieść jeśli datetimedodaje wsparcie nanosekund . Możesz zamiast tego użyćdatetime.combine() .
jfs
@chrisw Dlaczego nie napisać tego w jednym wierszu datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)?
3kstc
66

Użyj datenie, datetimejeśli nie zależy ci na czasie.

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

Możesz zaktualizować datę / godzinę w ten sposób:

>>> now.replace(minute=0, hour=0, second=0, microsecond=0)
datetime.datetime(2011, 3, 29, 0, 0)
Jochen Ritzel
źródło
9
Dzięki datetimes uwzględniającym strefę czasową, now.date () odrzuca informacje tzinfo.
ʇsәɹoɈ
1
@ ʇsәɹoɈ: oto jak można uzyskać północną strefę czasową
jfs
34

Cztery lata później: inny sposób, unikanie replace

Wiem, że przyjęta odpowiedź sprzed czterech lat działa, ale wydaje się to nieco lżejsze niż używanie replace:

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

Notatki

  • Kiedy tworzysz datetimeobiekt bez przekazywania właściwości konstruktora, dostajesz północ.
  • Jak zauważyli inni, zakłada się, że chcesz mieć obiekt datetime do późniejszego użycia z timedeltas.
  • Możesz oczywiście zastąpić to pierwszym wierszem: dt = datetime.datetime.now()
zx81
źródło
20

Nie można obciąć obiektu datetime, ponieważ jest on niezmienny .

Jest jednak jeden sposób na zbudowanie nowej daty / godziny z polami 0 godzin, minut, sekund i mikrosekund, bez wyrzucania oryginalnej daty lub tzinfo:

newdatetime = now.replace(hour=0, minute=0, second=0, microsecond=0)
ɈsәɹoɈ
źródło
1
+1: jeśli umieścisz replaceopcję na pierwszym miejscu, ponieważ prawdopodobnie tego właśnie chcą.
S.Lott
Jest nieprawidłowy w użyciu tzinfo=now.tzinfo. O tzinfopółnocy może być inna, np. Przesunięcie utc o 2012-04-01 00:09:00(9 rano) w strefie czasowej Australii / Melbourne jest, AEST+10:00ale jest AEDT+11:00o 2012-04-01 00:00:00(północy) - w tym dniu następuje przejście na koniec DST. Możesz użyć pytzmodułu, aby to naprawić, zobacz moją odpowiedź.
jfs
13

Aby uzyskać północ odpowiadającą danemu obiektowi datetime, możesz użyć datetime.combine()metody :

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

Zaletą w porównaniu do tej .replace()metody jest to datetime.combine(), że oparte na rozwiązaniu rozwiązanie będzie nadal działać, nawet jeśli datetimemoduł wprowadzi obsługę nanosekund .

tzinfomożna zachować, jeśli to konieczne, ale przesunięcie utc może być inne o północy, np. z powodu przejścia DST, a zatem naiwne rozwiązanie (ustawienie tzinfoatrybutu czasu) może się nie powieść. Zobacz Jak uzyskać czas UTC „północ” dla danej strefy czasowej?

jfs
źródło
7

Możesz do tego użyć pand (chociaż może to być narzut dla tego zadania). Możesz użyć okrągłego , podłogowego i sufitowego jak dla zwykłych liczb i dowolnej częstotliwości pand z przesuniętych aliasów :

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
źródło
4

Możesz użyć datetime.strftime, aby wyodrębnić dzień, miesiąc, rok ...

Przykład:

from datetime import datetime
d = datetime.today()

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

Wyjście (na dziś):

29-2011

Jeśli chcesz tylko pobrać dzień, możesz użyć atrybutu dnia, takiego jak:

from datetime import datetime
d = datetime.today()

# Retrieves the day
print d.day

Ouput (na dziś):

29
Sandro Munda
źródło
Chodzi o to, że już raz to robię, więc może to mieć więcej narzutu niż ustawienie pól min. Godziny itp. W obiekcie datetime.
Kyle Brandt
Heh, to dziwny sposób na zrobienie tego, tak naprawdę możesz to zrobić po prostu d.dayitp.
Jochen Ritzel
3

Istnieje wspaniała biblioteka używana do manipulowania datami: 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')

a jeśli chcesz odzyskać wartość daty / godziny:

>>> d.truncate('year').datetime
datetime.datetime(2015, 1, 1, 0, 0, tzinfo=<DstTzInfo 'US/Pacific' PDT-1 day, 17:00:00 DST>)
DmitrySemenov
źródło
zwraca niewłaściwy czas (nieprawidłowe przesunięcie utc), jeśli czas wyniku ma inne przesunięcie utc, np. z powodu przejścia DST. Zobacz Jak uzyskać czas UTC „północ” dla danej strefy czasowej?
jfs
1

Istnieje moduł datetime_truncate, który obsługuje to za Ciebie. Po prostu wywołuje datetime.replace.

kyrre
źródło
1

6 lat później ... Znalazłem ten post i bardziej podobało mi się podejrzane podejście:

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

Twoje zdrowie

Matias Thayer
źródło
1
>>> import datetime
>>> dt = datetime.datetime.now()
>>> datetime.datetime.date(dt)
datetime.date(2019, 4, 2)
markling
źródło
1

Możesz po prostu użyć

datetime.date.today()

Jest lekki i zwraca dokładnie to, czego chcesz.

Bordotti
źródło
Ta sama odpowiedź została już udzielona, ​​nie ma nic nowego.
slfan
Przepraszam @slfan, ale nie widziałem żadnego posta z twoim imieniem, nawet używając „szukaj” w przeglądarce.
Bordotti
1
Nie przeze mnie, przez zx81 3 lata temu. wyszukaj datetime.date.today (). Jeśli odpowiedź jest prawidłowa, należy głosować, a nie odpowiadać ponownie.
slfan
0

Jeśli masz do czynienia z serią typu DateTime, istnieje bardziej skuteczny sposób na ich obcięcie, szczególnie gdy obiekt Series ma wiele wierszy.

Możesz użyć funkcji podłogi

Na przykład, jeśli chcesz go skrócić do godzin:

Wygeneruj zakres dat

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

Możemy to sprawdzić, porównując czas działania funkcji wymiany i podłogi.

%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
źródło
0

Oto jeszcze jeden sposób, który pasuje do jednej linii, ale nie jest szczególnie elegancki:

dt = datetime.datetime.fromordinal(datetime.date.today().toordinal())
Valentin B.
źródło