2012-10-12 16 views
8

Qual è il modo più efficiente per calcolare il numero di giorni tra 2 date? Fondamentalmente sto chiedendo come sono implementate le nostre favourate librerie datetime.L'implementazione del calcolo del numero di giorni tra 2 date

Ho implementato rapidamente una soluzione che è ~ O (n) mentre eseguo 1 iterazione ogni 4 anni. (Codice allegato sotto)

mi è stato chiesto da un intro alla soluzione dei problemi con i computer di classe per implementare questo, ma sono semplicemente scorrendo quotidiana anziché ogni 4 anni .. quindi io non sono contento di questo soluzione e si avvicinò con quello sotto. Tuttavia, è disponibile una soluzione più efficiente? Se è così, come lo realizzano?

#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 formula check_leap è sbagliato , isLeapYear dal commento di Kiron è quello giusto: https://www.mathsisfun.com/leap-years.html –

risposta

20

È possibile convertire una data per un Julian day number in O (1).

Sottrarre i due numeri del giorno giuliano.

+0

Sono stupito che una linea di codice C++ complessa di 50 righe diventi solo 3 righe. Tx – Satbir

7

Tutta la divisione è divisione intera, l'operatore% è modulo.

Dato intero y, m, d, calcolare numero del giorno g come:

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

Puoi spiegare questa formula? – Pwnna

+0

Spiegare, come in come funziona o come codificarlo? Penso che g() calcoli un numero del giorno secondo il calendario gregoriano. Quindi il numero di giorni tra due date è la differenza tra i numeri del giorno. –

+1

C'è qualche prova da qualche parte? – Pwnna

4

La soluzione è in python, e non dovrebbe essere difficile da convertire in qualsiasi altra lingua.

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

Funziona correttamente. Il risultato è lo stesso di WolframAlpha. – maro

2

Ho scritto questa formula sulla base del suggerimento di Doug Currie. L'ho provato fino a 2147483647 giorni da oggi.

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

Funziona correttamente. Sebbene ci siano alcuni errori di arrotondamento. In alcuni casi si dovrebbe arrotondare e talvolta arrotondare. – maro

8

La soluzione di Tyler Durden è la più elegante, ma potrebbe richiedere qualche spiegazione.

La bellezza dell'algoritmo è la dichiarazione:

(m*306 + 5)/10 

che restituisce il numero di giorni tra il 1 ° marzo, e l'inizio del 'mese m'th dopo il marzo. (Se vuoi dimostrarlo, ricorda di usare la "divisione intera" che tronca le parti decimali)

Per adattare le convenzioni di datazione standard del corno a questa funzione, i valori di input per mese e anno vengono spostati in modo che il calendario 'inizi' a marzo invece di gennaio.

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

L'implementazione di questa gestisce il problema del calcolo "giorni al mese" e lascia solo 'giorni all'anno' essere calcolato. Wikipedia fornisce una spiegazione sufficiente per quella parte.

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

-1

Forse questo potrebbe aiutare: (questo programma è stato scritto in 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

semplice algoritmo utilizzando 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() 
Problemi correlati