Basit bir dizeden timedelta nesnesi nasıl oluşturulur


100

Bir timedelta girdisinin dizge olarak aktarılması gereken bir işlev yazıyorum. Kullanıcı "32m" veya "2h32m", hatta "4:13" veya "5hr34m56s" gibi bir şey girmelidir ... Bu tür bir şeyin zaten uygulanmış olduğu bir kütüphane veya başka bir şey var mı?


İnsanlar için sadece bir timedelta nesnesini oluşturmak isteyen dgünler, hsaatler, mdakika ve s(aktardıktan sonra bir satır kullanarak saniyeler datetime): datetime.timedelta(days = d, hours = h, minutes=m, seconds=s).
zthomas.nc

Yanıtlar:


74

İlk biçim için (5 saat 34 dakika 56s), normal ifadeler kullanarak ayrıştırmalısınız

İşte yeniden tabanlı çözüm:

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
Ona attığınız her şeyi alabilecek ve yine de timedelta'ya dönüştürmeyi başarabilecek bir tür işlev düşünüyordum.
priestc

2
Yeniden tabanlı çözüm örneği ekledim :)
virhilo

4
Dateutil.parser.parse'ın süreleri nasıl ayrıştırdığını anlamıyorum, her zaman bir datetime döndürüyor gibi görünüyor. Neyi kaçırıyorum?
Nickolay

7
dateutil.parser.parsetimedeltanesneleri ayrıştırmaz . A döndürür datetimeve gibi dizeler için bir istisnayı tetikler '28:32:11.10'.
Spak

99

Bana göre en zarif çözüm, dateutil gibi harici kitaplıklara başvurmak zorunda kalmadan veya girdiyi manuel olarak ayrıştırmak zorunda kalmadan , datetime'ın güçlü strptimedize ayrıştırma yöntemini kullanmaktır.

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)

Bundan sonra timedelta nesnenizi normal şekilde kullanabilir, doğru şeyi yaptığımızdan emin olmak için saniyeye dönüştürebilirsiniz.

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

33
Bu yaklaşımın yalnızca zaman aralığı 24 saatten azsa ( datetime.strptime("32:20:25","%H:%M:%S")işe yaramazsa) işe yaradığını ve giriş biçimini tam olarak bilmeniz gerektiğini unutmayın.
verdesmarald

Bu aynı zamanda sadece OP'nin sorusuna cevap verir. Fonksiyonun birden fazla formatla uğraşması gerekiyorsa - yine de ek format incelemesine ihtiyacınız var (1 kolon veya 2?).
Danny Zımba

3
@verdesmarald Öyleyse, python 3.5 itibariyle, dış kitaplıklar kullanmadan ve zaman aralığının 24 saatten az olduğunu varsaymadan zarif bir çözüm var mı?
en fazla

1
Parametre için adlandırılmış parametreleri manuel olarak belirtme ihtiyacını timedeltaoldukça sinir bozucu buluyorum , ancak bunu önlemek için bulabileceğim en iyi şey:, delta = t - datetime.combine(t.date(), time.min)ki bu ... korkunç.
Kyle Strand

2
Bu yaklaşımla ilgili ciddi bir sorun, günler eklerseniz, strptime içine% d göndermenin, bir tarih için yalnızca> = 1 günleri geçerli olacağından, 0 gününü girmenize olanak vermemesidir.
user1581390

77

Ben geliştirilen bu yüzden, dün elime biraz zaman vardı @virhilo 'ın cevabını talep ettiği tüm olanlar dahil birkaç kez daha ifade biçimlerini, ekleme, bir Python modülü içine @priestc .

Kaynak kodu, isteyen herkes için github'da (MIT Lisansı). Ayrıca PyPI'de:

pip install pytimeparse

Zamanı saniye sayısı olarak döndürür:

>>> 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 eşdeğeri var mı?
luca.giovagnoli

Harika! Çok teşekkürler
Bouncner

@ luca.giovagnoli Scala'da Süre sınıfını kullanabilirsiniz. Süre '15 saniye ',' 4 dakika 'vb. Dizelerden oluşturulabilir
Konrad Malik

14

Sadece bir saat girmek ve ardından bunu çeşitli tarihlere eklemek istedim, bu yüzden bu benim için çalıştı:

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)ayrıca çalışır ...
576i

8

Ben modifiye ettik virhilo en güzel cevabı birkaç yükseltmeleri:

  • dizenin geçerli bir zaman dizesi olduğuna dair bir iddia ekledi
  • "hr" saat göstergesini "h" ile değiştirin
  • "d" gün göstergesine izin verin
  • tamsayı olmayan zamanlara izin ver (ör 3m0.25s. 3 dakika, 0.25 saniye)

.

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
Harika! Ben de "1d 3h 5m" olanak sağlayacak unsurları arasındaki "*" eklendi
Marcel Waldvogel

@MarcelWaldvogel güzel, eğer yeni normal ifadenin metnini kopyalarsanız cevabınızı ekleyeceğim
Peter

@virhilo ve Peter: Kodunuzla ilgili ufak gelişimim burada: github.com/zeitgitter/zeitgitterd/blob/master/zeitgitter/… . Kodunuzu kullanmanın sorun olmadığını varsayıyorum. Lisans için herhangi bir tercihiniz var mı? MIT, Apache, GPL,…?
Marcel Waldvogel

1
Marcel, dava açabilmem için bana adresini gönderebilir misin? JK devam edin, herhangi bir lisans iyidir.
Peter

İşte yeni Regex; aradaki fark "*" s: regex = re.compile (r '^ ((? P <days> [\. \ d] +?) d)? *' r '((? P <saat> [\ . \ d] +?) h)? * 'r' ((? P <dakika> [\. \ d] +?) m)? * 'r' ((? P <saniye> [\. \ d] +?) s)? $ ')
Marcel Waldvogel

3

Django, yardımcı program işlevi ile birlikte gelir parse_duration(). Gönderen belgeler :

Bir dizeyi çözümler ve bir döndürür datetime.timedelta.

Biçiminde beklediği veri "DD HH:MM:SS.uuuuuu"(örneğin ISO 8601 tarafından belirtilen ya da P4DT1H15M20Seşdeğerdir 4 1:15:20ya da PostgreSQL'in günlük zaman aralığı bir biçimde (örneğin) 3 days 04:05:06).


Daha fazla bilgi için: Django'nun parse_duration()işlevi, başlık altında normal ifade eşleşmesini kullanır.
Eido95

2

Python 3 kullanıyorsanız, işte Hari Shankar'ın çözümü için benim kullandığım güncellenmiş sürümü:

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)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.