Was ist die Standardmethode zum Hinzufügen von N Sekunden zu datetime.time in Python?

369

Gibt es bei einem datetime.timeWert in Python eine Standardmethode, um eine ganzzahlige Anzahl von Sekunden hinzuzufügen, sodass beispielsweise 11:34:59+ 3 = 11:35:02?

Diese offensichtlichen Ideen funktionieren nicht:

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

Am Ende habe ich Funktionen wie diese geschrieben:

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)

Ich kann mir nicht helfen zu denken, dass mir ein einfacher Weg fehlt, dies zu tun.

verbunden

Paul Stephenson
quelle
verwandtes Python-Problem: datetime.time-Unterstützung für '+' und '-'
jfs

Antworten:

499

Sie können vollständige datetimeVariablen mit timedeltaund verwenden, indem Sie ein Dummy-Datum angeben und dann timenur den Zeitwert abrufen.

Zum Beispiel:

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

ergibt die zwei Werte im Abstand von drei Sekunden:

11:34:59
11:35:02

Sie können sich auch für die besser lesbare entscheiden

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

wenn du so geneigt bist.


Wenn Sie nach einer Funktion suchen, die dies kann, können Sie addSecsFolgendes untersuchen:

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)

Dies gibt aus:

 09:11:55.775695
 09:16:55
paxdiablo
quelle
6
Um OverflowErrors zu vermeiden, würde ich empfehlen, ein anderes Dummy-Datum zu verwenden, z. B. einige Jahre später: datetime (101,1,1,11,34,59). Wenn Sie versuchen, ein großes Zeitdelta vom obigen Datum zu subtrahieren, wird der Fehler "OverflowError: Datumswert außerhalb des Bereichs" angezeigt, da das Jahr für ein Datum / Uhrzeit-Objekt nicht kleiner als 1 sein kann
Pheelicks
2
@pheelicks, fertig, wenn auch etwas spät, nicht gerade agile Antwortzeiten :-) Da ich einen weiteren Fehler in meinem Code beheben musste, dachte ich, ich würde Ihren Vorschlag gleichzeitig einbeziehen.
Paxdiablo
53

Wie andere hier angegeben haben, können Sie nur vollständige Datetime-Objekte verwenden:

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

Ich denke jedoch, es lohnt sich zu erklären, warum vollständige Datetime-Objekte erforderlich sind. Überlegen Sie, was passieren würde, wenn ich 2 Stunden bis 23 Uhr hinzufügen würde. Was ist das richtige Verhalten? Eine Ausnahme, weil Sie keine Zeit haben können, die größer als 23:59 Uhr ist? Sollte es sich wieder umwickeln?

Unterschiedliche Programmierer werden unterschiedliche Dinge erwarten, sodass jedes Ergebnis, das sie ausgewählt haben, viele Menschen überraschen würde. Schlimmer noch, Programmierer schrieben Code, der beim ersten Testen einwandfrei funktionierte, und ließen ihn später brechen, indem sie etwas Unerwartetes taten. Dies ist sehr schlecht, weshalb Sie Zeitobjekten keine Zeitdelta-Objekte hinzufügen dürfen.

Eli Courtwright
quelle
22

Eine kleine Sache könnte Klarheit schaffen, um den Standardwert für Sekunden zu überschreiben

>>> b = a + datetime.timedelta(seconds=3000)
>>> b
datetime.datetime(1, 1, 1, 12, 24, 59)
unmontiert
quelle
4
Ich mag diesen. Schön und klar mit dem angegebenen Argument.
Vigrond
12

Vielen Dank an @Pax Diablo, @bvmou und @Arachnid für den Vorschlag, durchgehend vollständige Datenzeiten zu verwenden. Wenn ich datetime.time-Objekte von einer externen Quelle akzeptieren muss, scheint dies eine alternative add_secs_to_time()Funktion zu sein:

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

Dieser ausführliche Code kann auf diesen Einzeiler komprimiert werden:

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

aber ich denke, ich möchte das sowieso in eine Funktion einwickeln, um den Code klarer zu machen.

Paul Stephenson
quelle
Danke - tolle Idee
Anupam
9

Wenn es sich lohnt, Ihrem Projekt eine weitere Datei / Abhängigkeit hinzuzufügen, habe ich gerade eine kleine Klasse geschrieben, die sich datetime.timemit der Fähigkeit zum Rechnen erweitert. Wenn Sie nach Mitternacht gehen, geht es um Null. Nun, "Wie spät wird es in 24 Stunden sein?" Hat viele Eckfälle, einschließlich Sommerzeit, Schaltsekunden, historische Zeitzonenänderungen und so weiter. Aber manchmal braucht man wirklich den einfachen Fall, und genau das wird es tun.

Ihr Beispiel würde geschrieben werden:

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

nptimeerbt von datetime.time, daher sollte auch jede dieser Methoden verwendbar sein.

Es ist bei PyPi als nptime("nicht pedantische Zeit") oder auf GitHub erhältlich: https://github.com/tgs/nptime

resdsk
quelle
6

Sie können nicht einfach eine Zahl hinzufügen, datetimeda unklar ist, welche Einheit verwendet wird: Sekunden, Stunden, Wochen ...

Es gibt timedeltaKlassen für Manipulationen mit Datum und Uhrzeit. datetimeMinus datetimegibt timedelta, datetimePlus timedeltagibt datetime, zwei datetimeObjekte können nicht hinzugefügt werden, obwohl zwei timedeltakönnen.

Erstellen Sie ein timedeltaObjekt mit der Anzahl der Sekunden, die Sie hinzufügen möchten, und fügen Sie es dem datetimeObjekt hinzu:

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

In C ++ gibt es dasselbe Konzept : std::chrono::duration.

user2387567
quelle
4
Bitte fügen Sie IMMER Erklärungen hinzu, Neulinge haben möglicherweise keine Ahnung, was Sie hier tun.
Gerhard Barnard
3

Der Vollständigkeit halber ist hier die Vorgehensweise arrow(bessere Daten und Zeiten für Python):

sometime = arrow.now()
abitlater = sometime.shift(seconds=3)
Bart Van Loon
quelle
1

Versuchen Sie, a datetime.datetimezu a hinzuzufügen datetime.timedelta. Wenn Sie nur den Zeitteil möchten, können Sie die time()Methode für das resultierende datetime.datetimeObjekt aufrufen , um ihn abzurufen.

Nick Johnson
quelle
0

Alte Frage, aber ich dachte, ich würde eine Funktion einbauen, die Zeitzonen behandelt. Die Schlüsselteile übergeben das Attribut des datetime.timeObjekts an den tzinfoMähdrescher und verwenden dann timetz()anstelle time()der resultierenden Dummy-Datumszeit. Diese Antwort wurde teilweise von den anderen Antworten hier inspiriert.

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

Und hier ist eine wirklich einfache Testfallklasse (mit integriertem 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()
blthayer
quelle