Форматировать timedelta в строку

Я новичок в Python (2 недели) и у меня проблемы с форматированием объекта datetime.timedelta.

Вот что я пытаюсь сделать: у меня есть список объектов, и один из членов класса объекта является объектом timedelta, который показывает продолжительность события. Я хотел бы отобразить эту продолжительность в формате часов: минут.

Я испробовал множество способов сделать это, и у меня возникли трудности. Мой текущий подход заключается в добавлении методов в класс для моих объектов, которые возвращают часы и минуты. Я могу получить часы, разделив timedelta.seconds на 3600 и округлив его. У меня проблемы с получением оставшихся секунд и преобразованием их в минуты.

Кстати, я использую Google AppEngine с Google AppEngine Django Templates для презентации.

Ответ 1

Спасибо всем за вашу помощь. Я взял много ваших идей и собрал их вместе, дайте мне знать, что вы думаете.

Я добавил два метода в класс следующим образом:

def hours(self):
    retval = ""
    if self.totalTime:
        hoursfloat = self.totalTime.seconds / 3600
        retval = round(hoursfloat)
    return retval

def minutes(self):
    retval = ""
    if self.totalTime:
        minutesfloat = self.totalTime.seconds / 60
        hoursAsMinutes = self.hours() * 60
        retval = round(minutesfloat - hoursAsMinutes)
    return retval

В моем django я использовал это (сумма - это объект, и он находится в словаре):

<td>{{ sum.0 }}</td>    
<td>{{ sum.1.hours|stringformat:"d" }}:{{ sum.1.minutes|stringformat:"#02.0d" }}</td>

Ответ 2

Вы можете просто преобразовать timedelta в строку с помощью str(). Вот пример:

import datetime
start = datetime.datetime(2009,2,10,14,00)
end   = datetime.datetime(2009,2,10,16,00)
delta = end-start
print(str(delta))
# prints 2:00:00

Ответ 3

Как вы знаете, вы можете получить total_seconds из объекта timedelta, обратившись к .seconds.

Python предоставляет встроенную функцию divmod() которая позволяет:

s = 13420
hours, remainder = divmod(s, 3600)
minutes, seconds = divmod(remainder, 60)
print '{:02}:{:02}:{:02}'.format(int(hours), int(minutes), int(seconds))
# result: 03:43:40

или вы можете преобразовать в часы и остаток, используя комбинацию по модулю и вычитанию:

# arbitrary number of seconds
s = 13420
# hours
hours = s // 3600 
# remaining seconds
s = s - (hours * 3600)
# minutes
minutes = s // 60
# remaining seconds
seconds = s - (minutes * 60)
# total time
print '{:02}:{:02}:{:02}'.format(int(hours), int(minutes), int(seconds))
# result: 03:43:40

Ответ 4

>>> str(datetime.timedelta(hours=10.56))
10:33:36

>>> td = datetime.timedelta(hours=10.505) # any timedelta object
>>> ':'.join(str(td).split(':')[:2])
10:30

Передача объекта timedelta функции str() вызывает тот же самый используемый код форматирования, если мы просто наберем print td. Поскольку вы не хотите секунд, мы можем разделить строку на двоеточия (3 части) и вернуть их вместе только с первыми двумя частями.

Ответ 5

def td_format(td_object):
    seconds = int(td_object.total_seconds())
    periods = [
        ('year',        60*60*24*365),
        ('month',       60*60*24*30),
        ('day',         60*60*24),
        ('hour',        60*60),
        ('minute',      60),
        ('second',      1)
    ]

    strings=[]
    for period_name, period_seconds in periods:
        if seconds > period_seconds:
            period_value , seconds = divmod(seconds, period_seconds)
            has_s = 's' if period_value > 1 else ''
            strings.append("%s %s%s" % (period_value, period_name, has_s))

    return ", ".join(strings)

Ответ 6

У него уже есть объект timedelta, поэтому почему бы не использовать его встроенный метод total_seconds(), чтобы преобразовать его в секунды, а затем использовать divmod() для получения часов и минут?

hours, remainder = divmod(myTimeDelta.total_seconds(), 3600)
minutes, seconds = divmod(remainder, 60)

# Formatted only for hours and minutes as requested
print '%s:%s' % (hours, minutes)

Это работает независимо от того, имеет ли дельта времени даже дни или годы.

Ответ 7

Я лично использую библиотеку humanize для этого:

