วิธีสร้างอ็อบเจกต์ timedelta จากสตริงง่ายๆ


103

ฉันกำลังเขียนฟังก์ชันที่ต้องการอินพุตแบบกำหนดเวลาเพื่อส่งผ่านเป็นสตริง ผู้ใช้ต้องป้อนบางอย่างเช่น "32m" หรือ "2h32m" หรือแม้แต่ "4:13" หรือ "5hr34m56s" ... มีห้องสมุดหรือสิ่งที่มีการใช้งานประเภทนี้อยู่แล้วหรือไม่?


สำหรับคนเพียงแค่ต้องการสร้างวัตถุ timedelta ของdวันhชั่วโมงmนาทีและsวินาทีโดยใช้หนึ่งบรรทัด (หลังจากการนำเข้า):datetime datetime.timedelta(days = d, hours = h, minutes=m, seconds=s)
zthomas.nc

คำตอบ:


76

สำหรับรูปแบบแรก (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
ฉันกำลังคิดถึงฟังก์ชันบางอย่างที่สามารถใช้ทุกอย่างที่คุณขว้างใส่มันและยังสามารถจัดการกับการแปลงเป็นเวลาได้
นักบวช

2
ฉันเพิ่มตัวอย่างการแก้ปัญหาตามอีกครั้ง :)
virhilo

4
ฉันไม่เห็นว่า dateutil.parser.parse สามารถแยกวิเคราะห์ระยะเวลาได้อย่างไรดูเหมือนว่าจะส่งคืนวันที่และเวลาเสมอ ฉันขาดอะไรไป?
Nickolay

8
dateutil.parser.parseจะไม่แยกวิเคราะห์timedeltaวัตถุ มันส่งกลับและมันจะก่อให้เกิดข้อยกเว้นสำหรับสตริงเช่นdatetime '28:32:11.10'
Spak

103

สำหรับฉันวิธีแก้ปัญหาที่หรูหราที่สุดโดยไม่ต้องใช้ไลบรารีภายนอกเช่น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)

หลังจากนี้คุณสามารถใช้วัตถุจับเวลาได้ตามปกติแปลงเป็นวินาทีเพื่อให้แน่ใจว่าเราทำสิ่งที่ถูกต้องเป็นต้น

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

33
โปรดทราบว่าวิธีนี้ใช้ได้เฉพาะเมื่อช่วงเวลาน้อยกว่า 24 ชั่วโมง (ใช้datetime.strptime("32:20:25","%H:%M:%S")ไม่ได้) และคุณต้องทราบรูปแบบการป้อนข้อมูลที่แน่นอน
verdesmarald

นี่เป็นเพียงส่วนหนึ่งเท่านั้นที่ตอบคำถามของ OP หากฟังก์ชันต้องจัดการกับหลายรูปแบบ - คุณยังต้องตรวจสอบรูปแบบเพิ่มเติม (1 โคลอนหรือ 2?)
Danny Staple

3
@verdesmarald ดังนั้นใน python 3.5 มีวิธีแก้ปัญหาที่สวยงามโดยไม่ต้องใช้ไลบรารีภายนอกหรือไม่และโดยไม่ต้องสมมติว่าช่วงเวลาน้อยกว่า 24 ชั่วโมง?
สูงสุด

1
ฉันพบว่าจำเป็นต้องระบุพารามิเตอร์ที่ตั้งชื่อด้วยตนเองสำหรับtimedeltaพารามิเตอร์ที่ค่อนข้างน่ารำคาญ แต่สิ่งที่ดีที่สุดที่ฉันสามารถทำได้เพื่อหลีกเลี่ยงสิ่งนี้คือ: delta = t - datetime.combine(t.date(), time.min)ซึ่ง ... แย่มาก
Kyle Strand

2
ปัญหาร้ายแรงของวิธีนี้คือถ้าคุณรวมวันจากนั้นส่ง% d เข้าสู่ strptime จะไม่ช่วยให้คุณป้อนวันที่ 0 ได้เนื่องจากวันที่> = 1 เท่านั้นที่ใช้ได้สำหรับวันที่
user1581390

80

เมื่อวานฉันมีเวลาพอสมควรดังนั้นฉันจึงพัฒนาคำตอบของ@virhiloในโมดูล Python โดยเพิ่มรูปแบบนิพจน์เวลาอีกสองสามรูปแบบรวมถึงรูปแบบที่@priestcร้องขอทั้งหมด

ซอร์สโค้ดอยู่ใน github (MIT License) สำหรับทุกคนที่ต้องการ นอกจากนี้ยังอยู่ใน 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

เทียบเท่า Java / Scala หรือไม่
luca.giovagnoli

สุดยอด! ขอบคุณมาก
Bouncner

1
@ 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

เข้าใจแล้ว ฉันไม่แน่ใจว่า dtt (1900,1,1) จะใช้ได้กับทุกระบบปฏิบัติการที่เป็นไปได้
kztd

8

ฉันได้แก้ไขคำตอบที่ดีของ virhiloด้วยการอัพเกรดเล็กน้อย:

  • เพิ่มการยืนยันว่าสตริงเป็นสตริงเวลาที่ถูกต้อง
  • แทนที่ตัวบ่งชี้ชั่วโมง "hr" ด้วย "h"
  • อนุญาตให้มีตัวบ่งชี้ "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)

1
เยี่ยมมาก! ฉันเพิ่ม "*" ระหว่างองค์ประกอบเพื่อให้ "1d 3h 5m" ด้วย
Marcel Waldvogel

@MarcelWaldvogel ดีถ้าคุณคัดลอกข้อความของ regex ใหม่ฉันจะเพิ่มคำตอบของคุณใน
ปีเตอร์

@virhilo and Peter: วิวัฒนาการเล็กน้อยของฉันเกี่ยวกับรหัสของคุณอยู่ที่นี่: github.com/zeitgitter/zeitgitterd/blob/master/zeitgitter/… . ฉันคิดว่าสามารถใช้รหัสของคุณได้ คุณมีค่ากำหนดสำหรับใบอนุญาตหรือไม่? MIT, Apache, GPL, …?
Marcel Waldvogel

1
Marcel คุณช่วยส่งที่อยู่ของคุณมาให้ฉันเพื่อที่ฉันจะฟ้องได้ไหม JK ไปข้างหน้าใบอนุญาตใด ๆ ก็ดี
ปีเตอร์

นี่คือ Regex ใหม่ ความแตกต่างคือ "*" s: regex = re.compile (r '^ ((? P <days> [\. \ d] +?) d)? *' r '((? p <hours> [\ . \ d] +?) h)? * 'r' ((? P <minutes> [\. \ d] +?) ม.)? * 'r' ((? P <วินาที> [\. \ d] +?) s)? $ ')
Marcel Waldvogel

3

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 ใช้การจับคู่ regex ภายใต้ประทุน
Eido95

2

หากคุณใช้ 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)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.