Cách tiêu chuẩn để thêm N giây vào datetime.time trong Python là gì?


369

Cho một datetime.timegiá trị trong Python, có cách tiêu chuẩn nào để thêm số nguyên giây cho nó không, ví dụ như 11:34:59+ 3 = 11:35:02?

Những ý tưởng rõ ràng này không hoạt động:

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

Cuối cùng, tôi đã viết các hàm như thế này:

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)

Tôi không thể không nghĩ rằng tôi đang thiếu một cách dễ dàng hơn để làm điều này mặc dù.

Liên quan


vấn đề liên quan đến Python: datetime.time hỗ trợ cho '+' và '-'
jfs

Câu trả lời:


499

Bạn có thể sử dụng datetimecác biến đầy đủ với timedeltavà bằng cách cung cấp một ngày giả sau đó sử dụng timeđể chỉ nhận giá trị thời gian.

Ví dụ:

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

kết quả trong hai giá trị, cách nhau ba giây:

11:34:59
11:35:02

Bạn cũng có thể chọn để dễ đọc hơn

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

nếu bạn rất nghiêng.


Nếu bạn đang theo một chức năng có thể làm điều này, bạn có thể xem xét sử dụng addSecsbên dưới:

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)

Kết quả này:

 09:11:55.775695
 09:16:55

6
Để tránh OverflowErrors, tôi khuyên bạn nên sử dụng một ngày giả khác, ví dụ một vài năm sau: datetime (101,1,1,11,34,59). Nếu bạn thử trừ một thời gian lớn từ ngày ở trên, bạn sẽ gặp lỗi "OverflowError: giá trị ngày nằm ngoài phạm vi" vì năm đối với một đối tượng datetime không thể nhỏ hơn 1
pheelicks

2
@pheelicks, đã xong, mặc dù hơi muộn, không chính xác thời gian phản hồi nhanh :-) Vì tôi phải sửa một lỗi khác trong mã của mình, tôi nghĩ rằng tôi đã kết hợp đề xuất của bạn cùng một lúc.
paxdiablo

53

Như những người khác ở đây đã nêu, bạn chỉ có thể sử dụng các đối tượng datetime đầy đủ trong suốt:

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

Tuy nhiên, tôi nghĩ rằng nó đáng để giải thích tại sao các đối tượng datetime đầy đủ được yêu cầu. Hãy xem xét những gì sẽ xảy ra nếu tôi thêm 2 giờ đến 11 giờ tối. Hành vi đúng là gì? Một ngoại lệ, vì bạn không thể có thời gian lớn hơn 11:59 tối? Có nên quấn lại?

Các lập trình viên khác nhau sẽ mong đợi những điều khác nhau, vì vậy, bất kỳ kết quả nào họ chọn sẽ làm nhiều người ngạc nhiên. Tệ hơn nữa, các lập trình viên sẽ viết mã hoạt động tốt khi họ kiểm tra nó ban đầu, và sau đó sẽ phá vỡ nó sau đó bằng cách làm điều gì đó bất ngờ. Điều này rất tệ, đó là lý do tại sao bạn không được phép thêm các đối tượng timedelta vào các đối tượng thời gian.


22

Một điều nhỏ, có thể thêm sự rõ ràng để ghi đè giá trị mặc định trong vài giây

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

4
Tôi thích cái này. Đẹp và rõ ràng với các đối số được chỉ định.
Vigrond

12

Cảm ơn @Pax Diablo, @bvmou và @Arachnid về đề xuất sử dụng toàn bộ dữ liệu trong suốt thời gian. Nếu tôi phải chấp nhận các đối tượng datetime.time từ một nguồn bên ngoài, thì đây dường như là một add_secs_to_time()chức năng thay thế :

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

Mã dài dòng này có thể được nén vào một lớp lót này:

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

nhưng tôi nghĩ rằng tôi muốn kết thúc nó trong một hàm để rõ ràng về mã.


Cảm ơn - ý tưởng tuyệt vời
Anupam

9

Nếu đáng để thêm một tệp / phụ thuộc khác vào dự án của bạn, tôi vừa viết một lớp nhỏ xíu mở rộng datetime.timevới khả năng làm số học. Khi bạn đi qua nửa đêm, nó kết thúc bằng không. Bây giờ, "Sẽ là mấy giờ, 24 giờ kể từ bây giờ" có rất nhiều trường hợp góc, bao gồm thời gian tiết kiệm ánh sáng ban ngày, giây nhuận, thay đổi múi giờ lịch sử, v.v. Nhưng đôi khi bạn thực sự cần trường hợp đơn giản, và đó là những gì sẽ làm.

Ví dụ của bạn sẽ được viết:

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

nptimekế thừa từ datetime.time, vì vậy bất kỳ phương pháp nào cũng có thể sử dụng được.

Nó có sẵn từ PyPi dưới dạng nptime("thời gian phi phạm vi") hoặc trên GitHub: https://github.com/tgs/nptime


6

Bạn không thể chỉ cần thêm số vào datetimevì không rõ đơn vị nào được sử dụng: giây, giờ, tuần ...

timedeltalớp cho các thao tác với ngày và thời gian. datetimetrừ đi datetimecho timedelta, datetimecộng với timedeltacho datetime, hai datetimeđối tượng không thể được thêm vào mặc dù hai timedeltacó thể.

Tạo timedeltađối tượng với bao nhiêu giây bạn muốn thêm và thêm nó vào datetimeđối tượng:

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

Có cùng một khái niệm trong C ++ : std::chrono::duration.


4
xin vui lòng LUÔN thêm lời giải thích, người mới có thể không biết bạn đang làm gì ở đây.
Gerhard Barnard

3

Để hoàn thiện hơn, đây là cách thực hiện arrow(ngày và giờ tốt hơn cho Python):

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

1

Hãy thử thêm một datetime.datetimevào a datetime.timedelta. Nếu bạn chỉ muốn phần thời gian, bạn có thể gọi time()phương thức trên datetime.datetimeđối tượng kết quả để lấy nó.


0

Câu hỏi cũ, nhưng tôi nghĩ rằng tôi sẽ sử dụng một hàm xử lý các múi giờ. Các phần chính đang chuyển thuộc tính datetime.timecủa đối tượng tzinfothành kết hợp, và sau đó sử dụng timetz()thay vì time()trên datetime giả kết quả. Câu trả lời này một phần lấy cảm hứng từ các câu trả lời khác ở đây.

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

Và đây là một lớp trường hợp thử nghiệm thực sự đơn giản (sử dụng tích hợp 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()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.