วิธีมาตรฐานในการเพิ่ม N วินาทีใน datetime.time ใน Python คืออะไร?


369

เมื่อกำหนดdatetime.timeค่าใน Python จะมีวิธีมาตรฐานในการเพิ่มจำนวนเต็มเป็นวินาทีเพื่อให้ตัวอย่างเช่น11:34:59+ 3 = 11:35:02?

แนวคิดที่ชัดเจนเหล่านี้ใช้ไม่ได้:

>>> datetime.time(11, 34, 59) + 3
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'int'
>>> datetime.time(11, 34, 59) + datetime.timedelta(0, 3)
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'datetime.timedelta'
>>> datetime.time(11, 34, 59) + datetime.time(0, 0, 3)
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'datetime.time'

ในท้ายที่สุดฉันได้เขียนฟังก์ชันดังนี้:

def add_secs_to_time(timeval, secs_to_add):
    secs = timeval.hour * 3600 + timeval.minute * 60 + timeval.second
    secs += secs_to_add
    return datetime.time(secs // 3600, (secs % 3600) // 60, secs % 60)

ฉันอดคิดไม่ได้ว่าฉันขาดวิธีง่าย ๆ ในการทำสิ่งนี้

ที่เกี่ยวข้อง


ปัญหา Python ที่เกี่ยวข้อง: การสนับสนุน datetime.time สำหรับ '+' และ '-'
jfs

คำตอบ:


499

คุณสามารถใช้datetimeตัวแปรแบบเต็มด้วยtimedeltaและโดยการระบุวันที่จำลองแล้วใช้timeเพื่อรับค่าเวลา

ตัวอย่างเช่น:

import datetime
a = datetime.datetime(100,1,1,11,34,59)
b = a + datetime.timedelta(0,3) # days, seconds, then other fields.
print(a.time())
print(b.time())

ผลลัพธ์เป็นสองค่าแยกกันสามวินาที:

11:34:59
11:35:02

คุณยังสามารถเลือกอ่านได้มากขึ้น

b = a + datetime.timedelta(seconds=3)

หากคุณมีแนวโน้ม


หากคุณใช้ฟังก์ชันที่สามารถทำสิ่งนี้ได้คุณสามารถดูได้จากaddSecsด้านล่าง:

import datetime

def addSecs(tm, secs):
    fulldate = datetime.datetime(100, 1, 1, tm.hour, tm.minute, tm.second)
    fulldate = fulldate + datetime.timedelta(seconds=secs)
    return fulldate.time()

a = datetime.datetime.now().time()
b = addSecs(a, 300)
print(a)
print(b)

ผลลัพธ์นี้:

 09:11:55.775695
 09:16:55

6
เพื่อหลีกเลี่ยง OverflowErrors ฉันขอแนะนำให้ใช้วันที่จำลองที่แตกต่างกันเช่นสองสามปีต่อมา: datetime (101,1,1,11,34,59) หากคุณลองลบ timedelta ขนาดใหญ่ออกจากวันที่ด้านบนคุณจะได้รับข้อผิดพลาด "OverflowError: ค่าวันนอกช่วง" เนื่องจากปีสำหรับวัตถุ datetime ไม่สามารถมีขนาดเล็กกว่า 1
pheelicks

2
@pheelicks เสร็จแล้วถึงแม้ว่าจะสายไปนิด แต่ก็ไม่ได้เวลาตอบสนองแบบว่องไว :-) เนื่องจากฉันต้องแก้ไขข้อผิดพลาดอื่นในรหัสของฉันฉันคิดว่าฉันจะรวมข้อเสนอแนะของคุณในเวลาเดียวกัน
paxdiablo

53

ดังที่คนอื่น ๆ ได้ระบุไว้คุณสามารถใช้อ็อบเจกต์ datetime แบบเต็ม ๆ

from datetime import datetime, date, time, timedelta
sometime = time(8,00) # 8am
later = (datetime.combine(date.today(), sometime) + timedelta(seconds=3)).time()

