Qual è il modo standard per aggiungere N secondi a datetime.time in Python?


369

Dato un datetime.timevalore in Python, esiste un modo standard per aggiungere un numero intero di secondi, in modo che 11:34:59+ 3 = 11:35:02, ad esempio?

Queste idee ovvie non funzionano:

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

Alla fine ho scritto funzioni come questa:

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)

Non posso fare a meno di pensare che mi manca un modo più semplice per farlo.

Relazionato


relativo problema di Python: supporto datetime.time per '+' e '-'
jfs

Risposte:


499

È possibile utilizzare le datetimevariabili complete con timedeltae fornendo una data fittizia, quindi utilizzando timesolo per ottenere il valore dell'ora.

Per esempio:

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

risulta nei due valori, distanti tre secondi:

11:34:59
11:35:02

Puoi anche optare per il più leggibile

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

se sei così propenso.


Se stai cercando una funzione in grado di farlo, puoi esaminare usando di addSecsseguito:

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)

Questo produce:

 09:11:55.775695
 09:16:55

6
Per evitare OverflowErrors, consiglierei di utilizzare una data fittizia diversa, ad esempio un paio di anni dopo: datetime (101,1,1,11,34,59). Se provi a sottrarre un grande timedelta dalla data sopra, otterrai un errore "OverflowError: valore data fuori intervallo" poiché l'anno per un oggetto datetime non può essere inferiore a 1
pheelicks

2
@pheelicks, fatto, anche se un po 'in ritardo, tempi di risposta non esattamente agili :-) Dato che dovevo correggere un altro bug nel mio codice, ho pensato di incorporare il tuo suggerimento allo stesso tempo.
paxdiablo,

53

Come altri hanno già detto, puoi semplicemente usare oggetti datetime completi in tutto:

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

Tuttavia, penso che valga la pena spiegare perché sono richiesti gli oggetti datetime completi. Considera cosa accadrebbe se aggiungessi 2 ore alle 23:00. Qual è il comportamento corretto? Un'eccezione, perché non puoi avere un tempo superiore alle 23:59? Dovrebbe tornare indietro?

Programmatori diversi si aspetteranno cose diverse, quindi qualunque risultato abbiano sorpreso molte persone. Peggio ancora, i programmatori scrivevano codice che funzionava bene quando lo avevano testato inizialmente, e poi lo hanno rotto in seguito facendo qualcosa di inaspettato. Questo è molto male, motivo per cui non ti è permesso aggiungere oggetti timedelta agli oggetti tempo.


22

Una piccola cosa, potrebbe aggiungere chiarezza per sovrascrivere il valore predefinito per secondi

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

4
Mi piace questa. Bello e chiaro con l'argomento specificato.
Vigrond,

12

Grazie a @Pax Diablo, @bvmou e @Arachnid per il suggerimento di utilizzare i periodi di dati completi in tutto. Se devo accettare oggetti datetime.time da una fonte esterna, allora questa sembra essere una add_secs_to_time()funzione alternativa :

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

Questo codice dettagliato può essere compresso in questo one-liner:

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

ma penso che vorrei concludere questo in una funzione per chiarezza del codice comunque.


Grazie - ottima idea
Anupam il

9

Se vale la pena aggiungere un altro file / dipendenza al tuo progetto, ho appena scritto una piccola piccola classe che si estende datetime.timecon la capacità di fare l'aritmetica. Quando passi la mezzanotte, si avvolge intorno allo zero. Ora, "A che ora sarà, tra 24 ore" ci sono molti casi angolari, tra cui l'ora legale, i secondi bisestili, i cambiamenti storici del fuso orario e così via. Ma a volte hai davvero bisogno del semplice caso, ed è quello che farà.

Il tuo esempio sarebbe scritto:

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

nptimeeredita da datetime.time, quindi anche uno di questi metodi dovrebbe essere utilizzabile.

È disponibile da PyPi come nptime("tempo non pedante") o su GitHub: https://github.com/tgs/nptime


6

Non puoi semplicemente aggiungere un numero a datetime perché non è chiaro quale unità viene utilizzata: secondi, ore, settimane ...

Esiste una timedeltaclasse per manipolazioni con data e ora. datetimemeno datetimetimedelta, datetimepiù timedeltadatetime, due datetimeoggetti non possono essere aggiunti sebbene duetimedelta possano.

Crea un timedeltaoggetto con quanti secondi vuoi aggiungere e aggiungilo datetimeall'oggetto:

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

C'è stesso concetto in C ++: std::chrono::duration.


4
per favore aggiungi SEMPRE spiegazioni, i nuovi arrivati ​​potrebbero non avere idea di cosa stai facendo qui.
Gerhard Barnard,

3

Per completezza, ecco il modo di farlo arrow(date e orari migliori per Python):

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

1

Prova ad aggiungere datetime.datetimea a datetime.timedelta. Se si desidera solo la parte temporale, è possibile chiamare il time()metodo datetime.datetimesull'oggetto risultante per ottenerlo.


0

Vecchia domanda, ma ho pensato di lanciare una funzione che gestisce i fusi orari. Le parti chiave stanno passando l' attributo datetime.timedell'oggetto tzinfoin mietitrebbia e quindi utilizzano timetz()anziché time()il datetime fittizio risultante. Questa risposta è in parte ispirata dalle altre risposte qui.

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

Ed ecco una classe di test case davvero semplice (usando il built-in 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()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.