Cách tạo một đối tượng hẹn giờ từ một chuỗi đơn giản


96

Tôi đang viết một hàm cần một đầu vào hẹn giờ được chuyển vào dưới dạng một chuỗi. Người dùng phải nhập một cái gì đó như "32m" hoặc "2h32m", hoặc thậm chí "4:13" hoặc "5hr34m56s" ... Có thư viện hoặc cái gì đó đã được triển khai loại này không?


Đối với những người chỉ tìm cách để xây dựng một đối tượng timedelta của dngày, hgiờ, mphút và sgiây sử dụng một dòng (sau khi nhập datetime): datetime.timedelta(days = d, hours = h, minutes=m, seconds=s).
zthomas.nc

Câu trả lời:


72

Đối với định dạng đầu tiên (5hr34m56s), bạn nên phân tích cú pháp bằng cách sử dụng biểu thức chính quy

Đây là giải pháp dựa trên cơ sở lại:

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
Tôi đã nghĩ đến một số loại chức năng có thể lấy bất cứ thứ gì bạn ném vào nó và vẫn có thể xử lý việc chuyển đổi sang hẹn giờ.
Priestc

2
Tôi đã thêm lại dựa dụ giải pháp :)
virhilo

4
Tôi không biết dateutil.parser.parse có thể phân tích thời lượng như thế nào, có vẻ như nó luôn trả về ngày giờ. Tôi đang thiếu gì?
Nickolay

7
dateutil.parser.parsesẽ không phân tích cú pháp timedeltacác đối tượng. Nó trả về một datetimevà nó sẽ kích hoạt một ngoại lệ cho các chuỗi như '28:32:11.10'.
Spak

95

Đối với tôi, giải pháp tốt nhất mà không cần phải sử dụng đến các thư viện bên ngoài như dateutil hoặc phân tích cú pháp đầu vào theo cách thủ công, là sử dụng phương pháp phân tích chuỗi mạnh mẽ của 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)

Sau đó, bạn có thể sử dụng đối tượng hẹn giờ của mình như bình thường, chuyển đổi nó thành giây để đảm bảo chúng tôi đã làm đúng, v.v.

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

33
Lưu ý rằng phương pháp này chỉ hoạt động nếu khoảng thời gian dưới 24 giờ ( datetime.strptime("32:20:25","%H:%M:%S")không hoạt động) và bạn phải biết định dạng đầu vào chính xác.
verdesmarald

Đây cũng chỉ là một phần trả lời câu hỏi của OP. Nếu hàm cần xử lý nhiều định dạng - bạn vẫn cần kiểm tra định dạng bổ sung (1 dấu hai chấm hay 2?).
Danny Staple

3
@verdesmarald Vì vậy, kể từ python 3.5, có giải pháp nào tốt mà không cần sử dụng thư viện bên ngoài và không giả sử thời gian sử dụng là dưới 24 giờ không?
tối đa

1
Tôi thấy nhu cầu chỉ định thủ công các tham số được đặt tên cho timedeltatham số khá khó chịu, nhưng cách tốt nhất tôi có thể nghĩ ra để tránh điều này là delta = t - datetime.combine(t.date(), time.min):, thật ... kinh khủng.
Kyle Strand,

2
Một vấn đề nghiêm trọng với phương pháp này là nếu bạn bao gồm ngày sau đó gửi% d vào strptime, sẽ không cho phép bạn nhập ngày 0, vì chỉ những ngày> = 1 mới hợp lệ cho một ngày.
dùng1581390

75

Tôi đã có một chút thời gian vào ngày hôm qua, vì vậy tôi đã phát triển câu trả lời của @virhilo thành một mô-đun Python, thêm một vài định dạng biểu thức thời gian nữa, bao gồm tất cả những định dạng mà @priestc yêu cầu .

Mã nguồn có trên github (Giấy phép MIT) cho bất kỳ ai muốn nó. Nó cũng có trên PyPI:

pip install pytimeparse

Trả về thời gian dưới dạng một số giây:

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

có tương đương Java / Scala không?
luca.giovagnoli

Tuyệt vời! Cảm ơn rất nhiều
Bouncner

@ luca.giovagnoli Trong Scala, bạn có thể sử dụng lớp Thời lượng. Thời lượng có thể được xây dựng từ các chuỗi như '15 giây', '4 phút', v.v.
Konrad Malik

14

Tôi chỉ muốn nhập một thời gian và sau đó thêm nó vào các ngày khác nhau để điều này phù hợp với tôi:

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)cũng hoạt động ...
576i

8

Tôi đã sửa đổi câu trả lời hay của virhilo với một vài nâng cấp:

  • đã thêm một xác nhận rằng chuỗi là một chuỗi thời gian hợp lệ
  • thay thế chỉ báo giờ "hr" bằng "h"
  • cho phép chỉ báo "d" - ngày
  • cho phép thời gian không phải số nguyên (ví dụ: 3m0.25s3 phút, 0,25 giây)

.

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
Tuyệt quá! Tôi đã thêm "*" giữa các phần tử để cho phép "1ngày 3h 5phút"
Marcel Waldvogel

@MarcelWaldvogel thật tuyệt, nếu bạn sao chép văn bản của regex mới, tôi sẽ thêm câu trả lời của bạn vào
Peter

@virhilo và Peter: Tiến hóa nhỏ của tôi về mã của bạn ở đây: github.com/zeitgitter/zeitgitterd/blob/master/zeitgitter/… . Tôi cho rằng có thể sử dụng mã của bạn. Bạn có sở thích nào cho giấy phép không? MIT, Apache, GPL,…?
Marcel Waldvogel

1
Marcel, bạn có thể gửi cho tôi địa chỉ của bạn để tôi có thể kiện được không? JK đi trước giấy phép nào cũng được.
Peter

Đây là Regex mới; sự khác biệt là dấu "*" s: regex = re.compile (r '^ ((? p <ngày> [\. \ d] +?) d)? *' r '((? p <giờ> [\ . \ d] +?) h)? * 'r' ((? p <phút> [\. \ d] +?) m)? * 'r' ((? p <giây> [\. \ d] +?) s)? $ ')
Marcel Waldvogel

3

Nếu bạn sử dụng Python 3 thì đây là phiên bản cập nhật cho giải pháp của Hari Shankar, mà tôi đã sử dụng:

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 đi kèm với chức năng tiện ích parse_duration(). Từ tài liệu :

Phân tích cú pháp một chuỗi và trả về a datetime.timedelta.

Yêu cầu dữ liệu ở định dạng "DD HH:MM:SS.uuuuuu"hoặc theo chỉ định của ISO 8601 (ví dụ: P4DT1H15M20Stương đương với 4 1:15:20) hoặc định dạng khoảng thời gian trong ngày của PostgreSQL (ví dụ 3 days 04:05:06:).


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.