อย่างไรก็ตามฉันคิดว่ามันคุ้มค่าที่จะอธิบายว่าทำไมต้องใช้ออบเจ็กต์ datetime เต็มรูปแบบ พิจารณาสิ่งที่จะเกิดขึ้นถ้าฉันเพิ่ม 2 ชั่วโมงถึง 23.00 น. พฤติกรรมที่ถูกต้องคืออะไร? ข้อยกเว้นเพราะคุณไม่มีเวลามากกว่า 23:59 น. มันควรห่อกลับ?

โปรแกรมเมอร์ที่แตกต่างกันจะคาดหวังสิ่งที่แตกต่างดังนั้นสิ่งที่พวกเขาเลือกจะทำให้คนประหลาดใจมาก ยิ่งไปกว่านั้นโปรแกรมเมอร์จะเขียนโค้ดที่ใช้งานได้ดีเมื่อพวกเขาทำการทดสอบในตอนแรกและหลังจากนั้นก็ทำให้มันแตกในภายหลัง สิ่งนี้แย่มากซึ่งเป็นสาเหตุที่คุณไม่ได้รับอนุญาตให้เพิ่มวัตถุ timedelta ลงในวัตถุเวลา


22

สิ่งเล็กน้อยสิ่งหนึ่งอาจเพิ่มความคมชัดเพื่อแทนที่ค่าเริ่มต้นสำหรับวินาที

>>> b = a + datetime.timedelta(seconds=3000)
>>> b
datetime.datetime(1, 1, 1, 12, 24, 59)

4
ฉันชอบอันนี้ ดีและชัดเจนพร้อมอาร์กิวเมนต์ที่ระบุ
Vigrond

12

ขอบคุณ @Pax Diablo, @bvmou และ @Arachnid สำหรับคำแนะนำในการใช้ชุดข้อมูลแบบเต็มตลอด ถ้าฉันต้องยอมรับวัตถุ datetime.time จากแหล่งภายนอกแล้วนี่น่าจะเป็นadd_secs_to_time()ฟังก์ชั่นทางเลือก:

def add_secs_to_time(timeval, secs_to_add):
    dummy_date = datetime.date(1, 1, 1)
    full_datetime = datetime.datetime.combine(dummy_date, timeval)
    added_datetime = full_datetime + datetime.timedelta(seconds=secs_to_add)
    return added_datetime.time()

รหัส verbose นี้สามารถบีบอัดหนึ่งซับนี้:

(datetime.datetime.combine(datetime.date(1, 1, 1), timeval) + datetime.timedelta(seconds=secs_to_add)).time()

แต่ฉันคิดว่าฉันต้องการสรุปในฟังก์ชั่นเพื่อความชัดเจนของรหัสอยู่ดี


ขอบคุณ - แนวคิดที่ดี
Anupam

9

ถ้ามันคุ้มค่าที่จะเพิ่มไฟล์ / การพึ่งพาโครงการของคุณอีกฉันเพิ่งเขียนคลาสเล็ก ๆ ที่datetime.timeเพิ่มความสามารถในการทำเลขคณิต เมื่อคุณผ่านไปเที่ยงคืนมันจะพันรอบ ตอนนี้ "จะเป็นเวลาอะไรอีก 24 ชั่วโมงนับจากนี้" มีหลายกรณีที่มีมุมรวมถึงเวลาออมแสงตามฤดูกาลวินาทีกระโดดการเปลี่ยนแปลงเขตเวลาในอดีตและอื่น ๆ แต่บางครั้งคุณจำเป็นต้องมีกรณีง่าย ๆ และนั่นคือสิ่งที่จะทำ

ตัวอย่างของคุณจะถูกเขียน:

>>> import datetime
>>> import nptime
>>> nptime.nptime(11, 34, 59) + datetime.timedelta(0, 3)
nptime(11, 35, 2)

nptimeสืบทอดมาจากdatetime.timeดังนั้นวิธีการใด ๆ เหล่านั้นควรใช้งานได้เช่นกัน

สามารถใช้ได้จาก PyPi เป็นnptime("เวลาที่ไม่ใช่ทางเดินเท้า") หรือใน GitHub: https://github.com/tgs/nptime


