2012-04-03 25 views
6

Sto cercando di capire un modo per il mio programma di prendere una data (come il 2 febbraio 2003) e mostrare la differenza tra i due con un'altra data (come 2 aprile 2012), escluso anni bisestili. Finora sono stato in grado di capirlo solo se le date sono nello stesso mese, semplicemente sottraendo il "giorno". In questo programma utilizzo 2 serie di numeri interi "mese", "giorno" e "anno". Sono praticamente in perdita da dove andare da qui. Questa è una parte del mio compito completamente facoltativa, ma mi piacerebbe avere un'idea su come farlo funzionare. Mi sembra una seccatura, ma forse c'è una formula matematica semplice a cui non sto pensando?Determinazione della differenza tra le date

Spiacente, non ho alcun codice preesistente per questa parte perché il resto del compito riguarda solo l'inserimento delle date da parte dell'utente e l'aggiunta e la sottrazione di un singolo giorno.

risposta

4

Ecco un codice completo per calcolare la differenza di data in y/m/d.

Supponendo che a e da sono data tipi, e che i mesi ei giorni partono da (simile a Qt):

static int increment[12] = { 1, -2, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 }; 

int daysInc = 0; 
if (to.day() - from.day() < 0) 
{ 
    int month = to.month() - 2; // -1 from zero, -1 previous month. 
    if (month < 0) 
     month = 11; // Previous month is December. 
    daysInc = increment[month]; 
    if ((month == 1) && (to.year()%4 == 0)) 
     daysInc++; // Increment days for leap year. 
} 

int total1 = from.year()*360 + from.month()*30 + from.day(); 
int total2 = to.year()*360 + to.month()*30 + to.day(); 
int diff = total2 - total1; 
int years = diff/360; 
int months = (diff - years*360)/30; 
int days = diff - years*360 - months*30 + daysInc; 

// Extra calculation when we can pass one month instead of 30 days. 
if (from.day() == 1 && to.day() == 31) { 
    months--; 
    days = 30; 
} 

Ho provato questo algoritmo e si sta lavorando va bene. Fammi sapere se hai problemi ad usarlo/comprenderlo.

1

Se è necessario farlo da soli, un modo per farlo è piuttosto semplice convertendo le date in Julian Day. Ottieni le formule a quel link e, dalla conversione in poi, lavori solo con i float, dove ogni giorno è 1 unità.

+0

Grande riferimento, grazie! – Hydlide

2

Non sono sicuro su quale piattaforma ci si trova? Windows, Linux? Ma facciamo finta che ti piacerebbe avere una soluzione indipendente dalla piattaforma e il langugage è C++ standard.

