C... come Cesare

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.


OK, andiamo avanti a leggere il libro...

Non ci ho capito niente! Ricominciamo...