2011-12-06 15 views
19

Ciao, ho bisogno di aiuto per capire perché questo sta accadendo. Ho un metodo per tenere traccia 'tempo rimanente' in un programma di eventi:Problema timedelta Python con valori negativi

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

Tutto bene quando l'estimated_duration> self.get_program_duration(), ma quando questo va nella direzione opposta le cose si fanno divertenti.

I risultati vengono visualizzati per l'utente:

Estimated 11 hours Allocated  10 hours 55 minutes  Remaining  5 minutes 

Quando il risultato va negativo significa questo:

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

Delle idee come ottenere il risultato -5 minuti?

EDIT: Ecco il formattatore timedelta (Nota: questo è un filtro Django, quindi riceve il valore timedelta come str - ma viene memorizzato come un 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 
+1

Questo è il modo in cui 'timedelta' funziona per valori negativi. I risultati sono sempre normalizzati in modo che solo il valore 'days' sia negativo. Vorresti negare gli altri campi se il valore dei giorni fosse, ad esempio, -5? –

+3

Sappiamo come sottrarre due timedeltas. Quello che non sappiamo è quale codice hai usato per visualizzare il risultato. Per un consiglio migliore, per favore divulga. –

+1

Se si desidera lavorare con valori di timedelta negativi in ​​modo corretto ("-1 minuto" è solo "-1 minuto" e ** non ** "-1 giorno più 23h59"), è possibile utilizzare il modulo 'relativetimedelta' presente in [dateutil] (http://labix.org/python-dateutil). – florisla

risposta

35

Se si sta utilizzando Python 2.7 o superiore, è possibile utilizzare timedelta.total_seconds() per ottenere una rappresentazione di float timedelta come numero positivo o negativo di secondi.

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

Si dovrebbe essere in grado di utilizzare questo per calcolare un numero di minuti abbastanza facilmente.

Se non si utilizza Python 2.7 è possibile utilizzare la seguente formula equivalente dalla documentazione:

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

Edit: Sembra che probabilmente si sta utilizzando la rappresentazione di stringa di default per timedelta per visualizzare il risultato , quindi la mia risposta originale potrebbe non essere così utile. Vorrei suggerire qualcosa di simile per la visualizzazione del risultato:

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) 

questo sarebbe ora restituire una stringa invece di un timedelta, e per timedeltas negativo sarebbe anteporre un '-' per un timedelta positiva.

+0

Grazie. Ottima risposta, ma non uso il formato str predefinito. Ho il mio formattatore (per togliere i secondi, ecc.) Quindi ho bisogno di un timedelta, non di uno str. Proverà la formula dai documenti per fare total_seconds. –

+0

In tal caso dovresti cambiare il tuo formattatore per convertirlo in un timedelta positivo e solo in un '-' in primo piano. –

+0

Sfortunatamente il codice del formatter è un filtro Django, quindi prende il timedelta .__ str__ (o __unicode__) come valore - vedi la domanda per il codice. –

11

Perché?

Possibilmente come un effetto collaterale imprevisto del modo in cui sono definiti // e %.

Forse perché semplifica l'implementazione della classe datetime. Cinque minuti prima dell'epoca è 23:55, non 0: -5.

Non importa. Sappi solo che è come normalizzato days, seconds e microseconds. E che può essere facilmente risolto.

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' 
+2

Questo è così semplice e geniale. Grazie! –