So erstellen Sie ein Timedelta-Objekt aus einer einfachen Zeichenfolge

96

Ich schreibe eine Funktion, die eine Timedelta-Eingabe benötigt, um als Zeichenfolge übergeben zu werden. Der Benutzer muss so etwas wie "32m" oder "2h32m" oder sogar "4:13" oder "5hr34m56s" eingeben ... Gibt es eine Bibliothek oder etwas, in dem so etwas bereits implementiert ist?

Priester
quelle
Für Personen, die nur ein Zeitdelta-Objekt aus dTagen, hStunden, mMinuten und sSekunden mit einer Zeile (nach dem Import datetime) erstellen möchten : datetime.timedelta(days = d, hours = h, minutes=m, seconds=s).
zthomas.nc

Antworten:

72

Für das erste Format (5hr34m56s) sollten Sie mit regulären Ausdrücken analysieren

Hier ist eine neu basierte Lösung:

import re
from datetime import timedelta


regex = re.compile(r'((?P<hours>\d+?)hr)?((?P<minutes>\d+?)m)?((?P<seconds>\d+?)s)?')


def parse_time(time_str):
    parts = regex.match(time_str)
    if not parts:
        return
    parts = parts.groupdict()
    time_params = {}
    for (name, param) in parts.iteritems():
        if param:
            time_params[name] = int(param)
    return timedelta(**time_params)


>>> from parse_time import parse_time
>>> parse_time('12hr')
datetime.timedelta(0, 43200)
>>> parse_time('12hr5m10s')
datetime.timedelta(0, 43510)
>>> parse_time('12hr10s')
datetime.timedelta(0, 43210)
>>> parse_time('10s')
datetime.timedelta(0, 10)
>>> 
virhilo
quelle
4
Ich dachte an eine Funktion, die alles aufnehmen kann, was man darauf wirft, und trotzdem in der Lage ist, in Zeitdelta zu konvertieren.
Priester
2
Ich habe ein re-basiertes Lösungsbeispiel hinzugefügt :)
virhilo
4
Ich sehe nicht, wie dateutil.parser.parse die Dauer analysieren kann. Es scheint, als würde immer eine Datums- / Uhrzeitangabe zurückgegeben. Was vermisse ich?
Nickolay
7
dateutil.parser.parsetimedeltaObjekte werden nicht analysiert . Es gibt a zurück datetimeund würde eine Ausnahme für Zeichenfolgen wie auslösen '28:32:11.10'.
Spak
95

Für mich ist die eleganteste Lösung, ohne auf externe Bibliotheken wie dateutil zurückgreifen oder die Eingabe manuell analysieren zu müssen, die Verwendung der leistungsstarken strptimeString-Parsing-Methode von datetime .

from datetime import datetime, timedelta
# we specify the input and the format...
t = datetime.strptime("05:20:25","%H:%M:%S")
# ...and use datetime's hour, min and sec properties to build a timedelta
delta = timedelta(hours=t.hour, minutes=t.minute, seconds=t.second)

Danach können Sie Ihr timedelta-Objekt wie gewohnt verwenden, es in Sekunden konvertieren, um sicherzustellen, dass wir das Richtige getan haben usw.

print(delta)
assert(5*60*60+20*60+25 == delta.total_seconds())
Metakermit
quelle
33
Beachten Sie, dass dieser Ansatz nur funktioniert, wenn die Zeitspanne weniger als 24 Stunden beträgt ( datetime.strptime("32:20:25","%H:%M:%S")funktioniert nicht) und Sie das genaue Eingabeformat kennen müssen.
Verdesmarald
Dies beantwortet auch nur einen Teil die Frage des OP. Wenn die Funktion mehrere Formate verarbeiten muss, benötigen Sie noch eine zusätzliche Formatprüfung (1 Doppelpunkt oder 2?).
Danny Staple
3
@verdesmarald Gibt es ab Python 3.5 eine elegante Lösung ohne Verwendung externer Bibliotheken und ohne die Annahme, dass die Zeitspanne weniger als 24 Stunden beträgt?
Max
1
Ich finde die Notwendigkeit, die benannten Parameter für den timedeltaParameter manuell anzugeben, ziemlich ärgerlich, aber das Beste, was ich finden kann, um dies zu vermeiden, ist : delta = t - datetime.combine(t.date(), time.min), was ... schrecklich ist.
Kyle Strand
2
Ein ernstes Problem bei diesem Ansatz besteht darin, dass Sie, wenn Sie Tage einschließen und% d in strptime senden, Tag 0 nicht eingeben können, da nur Tage mit> = 1 für ein Datum gültig sind.
user1581390
74

Ich hatte gestern ein bisschen Zeit, also entwickelte ich die Antwort von @virhilo zu einem Python-Modul und fügte ein paar weitere Zeitausdrucksformate hinzu, einschließlich aller von @priestc angeforderten .

Der Quellcode ist auf Github (MIT-Lizenz) für alle, die ihn möchten . Es ist auch auf PyPI:

pip install pytimeparse

Gibt die Zeit in Sekunden zurück:

>>> from pytimeparse.timeparse import timeparse
>>> timeparse('32m')
1920
>>> timeparse('2h32m')
9120
>>> timeparse('4:13')
253
>>> timeparse('5hr34m56s')
20096
>>> timeparse('1.2 minutes')
72
wildwilhelm
quelle
Gibt es ein Java / Scala-Äquivalent?
luca.giovagnoli
Genial! Vielen Dank
Bouncner
@ luca.giovagnoli In Scala können Sie die Dauer-Klasse verwenden. Die Dauer kann aus Saiten wie '15 Sekunden ',' 4 Minuten 'usw. aufgebaut werden
Konrad Malik
14

Ich wollte nur eine Zeit eingeben und sie dann zu verschiedenen Daten hinzufügen, damit dies für mich funktionierte:

from datetime import datetime as dtt

time_only = dtt.strptime('15:30', "%H:%M") - dtt.strptime("00:00", "%H:%M")
kztd
quelle
dtt.strptime(myduration, "%H:%M:%S") - dtt(1900, 1, 1)funktioniert auch ...
576i
8

Ich habe die nette Antwort von virhilo mit ein paar Upgrades geändert :

  • Es wurde die Behauptung hinzugefügt, dass die Zeichenfolge eine gültige Zeitzeichenfolge ist
  • Ersetzen Sie die Stundenanzeige "hr" durch "h".
  • Lassen Sie einen "d" -Tage-Indikator zu
  • Nicht ganzzahlige Zeiten zulassen (z. B. 3m0.25s3 Minuten, 0,25 Sekunden)

.

import re
from datetime import timedelta


regex = re.compile(r'^((?P<days>[\.\d]+?)d)?((?P<hours>[\.\d]+?)h)?((?P<minutes>[\.\d]+?)m)?((?P<seconds>[\.\d]+?)s)?$')


def parse_time(time_str):
    """
    Parse a time string e.g. (2h13m) into a timedelta object.

    Modified from virhilo's answer at https://stackoverflow.com/a/4628148/851699

    :param time_str: A string identifying a duration.  (eg. 2h13m)
    :return datetime.timedelta: A datetime.timedelta object
    """
    parts = regex.match(time_str)
    assert parts is not None, "Could not parse any time information from '{}'.  Examples of valid strings: '8h', '2d8h5m20s', '2m4s'".format(time_str)
    time_params = {name: float(param) for name, param in parts.groupdict().items() if param}
    return timedelta(**time_params)
Peter
quelle
1
Toll! Ich habe "*" zwischen den Elementen hinzugefügt, um auch "1d 3h 5m" zu ermöglichen
Marcel Waldvogel
@MarcelWaldvogel schön, wenn Sie den Text der neuen Regex kopieren, werde ich Ihre Antwort in
Peter
@virhilo und Peter: Meine leichte Weiterentwicklung Ihres Codes ist hier: github.com/zeitgitter/zeitgitterd/blob/master/zeitgitter/… . Ich nehme an, es ist in Ordnung, Ihren Code zu verwenden. Haben Sie Präferenzen für die Lizenz? MIT, Apache, GPL,…?
Marcel Waldvogel
1
Marcel, kannst du mir deine Adresse schicken, damit ich klagen kann? JK mach weiter, jede Lizenz ist in Ordnung.
Peter
Hier ist der neue Regex; der Unterschied ist das "*" s: regex = re.compile (r '^ ((? P <Tage> [\. \ d] +?) d)? *' r '((? P <Stunden> [\ . \ d] +?) h)? * 'r' ((? P <Minuten> [\. \ d] +?) m)? * 'r' ((? P <Sekunden> [\. \ d]) +?) s)? $ ')
Marcel Waldvogel
3

Wenn Sie Python 3 verwenden, ist hier die aktualisierte Version für Hari Shankars Lösung, die ich verwendet habe:

from datetime import timedelta
import re

regex = re.compile(r'(?P<hours>\d+?)/'
                   r'(?P<minutes>\d+?)/'
                   r'(?P<seconds>\d+?)$')

def parse_time(time_str):
    parts = regex.match(time_str)
    if not parts:
        return
    parts = parts.groupdict()
    print(parts)
    time_params = {}
    for name, param in parts.items():
        if param:
            time_params[name] = int(param)
    return timedelta(**time_params)
Alexey Kislitsin
quelle
3

Django kommt mit der Utility-Funktion parse_duration(). Aus der Dokumentation :

Analysiert einen String und gibt a zurück datetime.timedelta.

Erwartet Daten im Format "DD HH:MM:SS.uuuuuu"oder gemäß ISO 8601 (z. B. P4DT1H15M20Sentsprechend 4 1:15:20) oder dem Tageszeitintervallformat von PostgreSQL (z 3 days 04:05:06. B. ).

Don Kirkby
quelle