Il Cesare in questione è proprio Caio Giulio Cesare, il noto imperatore romano al quale si deve la riforma del calendario, effettuata nell'anno 46 a.C., volta, tra l'altro, al perfezionamento della tecnica di calcolo degli anni bisestili. Dal nome dell'imperatore deriva l'appellativo "numero giuliano", indicante l'integral ottenibile applicando una formula nota a giorno, mese ed anno di una qualsiasi data: esso esprime il numero di giorni trascorsi da una origine nota (che si colloca più o meno intorno al 5000 a.C.) alla data medesima.
La funzione date2jul() consente di calcolare il numero giuliano corrispondente alla data voluta; va osservato che l'anno può essere qualsiasi intero maggiore o uguale a 0: date2jul() si occupa di ricondurlo ad un valore "accettabile" sommandovi una costante esprimente il secolo, come descritto nel commento in testa al listato.
/*****************************************************************
Barninga_Z! - 1994
long cdecl date2jul(int day,int month,int year);
int day; giorno della data da convertire (1-31).
int month; mese della data da convertire (1-12).
int year; anno della data da convertire. Può essere
espresso con qualsiasi numero di cifre.
RESTITUISCE Il numero giuliano corrispondente alla data
specificata. Attenzione: se il valore fornito
come anno è minore di 100 viene sommato 1900 al
valore, perciò 2 = 1902 e 94 = 1994. Per
esprimere 2002 bisogna quindi fornire il valore
reale. Se il valore fornito è compreso tra 100 e
999 la funzione somma 2000 al valore, perciò
995 = 2955. Quindi 1995 deve essere 95 o 1995.
Compilato con Borland C++ 3.1
bcc -O -d -c -mx date2jul.c
dove -mx può essere -mt -ms -mc -mm -ml -mh
*****************************************************************/
long cdecl date2jul(int day,int month,int year)
{
long ctmp, dtmp, mtmp, ytmp;
if(year < 100)
year += 1900; // se anno = 82 si assume 1982
if(year < 1000)
year += 2000; // se anno = 182 si assume 2182
if(month > 2) {
mtmp = (long)(month-3);
ytmp = (long)year;
}
else {
mtmp = (long)(month+9);
ytmp = (long)(year-1);
}
ctmp = (ytmp/100);
dtmp = ytmp-(100*ctmp);
return((146097L*ctmp)/4L+(1461L*dtmp)/4L+(153L*mtmp+2)/5L+1721119L+(long)day);
}
La formula applicata, di natura strettamente tecnica, non necessita commenti; ne deriva inoltre una funzione C del tutto banale. Il numero giuliano corrispondente alla data è restituito come long e può essere validamente utilizzato, ad esempio, nel calcolo dei giorni intercorsi tra due date: essi sono infatti pari alla differenza tra i due numeri giuliani.
La funzione jul2date() effettua l'operazione inversa a quella di date2jul(): essa infatti calcola la data corrispondente a un numero giuliano.
/*****************************************************************
Barninga_Z! - 1994
int cdecl jul2date(long jul,int *day,int *month,int *year);
long jul; il numero giuliano da convertire.
int *day; puntatore al giorno della data da
convertire (conterrà 1-31).
int *month; puntatore al mese della data da convertire
(conterrà 1-12).
int *year; puntatore all'anno della data da convertire.
Vedere anche il valore restituito.
RESTITUISCE Il secolo corrispondente alla data espressa
dal numero giuliano (19 per 1900, e così via).
Attenzione: l'anno è sempre calcolato
sottraendovi il secolo, pertanto occorre
controllare il valore restituito dalla funzione
per determinare la data in modo completo (se
*year vale 82 e la funzione restituisce 19,
allora l'anno è 1982; se la funzione restituisce
20, allora l'anno è 2082).
Compilato con Borland C++ 3.1
bcc -O -d -c -mx jul2date.c
dove -mx può essere -mt -ms -mc -mm -ml -mh
*****************************************************************/
int cdecl jul2date(long jul,int *day,int *month,int *year)
{
register cent;
long dayTmp, monthTmp, yearTmp, tmp;
tmp = jul-1721119L;
yearTmp = (4*tmp-1)/146097L;
tmp = 4*tmp-1-146097L*yearTmp;
dayTmp = tmp/4;
tmp = (4*dayTmp+3)/1461;
dayTmp = 4*dayTmp+3-1461*tmp;
dayTmp = (dayTmp+4)/4;
monthTmp = (5*dayTmp-3)/153;
dayTmp = 5*dayTmp-3-153*monthTmp;
dayTmp = (dayTmp+5)/5;
yearTmp = 100*yearTmp+tmp;
*day = (int)dayTmp;
*year = (int)yearTmp;
if((int)monthTmp < 10)
*month = (int)monthTmp+3;
else {
*month = (int)monthTmp-9;
(*year)++;
}
cent = (*year)/100;
(*year) -= (cent*100);
return(cent);
}
Anche la jul2date() utilizza una formula tecnica, che non richiede alcun commento. Più interessante è sottolineare che la funzione richiede quali parametri, oltre al numero giuliano da convertire, anche i puntatori alle variabili in cui memorizzare giorno, mese e anno della data calcolata: ciò si rende necessario in quanto le funzioni C possono restituire un solo valore. Si noti, inoltre, che l'anno memorizzato all'indirizzo year è sempre un numero compreso tra 0 e 99 (estremi inclusi): infatti il secolo è restituito dalla funzione (incrementato di uno), cosicché l'anno espresso in 4 cifre può essere calcolato, dopo la chiamata a jul2date(), sommando il valore memorizzato all'indirizzo year al valore restituito moltiplicato per 1000. Il secolo corrispondente alla data è ottenibile sottraendo 1 al valore restituito.
Presentiamo ancora una funzione che, pur non operando sui numeri giuliani, risulta utile nell'effettuazione di calcoli sulle date: la isleapyear() consente di determinare se un anno è bisestile.
/*****************************************************************
Barninga_Z! - 1994
int cdecl isleapyear(int year);
int year; anno sul quale effettuare il controllo. Può essere
espresso con qualsiasi numero di cifre.
RESTITUISCE 0 l'anno non è bisestile.
1 l'anno è bisestile.
Attenzione: se il valore fornito come anno è
< 100 viene sommato 1900 al valore stesso,
perciò 2 = 1902 e 94 = 1994. Per esprimere 2002
bisogna quindi fornire il valore reale. Se il
valore fornito è compreso tra 100 e 999 la
funzione somma 2000 al valore, così 995 = 2955.
Quindi il 1995 va indicato come 95 o 1995.
Compilato con Borland C++ 3.1
bcc -O -d -c -mx isleapyr.c
dove -mx può essere -mt -ms -mc -mm -ml -mh
*****************************************************************/
#include "dates.h"
int cdecl isleapyear(int year)
{
register lyr;
lyr = 0;
if(year < 100)
year += 1900; // se anno = 82 si presume 1982
if(year < 1000)
year += 2000; // se anno = 182 si presume 2182
if(year == (4*(year/4)))
lyr = 1;
if(year == (100*(year/100)) )
lyr = 0;
if(year == (400*(year/400)) )
lyr = 1;
return(lyr);
}
La isleapyear() adotta le medesime convenzioni di date2jul() circa il formato del parametro year.
Di seguito presentiamo un semplice programma adatto a collaudare le funzioni sin qui descritte (i listati di queste non compaiono nel programma):
/*******************************************************************
JULTEST.C - Barninga Z! - 1994
Programma di prova per funzioni calcolo date.
Compilato con Borland C++ 3.1
bcc jultest.c
*******************************************************************/
#include <stdio.h>
#include <stdlib.h>
long cdecl date2jul(iny day,int month,int year);
int cdecl isleapyear(int year);
int cdecl jul2date(long jul,int *day,int *month,iny *year);
int main(int argc,char **argv)
{
int century, year, month, day;
static char *answers[] = {
"NO",
"YES"
};
switch(argc) {
case 2:
century = jul2date(atol(argv[1]),&day,&month,&year);
printf("%d/%d/%d%d (leapyear: %s)\n",day,month,century,year,
answers[isleapyear(year)]);
break;
case 4:
printf("julian number: %ld (leapyear: %s)\n",date2jul(atoi(argv[1]),
atoi(argv[2]),atoi(argv[3])),isleapyear(atoi(argv[3])));
break;
default:
printf("Usage: jultest julnum | day month year\n");
return(1);
}
return(0);
}
Il programma può essere invocato con uno oppure quattro
parametri numerici sulla riga di comando:
nel primo caso esso assume che il parametro rappresenti un numero
giuliano da convertire in data; nel secondo caso i tre argomenti
sono interpretati come giorno, mese e, rispettivamente, anno esprimenti
una data da convertire in numero giuliano.
Non ci ho capito niente! Ricominciamo...