Quelle est la méthode standard pour ajouter N secondes à datetime.time en Python?

370

Étant donné une datetime.timevaleur en Python, existe-t-il un moyen standard d'y ajouter un nombre entier de secondes, de sorte que 11:34:59+ 3 = 11:35:02, par exemple?

Ces idées évidentes ne fonctionnent pas:

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

Au final, j'ai écrit des fonctions comme celle-ci:

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)

Je ne peux pas m'empêcher de penser que je manque un moyen plus facile de le faire.

en relation

Paul Stephenson
la source

Réponses:

500

Vous pouvez utiliser des datetimevariables complètes avec timedeltaet en fournissant une date fictive, puis en utilisant timesimplement pour obtenir la valeur de l'heure.

Par exemple:

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

donne les deux valeurs, à trois secondes d'intervalle:

11:34:59
11:35:02

Vous pouvez également opter pour le plus lisible

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

si vous êtes si enclin.


Si vous recherchez une fonction qui peut le faire, vous pouvez examiner l'utilisation addSecsci-dessous:

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)

Cela produit:

 09:11:55.775695
 09:16:55
paxdiablo
la source
6
Pour éviter OverflowErrors, je recommanderais d'utiliser une date fictive différente, par exemple quelques années plus tard: datetime (101,1,1,11,34,59). Si vous essayez de soustraire un grand timedelta de la date ci-dessus, vous obtiendrez une erreur "OverflowError: date value out of range" car l'année pour un objet datetime ne peut pas être inférieure à 1
pheelicks
2
@pheelicks, c'est fait, quoique un peu tard, pas exactement des temps de réponse agiles :-) Comme je devais corriger un autre bug dans mon code, j'ai pensé incorporer votre suggestion en même temps.
paxdiablo
53

Comme d'autres l'ont indiqué ici, vous pouvez simplement utiliser des objets datetime complets tout au long:

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

Cependant, je pense qu'il vaut la peine d'expliquer pourquoi des objets datetime complets sont requis. Considérez ce qui se passerait si j'ajoutais 2 heures à 23 heures. Quel est le bon comportement? Une exception, car vous ne pouvez pas avoir une heure supérieure à 23h59? Doit-il s'enrouler autour?

Différents programmeurs s'attendent à des choses différentes, donc quel que soit le résultat choisi, il surprendra beaucoup de gens. Pire encore, les programmeurs écriraient du code qui fonctionnait très bien lorsqu'ils le testaient initialement, puis le cassaient plus tard en faisant quelque chose d'inattendu. C'est très mauvais, c'est pourquoi vous n'êtes pas autorisé à ajouter des objets timedelta aux objets temporels.

Eli Courtwright
la source
22

Une petite chose, pourrait ajouter de la clarté pour remplacer la valeur par défaut pendant quelques secondes

>>> b = a + datetime.timedelta(seconds=3000)
>>> b
datetime.datetime(1, 1, 1, 12, 24, 59)
démonté
la source
4
J'aime celui la. Agréable et clair avec l'argument spécifié.
Vigrond
12

Merci à @Pax Diablo, @bvmou et @Arachnid pour la suggestion d'utiliser des heures de données complètes tout au long. Si je dois accepter des objets datetime.time d'une source externe, cela semble être une add_secs_to_time()fonction alternative :

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

Ce code prolixe peut être compressé dans ce one-liner:

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

mais je pense que je voudrais quand même envelopper dans une fonction pour la clarté du code.

Paul Stephenson
la source
Merci - bonne idée
Anupam
9

Si cela vaut la peine d'ajouter un autre fichier / dépendance à votre projet, je viens d'écrire une toute petite classe qui s'étend datetime.timeavec la possibilité de faire de l'arithmétique. Lorsque vous dépassez minuit, il s'enroule autour de zéro. Désormais, «Quelle heure sera-t-il dans 24 heures?» Contient de nombreux cas d'angle, y compris l'heure d'été, les secondes intercalaires, les changements de fuseau horaire historiques, etc. Mais parfois, vous avez vraiment besoin du boîtier simple, et c'est ce que cela fera.

Votre exemple serait écrit:

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

nptimehérite de datetime.time, donc toutes ces méthodes devraient également être utilisables.

Il est disponible sur PyPi en tant que nptime("temps non pédant"), ou sur GitHub: https://github.com/tgs/nptime

rescdsk
la source
6

Vous ne pouvez pas simplement ajouter un nombre datetimecar il n'est pas clair à quelle unité est utilisée: secondes, heures, semaines ...

Il y a une timedeltaclasse pour les manipulations avec la date et l'heure. datetimemoins datetimedonne timedelta, datetimeplus timedeltadonne datetime, deux datetimeobjets ne peuvent pas être ajoutés bien que deux le timedeltapuissent.

Créez un timedeltaobjet avec combien de secondes vous voulez ajouter et ajoutez-le à l' datetimeobjet:

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

Il y a même concept en C ++: std::chrono::duration.

user2387567
la source
4
veuillez TOUJOURS ajouter des explications, les nouveaux arrivants pourraient ne pas avoir la moindre idée de ce que vous faites ici.
Gerhard Barnard
3

Par souci d'exhaustivité, voici la façon de le faire avec arrow(meilleures dates et heures pour Python):

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

Essayez d'ajouter un datetime.datetimeà un datetime.timedelta. Si vous ne voulez que la portion de temps, vous pouvez appeler la time()méthode sur l' datetime.datetimeobjet résultant pour l'obtenir.

Nick Johnson
la source
0

Vieille question, mais je pensais que je mettrais dans une fonction qui gère les fuseaux horaires. Les éléments clés passent l' attribut de l' datetime.timeobjet tzinfodans la moissonneuse-batteuse, puis les utilisent à la timetz()place de time()la date / heure fictive résultante. Cette réponse s'inspire en partie des autres réponses ici.

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

Et voici une classe de cas de test vraiment simple (en utilisant intégrée 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
la source