>>> import datetime
>>> humanize.naturalday(datetime.datetime.now())
'today'
>>> humanize.naturalday(datetime.datetime.now() - datetime.timedelta(days=1))
'yesterday'
>>> humanize.naturalday(datetime.date(2007, 6, 5))
'Jun 05'
>>> humanize.naturaldate(datetime.date(2007, 6, 5))
'Jun 05 2007'
>>> humanize.naturaltime(datetime.datetime.now() - datetime.timedelta(seconds=1))
'a second ago'
>>> humanize.naturaltime(datetime.datetime.now() - datetime.timedelta(seconds=3600))
'an hour ago'

Конечно, это не дает вам именно ответ, который вы искали (что, действительно, str(timeA - timeB), но я обнаружил, что когда вы идете за несколько часов, дисплей быстро становится нечитаемым. humanize имеет поддержка гораздо больших значений, которые удобочитаемы для человека, а также хорошо локализованы.

По-видимому, это вдохновило модуль Django contrib.humanize, так как вы используете Django, вам, вероятно, следует это использовать.

Ответ 8

Я знаю, что это старый ответ, но я использую datetime.utcfromtimestamp() для этого. Он занимает несколько секунд и возвращает datetime, который можно отформатировать как любой другой datetime.

duration = datetime.utcfromtimestamp(end - begin)
print duration.strftime('%H:%M')

Если вы остаетесь в правовых диапазонах для временных частей, это должно работать, то есть оно не возвращает 1234: 35, так как часы равны <= 23.

Ответ 9

Вопросник требует более приятного формата, чем типичный:

  >>> import datetime
  >>> datetime.timedelta(seconds=41000)
  datetime.timedelta(0, 41000)
  >>> str(datetime.timedelta(seconds=41000))
  '11:23:20'
  >>> str(datetime.timedelta(seconds=4102.33))
  '1:08:22.330000'
  >>> str(datetime.timedelta(seconds=413302.33))
  '4 days, 18:48:22.330000'

Итак, на самом деле есть два формата: один, где дни равны 0, а он оставлен, а другой, где есть текст "n дней, h: m: s". Но секунды могут иметь дроби, и в распечатках нет нулевых нулей, поэтому столбцы беспорядочны.

Вот моя рутина, если вам это нравится:

def printNiceTimeDelta(stime, etime):
    delay = datetime.timedelta(seconds=(etime - stime))
    if (delay.days > 0):
        out = str(delay).replace(" days, ", ":")
    else:
        out = "0:" + str(delay)
    outAr = out.split(':')
    outAr = ["%02d" % (int(float(x))) for x in outAr]
    out   = ":".join(outAr)
    return out

это возвращает выход как dd: hh: mm: ss format:

00:00:00:15
00:00:00:19
02:01:31:40
02:01:32:22

Я думал о добавлении лет к этому, но это остается для упражнения читателем, так как выход безопасен более 1 года:

>>> str(datetime.timedelta(seconds=99999999))
'1157 days, 9:46:39'

Ответ 10

Вот универсальная функция для преобразования объекта timedelta или обычного числа (в виде секунд или минут и т.д.) в красиво отформатированную строку. Я принял фантастический ответ mpounsett по дублирующему вопросу, сделал его немного более гибким, улучшенным читабельностью и добавленной документацией.

Вы найдете, что это самый гибкий ответ здесь, поскольку он позволяет вам:

  • Настроить формат строки "на лету" вместо жесткого кодирования.
  • Оставьте определенные промежутки времени без проблем (см. примеры ниже).

Функция:

from string import Formatter
from datetime import timedelta

def strfdelta(tdelta, fmt='{D:02}d {H:02}h {M:02}m {S:02}s', inputtype='timedelta'):
    """Convert a datetime.timedelta object or a regular number to a custom-
    formatted string, just like the stftime() method does for datetime.datetime
    objects.

    The fmt argument allows custom formatting to be specified.  Fields can 
    include seconds, minutes, hours, days, and weeks.  Each field is optional.

    Some examples:
        '{D:02}d {H:02}h {M:02}m {S:02}s' --> '05d 08h 04m 02s' (default)
        '{W}w {D}d {H}:{M:02}:{S:02}'     --> '4w 5d 8:04:02'
        '{D:2}d {H:2}:{M:02}:{S:02}'      --> ' 5d  8:04:02'
        '{H}h {S}s'                       --> '72h 800s'

    The inputtype argument allows tdelta to be a regular number instead of the  
    default, which is a datetime.timedelta object.  Valid inputtype strings: 
        's', 'seconds', 
        'm', 'minutes', 
        'h', 'hours', 
        'd', 'days', 
        'w', 'weeks'
    """

    # Convert tdelta to integer seconds.
    if inputtype == 'timedelta':
        remainder = int(tdelta.total_seconds())
    elif inputtype in ['s', 'seconds']:
        remainder = int(tdelta)
    elif inputtype in ['m', 'minutes']:
        remainder = int(tdelta)*60
    elif inputtype in ['h', 'hours']:
        remainder = int(tdelta)*3600
    elif inputtype in ['d', 'days']:
        remainder = int(tdelta)*86400
    elif inputtype in ['w', 'weeks']:
        remainder = int(tdelta)*604800

    f = Formatter()
    desired_fields = [field_tuple[1] for field_tuple in f.parse(fmt)]
    possible_fields = ('W', 'D', 'H', 'M', 'S')
    constants = {'W': 604800, 'D': 86400, 'H': 3600, 'M': 60, 'S': 1}
    values = {}
    for field in possible_fields:
        if field in desired_fields and field in constants:
            values[field], remainder = divmod(remainder, constants[field])
    return f.format(fmt, **values)

Demo:

>>> td = timedelta(days=2, hours=3, minutes=5, seconds=8, microseconds=340)

>>> print strfdelta(td)
02d 03h 05m 08s

>>> print strfdelta(td, '{D}d {H}:{M:02}:{S:02}')
2d 3:05:08

>>> print strfdelta(td, '{D:2}d {H:2}:{M:02}:{S:02}')
 2d  3:05:08

>>> print strfdelta(td, '{H}h {S}s')
51h 308s

>>> print strfdelta(12304, inputtype='s')
00d 03h 25m 04s

>>> print strfdelta(620, '{H}:{M:02}', 'm')
10:20

>>> print strfdelta(49, '{D}d {H}h', 'h')
2d 1h

Ответ 11

Мои объекты datetime.timedelta прошли больше суток. Итак, вот еще одна проблема. Все вышеприведенное обсуждение предполагает менее одного дня. A timedelta на самом деле представляет собой набор дней, секунд и микросекунд. В приведенном выше обсуждении следует использовать td.seconds как joe, но если у вас есть дни, он не включен в значение секунд.

Я получаю промежуток времени между 2 датами и печатьми дней и часов.

span = currentdt - previousdt
print '%d,%d\n' % (span.days,span.seconds/3600)

Ответ 12

Я бы всерьез рассмотрел подход Оккама Бритвы:

td = str(timedelta).split('.')[0]

Это возвращает строку без микросекунд

Если вы хотите восстановить объект datetime.timedelta, просто выполните следующее:

h,m,s = re.split(':', td)
new_delta = datetime.timedelta(hours=int(h),minutes=int(m),seconds=int(s))

2 года, я люблю этот язык!

Ответ 13

Следуя приведенному выше примеру Джо, я бы использовал оператор арифметики модуля:

td = datetime.timedelta(hours=10.56)
td_str = "%d:%d" % (td.seconds/3600, td.seconds%3600/60)

Обратите внимание, что целочисленное деление в Python округляется по умолчанию; если вы хотите быть более явным, используйте math.floor() или math.ceil(), если это необходимо.

Ответ 14

Для этого я использовал humanfriendly библиотеку Python, она работает очень хорошо.

import humanfriendly
from datetime import timedelta
delta = timedelta(seconds = 321)
humanfriendly.format_timespan(delta)

'5 minutes and 21 seconds'

Доступно по адресу https://pypi.org/project/humanfriendly/

Ответ 15

def seconds_to_time_left_string(total_seconds):
    s = int(total_seconds)
    years = s // 31104000
    if years > 1:
        return '%d years' % years
    s = s - (years * 31104000)
    months = s // 2592000
    if years == 1:
        r = 'one year'
        if months > 0:
            r += ' and %d months' % months
        return r
    if months > 1:
        return '%d months' % months
    s = s - (months * 2592000)
    days = s // 86400
    if months == 1:
        r = 'one month'
        if days > 0:
            r += ' and %d days' % days
        return r
    if days > 1:
        return '%d days' % days
    s = s - (days * 86400)
    hours = s // 3600
    if days == 1:
        r = 'one day'
        if hours > 0:
            r += ' and %d hours' % hours
        return r 
    s = s - (hours * 3600)
    minutes = s // 60
    seconds = s - (minutes * 60)
    if hours >= 6:
        return '%d hours' % hours
    if hours >= 1:
        r = '%d hours' % hours
        if hours == 1:
            r = 'one hour'
        if minutes > 0:
            r += ' and %d minutes' % minutes
        return r
    if minutes == 1:
        r = 'one minute'
        if seconds > 0:
            r += ' and %d seconds' % seconds
        return r
    if minutes == 0:
        return '%d seconds' % seconds
    if seconds == 0:
        return '%d minutes' % minutes
    return '%d minutes and %d seconds' % (minutes, seconds)

for i in range(10):
    print pow(8, i), seconds_to_time_left_string(pow(8, i))


Output:
1 1 seconds
8 8 seconds
64 one minute and 4 seconds
512 8 minutes and 32 seconds
4096 one hour and 8 minutes
32768 9 hours
262144 3 days
2097152 24 days
16777216 6 months
134217728 4 years

Ответ 16

У меня была аналогичная проблема с выводом расчета сверхурочных на работу. Значение должно всегда отображаться в HH: MM, даже если оно больше одного дня, и значение может стать отрицательным. Я объединил некоторые из представленных решений, и, возможно, кто-то еще найдет это решение полезным. Я понял, что если значение timedelta отрицательно, большинство представленных решений с использованием метода divmod не работают из коробки:

def td2HHMMstr(td):
  '''Convert timedelta objects to a HH:MM string with (+/-) sign'''
  if td < datetime.timedelta(seconds=0):
    sign='-'
    td = -td
  else:
    sign = ''
  tdhours, rem = divmod(td.total_seconds(), 3600)
  tdminutes, rem = divmod(rem, 60)
  tdstr = '{}{:}:{:02d}'.format(sign, int(tdhours), int(tdminutes))
  return tdstr

timedelta to HH: MM string:

td2HHMMstr(datetime.timedelta(hours=1, minutes=45))
'1:54'

td2HHMMstr(datetime.timedelta(days=2, hours=3, minutes=2))
'51:02'

td2HHMMstr(datetime.timedelta(hours=-3, minutes=-2))
'-3:02'

td2HHMMstr(datetime.timedelta(days=-35, hours=-3, minutes=-2))
'-843:02'

Ответ 17

import datetime
hours = datetime.timedelta(hours=16, minutes=30)
print((datetime.datetime(1,1,1) + hours).strftime('%H:%M'))

Ответ 18

from django.utils.translation import ngettext

def localize_timedelta(delta):
    ret = []
    num_years = int(delta.days / 365)
    if num_years > 0:
        delta -= timedelta(days=num_years * 365)
        ret.append(ngettext('%d year', '%d years', num_years) % num_years)

    if delta.days > 0:
        ret.append(ngettext('%d day', '%d days', delta.days) % delta.days)

    num_hours = int(delta.seconds / 3600)
    if num_hours > 0:
        delta -= timedelta(hours=num_hours)
        ret.append(ngettext('%d hour', '%d hours', num_hours) % num_hours)

    num_minutes = int(delta.seconds / 60)
    if num_minutes > 0:
        ret.append(ngettext('%d minute', '%d minutes', num_minutes) % num_minutes)

    return ' '.join(ret)

Это даст:

>>> from datetime import timedelta
>>> localize_timedelta(timedelta(days=3660, minutes=500))
'10 years 10 days 8 hours 20 minutes'

Ответ 19

Пожалуйста, проверьте эту функцию - она ​​преобразует объект timedelta в строку "HH: MM: SS"

def format_timedelta(td):
    hours, remainder = divmod(td.total_seconds(), 3600)
    minutes, seconds = divmod(remainder, 60)
    hours, minutes, seconds = int(hours), int(minutes), int(seconds)
    if hours < 10:
        hours = '0%s' % int(hours)
    if minutes < 10:
        minutes = '0%s' % minutes
    if seconds < 10:
        seconds = '0%s' % seconds
    return '%s:%s:%s' % (hours, minutes, seconds)

Ответ 20

Прямой шаблонный фильтр для этой проблемы. Встроенная функция int() никогда не округляется. F-строки (т.е. f '') требуют Python 3.6.

@app_template_filter()
def diffTime(end, start):
    diff = (end - start).total_seconds()
    d = int(diff / 86400)
    h = int((diff - (d * 86400)) / 3600)
    m = int((diff - (d * 86400 + h * 3600)) / 60)
    s = int((diff - (d * 86400 + h * 3600 + m *60)))
    if d > 0:
        fdiff = f'{d}d {h}h {m}m {s}s'
    elif h > 0:
        fdiff = f'{h}h {m}m {s}s'
    elif m > 0:
        fdiff = f'{m}m {s}s'
    else:
        fdiff = f'{s}s'
    return fdiff

Ответ 21

Если у вас уже есть timedelta obj, просто конвертируйте этот объект в строку. Удалите последние 3 символа строки и напечатайте. Это обрезает секцию секунд и печатает остальную часть в формате Часы: Минуты.

t = str(timedeltaobj) 

print t[:-3]

Ответ 22

t1 = datetime.datetime.strptime(StartTime, "%H:%M:%S %d-%m-%y")

t2 = datetime.datetime.strptime(EndTime, "%H:%M:%S %d-%m-%y")

return str(t2-t1)

Итак, для:

StartTime = '15:28:53 21-07-13'
EndTime = '15:32:40 21-07-13'

возвращает:

'0:03:47'