Partage
  • Partager sur Facebook
  • Partager sur Twitter

Calculer le nombre de jour entre deux dates

Sujet résolu
    31 juillet 2011 à 0:40:48

    Bonsoir,
    je viens de croiser Python dans mon chemin :D
    J'ai une fonction qui calcule le nombre de jours entre deux date :

    def _get_number_of_days(self, date_from, date_to):
            """Returns a float equals to the timedelta between two dates given as string."""
    
            DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
            from_dt = datetime.datetime.strptime(date_from, DATETIME_FORMAT)
            to_dt = datetime.datetime.strptime(date_to, DATETIME_FORMAT)
            timedelta = to_dt - from_dt
            diff_day = timedelta.days + float(timedelta.seconds) / 86400
            return diff_day
    


    j'aimerais modifier cette fonction de telle façon que les jours Samedi et Dimanche ne soient pas comptés .

    C'est urgent .. Mercii d'avance pour votre aide .
    • Partager sur Facebook
    • Partager sur Twitter
      1 août 2011 à 0:28:43

      Bonsoir,


      Citation :

      je viens de croiser Python dans mon chemin :D


      ;)

      Citation :

      'aimerais modifier cette fonction de telle façon que les jours Samedi et Dimanche ne soient pas comptés .



      À priori, il suffit de compter chaque jour qui ne sont pas un samedi ou un dimanche entre la date de début et la date de fin. Deux cas particuliers cependant : la date de début ou (inclusif) la date de fin tombe un weekend. Dans ce cas il faut s'arranger pour que la date de début se décale au lundi qui suit, ou la date de fin au samedi (à 0h00) qui précède.


      Je ne suis pas habitué aux dates mais je m'en suis sorti en particulier avec :

      - Pour déterminer si une date tombe un weekend :

      WEEKEND = 5, 6
      
      ma_date.weekday() is in WEEKEND # edit: correction
      


      - Pour parcourir les jours d'une date à une autre :

      for d in range(date1.toordinal(), date2.toordinal()+1):
              current_date = date.fromordinal(d)
      

      (par contre dans ce dernier exemple on perd les infos sur l'heure, mais ici ça ne doit pas avoir d'importance).
      • Partager sur Facebook
      • Partager sur Twitter
        1 août 2011 à 4:10:19

        Bonjour.
        J'apporte juste une petite rectification.

        Citation : Grinwik


        WEEKEND = 5, 6
        
        ma_date.toordinal() is in WEEKEND
        



        J'ai l'impression que tu as écrit autre chose que ce que tu voulais.
        toordinal() renvoie le nombre de jours écoulés depuis le 1er janvier de l'an 1, donc les seuls jours qui rentreraient dans cette condition seraient le 5 et le 6 janvier de l'an 1 (ce qui ne nous aide pas beaucoup).
        >>> date.today().toordinal()
        734350
        

        Je suppose que c'est ce que tu voulais écrire, mais c'est la méthode weekday() et non pas toordinal() qu'il faut utiliser ici.
        >>> date.today().weekday()
        0
        

        Selon la doc, "Monday is 0", donc pas de souci.
        Sinon pour parcourir les jours sans trop jouer avec toordinal et fromordinal,
        from datetime import *
        first_date, second_date = date(2000,2,10), date.today()
        tmp = first_date
        while tmp < second_date:
          #on traite la date comme on veut
          tmp += timedelta(days=1) #on incrémente d'un jour
        
        • Partager sur Facebook
        • Partager sur Twitter
          1 août 2011 à 9:32:15

          Citation : Grinwik


          - Pour parcourir les jours d'une date à une autre :

          for d in range(date1.toordinal(), date2.toordinal()+1):
                  current_date = date.fromordinal(d)
          





          Citation : Shaddan


          Sinon pour parcourir les jours sans trop jouer avec toordinal et fromordinal,

          from datetime import *
          first_date, second_date = date(2000,2,10), date.today()
          tmp = first_date
          while tmp < second_date:
            #on traite la date comme on veut
            tmp += timedelta(days=1) #on incrémente d'un jour
          



          Ce genre de parcours me paraît peu efficace, imaginons un soft de la sécu qui doit calculer en permancence le nombre de jours travaillés, s'il faut compter un à un les jours, ça risque de plomber encore plus notre sécu ;)


          Or, Python sait calculer «directement»(*) le nombre de jours entre deux dates données donc grosso modo, il suffit de diviser par 7 pour avoir le nombre de semaines et multiplier par 5 pour avoir le nombre de jours ouvrés. En fait ce calcul revient un calculer le nombre de semaines entières ouvrés auquel il faut ajouter un reliquat. Ce réliquat on le calcule avec une table lookup.


          [EDIT (*) : par une formule, ou plutôt une suite de calculs, cf. les sources de Python, le fichier datetimemodule.c.]


          Donc j'obtiens le code ci-dessous. Je l'ai comparé à un décompte jour à jour et sur quelques tests ça semble correspondre :

          from datetime import *
          
          lookup=[
                  [1, 2, 3, 4, 5, 5, 5],
                  [5, 1, 2, 3, 4, 4, 4],
                  [4, 5, 1, 2, 3, 3, 3],
                  [3, 4, 5, 1, 2, 2, 2],
                  [2, 3, 4, 5, 1, 1, 1],
                  [1, 2, 3, 4, 5, 0, 0],
                  [1, 2, 3, 4, 5, 5, 0]
                  ]    
           
          def delta(d,f):
              ecart_jours=(date(*f)-date(*d)).days
              return ecart_jours/7*5+lookup[date(*d).weekday()][date(*f).weekday()]
          
          def delta_bis(d,f):
              d,f= date(*d), date(*f)
              cpt=0
              while True:
                  if d.weekday()!=5 and d.weekday()!=6:
                      cpt+=1
                  if d==f:
                      break
                  d+=timedelta(1)
              return cpt
          
          for d,f in [
              [(2011,7,14),(2011,7,14)],
              [(2011,7,14),(2011,7,15)],
              [(2011,7,14),(2011,7,16)],
              [(2011,7,14),(2011,7,17)],
              [(2011,7,14),(2011,7,18)],
              [(2011,7,14),(2011,7,22)],
              [(2011,7,14),(2011,7,24)],
              [(2011,7,14),(2011,8,1)],
              [(2011,8,6),(2011,8,7)],
              [(2011,8,6),(2011,8,19)],
              [(2011,8,6),(2011,8,20)],
              [(1850,8,6),(2180,8,20)]
              ]:
                  print delta(d,f),delta_bis(d,f)
          

          1 1
          2 2
          2 2
          2 2
          3 3
          7 7
          7 7
          13 13
          0 0
          10 10
          10 10
          86104 86104
          • Partager sur Facebook
          • Partager sur Twitter
          Anonyme
            1 août 2011 à 10:21:44

            @Candide

            Euh je vais paraître un peu ignare sur ce coup, mais la table de lookup tu la détermines comment?

            Sinon le code est très compréhensible et logique, ;)
            • Partager sur Facebook
            • Partager sur Twitter
              1 août 2011 à 10:50:21

              Citation : fred1599

              @Candide

              Euh je vais paraître un peu ignare sur ce coup, mais la table de lookup tu la détermines comment?

              Sinon le code est très compréhensible et logique, ;)





              Soit à la main en faisant un petit dessin, genre :


              01234560123456


              (rappel : 5 = samedi, 6 = dimanche et il faut les sauter quand on compte)

              et tu comptes, par exemple, de 4 à 2, il faut compter 4, 0, 1, 2 et donc ça fait 4 jours ouvrés. Sinon, tu écris un petit programme Python qui t'évite le calcul à la main, le voici d'ailleurs :

              jours=range(7)*2
              lookup=[[a for a in range(7)] for b in range(7)]
              
              for d in range(7): 
                  for f in range(7):
                      i=d
                      cpt=0
                      while True:       
                          if jours[i]!=5 and jours[i]!=6:
                              cpt+=1
                          if jours[i]==f:
                              break
                          i+=1
                      lookup[d][f]=cpt
                      
              print lookup[4][2]
              



              Il doit être possible de trouver une formule mais j'ai eu la flemme de chercher et de toute façon, c'est moins efficace qu'une table lookup.
              • Partager sur Facebook
              • Partager sur Twitter
              Anonyme
                1 août 2011 à 13:31:30

                Citation

                Ce genre de parcours me paraît peu efficace, imaginons un soft de la sécu qui doit calculer en permancence le nombre de jours travaillés, s'il faut compter un à un les jours, ça risque de plomber encore plus notre sécu



                C'est malheureusement la seule solution que j'ai trouvée (naïve je suppose) :(

                Je propose ma solution qui malgré le fait d'être lente, est assez simple à comprendre.

                from datetime import *
                
                def nb_week(debut, fin):
                    compteur = 0
                    nb = nb_jours(debut, fin)
                    for i in range(1, nb+1):
                        new = date(*debut) + timedelta(days=i)
                        if new.weekday() == 5 or new.weekday() == 6:
                            compteur += 1
                    return compteur
                
                def nb_jours(debut, fin):
                    return abs(date(*debut)-date(*fin)).days
                
                print(nb_jours((1850,8,6),(2180,8,20)) - nb_week((1850,8,6),(2180,8,20)))
                


                • Partager sur Facebook
                • Partager sur Twitter
                  1 août 2011 à 13:49:17

                  Citation : fred1599


                  Je propose ma solution qui malgré le fait d'être lente, est assez simple à comprendre.




                  Tu obtiens des résultats différents (mais pas toujours) d'avec les miens (tu as au plus 1 jour d'écart).



                  from datetime import *
                  
                  lookup=[
                          [1, 2, 3, 4, 5, 5, 5],
                          [5, 1, 2, 3, 4, 4, 4],
                          [4, 5, 1, 2, 3, 3, 3],
                          [3, 4, 5, 1, 2, 2, 2],
                          [2, 3, 4, 5, 1, 1, 1],
                          [1, 2, 3, 4, 5, 0, 0],
                          [1, 2, 3, 4, 5, 5, 0]
                          ]    
                   
                  def delta(d,f):
                      ecart_jours=(date(*f)-date(*d)).days
                      return ecart_jours/7*5+lookup[date(*d).weekday()][date(*f).weekday()]
                  
                  def delta_bis(d,f):
                      d,f= date(*d), date(*f)
                      cpt=0
                      while True:
                          if d.weekday()!=5 and d.weekday()!=6:
                              cpt+=1
                          if d==f:
                              break
                          d+=timedelta(1)
                      return cpt
                  
                  
                  
                  
                  def delta_fred(debut,fin):
                      def nb_week(debut, fin):
                          compteur = 0
                          nb = nb_jours(debut, fin)
                          for i in range(1, nb+1):
                              new = date(*debut) + timedelta(days=i)
                              if new.weekday() == 5 or new.weekday() == 6:
                                  compteur += 1
                          return compteur
                  
                      def nb_jours(debut, fin):
                          return abs(date(*debut)-date(*fin)).days
                  
                      return nb_jours(debut,fin) - nb_week(debut,fin)
                      
                  
                  for d,f in [
                      [(2011,7,14),(2011,7,14)],
                      [(2011,7,14),(2011,7,15)],
                      [(2011,7,14),(2011,7,16)],
                      [(2011,7,14),(2011,7,17)],
                      [(2011,7,14),(2011,7,18)],
                      [(2011,7,14),(2011,7,22)],
                      [(2011,7,14),(2011,7,24)],
                      [(2011,7,14),(2011,8,1)],
                      [(2011,8,6),(2011,8,7)],
                      [(2011,8,6),(2011,8,19)],
                      [(2011,8,6),(2011,8,20)],
                      [(1850,8,6),(2180,8,20)]
                      ]:
                          print delta(d,f),delta_bis(d,f),delta_fred(d,f)
                  



                  1 1 0
                  2 2 1
                  2 2 1
                  2 2 1
                  3 3 2
                  7 7 6
                  7 7 6
                  13 13 12
                  0 0 0
                  10 10 10
                  10 10 10
                  86104 86104 86103




                  Je pense qu'il s'entend que si par exemple on cherche le nombre de jours ouvrés entre, disons, lundi 1er août 2011 et vendredi 12 août 2011, on doit trouver 10 jours, autrement dit les jours limites doivent être inclus s'il ne sont pas un samedi ou dimanche.



                  On pourrait faire quelque chose de plus original en créant un «itérateur (borné ou pas) de jours», ça m'étonne que Python ne propose pas ça nativement, c'est tellement naturel. Et au passage, avec une surcharge des opérateurs +/- pour avancer ou reculer de x jours (mais peut-être que ça existe déjà, j'ai pas regardé la doc avec assez d'attention, sans compter que je ne l'ai pas trouvé facile à comprendre alors que je connais déjà les dates avec la lib standard du C).
                  • Partager sur Facebook
                  • Partager sur Twitter
                  Anonyme
                    1 août 2011 à 13:53:50

                    Il recherche bien le nombre de jours entre 2 dates, donc si la date est identique, il n'y a aucun jour de différence.

                    Enfin c'est comme ça que je l'ai compris.

                    Citation

                    On pourrait faire quelque chose de plus original en créant un «itérateur (borné ou pas) de jours», ça m'étonne que Python ne propose pas ça nativement



                    à part le module datetime, peut-être voir du côté de calendar

                    • Partager sur Facebook
                    • Partager sur Twitter
                      1 août 2011 à 14:37:19

                      Citation : fred1599

                      Il recherche bien le nombre de jours entre 2 dates, donc si la date est identique, il n'y a aucun jour de différence.

                      Enfin c'est comme ça que je l'ai compris.



                      Oui mais à l'usage, une date ne se comporte par comme un nombre entier car une journée a une durée, c'est un objet continu et non discret. Par exemple, si on dit «il y a combien de jours entre lundi et vendredi ?», on répondra spontanément 5 et pas 4. Par contre, si on demande «il y a combien entre 1 et 6 ?» on dira bien 5.


                      Mais je ne disconviens pas en effet que l'énoncé porte à donner plusieurs interprétations.


                      Citation : fred1599


                      peut-être voir du côté de calendar




                      Il y a bien des itérateurs mais pas celui dont je parle.
                      • Partager sur Facebook
                      • Partager sur Twitter
                        1 août 2011 à 15:42:19

                        from datetime import *
                        
                        def nb_week(debut, fin):
                            compteur = 0
                            nb = nb_jours(debut, fin)
                            for i in range(1, nb+1):
                                new = date(*debut) + timedelta(days=i)
                                if new.weekday() == 5 or new.weekday() == 6:
                                    compteur += 1
                            return compteur
                        
                        def nb_jours(debut, fin):
                            return abs(date(*debut)-date(*fin)).days
                        
                        print(nb_jours((2011,8,1),(2011,8,5)) - nb_week((2011,8,1),(2011,8,5)))
                        


                        Apparemment c'est bien ce que je cherche ..

                        Mercii à vous
                        • Partager sur Facebook
                        • Partager sur Twitter
                          2 août 2011 à 23:08:56

                          @Shaddan: bien vu je corrige, merci.

                          @candide : effectivement, solution simple et efficace, joli ! j'en prend bonne note =)
                          • Partager sur Facebook
                          • Partager sur Twitter

                          Calculer le nombre de jour entre deux dates

                          × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                          × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                          • Editeur
                          • Markdown