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?


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

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

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

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

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)

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)

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


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.