Python timedelta 处理负值的问题

53 投票
3 回答
78607 浏览
提问于 2025-04-17 07:51

你好,我需要一些帮助来理解为什么会发生这种情况。

我有一个方法用来追踪事件程序中的“剩余时间”:

def get_program_time_budget(self):
    return self.estimated_duration-self.get_program_duration() 

estimated_duration > self.get_program_duration() 时,一切都很好,但当情况反过来时,就会出现一些奇怪的事情。

结果会显示给用户:

Estimated   11 hours    Allocated       10 hours 55 minutes     Remaining       5 minutes

当结果变成负数时,它会这样显示:

Estimated   11 hours    Allocated       11 hours 5 minutes  Remaining       -1 day 23 hours 55 minutes

有没有什么想法可以让结果显示为-5分钟?

这里是 timedelta 格式化器(注意这是一个Django过滤器,所以它接收 timedelta 值作为 str - 但它是以 timedelta 的形式存储的):

def format_duration(value):
  try:
    delim = ':'
    toks = value.split(',')
    hour = minute = ''
    d_string = value.count('day') and toks[0] or ''
    h, m, s = d_string and toks[-1].strip().split(delim) or value.split(delim)
    try:
        hour = int(h)
    except:
        pass
    try:
        minute = int(m)
    except:
        pass  
    h_string = "%s%s%s" % (hour and hour or '', (hour and ' hour' or ''),(hour and hour > 1 and 's' or '')  )
    m_string = "%s%s%s" % (minute and minute or '', (minute and ' minute' or ''),(minute and minute > 1 and 's' or ''))
    return "%s %s %s" % (d_string, h_string, m_string)
  except Exception, e:
    logging.error("Error in format_duration -> %s. Duration value=%s" % (e, value))
    return ''v 

3 个回答

3

如果你遇到一个 timedelta 对象里面有负值,而这些值其实应该是正值的情况,你可以使用 Python 内置的 abs(td_object) 来解决这个问题。

>>> current_time = datetime.datetime.now()
>>> fifteen_seconds = datetime.timedelta(seconds=15)
>>> time_delta_after_calculations = current_time - (current_time + fifteen_seconds)  # It should give a timedelta with 15 seconds but it does not
>>> time_delta_after_calculations
datetime.timedelta(days=-1, seconds=86385)
>>> # The above is kind of True but not what expected (A day contains 86400 seconds)
>>> abs(time_delta_after_calculations)  # Gives expected output
datetime.timedelta(seconds=15)
28

为什么会这样呢?

可能是因为//%的定义方式造成的意外结果。

也可能是为了让datetime这个类的实现更简单。比如说,距离时间起点的五分钟是23:55,而不是0:-5。

其实这并不重要。只要知道这是dayssecondsmicroseconds如何被标准化的方式就可以了。而且,这个问题是可以很容易地解决的。

def format_timedelta(td):
    if td < timedelta(0):
        return '-' + format_timedelta(-td)
    else:
        # Change this to format positive timedeltas the way you want
        return str(td)

 >>> format_timedelta(timedelta(minutes=-5))
 '-0:05:00'
77

如果你使用的是Python 2.7或更高版本,你可以使用timedelta.total_seconds()这个方法来获取一个时间差的浮点数表示,这个数可以是正数也可以是负数,表示秒数。

>>> datetime.timedelta(-1, 86100).total_seconds()
-300.0

你可以很容易地用这个方法来计算分钟数。

如果你不是在使用Python 2.7,你可以参考文档中的以下等效公式:

(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10.0**6

补充:看起来你可能在用默认的字符串方式来显示时间差的结果,所以我之前的回答可能没那么有用。我建议你可以用下面这种方式来显示结果:

def get_program_time_budget(self):
    td = self.estimated_duration-self.get_program_duration()
    if td.days < 0:
        return '-' + str(datetime.timedelta() - td)
    return str(td)

这样的话,返回的结果会是一个字符串,而不是时间差。如果时间差是负数,它会在正的时间差前面加上一个'-'符号。

撰写回答