Bagaimana cara memotong waktu pada objek DateTime dengan Python?

251

Apa cara berkelas untuk memotong objek datetime python?

Dalam kasus khusus ini, hingga hari ini. Jadi pada dasarnya mengatur jam, menit, detik, dan mikrodetik ke 0.

Saya ingin output juga menjadi objek datetime, bukan string.

Kyle Brandt
sumber

Jawaban:

376

Saya pikir ini yang Anda cari ...

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

Tetapi jika Anda benar-benar tidak peduli dengan aspek waktu dari hal-hal, maka Anda harus benar-benar hanya berkeliling datebenda ...

>>> d_truncated = datetime.date(dt.year, dt.month, dt.day)
>>> d_truncated
datetime.date(2011, 3, 29)
Chris W.
sumber
14
Dengan dt time-aware, datetime.datetime (dt.year, dt.month, dt.day) membuang informasi tzinfo.
ʇsәɹoɈ
1
jika Anda hanya mencari hari ini, Anda juga dapat melakukan datetime.date.today ()
galarant
4
Perhatikan bahwa python 2 dan python 3 docs keduanya menyatakan bahwa replace()metode mengembalikan objek datetime, sehingga mantera yang benar adalah:dt = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
Brad M
3
OP ingin datetime, bukan datekeberatan (bahwa Anda bisa menggunakan dt.date()panggilan (tidak perlu menggunakan konstruktor eksplisit)). The .replace()metode mungkin gagal jika datetimemenambahkan dukungan nanodetik . Anda bisa menggunakannya datetime.combine()sebagai gantinya .
jfs
@ chrisw Mengapa tidak menuliskannya dalam satu baris saja datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)?
3kstc
66

Gunakan datebukan datetimejika Anda tidak peduli waktu.

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

Anda dapat memperbarui datetime seperti ini:

>>> now.replace(minute=0, hour=0, second=0, microsecond=0)
datetime.datetime(2011, 3, 29, 0, 0)
Jochen Ritzel
sumber
9
Dengan datetimes-aware time, now.date () membuang informasi tzinfo.
ʇsәɹoɈ
1
@ ʇsәɹoɈ: inilah cara Anda bisa mendapatkan tengah malam yang sadar zona waktu
jfs
34

Empat tahun kemudian: cara lain, menghindari replace

Saya tahu jawaban yang diterima dari empat tahun lalu berhasil, tetapi ini agak lebih ringan daripada menggunakan replace:

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

Catatan

  • Saat Anda membuat datetimeobjek tanpa meneruskan properti waktu ke konstruktor, Anda mendapatkan tengah malam.
  • Seperti yang telah dicatat orang lain, ini mengasumsikan Anda menginginkan objek datetime untuk digunakan nanti dengan timedeltas.
  • Anda tentu saja dapat mengganti ini dengan baris pertama: dt = datetime.datetime.now()
zx81
sumber
20

Anda tidak dapat memotong objek datetime karena tidak dapat diubah .

Namun, berikut adalah salah satu cara untuk membangun datetime baru dengan bidang 0 jam, menit, detik, dan mikrodetik, tanpa membuang tanggal asli atau tzinfo:

newdatetime = now.replace(hour=0, minute=0, second=0, microsecond=0)
ʇsәɹoɈ
sumber
1
+1: jika Anda menempatkan replaceopsi terlebih dahulu, karena mungkin itulah yang mereka inginkan.
S.Lott
Itu tidak benar untuk digunakan tzinfo=now.tzinfo. Pada tzinfotengah malam mungkin berbeda misalnya, utc offset pada 2012-04-01 00:09:00(09:00) di zona waktu Australia / Melbourne AEST+10:00tetapi AEDT+11:00pada 2012-04-01 00:00:00(tengah malam) - ada transisi akhir-DST pada hari itu. Anda dapat menggunakan pytzmodul untuk memperbaikinya, lihat jawaban saya.
jfs
13

