簡體   English   中英

在 Python 中將 N 秒添加到 datetime.time 的標准方法是什么?

[英]What is the standard way to add N seconds to datetime.time in Python?

給定 Python 中的datetime.time值,是否有標准方法向其添加 integer 秒數,例如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)

我不禁想到我錯過了一種更簡單的方法來做到這一點。

有關的

您可以將完整的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)

如果你願意的話。


如果您正在尋找可以執行此操作的 function,您可以使用下面的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

正如這里的其他人所說,您可以在整個過程中使用完整的日期時間對象:

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

但是,我認為有必要解釋一下為什么需要完整的日期時間對象。 考慮一下如果我在晚上 11 點增加 2 小時會發生什么。 什么是正確的行為? 一個例外,因為你的時間不能超過晚上 11:59? 它應該繞回去嗎?

不同的程序員會有不同的期望,所以無論他們選擇哪個結果都會讓很多人感到驚訝。 更糟糕的是,程序員編寫的代碼在最初測試時工作得很好,然后由於一些意想不到的事情而導致代碼崩潰。 這非常糟糕,這就是為什么不允許將 timedelta 對象添加到時間對象的原因。

一件小事,可能會增加清晰度以覆蓋秒數的默認值

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

感謝@Pax Diablo、@bvmou 和@Arachnid 建議在整個過程中使用完整的日期時間。 如果我必須接受來自外部源的 datetime.time 對象,那么這似乎是一個替代add_secs_to_time() function:

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

這段冗長的代碼可以壓縮成一行代碼:

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

但我想無論如何我都想將其包裝在 function 中以確保代碼清晰。

您不能簡單地將數字添加到datetime時間,因為不清楚使用的是什么單位:秒、小時、周......

timedelta class 用於日期和時間的操作。 datetime減去datetime得到timedeltadatetime加上timedelta得到datetime ,兩個datetime對象不能相加,盡管兩個timedelta可以。

使用要添加的秒數創建timedelta object,並將其添加到datetime時間 object:

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

如果值得向您的項目添加另一個文件/依賴項,我剛剛編寫了一個小的 class,它擴展了datetime.time並具有計算能力。 當你在午夜 go 時,它會繞零。 現在,“What time will it will be, 24 hours from now”有很多corner cases,包括夏令時、閏秒、歷史時區變化等等。 但有時您確實需要簡單的案例,這就是它要做的。

你的例子會寫成:

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

為了完整起見,這里是使用arrow的方法(Python 的更好的日期和時間):

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

在現實世界的環境中,只使用time從來都不是一個好主意,總是使用datetime ,甚至更好utc ,以避免像隔夜、夏令時、用戶和服務器之間的不同時區等沖突。

所以我推薦這種方法:

import datetime as dt

_now = dt.datetime.now()  # or dt.datetime.now(dt.timezone.utc)
_in_5_sec = _now + dt.timedelta(seconds=5)

# get '14:39:57':
_in_5_sec.strftime('%H:%M:%S')

如果您還沒有 timedelta object,另一種可能性是只使用舊時間的屬性初始化一個新時間 object 並在需要的地方添加值:

   new_time:time = time(
       hour=curr_time.hour + n_hours,
       minute=curr_time.minute + n_minutes,
       seconds=curr_time.second + n_seconds
    )

不可否認,這只有在您對您的值做出一些假設時才有效,因為這里沒有處理溢出。 但我只是認為值得記住這一點,因為它可以節省一兩行

嘗試將datetime.datetime添加到datetime.timedelta 如果你只想要時間部分,你可以在結果datetime.datetime object 上調用time()方法來獲取它。

老問題,但我想我會拋出一個 function 來處理時區。 關鍵部分是將datetime.time對象的tzinfo屬性傳遞給 combine,然后在生成的虛擬日期時間上使用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()

這是一個非常簡單的測試用例 class(使用內置的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()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM