如何从一个简单的字符串构造一个timedelta对象

问题:如何从一个简单的字符串构造一个timedelta对象

我正在编写一个需要将timedelta输入作为字符串传递的函数。用户必须输入诸如“ 32m”或“ 2h32m”,甚至是“ 4:13”或“ 5hr34m56s”之类的东西…是否存在已经实现了这种东西的图书馆或东西?

I’m writing a function that needs a timedelta input to be passed in as a string. The user must enter something like “32m” or “2h32m”, or even “4:13” or “5hr34m56s”… Is there a library or something that has this sort of thing already implemented?


回答 0

对于第一种格式(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)
>>> 

For the first format(5hr34m56s), you should parse using regular expressions

Here is re-based solution:

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

回答 1

对我来说,最优雅的解决方案是使用datetime强大的字符串解析方法,而不必诉诸dateutil等外部库或手动解析输入。strptime

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

To me the most elegant solution, without having to resort to external libraries such as dateutil or manually parsing the input, is to use datetime’s powerful strptime string parsing method.

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)

After this you can use your timedelta object as normally, convert it to seconds to make sure we did the correct thing etc.

print(delta)
assert(5*60*60+20*60+25 == delta.total_seconds())

回答 2

昨天我花了点时间,所以我将@virhilo答案开发到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

I had a bit of time on my hands yesterday, so I developed @virhilo‘s answer into a Python module, adding a few more time expression formats, including all those requested by @priestc.

Source code is on github (MIT License) for anybody that wants it. It’s also on PyPI:

pip install pytimeparse

Returns the time as a number of seconds:

>>> from pytimeparse.timeparse import timeparse
>>> timeparse('32m')
1920
>>> timeparse('2h32m')
9120
>>> timeparse('4:13')
253
>>> timeparse('5hr34m56s')
20096
>>> timeparse('1.2 minutes')
72

回答 3

我只想输入一个时间,然后将其添加到各个日期,所以这对我有用:

from datetime import datetime as dtt

time_only = dtt.strptime('15:30', "%H:%M") - dtt.strptime("00:00", "%H:%M")

I wanted to input just a time and then add it to various dates so this worked for me:

from datetime import datetime as dtt

time_only = dtt.strptime('15:30', "%H:%M") - dtt.strptime("00:00", "%H:%M")

回答 4

我通过一些升级修改了virhilo的不错答案

  • 添加断言该字符串是有效的时间字符串
  • 用“ h”代替“ hr”小时指示器
  • 允许使用“ d”-天指示器
  • 允许非整数时间(例如3m0.25s3分钟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)

I’ve modified virhilo’s nice answer with a few upgrades:

  • added a assertion that the string is a valid time string
  • replace the “hr” hour-indicator with “h”
  • allow for a “d” – days indicator
  • allow non-integer times (e.g. 3m0.25s is 3 minutes, 0.25 seconds)

.

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)

回答 5

如果您使用Python 3,那么以下是Hari Shankar解决方案的更新版本,我使用了它:

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)

If you use Python 3 then here’s updated version for Hari Shankar’s solution, which I used:

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)

回答 6

Django带有实用程序功能parse_duration()。从文档中

解析字符串并返回datetime.timedelta

期望数据"DD HH:MM:SS.uuuuuu"采用ISO 8601 格式或指定的格式(例如P4DT1H15M20S,等同于4 1:15:20)或PostgreSQL的白天间隔格式(例如3 days 04:05:06)指定的格式。

Django comes with the utility function parse_duration(). From the documentation:

Parses a string and returns a datetime.timedelta.

Expects data in the format "DD HH:MM:SS.uuuuuu" or as specified by ISO 8601 (e.g. P4DT1H15M20S which is equivalent to 4 1:15:20) or PostgreSQL’s day-time interval format (e.g. 3 days 04:05:06).


回答 7

使用isodate库解析ISO 8601持续时间字符串。例如:

isodate.parse_duration('PT1H5M26S')

另请参阅是否有一种简单的方法可以将ISO 8601持续时间转换为timedelta?

Use isodate library to parse ISO 8601 duration string. For example:

isodate.parse_duration('PT1H5M26S')

Also see Is there an easy way to convert ISO 8601 duration to timedelta?