Esercizio: Calcolo della data a partire dal numero di giorno dell'anno

Esercizio

Vogliamo realizzare una funzione in linguaggio C che prenda in ingresso due valori:

  • un numero indicante l'anno;
  • un numero che indica il numero di giorni trascorsi a partire dal 1° gennaio di quell'anno.

Questa funzione deve restituire il mese e il giorno del mese corrispondenti.

Ad esempio, prendendo in ingresso i valori anno = 2023 e giorni = 156 restituisca in uscita i valori mese = 6 (ossia giugno) e giorno = 5.

Implementazione

Come si evince dal testo dell'esercizio, la funzione da implementare deve prendere in ingresso due valori e restituire due valori in uscita. Motivo per cui dobbiamo utilizzare i puntatori come argomento della funzione.

La possibile firma della funzione può essere la seguente:

void calcola_data(unsigned int anno,
                  unsigned int giorno_dell_anno,
                  unsigned int *mese,
                  unsigned int *giorno);

Per prima cosa dobbiamo notare che i mesi dell'anno non hanno durata uguale. Alcuni mesi sono di 30 giorni, altri di 31 mentre febbraio è di 28.

Per poter gestire queste differenze possiamo utilizzare un array che contiene il numero di giorni corrispondenti al mese:

void calcola_data(unsigned int anno,
                  unsigned int giorno_dell_anno,
                  unsigned int *mese,
                  unsigned int *giorno) {

    unsigned int giorni_mese[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    /* ... */
}

L'array giorni_mese contiene i giorni del mese a seconda dell'indice corrispondente. Per cui 0 equivale a gennaio e quindi il valore corrispondente sarà 31. Analogamente febbraio corrisponderà all'indice 1, per cui alla locazione con indice 1 è memorizzato 28, e così via.

Il secondo passaggio consiste nel verificare se l'anno passato come parametro sia bisestile o meno. Nel caso in cui sia bisestile dobbiamo cambiare il valore corrispondente al numero di giorni di febbraio.

Un anno è bisestile quando:

  • Non è un anno secolare (ossia non è divisibile per 100) ed è divisibile per 4;
  • Oppure è un anno secolare divisibile per 400.

Per cui:

void calcola_data(unsigned int anno,
                  unsigned int giorno_dell_anno,
                  unsigned int *mese,
                  unsigned int *giorno) {

    unsigned int giorni_mese[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    /* Verifica se l'anno è bisestile o meno */
    if (anno % 4 == 0 && anno % 100 != 0 || anno % 400 == 0) {
        /* Nel caso di anno bisestile aggiunge un giorno a febbraio */
        giorni_mese[1] = 29;
    }

    /* ... */
}

Il passo successivo consiste nel realizzare un ciclo for che scorre i mesi e sottrae il numero di giorni corrispondenti al valore di giorno_dell_anno. Il ciclo termina quando il numero di giorni rimanenti è inferiore al numero di giorni del mese successivo. In tal caso l'indice del ciclo for rappresenta il mese calcolato.

Per cui aggiungiamo una variabile i di supporto per il ciclo e scriviamo la funzione in questo modo:

void calcola_data(unsigned int anno,
                  unsigned int giorno_dell_anno,
                  unsigned int *mese,
                  unsigned int *giorno) {

    unsigned int giorni_mese[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    /* Verifica se l'anno è bisestile o meno */
    if (anno % 4 == 0 && anno % 100 != 0 || anno % 400 == 0) {
        /* Nel caso di anno bisestile aggiunge un giorno a febbraio */
        giorni_mese[1] = 29;
    }

    /*
     * Scorre l'array giorni_mese e decrementa giorno_dell_anno
     * fino a quando non si arriva al mese corrispondente,
     * cioè quando giorno_dell_anno è minore o uguale al numero
     * di giorni del mese corrente
     */
    for (i = 0; giorno_dell_anno > giorni_mese[i]; i++) {
        giorno_dell_anno -= giorni_mese[i];
    }

    *mese = i + 1;
    *giorno = giorno_dell_anno;
}

Programma completo

Proviamo adesso a scrivere il programma completo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <stdio.h>

void calcola_data(unsigned int anno,
                  unsigned int giorno_dell_anno,
                  unsigned int *mese,
                  unsigned int *giorno);

int main() {
    unsigned int anno, giorno_dell_anno, mese, giorno;

    printf("Inserisci l'anno: ");
    scanf("%u", &anno);

    printf("Inserisci il giorno dell'anno: ");
    scanf("%u", &giorno_dell_anno);

    calcola_data(anno, giorno_dell_anno, &mese, &giorno);

    printf("Il giorno %u dell'anno %u corrisponde al %u/%u/%u\n",
              giorno_dell_anno, anno, giorno, mese, anno);

    return 0;
}

void calcola_data(unsigned int anno,
                  unsigned int giorno_dell_anno,
                  unsigned int *mese,
                  unsigned int *giorno) {

    unsigned int giorni_mese[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    unsigned int i;

    /* Verifica se l'anno è bisestile o meno */
    if (anno % 4 == 0 && anno % 100 != 0 || anno % 400 == 0) {
        /* Nel caso di anno bisestile aggiunge un giorno a febbraio */
        giorni_mese[1] = 29;
    }

    /*
     * Scorre l'array giorni_mese e decrementa giorno_dell_anno
     * fino a quando non si arriva al mese corrispondente,
     * cioè quando giorno_dell_anno è minore o uguale al numero
     * di giorni del mese corrente
     */
    for (i = 0; giorno_dell_anno > giorni_mese[i]; i++) {
        giorno_dell_anno -= giorni_mese[i];
    }

    *mese = i + 1;
    *giorno = giorno_dell_anno;
}

Se proviamo a compilare e eseguire il programma otteniamo:

Inserisci l'anno: 2019
Inserisci il giorno dell'anno: 365
Il giorno 365 dell'anno 2019 corrisponde al 31/12/2019

Provando ad eseguire con dati diversi otteniamo:

Inserisci l'anno: 2023
Inserisci il giorno dell'anno: 156
Il giorno 156 dell'anno 2023 corrisponde al 5/6/2023