Cara membuat objek timedelta dari string sederhana

96

Saya sedang menulis fungsi yang membutuhkan masukan timedelta untuk diteruskan sebagai string. Pengguna harus memasukkan sesuatu seperti "32m" atau "2h32m", atau bahkan "4:13" atau "5hr34m56s" ... Apakah ada perpustakaan atau sesuatu yang sudah menerapkan hal semacam ini?

pendeta
sumber
Bagi orang-orang hanya ingin membangun objek timedelta dari dhari, hjam, mmenit dan sdetik menggunakan satu baris (setelah mengimpor datetime): datetime.timedelta(days = d, hours = h, minutes=m, seconds=s).
zthomas.nc

Jawaban:

72

Untuk format pertama (5 jam34m56d), Anda harus mengurai menggunakan ekspresi reguler

Berikut adalah solusi berbasis ulang:

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)
>>> 
virhilo.dll
sumber
4
Saya sedang memikirkan beberapa jenis fungsi yang dapat mengambil apa pun yang Anda lemparkan dan masih dapat menangani pengubahan ke timedelta.
pendeta
2
Saya menambahkan contoh solusi berbasis ulang :)
virhilo
4
Saya tidak melihat bagaimana dateutil.parser.parse dapat mengurai durasi, sepertinya itu selalu mengembalikan datetime. Apa yang saya lewatkan?
Nickolay
7
dateutil.parser.parsetidak akan mengurai timedeltaobjek. Ini mengembalikan a datetime, dan itu akan memicu pengecualian untuk string seperti '28:32:11.10'.
Bicaralah
95

Bagi saya, solusi paling elegan, tanpa harus menggunakan pustaka eksternal seperti dateutil atau mengurai input secara manual, adalah dengan menggunakan metode parsing string yang kuat dari 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)

Setelah ini, Anda dapat menggunakan objek timedelta seperti biasa, mengubahnya menjadi detik untuk memastikan kami melakukan hal yang benar, dll.

print(delta)
assert(5*60*60+20*60+25 == delta.total_seconds())
metakermit
sumber
33
Perhatikan bahwa pendekatan ini hanya berfungsi jika rentang waktu kurang dari 24 jam ( datetime.strptime("32:20:25","%H:%M:%S")tidak berfungsi), dan Anda harus mengetahui format input yang tepat.
verdesmarald
Ini juga hanya sebagian menjawab pertanyaan OP. Jika fungsi perlu menangani beberapa format - Anda masih memerlukan pemeriksaan format tambahan (1 titik dua atau 2?).
Danny Staple
3
@verdesmarald Jadi, pada python 3.5, apakah ada solusi elegan tanpa menggunakan pustaka eksternal dan tanpa asumsi rentang waktu kurang dari 24 jam?
maks
1
Saya menemukan kebutuhan untuk secara manual menentukan parameter bernama untuk timedeltaparameter cukup menjengkelkan, tetapi yang terbaik yang bisa saya lakukan untuk menghindari ini adalah:, delta = t - datetime.combine(t.date(), time.min)yang ... mengerikan.
Kyle Strand
2
Masalah serius dengan pendekatan ini adalah jika Anda memasukkan hari kemudian mengirim% d ke strptime, Anda tidak dapat memasukkan hari ke 0, karena hanya hari> = 1 yang valid untuk sebuah tanggal.
pengguna1581390
75

Aku punya sedikit waktu di tangan saya kemarin, jadi saya mengembangkan @virhilo 's jawaban ke dalam modul Python, menambahkan beberapa format ekspresi lebih banyak waktu, termasuk semua yang diminta oleh @priestc .

Kode sumber ada di github (Lisensi MIT) untuk siapa saja yang menginginkannya. Ini juga ada di PyPI:

pip install pytimeparse

Mengembalikan waktu sebagai jumlah detik:

>>> from pytimeparse.timeparse import timeparse
>>> timeparse('32m')
1920
>>> timeparse('2h32m')
9120
>>> timeparse('4:13')
253
>>> timeparse('5hr34m56s')
20096
>>> timeparse('1.2 minutes')
72
wildwilhelm.dll
sumber
apakah ada padanan Java / Scala?
luca.giovagnoli
Hebat! Terima kasih banyak
Bouncner
@ luca.giovagnoli Di Scala Anda dapat menggunakan kelas Durasi. Durasi dapat dibangun dari string seperti '15 detik ',' 4 menit 'dll.
Konrad Malik
14

Saya ingin memasukkan hanya satu waktu dan kemudian menambahkannya ke berbagai tanggal jadi ini berhasil untuk saya:

from datetime import datetime as dtt

time_only = dtt.strptime('15:30', "%H:%M") - dtt.strptime("00:00", "%H:%M")
kztd
sumber
dtt.strptime(myduration, "%H:%M:%S") - dtt(1900, 1, 1)juga bekerja ...
576i
8

Saya telah mengubah jawaban bagus virhilo dengan beberapa peningkatan:

  • menambahkan pernyataan bahwa string tersebut adalah string waktu yang valid
  • ganti indikator jam "jam" dengan "h"
  • memungkinkan untuk indikator "d" - hari
  • memungkinkan waktu non-integer (misalnya 3m0.25s3 menit, 0,25 detik)

.

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)
Peter
sumber
1
Bagus! Saya menambahkan "*" di antara elemen untuk juga memungkinkan "1d 3h 5m"
Marcel Waldvogel
@MarcelWaldvogel bagus, jika Anda menyalin teks regex baru saya akan menambahkan jawaban Anda di
Peter
@virhilo dan Peter: Evolusi kecil saya pada kode Anda ada di sini: github.com/zeitgitter/zeitgitterd/blob/master/zeitgitter/… . Saya kira tidak apa-apa menggunakan kode Anda. Apakah Anda memiliki preferensi untuk lisensi? MIT, Apache, GPL,…?
Marcel Waldvogel
1
Marcel, bisakah kamu mengirimkan alamatmu supaya aku bisa menuntut? JK silakan lisensi apa pun baik-baik saja.
Peter
Inilah Regex baru; perbedaannya adalah "*" s: regex = re.compile (r '^ ((? P <days> [\. \ d] +?) d)? *' r '((? P <hours> [\ . \ d] +?) h)? * 'r' ((? P <minutes> [\. \ d] +?) m)? * 'r' ((? P <seconds> [\. \ d] +?) s)? $ ')
Marcel Waldvogel
4

Django hadir dengan fungsi utilitas parse_duration(). Dari dokumentasi :

Mengurai string dan mengembalikan a datetime.timedelta.

Mengharapkan data dalam format "DD HH:MM:SS.uuuuuu"atau seperti yang ditentukan oleh ISO 8601 (misalnya P4DT1H15M20Syang setara dengan 4 1:15:20) atau format interval waktu hari PostgreSQL (misalnya 3 days 04:05:06).

Don Kirkby
sumber
Untuk informasi lebih lanjut: parse_duration()fungsi Django menggunakan pencocokan regex di bawah tenda.
Eido95
3

Jika Anda menggunakan Python 3 maka inilah versi terbaru untuk solusi Hari Shankar, yang saya gunakan:

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)
Alexey Kislitsin
sumber