Se è possibile utilizzare le librerie è possibile utilizzare il boost :: Date_Time libreria (http://www.boost.org/doc/libs/1_49_0/doc/html/date_time.html)

Se non è possibile usa le librerie per risolvere il tuo compito, dovrai trovare un terreno semplice comune. Forse potresti convertire tutte le date in secondi, oppure giorni sottraendoli e poi riconvertirli di nuovo ai dati. I giorni o i mesi di sottrazione come numeri interi non saranno di aiuto in quanto condurranno a risultati errati a meno che non si tenga conto del resto. Spero che questo aiuti.

Come dbrank0 l'ha fatto notare. :)

11

Utilizzando solo la libreria standard, è possibile convertire una struttura di data moderatamente insana in un conteggio di secondi da un punto zero arbitrario; quindi sottrarre e convertire in giorni:

#include <ctime> 

// Make a tm structure representing this date 
std::tm make_tm(int year, int month, int day) 
{ 
    std::tm tm = {0}; 
    tm.tm_year = year - 1900; // years count from 1900 
    tm.tm_mon = month - 1; // months count from January=0 
    tm.tm_mday = day;   // days count from 1 
    return tm; 
} 

// Structures representing the two dates 
std::tm tm1 = make_tm(2012,4,2); // April 2nd, 2012 
std::tm tm2 = make_tm(2003,2,2); // February 2nd, 2003 

// Arithmetic time values. 
// On a posix system, these are seconds since 1970-01-01 00:00:00 UTC 
std::time_t time1 = std::mktime(&tm1); 
std::time_t time2 = std::mktime(&tm2); 

// Divide by the number of seconds in a day 
const int seconds_per_day = 60*60*24; 
std::time_t difference = (time1 - time2)/seconds_per_day;  

// To be fully portable, we shouldn't assume that these are Unix time; 
// instead, we should use "difftime" to give the difference in seconds: 
double portable_difference = std::difftime(time1, time2)/seconds_per_day; 

Uso Boost.Date_Time è un po 'meno strano:

#include "boost/date_time/gregorian/gregorian_types.hpp" 

using namespace boost::gregorian; 
date date1(2012, Apr, 2); 
date date2(2003, Feb, 2); 
long difference = (date1 - date2).days(); 

Sembra una seccatura per me, ma forse c'è una semplice formula matematica I' non ci pensi?

È davvero una seccatura, ma c'è un formula, se si vuole fare il calcolo da soli.

+0

Solo un pignolo (dal momento che non conosco nessuna piattaforma dove fallirà), ma lo standard non dice nulla sulla rappresentazione del tempo in un 'time_t', o cosa sottrarrà' time_t'. Dovresti usare 'difftime' (che restituisce un' double', che introduce il proprio insieme di problemi). –

+0

@JamesKanze: buon punto; POSIX specifica che è un conteggio di secondi, ma C lo lascia definito dall'implementazione. –

7

Dato che stai cercando una formula matematica, ti aiuterà a trovare una soluzione al tuo problema. Sia Y l'anno, M il mese e D il giorno. Fai questo calcolo per entrambe le date.

Totale = Y * 365 + M * 30 + D, quindi individuare la differenza tra 2 totali delle date corrispondenti.

Mentre si moltiplica 30 con il valore M, è necessario indicare il numero di giorni in quel mese. Puoi farlo con il valore #define o se loop.Allo stesso modo si può fare anche per l'anno bisestile moltiplicando 366 con Y.

Spero che questo vi aiuterà u ....

+0

Buona risposta, ma non è completa, quindi aggiungo il codice completo di seguito. – Borzh

2

C'è un altro modo rotondo ...

  • dato due date, prendere l'anno della data in precedenza come l'anno di riferimento .
  • Quindi calcolare no. di giorni tra ciascuna delle due date date e 1/1/< quell'anno >
  • Mantenere una funzione separata che indica il numero di giorni trascorsi fino a un mese specifico.
  • La differenza assoluta di questi due no. di giorni darà la differenza tra le due date date.
  • Inoltre, non dimenticare di considerare anni bisestili!

Il codice:

#‎include‬<stdio.h> 
#include<math.h> 
typedef struct 
{ 
    int d, m, y; 
} Date; 
int isLeap (int y) 
{ 
    return (y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0); 
} 
int diff (Date d1, Date d2)       //logic here! 
{ 
    int dd1 = 0, dd2 = 0, y, yref;     //dd1 and dd2 store the <i>no. of days</i> between d1, d2 and the reference year 
    yref = (d1.y < d2.y)? d1.y: d2.y;    //that <b>reference year</b> 
    for (y = yref; y < d1.y; y++) 
     if (isLeap(y))        //check if there is any leap year between the reference year and d1's year (exclusive) 
      dd1++; 
    if (isLeap(d1.y) && d1.m > 2) dd1++;    //add another day if the date is past a leap year's February 
    dd1 += daysTill(d1.m) + d1.d + (d1.y - yref) * 365;  //sum up all the tiny bits (days) 
    for (y = yref; y < d2.y; y++)      //repeat for d2 
     if(isLeap(y)) 
      dd2++; 
    if (isLeap(y) && d2.m > 2) dd2++; 
    dd2 += daysTill(d2.m) + d2.d + (d2.y - yref) * 365; 
    return abs(dd2 - dd1);       //return the absolute difference between the two <i>no. of days elapsed past the reference year</i> 
} 
int daysTill (int month)       //some logic here too!! 
{ 
    int days = 0; 
    switch (month) 
    { 
     case 1: days = 0; 
     break; 
     case 2: days = 31; 
     break; 
     case 3: days = 59; 
     break; 
     case 4: days = 90;  //number of days elapsed before April in a non-leap year 
     break; 
     case 5: days = 120; 
     break; 
     case 6: days = 151; 
     break; 
     case 7: days = 181; 
     break; 
     case 8: days = 212; 
     break; 
     case 9: days = 243; 
     break; 
     case 10:days = 273; 
     break; 
     case 11:days = 304; 
     break; 
     case 12:days = 334; 
     break; 
    } 
    return days; 
} 
main() 
{ 
    int t;   //no. of test cases 
    Date d1, d2; //d1 is the first date, d2 is the second one! obvious, duh!? 
    scanf ("%d", &t); 
    while (t--) 
    { 
     scanf ("%d %d %d", &d1.d, &d1.m, &d1.y); 
     scanf ("%d %d %d", &d2.d, &d2.m, &d2.y); 
     printf ("%d\n", diff(d1, d2)); 
    } 
} 

ingresso Standard:

1 
23 9 1960 
11 3 2015 

di uscita standard:

19892 

Codice in azione: https://ideone.com/RrADFR

Migliori algoritmi, ottimizzazioni e modifiche sono sempre ben accetti!

6

risposta nuova per una vecchia questione:

chrono-Compatible Low-Level Date Algorithms

ha formule per la conversione di un {anno, mese, giorno} tripla ad un conteggio di serie di giorni e schiena. Si può usare per calcolare il numero di giorni tra due date in questo modo:

std::cout << days_from_civil(2012, 4, 2) - days_from_civil(2003, 2, 2) << '\n'; 

quali uscite:

3347 

La carta è un how-to manuale, non una biblioteca. Usa C++ 14 per dimostrare le formule. Ogni formula viene fornita con una descrizione dettagliata e una derivazione, che devi leggere solo se ti interessa sapere come funziona la formula.

Le formule sono molto efficienti e valide su una gamma estremamente ampia. Ad esempio, utilizzando l'aritmetica a 32 bit, +/- 5 milioni di anni (più che sufficiente).

Il conteggio del giorno seriale è un conteggio dei giorni da (o prima per i valori negativi) Capodanno 1970, rendendo le formule compatibili con Unix Time e tutte le implementazioni note di std::chrono::system_clock.

L'algoritmo days_from_civil non è nuovo e dovrebbe sembrare molto simile ad altri algoritmi per fare la stessa cosa. Ma andando dall'altra parte, da un conteggio di giorni a un {anno, mese, giorno} la tripla è più complicata.Questa è la formula documentata da civil_from_days e non ho visto altre formulazioni così compatte come questa.

La carta include esempio utilizza mostrando typical computations, std::chrono interoperability, ed estesa unit tests dimostrare la correttezza oltre escursione di +/- 1 milioni di anni (utilizzando un proleptic Gregorian calendar).

Tutte le formule e il software sono di dominio pubblico.