web-dev-qa-db-fra.com

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

Étant donné une valeur de datetime.time en Python, existe-t-il une méthode standard pour lui ajouter un nombre entier de secondes, afin 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'

Finalement, 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 qu'il me manque un moyen plus facile de faire cela.

Apparenté, relié, connexe

315
Paul Stephenson

Vous pouvez utiliser des variables datetime complètes avec timedelta et en fournissant une date fictive, puis en utilisant time pour obtenir simplement 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 capable de le faire, vous pouvez utiliser addSecs ci-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 génère:

 09:11:55.775695
 09:16:55
445
paxdiablo

Comme d'autres l'ont dit, vous pouvez simplement utiliser des objets datetime complets:

sometime = get_some_time() # the time to which you want to add 3 seconds
later = (datetime.combine(date.today(), sometime) + timedelta(seconds=3)).time()

Cependant, je pense qu'il est utile d'expliquer pourquoi des objets datetime complets sont requis. Considérez ce qui arriverait si j'ajoutais 2 heures à 23 heures. Quel est le comportement correct? Une exception, parce que vous ne pouvez pas avoir une heure supérieure à 23h59? Est-ce que ça devrait revenir?

Différents programmeurs s'attendent à des choses différentes, alors quel que soit le résultat choisi, cela surprendrait beaucoup de monde. Pire encore, les programmeurs écrivaient du code qui fonctionnait parfaitement lorsqu’ils l’avaient initialement testé, puis le cassaient plus tard en faisant quelque chose d’inattendu. C'est très grave, c'est pourquoi vous n'êtes pas autorisé à ajouter des objets timedelta aux objets de temps.

45
Eli Courtwright

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

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

Merci à @Pax Diablo, @bvmou et @Arachnid pour leur suggestion d'utiliser des dates-heures complètes. Si je dois accepter des objets datetime.time d'une source externe, cela semble être une autre fonction add_secs_to_time():

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 cette ligne unique:

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

mais je pense que je voudrais de toute façon résumer cela dans une fonction visant à clarifier le code.

11
Paul Stephenson

Si cela vaut la peine d'ajouter un autre fichier/dépendance à votre projet, je viens d'écrire une toute petite classe qui étend datetime.time avec la possibilité de faire de l'arithmétique. Lorsque vous passez minuit, il tourne à zéro. Désormais, "Quelle heure sera-t-il dans 24 heures" comporte de nombreux cas délicats, notamment l'heure avancée, les secondes intercalaires, les modifications du fuseau horaire historique, etc. Mais parfois, vous avez vraiment besoin du cas simple, et c'est ce que cela va faire.

Votre exemple serait écrit:

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

nptime hérite de datetime.time, donc aucune de ces méthodes ne devrait être utilisable également.

Il est disponible sur PyPi sous la forme nptime ("heure non pédante"), ou sur GitHub: https://github.com/tgs/nptime

8
rescdsk

Vous ne pouvez pas simplement ajouter un numéro à datetime car il est difficile de savoir quelle unité est utilisée: secondes, heures, semaines ...

Il y a la classe timedelta pour les manipulations avec la date et l'heure. datetime moins datetime donne timedelta, datetime plus timedelta donne datetime, deux datetime objets ne peuvent pas être ajoutés bien que deux timedelta can.

Créez l'objet timedelta avec le nombre de secondes que vous souhaitez ajouter et ajoutez-le à l'objet datetime:

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

Il existe le même concept en C++: std::chrono::duration.

5
user2387567

Par souci d'exhaustivité, voici comment procéder avec arrow (meilleures dates et heures pour Python):

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

Essayez d’ajouter un datetime.datetime à un datetime.timedelta. Si vous voulez seulement la partie heure, vous pouvez appeler la méthode time() sur l'objet résultant datetime.datetime pour l'obtenir.

1
Nick Johnson

Vieille question, mais je pensais que je jetterais une fonction qui gère les fuseaux horaires. Les parties clés passent l'attribut tzinfo de l'objet datetime.time dans la combinaison, puis utilisent timetz() au lieu de time() sur la date/heure fictive résultante. Cette réponse est en partie inspirée 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 très simple (utilisant unittest intégré):

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 __== '__main__':
    unittest.main()
0
blthayer