Как построить объект timedelta из простой строки

96

Я пишу функцию, которой требуется ввод timedelta в виде строки. Пользователь должен ввести что-то вроде "32m" или "2h32m", или даже "4:13" или "5hr34m56s" ... Есть ли библиотека или что-то подобное, что уже реализовано?

священник
источник
Для людей , просто глядя построить timedelta объект dдней, hчасов, mминут и sсекунд , используя одну строку (после импорта datetime): datetime.timedelta(days = d, hours = h, minutes=m, seconds=s).
zthomas.nc

Ответы:

72

Для первого формата (5hr34m56s) вы должны анализировать с помощью регулярных выражений

Вот новое решение:

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
Я думал о какой-то функции, которая могла бы принять все, что вы ей бросили, и при этом иметь возможность обрабатывать преобразование в timedelta.
priestc
2
Я добавил пример решения на основе re :)
virhilo
4
Я не понимаю, как dateutil.parser.parse может анализировать продолжительность, похоже, что он всегда возвращает datetime. Что мне не хватает?
Николай
7
dateutil.parser.parseне будет анализировать timedeltaобъекты. Он возвращает a datetimeи вызывает исключение для таких строк, как '28:32:11.10'.
Spak
95

Для меня наиболее элегантным решением без использования внешних библиотек, таких как dateutil или ручного синтаксического анализа ввода, является использование мощного метода синтаксического анализа строк datetimestrptime .

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)

После этого вы можете использовать свой объект timedelta как обычно, преобразовать его в секунды, чтобы убедиться, что мы все сделали правильно и т. Д.

print(delta)
assert(5*60*60+20*60+25 == delta.total_seconds())
метакермит
источник
33
Обратите внимание, что этот подход работает только в том случае, если временной интервал составляет менее 24 часов ( datetime.strptime("32:20:25","%H:%M:%S")не работает), и вы должны знать точный формат ввода.
Verdesmarald 02
Это также только часть ответа на вопрос ОП. Если функции необходимо иметь дело с несколькими форматами, вам все равно потребуется дополнительная проверка формата (1 двоеточие или 2?).
Дэнни Стейпл
3
@verdesmarald Итак, начиная с python 3.5, есть ли элегантное решение без использования внешних библиотек и без предположения, что временной интервал составляет менее 24 часов?
max
1
Я считаю, что необходимость вручную указывать именованные параметры для timedeltaпараметра довольно раздражает, но лучшее, что я могу придумать, чтобы избежать этого, - это:, delta = t - datetime.combine(t.date(), time.min)что ... ужасно.
Kyle Strand
2
Серьезная проблема с этим подходом заключается в том, что если вы включите дни, а затем отправите% d в strptime, вы не сможете ввести день 0, поскольку для даты действительны только дни> = 1.
user1581390
75

У меня было немного времени на моих руках вчера, поэтому я разработал @virhilo «s ответ в модуль Python, добавив несколько времени более форматов выражений, в том числе все те , по просьбе @priestc .

Исходный код находится на github (лицензия MIT) для всех, кто этого хочет. Это также на PyPI:

pip install pytimeparse

Возвращает время в секундах:

>>> from pytimeparse.timeparse import timeparse
>>> timeparse('32m')
1920
>>> timeparse('2h32m')
9120
>>> timeparse('4:13')
253
>>> timeparse('5hr34m56s')
20096
>>> timeparse('1.2 minutes')
72
Wildwilhelm
источник
есть ли эквивалент Java / Scala?
luca.giovagnoli
Потрясающие! Большое спасибо
Bouncner
@ luca.giovagnoli В Scala вы можете использовать класс Duration. Продолжительность может быть построена из таких строк, как «15 секунд», «4 минуты» и т. Д.
Конрад Малик
14

Я хотел ввести только время, а затем добавить его к различным датам, чтобы у меня это сработало:

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)тоже работает ...
576i
8

Я изменил хороший ответ Вирхило, добавив несколько обновлений:

  • добавлено утверждение, что строка является допустимой строкой времени
  • замените часовой индикатор "hr" на "h"
  • включить индикатор "d" дней
  • разрешить нецелое время (например 3m0.25s, 3 минуты 0,25 секунды)

.

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
Большой! Я добавил "*" между элементами, чтобы также было "1d 3h 5m"
Марсель Вальдфогель
@MarcelWaldvogel, хорошо, если вы скопируете текст нового регулярного выражения, я добавлю ваш ответ
Питер
@virhilo и Питер: Моя небольшая эволюция вашего кода здесь: github.com/zeitgitter/zeitgitterd/blob/master/zeitgitter/… . Я полагаю, можно использовать ваш код. Есть ли у вас какие-либо предпочтения по лицензии? MIT, Apache, GPL,…?
Марсель Вальдфогель
1
Марсель, можешь прислать мне свой адрес, чтобы я мог подать в суд? JK продолжай, любая лицензия в порядке.
Питер
Вот новое регулярное выражение; разница в "*" s: regex = re.compile (r '^ ((? P <days> [\. \ d] +?) d)? *' r '((? P <hours> [\ . \ d] +?) h)? * 'r' ((? P <минуты> [\. \ d] +?) m)? * 'r' ((? P <seconds> [\. \ d] +?) s)? $ ')
Марсель Вальдфогель
4

Django имеет служебную функцию parse_duration(). Из документации :

Разбирает строку и возвращает datetime.timedelta.

Ожидает данные в формате "DD HH:MM:SS.uuuuuu"или в соответствии с ISO 8601 (например, P4DT1H15M20Sкоторый эквивалентен 4 1:15:20) или в формате дневного интервала PostgreSQL (например 3 days 04:05:06).

Дон Киркби
источник
Для получения дополнительной информации: parse_duration()функция Django использует регулярное выражение под капотом.
Eido95
3

Если вы используете Python 3, то вот обновленная версия решения Хари Шанкара, которое я использовал:

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)
Алексей Кислицин
источник