私はPython初心者(2週間)で、datetime.timedelta
オブジェクトのフォーマットに問題があります。
私がやろうとしていることは次のとおりです。オブジェクトのリストがあり、オブジェクトのクラスのメンバーの1つは、イベントの継続時間を示すtimedeltaオブジェクトです。その期間を時間:分という形式で表示したいと思います。
これを行うためにさまざまな方法を試してみましたが、問題があります。私の現在のアプローチは、時間と分を返すオブジェクトのメソッドをクラスに追加することです。 timedelta.secondsを3600で割って丸めると、時間を取得できます。残りの秒を取得してそれを分に変換するのに問題があります。
ちなみに、プレゼンテーションにはGoogle AppEngine
とDjango Templates
を使用しています。
誰かがこれを解決するより良い方法を手伝うことができるか、知っているなら、私はとても幸せです。
おかげで、
Str()を使用して、timedeltaを文字列に変換するだけです。次に例を示します。
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
ご存知のように、.seconds
属性にアクセスすることで、timedeltaオブジェクトからtotal_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
>>> 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つの部分)で分割し、最初の2つの部分だけで元に戻すことができます。
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)
彼はすでに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)
これは、時間差に日数や年数がある場合でも機能します。
私は個人的に 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を使用しているので、おそらくそれを使用する必要があります。
これは古い答えの質問であることは知っていますが、これにはdatetime.utcfromtimestamp()
を使用します。秒数を要し、他のdatetime
と同様にフォーマットできるdatetime
を返します。
duration = datetime.utcfromtimestamp(end - begin)
print duration.strftime('%H:%M')
時間部分の有効範囲内にある限り、これは機能するはずです。つまり、時間は23以下なので1234:35を返しません。
質問者は、一般的な形式よりも優れた形式を求めています
>>> 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'
したがって、実際には2つの形式があります。1つは日が0で省略されている形式で、もう1つは「n days、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形式で返します。
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'
datetime.timedelta
オブジェクトが1日を超えました。そこで、さらに問題があります。上記の説明はすべて1日未満を想定しています。 timedelta
は、実際には日、秒、マイクロ秒のタプルです。上記の説明では、joeのようにtd.seconds
を使用する必要がありますが、日数がある場合は、seconds値に含まれません。
2つの日付時刻と印刷日と時間の間の期間を取得しています。
span = currentdt - previousdt
print '%d,%d\n' % (span.days,span.seconds/3600)
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)
デモ:
>>> 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
ここで、OccamのRazorアプローチを真剣に検討します。
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年後、私はこの言語が大好きです!
上記のJoeの値の例に従って、モジュラス算術演算子を使用します。
td = datetime.timedelta(hours=10.56)
td_str = "%d:%d" % (td.seconds/3600, td.seconds%3600/60)
Pythonの整数除算はデフォルトで切り捨てられることに注意してください。より明示的にしたい場合は、必要に応じてmath.floor()またはmath.ceil()を使用します。
humanfriendly
pythonライブラリを使用してこれを実行しましたが、非常にうまく機能します。
import humanfriendly
from datetime import timedelta
delta = timedelta(seconds = 321)
humanfriendly.format_timespan(delta)
'5 minutes and 21 seconds'
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
職場での残業計算の出力にも同様の問題がありました。値は、1日より長く、値が負になる場合でも、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からHH:MM文字列へ:
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'
import datetime
hours = datetime.timedelta(hours=16, minutes=30)
print((datetime.datetime(1,1,1) + hours).strftime('%H:%M'))
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'
この関数を確認してください-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)