2010-09-15 4 views
11

Pour des raisons d'intérêt général, je me demande s'il existe une façon plus élégante/efficace de le faire. J'ai une fonction qui compare deux tuples début/fin de dates retournant vrai si elles se croisent.python date intervalle d'intersection

from datetime import date 
def date_intersection(t1, t2): 
    t1start, t1end = t1[0], t1[1] 
    t2start, t2end = t2[0], t2[1] 

    if t1end < t2start: return False 
    if t1end == t2start: return True 
    if t1start == t2start: return True 
    if t1start < t2start and t2start < t1end: return True 
    if t1start > t2start and t1end < t2end: return True 
    if t1start < t2start and t1end > t2end: return True 
    if t1start < t2end and t1end > t2end: return True 
    if t1start > t2start and t1start < t2end: return True 
    if t1start == t2end: return True 
    if t1end == t2end: return True 
    if t1start > t2end: return False 

si:

d1 = date(2000, 1, 10) 
d2 = date(2000, 1, 11) 
d3 = date(2000, 1, 12) 
d4 = date(2000, 1, 13) 

alors:

>>> date_intersection((d1,d2),(d3,d4)) 
False 
>>> date_intersection((d1,d2),(d2,d3)) 
True 
>>> date_intersection((d1,d3),(d2,d4)) 
True 

etc.

Je suis curieux de savoir s'il y a un plus pythonique/élégante/plus efficace/moins verbose/généralement mieux, façon de le faire avec peut-être mxDateTime ou un bidouillage intelligent avec timedelta ou set()?

Une autre forme et utile serait la fonction pour retourner un tuple début/fin de l'intersection si l'on se trouve

Merci

+0

Vous devez utiliser des timedeltas pour représenter les durées de 't1 ⇔ t2' et je pense au bit suivant. http://docs.python.org/library/datetime.html#datetime.timedelta – msw

Répondre

19

Ce n'est pas vraiment plus Pythonic, mais vous pouvez simplement décider de l'intersection. Ce problème particulier affleure beaucoup:

return (t1start <= t2start <= t1end) or (t2start <= t1start <= t2end) 

Pour voir pourquoi cela fonctionne réfléchir sur les différentes façons possibles que les deux intervalles peuvent se croiser et voir que le point de départ d'un doit toujours être dans la plage de l'autre.

+0

oui! très agréable. Cela me fait encore un peu louche, mais vous avez réduit mon analyse de cas exhaustive à une expression très nette. Merci beaucoup, cela aide à clarifier ma pensée. – jjon

+0

Selon mon test unitaire, cela ne fonctionne pas si la fin de 't1' est le début de' t2'. –

+0

Lorsque t2start <= t1end? Je pense que vous devriez revoir votre test unitaire. – Amoss

0
if t1end < t2start or t1start > t2end: return False 
if t1start <= t2end or t2start <= t1start: return True 
return False 

Pas qui couvre toutes les séries intersectées?

+0

Eh bien, votre code simplifie pour retourner t1end> = t2start et t1start <= t2end – Amoss

+0

ah, à droite ... 1 plus – nmichaels

6

Voici une version qui vous donne la plage d'intersection. À mon humble avis, ce n'est peut-être pas le # le plus optimisé des conditions, mais il montre clairement quand t2 chevauche t1. Vous pouvez modifier en fonction d'autres réponses si vous voulez juste le vrai/faux.

if (t1start <= t2start <= t2end <= t1end): 
    return t2start,t2end 
elif (t1start <= t2start <= t1end): 
    return t2start,t1end 
elif (t1start <= t2end <= t1end): 
    return t1start,t2end 
elif (t2start <= t1start <= t1end <= t2end): 
    return t1start,t1end 
else: 
    return None 
+1

FYI ... opérateurs de comparaison de chaînage: http://stackoverflow.com/questions/101268/hidden-features-of-python/101945#101945 – fseto

+0

Merci beaucoup pour le lien avec les opérateurs de chaînage, c'est très utile. – jjon

+0

Cela peut être justifié en utilisant min sur les dates de fin. par exemple. si start1 <= start2 <= end1: return (start2, min (end1, end2)) – JeremyKun

7

Une solution alternative et je l'espère plus intelligible:

def has_overlap(A_start, A_end, B_start, B_end): 
    latest_start = max(A_start, B_start) 
    earliest_end = min(A_end, B_end) 
    return latest_start <= earliest_end: 

Nous pouvons obtenir l'intervalle du chevauchement facilement, il est (latest_start, earliest_end). Notez que latest_start peut être égal à earliest_end. Il convient de noter que cela suppose que A_start <= A_end et B_start <= B_end.

+2

Celui-ci est le plus lisible. En outre, vous pouvez facilement retourner (earliest_end-latest_start) pour obtenir une mesure de la quantité totale de chevauchement. – cxrodgers

2
Final Comparison: start <= other_finish and other_start <= finish 

# All of the conditions below result in overlap I have left out the non overlaps 

start <= other_start | start <= other_finish | other_start <= finish | finish <= other_finish 

     0      1      1      0     
     0      1      1      1 
     1      1      1      0   
     1      1      1      1 

Seul le début < = other_finish et other_start < = finition besoin d'être vrai pour retourner un chevauchement.

+0

Une bonne réponse comprendrait une explication de la raison pour laquelle cela résoudrait n'importe quoi. – Qirel