6

คุณไม่สามารถเพิ่มตัวเลขให้กับdatetimeเพราะมันไม่ชัดเจนว่าหน่วยใดที่ใช้: วินาที, ชั่วโมง, สัปดาห์ ...

มีtimedeltaคลาสสำหรับการจัดการกับวันที่และเวลา datetimeลบdatetimeให้timedelta, datetimeบวกtimedeltaจะช่วยให้datetimeทั้งสองdatetimeวัตถุที่ไม่สามารถเพิ่มแม้ว่าสองtimedeltaกระป๋อง

สร้างtimedeltaวัตถุด้วยจำนวนวินาทีที่คุณต้องการเพิ่มและเพิ่มลงในdatetimeวัตถุ:

>>> from datetime import datetime, timedelta
>>> t = datetime.now() + timedelta(seconds=3000)
>>> print(t)
datetime.datetime(2018, 1, 17, 21, 47, 13, 90244)

มีแนวคิดเดียวกันใน C ++ std::chrono::durationคือ:


4
โปรดเพิ่มคำอธิบายเสมอผู้มาใหม่อาจไม่มีเงื่อนงำสิ่งที่คุณกำลังทำที่นี่
แกร์ฮาร์ดบาร์นาร์ด

3

เพื่อประโยชน์ของความครบถ้วนสมบูรณ์ต่อไปนี้เป็นวิธีดำเนินการกับarrow(วันที่และเวลาที่ดีกว่าสำหรับ Python):

sometime = arrow.now()
abitlater = sometime.shift(seconds=3)

1

ลองเพิ่มไปยังdatetime.datetime datetime.timedeltaถ้าคุณต้องการเพียงส่วนเวลาคุณสามารถเรียกtime()วิธีการบนdatetime.datetimeวัตถุผลลัพธ์เพื่อรับ


0

คำถามเก่า แต่ฉันคิดว่าฉันจะโยนในฟังก์ชั่นที่จัดการเขตเวลา ส่วนสำคัญคือการส่งผ่านคุณสมบัติdatetime.timeของวัตถุtzinfoในการรวมกันและจากนั้นใช้timetz()แทนtime()ในวันที่เกิดหุ่นจำลอง คำตอบนี้ได้แรงบันดาลใจบางส่วนจากคำตอบอื่น ๆ ที่นี่

def add_timedelta_to_time(t, td):
    """Add a timedelta object to a time object using a dummy datetime.

    :param t: datetime.time object.
    :param td: datetime.timedelta object.

    :returns: datetime.time object, representing the result of t + td.

    NOTE: Using a gigantic td may result in an overflow. You've been
    warned.
    """
    # Create a dummy date object.
    dummy_date = date(year=100, month=1, day=1)

    # Combine the dummy date with the given time.
    dummy_datetime = datetime.combine(date=dummy_date, time=t, tzinfo=t.tzinfo)

    # Add the timedelta to the dummy datetime.
    new_datetime = dummy_datetime + td

    # Return the resulting time, including timezone information.
    return new_datetime.timetz()

และนี่คือคลาสกรณีทดสอบที่ง่ายมาก (ใช้ในตัวunittest):

import unittest
from datetime import datetime, timezone, timedelta, time

class AddTimedeltaToTimeTestCase(unittest.TestCase):
    """Test add_timedelta_to_time."""

    def test_wraps(self):
        t = time(hour=23, minute=59)
        td = timedelta(minutes=2)
        t_expected = time(hour=0, minute=1)
        t_actual = add_timedelta_to_time(t=t, td=td)
        self.assertEqual(t_expected, t_actual)

    def test_tz(self):
        t = time(hour=4, minute=16, tzinfo=timezone.utc)
        td = timedelta(hours=10, minutes=4)
        t_expected = time(hour=14, minute=20, tzinfo=timezone.utc)
        t_actual = add_timedelta_to_time(t=t, td=td)
        self.assertEqual(t_expected, t_actual)


if __name__ == '__main__':
    unittest.main()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.