Untuk mendapatkan tengah malam yang terkait dengan objek datetime tertentu, Anda bisa menggunakan datetime.combine()metode :

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

Keuntungan dibandingkan dengan yang .replace()metode adalah bahwa datetime.combine()solusi berbasis akan terus bekerja bahkan jika datetimememperkenalkan modul nanodetik mendukung .

tzinfodapat dipertahankan jika perlu tetapi utc offset mungkin berbeda pada tengah malam misalnya, karena transisi DST dan karena itu solusi naif (pengaturan tzinfoatribut waktu) mungkin gagal. Lihat Bagaimana cara saya mendapatkan waktu UTC "tengah malam" untuk zona waktu tertentu?

jfs
sumber
7

Anda bisa menggunakan panda untuk itu (meskipun bisa jadi overhead untuk tugas itu). Anda bisa menggunakan round , floor , dan ceil seperti untuk nomor biasa dan frekuensi panda dari 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
sumber
4

Anda dapat menggunakan datetime.strftime untuk mengekstrak hari, bulan, tahun ...

Contoh:

from datetime import datetime
d = datetime.today()

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

Output (untuk hari ini):

29-2011

Jika Anda hanya ingin mengambil hari, Anda dapat menggunakan atribut hari seperti:

from datetime import datetime
d = datetime.today()

# Retrieves the day
print d.day

Ouput (untuk hari ini):

29
Sandro Munda
sumber
Yah masalahnya adalah saya sudah melakukan ini sekali, sehingga mungkin memiliki lebih banyak overhead kemudian hanya mengatur bidang jam min dll dalam objek datetime.
Kyle Brandt
Heh, itu cara yang aneh untuk melakukannya, Anda sebenarnya bisa melakukannya d.daydll.
Jochen Ritzel
3

Ada perpustakaan hebat yang digunakan untuk memanipulasi tanggal: 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')

dan jika Anda ingin mendapatkan nilai datetime kembali:

>>> d.truncate('year').datetime
datetime.datetime(2015, 1, 1, 0, 0, tzinfo=<DstTzInfo 'US/Pacific' PDT-1 day, 17:00:00 DST>)
DmitrySemenov
sumber
ia mengembalikan waktu yang salah (offset utc salah) jika waktu hasil memiliki offset utc yang berbeda misalnya, karena transisi DST. Lihat Bagaimana cara saya mendapatkan waktu UTC "tengah malam" untuk zona waktu tertentu?
jfs
1

Ada modul datetime_truncate yang menangani ini untuk Anda. Itu hanya panggilan datetime.replace.

kyrre
sumber
1

6 tahun kemudian ... Saya menemukan posting ini dan saya lebih suka pendekatan 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]')

Bersulang

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

Anda bisa menggunakannya

datetime.date.today()

Ringan dan mengembalikan apa yang Anda inginkan.

Bordotti
sumber
Jawaban yang sama telah diberikan sebelumnya, tidak ada yang baru.
Slfan
Maaf @ slfan tetapi saya tidak melihat kiriman dengan nama Anda, bahkan menggunakan "pencarian" dari browser.
Bordotti
1
Bukan oleh saya, oleh zx81 3 tahun lalu. cari datetime.date.today (). Jika jawaban itu benar, Anda harus mengambil alih, bukan menjawab lagi.
slfan
0

Jika Anda berurusan dengan serangkaian tipe DateTime, ada cara yang lebih efisien untuk memotongnya, khususnya ketika objek Seri memiliki banyak baris.

Anda dapat menggunakan fungsi lantai

Misalnya, jika Anda ingin memotongnya menjadi jam:

Hasilkan kisaran tanggal

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

Kita bisa memeriksanya membandingkan waktu berjalan antara fungsi ganti dan fungsi lantai.

%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
sumber
0

Inilah cara lain yang cocok dalam satu baris tetapi tidak terlalu elegan:

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