2012-10-12 116 views
8

¿Cuál es la forma más eficiente de calcular el número de días entre 2 fechas? Básicamente, estoy preguntando cómo se implementan nuestras bibliotecas de fecha y hora favoritas.La implementación del cálculo del número de días entre 2 fechas

Implementé rápidamente una solución que es ~ O (n) mientras ejecuto 1 iteración cada 4 años. (Código adjunta a continuación)

me pidió una introducción a la resolución de problemas con la clase de computadoras para implementar esto, pero están simplemente iterar a través de todos los días en lugar de cada 4 años .. así que no estoy contento con eso solución y se le ocurrió la siguiente. Sin embargo, ¿hay una solución más eficiente disponible? Si es así, ¿cómo lo logran?

#include <iostream> 

using namespace std; 

#define check_leap(year) ((year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0))) 
#define debug(n) cout << n << endl 

int get_days(int month, bool leap){ 
    if (month == 2){ 
     if (leap) return 29; 
     return 28; 
    } else if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12){ 
     return 31; 
    } else { 
     return 30; 
    } 
} 


int days[] = {31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; 

#define days_prior_to_month(n) days[n-2] 
int num_days_between(int month1, int day1, int month2, int day2, bool leap){ 
    if (month2 > month1) 
     return ((days_prior_to_month(month2) - days_prior_to_month(month1+1)) + get_days(month1, leap) - day1 + 1 + day2) + ((leap && month1 <= 2 && 2 <= month2) ? 1 : 0); 
    else if (month2 == month1) 
     return day2; 
    return -1; 
} 

int main(int argc, char * argv[]){ 
    int year, month, day, year2, month2, day2; 
    cout << "Year: "; cin >> year; 
    cout << "Month: "; cin >> month; 
    cout << "Day: "; cin >> day; 
    cout << "Year 2: "; cin >> year2; 
    cout << "Month 2: "; cin >> month2; 
    cout << "Day 2: "; cin >> day2; 

    int total = 0; 
    if (year2 != year){ 
     int leapyears = 0; 
     total += num_days_between(month, day, 12, 31, check_leap(year)); 
     debug(total); 
     total += num_days_between(1, 1, month2, day2, check_leap(year2)); 
     debug(total); 
     int originalyear = year; 
     year++; 
     year = year + year % 4; 
     while (year <= year2-1){ 
      leapyears += check_leap(year) ? 1 : 0; 
      year += 4; 
     } 

     total += leapyears * 366; 
     debug(total); 
     total += max(year2 - originalyear - leapyears - 1, 0) * 365; 
     debug(total); 

    } else { 
     total = num_days_between(month, day, month2, day2, check_leap(year)); 
    } 
     cout << "Total Number of Days In Between: " << total << endl; 
    system("PAUSE"); 
    return 0; 
} 
+0

la fórmula check_leap está mal , es LeapYear del comentario de Kiron el correcto: https://www.mathsisfun.com/leap-years.html –

Respuesta

20

Puede convertir una fecha a un Julian day number en O (1).

Resta los dos números del día juliano.

+0

Me sorprende una compleja línea de código de 50 líneas de C++ convertirse en solo 3 líneas.Thx – Satbir

7

Todas las divisiones son divisiones enteras, el operador% es módulo.

Dada número entero y, m, d, calcular el número días g como:

function g(y,m,d) 
m = (m + 9) % 12 
y = y - m/10 
return 365*y + y/4 - y/100 + y/400 + (m*306 + 5)/10 + (d - 1) 

Difference between two dates = g(y2,m2,d2) - g(y1,m1,d1) 
+0

¿Puede explicar esta fórmula? – Pwnna

+0

Explicar, como en cómo funciona o cómo codificarlo? Creo que g() calcula un número de día según el calendario gregoriano. Entonces la cantidad de días entre dos fechas es la diferencia entre los números del día. –

+1

¿Hay alguna prueba en alguna parte? – Pwnna

4

La solución está en pitón, y no debe ser difícil de convertir a cualquier otro idioma.

def isLeapYear(year): 
    if year%4 == 0: 
     if year%100 == 0: 
      if year%400 == 0: 
       return True 
      else: 
       return False 
     else: 
      return True 
    else: 
     return False 

def daysBetweenDates(year1, month1, day1, year2, month2, day2): 
    cumDays = [0,31,59,90,120,151,181,212,243,273,304,334] #cumulative Days by month 
    leapcumDays = [0,31,60,91,121,152,182,213,244,274,305,335] # Cumulative Days by month for leap year 
    totdays = 0 
    if year1 == year2: 
     if isLeapYear(year1): 
      return (leapcumDays[month2-1] + day2) - (leapcumDays[month1-1] + day1) 
     else: 
      return (cumDays[month2-1] + day2) - (cumDays[month1-1] + day1) 

    if isLeapYear(year1): 
     totdays = totdays + 366 - (leapcumDays[month1-1] + day1) 
    else: 
     totdays = totdays + 365 - (cumDays[month1-1] + day1) 

    year = year1 + 1 
    while year < year2: 
     if isLeapYear(year): 
      totdays = totdays + 366 
     else: 
      totdays = totdays + 365 
     year = year + 1 

    if isLeapYear(year2): 
     totdays = totdays + (leapcumDays[month2-1] + day2) 
    else: 
     totdays = totdays + (cumDays[month2-1] + day2) 
    return totdays 
+0

Esto funciona correctamente. El resultado es el mismo que en WolframAlpha. – maro

2

Escribí esta fórmula según la sugerencia de Doug Currie. Lo probé hasta 2147483647 días atrás desde hoy.

static int daysElapsed(int yearOne,int monthOne,int daysOne,int yearTwo,int monthTwo,int daysTwo) 
{ 
    return (daysTwo-32075+1461*(yearTwo+4800+(monthTwo-14)/12)/4+367*(monthTwo-2-(monthTwo-14)/12*12)/12-3*((yearTwo+4900+(monthTwo-14)/12)/100)/4)- 
        (daysOne-32075+1461*(yearOne+4800+(monthOne-14)/12)/4+367*(monthOne-2-(monthOne-14)/12*12)/12-3*((yearOne+4900+(monthOne-14)/12)/100)/4);    
    } 
+0

Funciona correctamente. Aunque hay algunos errores de redondeo. En algunos casos, uno debe redondear y, a veces, redondear. – maro

8

La solución de Tyler Durden es muy elegante, pero puede necesitar alguna explicación.

La belleza del algoritmo es la declaración:

(m*306 + 5)/10 

que devuelve el número de días entre el 1 de marzo, y el inicio de la 'm'th meses después de marzo. (Si desea probarlo, simplemente recuerde utilizar 'división entera' que trunca partes decimales)

Para calzar las convenciones de citas estándar de cuerno en esta función, los valores de entrada para el mes y el año se cambian para que el calendario 'comience' en marzo en lugar de enero.

m = (m + 9) % 12 
y = y - m/10 

La implementación de este maneja el problema de calcular "días al mes" y deja sólo 'días al año' a calcular. Wikipedia proporciona una explicación suficiente para esa parte.

http://en.wikipedia.org/wiki/Leap_year#Algorithm

-1

Tal vez esto podría ayudar: (este programa fue escrito en C)

#include<stdio.h> 


int gauss(int y) 
{ 
int r; 
r=(1+(5*((y-1)%4))+(4*((y-1)%100))+(6*((y-1)%400)))%7; 
return r; 
/* 
using gauss's algorithm to figure out the first day of the given year 
* visit:   https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week#Gauss.27s_algorithm 

*/ 
} 
int julian(int d,int m,int y){ 
int j; 
j=(d + (((153*m)+2)/5) + (365*y) + (y/4) - (y/100) + (y/400)); 
return j; 
/* 
    * calculating julian number for a date so that we can calculate number of days passed between the 
* first day of that year and the given date 
* visit: http://www.had2know.com/society/number-days-between-two-dates.html 
*/ 
} 

int main(){ 
int d1,m1,y1,j1,j2,n,r,n1,abs; 

int d,m,y; 

/* 
* d1= day date 
* d=d1 
* m1= month given by the user 
* m= month required for calculation 
* y1= year given by the user 
* y=year required for calculation 
*/ 
printf("Enter the day number of date: "); 
scanf("%d",&d1); 
printf("\nEnter the month number of date: "); 
scanf("%d",&m1); 
printf("\nEnter the year of date: "); 
scanf("%d",&y1); 
if(m1<13&&m1>2){ 
    m=m1-3; 
    d=d1; 
    y=y1; 
    j1=julian(1,10,y-1);//did y-1 as for jan and feb we did the same 

    j2=julian(d,m,y); 

} 
else if(m1==1 || m1==2) 
{ 
    m=m1+9; 
    d=d1; 
    y=y1-1; 
    j1=julian(1,10,y); 

    j2=julian(d,m,y); 

    } 
printf("\ndate: %d/%d/%d",d1,m1,y1); 

r=gauss(y1); 

abs=(j1-j2); 

if(abs<0){ 
    abs=0-abs; 
} 

n=(abs)%7; 


n1=(n+r)%7; 


if(n1==0) 
{ 
    printf("\nThe day is: Sunday"); 
} 
else if(n1==1) 
{ 
    printf("\nThe day is: Monday"); 
} 
else if(n1==2) 
{ 
    printf("\nThe day is: Tuesday"); 
} 
else if(n1==3) 
{ 
    printf("\nThe day is: Wednesday"); 
} 
else if(n1==4) 
{ 
    printf("\nThe day is: Thursday"); 
} 
else if(n1==5) 
{ 
    printf("\nThe day is: Friday"); 
} 
else if(n1==6) 
{ 
    printf("\nThe day is: Saturday"); 
} 
return 0; 

}

-1

algoritmo simple usando Python:

#Calculate the Days between Two Date 

    daysOfMonths = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 

    def isLeapYear(year): 

     # Pseudo code for this algorithm is found at 
     # http://en.wikipedia.org/wiki/Leap_year#Algorithm 
     ## if (year is not divisible by 4) then (it is a common Year) 
     #else if (year is not divisable by 100) then (ut us a leap year) 
     #else if (year is not disible by 400) then (it is a common year) 
     #else(it is aleap year) 
     return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0 

    def Count_Days(year1, month1, day1): 
     if month1 ==2: 
      if isLeapYear(year1): 
       if day1 < daysOfMonths[month1-1]+1: 
        return year1, month1, day1+1 
       else: 
        if month1 ==12: 
         return year1+1,1,1 
        else: 
         return year1, month1 +1 , 1 
      else: 
       if day1 < daysOfMonths[month1-1]: 
        return year1, month1, day1+1 
       else: 
        if month1 ==12: 
         return year1+1,1,1 
        else: 
         return year1, month1 +1 , 1 
     else: 
      if day1 < daysOfMonths[month1-1]: 
       return year1, month1, day1+1 
      else: 
       if month1 ==12: 
        return year1+1,1,1 
       else: 
         return year1, month1 +1 , 1 


    def daysBetweenDates(y1, m1, d1, y2, m2, d2,end_day): 

     if y1 > y2: 
      m1,m2 = m2,m1 
      y1,y2 = y2,y1 
      d1,d2 = d2,d1 
     days=0 
     while(not(m1==m2 and y1==y2 and d1==d2)): 
      y1,m1,d1 = Count_Days(y1,m1,d1) 
      days+=1 
     if end_day: 
      days+=1 
     return days 


    # Test Case 

    def test(): 
     test_cases = [((2012,1,1,2012,2,28,False), 58), 
         ((2012,1,1,2012,3,1,False), 60), 
         ((2011,6,30,2012,6,30,False), 366), 
         ((2011,1,1,2012,8,8,False), 585), 
         ((1994,5,15,2019,8,31,False), 9239), 
         ((1999,3,24,2018,2,4,False), 6892), 
         ((1999,6,24,2018,8,4,False),6981), 
         ((1995,5,24,2018,12,15,False),8606), 
         ((1994,8,24,2019,12,15,True),9245), 
         ((2019,12,15,1994,8,24,True),9245), 
         ((2019,5,15,1994,10,24,True),8970), 
         ((1994,11,24,2019,8,15,True),9031)] 

     for (args, answer) in test_cases: 
      result = daysBetweenDates(*args) 
      if result != answer: 
       print "Test with data:", args, "failed" 
      else: 
       print "Test case passed!" 
    test() 
Cuestiones relacionadas