PHP e date future: come calcolare scadenze e promemoria con DateTimeImmutable

by theArchitect
SHARE
PHP e date future: come calcolare scadenze e promemoria con DateTimeImmutable
© Guida-HTML5.it

Introduzione

Quando si lavora con date e orari in PHP, uno degli scenari più utili e frequenti è il calcolo di una scadenza futura a partire da una data iniziale. Pensiamo a casi reali: la data di un abbonamento che dura 30 giorni, la scadenza di una prova gratuita, la data di rinnovo di un servizio, oppure un promemoria da inviare dopo un certo intervallo di tempo.

Questo argomento è diverso dal semplice “pianificare eventi futuri”, perché qui ci concentriamo su una pratica molto concreta: partire da una data di riferimento e ottenere una nuova data futura in modo affidabile. In PHP, il modo più sicuro per farlo è usare DateTimeImmutable, che evita modifiche involontarie all’oggetto originale.

In questo tutorial vedremo come:

  • creare una data iniziale in modo corretto;
  • aggiungere giorni, mesi o settimane;
  • gestire casi delicati come i mesi con numero di giorni diverso;
  • formattare il risultato per mostrarlo all’utente;
  • applicare best practice utili nei progetti reali.

Codice completo

<?php

declare(strict_types=1);

// Impostiamo il fuso orario in modo esplicito.
// Questo evita risultati diversi tra ambiente locale, staging e produzione.
date_default_timezone_set(´Europe/Rome´);

/**
 * Calcola una data futura a partire da una data iniziale.
 *
 * @param string $startDate Data iniziale nel formato Y-m-d H:i:s
 * @param string $interval  Intervallo da aggiungere, ad esempio:
 *                          ´P30D´  = 30 giorni
 *                          ´P2W´   = 2 settimane
 *                          ´P3M´   = 3 mesi
 *                          ´P1Y´   = 1 anno
 * @return DateTimeImmutable
 */
function calcolaScadenza(string $startDate, string $interval): DateTimeImmutable
{
    $dataIniziale = new DateTimeImmutable($startDate);
    $intervallo = new DateInterval($interval);

    return $dataIniziale->add($intervallo);
}

// Esempio 1: prova gratuita di 30 giorni
$inizioProva = ´2026-04-30 10:00:00´;
$scadenzaProva = calcolaScadenza($inizioProva, ´P30D´);

// Esempio 2: rinnovo trimestrale
$inizioAbbonamento = ´2026-01-15 09:30:00´;
$rinnovoTrimestrale = calcolaScadenza($inizioAbbonamento, ´P3M´);

// Esempio 3: promemoria dopo 2 settimane
$dataOrdine = ´2026-05-02 14:15:00´;
$promemoria = calcolaScadenza($dataOrdine, ´P2W´);

// Stampiamo i risultati in un formato leggibile
echo "<h3>Risultati</h3>";
echo "<ul>";
echo "<li>Inizio prova: " . (new DateTimeImmutable($inizioProva))->format(´d/m/Y H:i´) . "</li>";
echo "<li>Scadenza prova: " . $scadenzaProva->format(´d/m/Y H:i´) . "</li>";
echo "<li>Inizio abbonamento: " . (new DateTimeImmutable($inizioAbbonamento))->format(´d/m/Y H:i´) . "</li>";
echo "<li>Rinnovo trimestrale: " . $rinnovoTrimestrale->format(´d/m/Y H:i´) . "</li>";
echo "<li>Data ordine: " . (new DateTimeImmutable($dataOrdine))->format(´d/m/Y H:i´) . "</li>";
echo "<li>Promemoria: " . $promemoria->format(´d/m/Y H:i´) . "</li>";
echo "</ul>";

// Caso delicato: fine mese
$dataFineMese = new DateTimeImmutable(´2026-01-31 12:00:00´);
$aggiungiUnMese = $dataFineMese->add(new DateInterval(´P1M´));

echo "<p>Data di partenza: " . $dataFineMese->format(´d/m/Y´) . "</p>";
echo "<p>Aggiungendo 1 mese: " . $aggiungiUnMese->format(´d/m/Y´) . "</p>";

// Se vuoi una logica più controllata per le scadenze mensili,
// puoi usare una funzione che corregge il giorno finale.
function aggiungiMesiSicuri(DateTimeImmutable $data, int $mesi): DateTimeImmutable
{
    $anno = (int)$data->format(´Y´);
    $mese = (int)$data->format(´m´);
    $giorno = (int)$data->format(´d´);

    $nuovoMese = $mese + $mesi;
    $nuovoAnno = $anno + intdiv($nuovoMese - 1, 12);
    $nuovoMese = (($nuovoMese - 1) % 12) + 1;

    $ultimoGiornoDelMese = cal_days_in_month(CAL_GREGORIAN, $nuovoMese, $nuovoAnno);
    $giornoCorretto = min($giorno, $ultimoGiornoDelMese);

    return $data->setDate($nuovoAnno, $nuovoMese, $giornoCorretto);
}

$scadenzaMensileSicura = aggiungiMesiSicuri($dataFineMese, 1);
echo "<p>Aggiunta mese sicura: " . $scadenzaMensileSicura->format(´d/m/Y´) . "</p>";

Spiegazione

Il codice parte da una funzione generica, calcolaScadenza(), che riceve una data iniziale e un intervallo in formato ISO 8601. Questo formato è quello usato da DateInterval: ad esempio P30D significa 30 giorni, P2W due settimane, P3M tre mesi.

Perché DateTimeImmutable?

In PHP esistono sia DateTime sia DateTimeImmutable. La differenza è importante:

  • DateTime modifica lo stesso oggetto quando applichi operazioni come add() o modify();
  • DateTimeImmutable restituisce sempre un nuovo oggetto, lasciando intatto l’originale.

Per calcolare scadenze e date future, l’approccio immutabile è spesso più sicuro, perché riduce errori difficili da individuare, soprattutto in funzioni complesse o in applicazioni con più passaggi di elaborazione.

Aggiungere giorni, settimane, mesi e anni

Il cuore della soluzione è $dataIniziale->add(new DateInterval($interval)). Questo permette di sommare un intervallo alla data di partenza. Alcuni esempi pratici:

  • P30D per una prova gratuita di 30 giorni;
  • P2W per un promemoria dopo 2 settimane;
  • P3M per un rinnovo trimestrale;
  • P1Y per un abbonamento annuale.

La parte più delicata riguarda i mesi. Se parti dal 31 gennaio e aggiungi un mese, PHP può portarti a una data non immediata da prevedere, perché febbraio non ha 31 giorni. In alcuni casi il risultato può “slittare” al mese successivo. Questo comportamento è corretto dal punto di vista del motore date, ma non sempre è quello che un’applicazione commerciale desidera.

Gestione sicura dei mesi

Per questo motivo, nel codice trovi anche la funzione aggiungiMesiSicuri(). Questa funzione calcola manualmente il nuovo anno e il nuovo mese, poi verifica quanti giorni ha quel mese con cal_days_in_month(). Se il giorno originale è troppo alto, viene corretto automaticamente con min().

Questa strategia è molto utile quando devi gestire:

  • scadenze mensili di abbonamenti;
  • rinnovi di licenze;
  • piani di pagamento ricorrenti;
  • date contrattuali che devono restare coerenti con il giorno del mese.

Formattazione dell’output

Per mostrare le date all’utente usiamo format(´d/m/Y H:i´). È una scelta semplice e leggibile, adatta alla maggior parte delle interfacce italiane. In un’applicazione reale, la formattazione potrebbe essere diversa a seconda della lingua o del contesto, ma il principio resta lo stesso: separa sempre il calcolo della data dalla sua rappresentazione grafica.

Best practice

  • Usa DateTimeImmutable quando possibile: rende il codice più prevedibile e riduce gli effetti collaterali.
  • Imposta sempre il fuso orario: anche se in questo tutorial non ci concentriamo sui timezone, è fondamentale definire il contesto temporale dell’applicazione.
  • Preferisci intervalli espliciti: invece di affidarti a stringhe ambigue come “next month”, usa DateInterval quando hai bisogno di precisione.
  • Attenzione ai mesi con giorni diversi: per le scadenze mensili, verifica se il comportamento standard di PHP è davvero quello desiderato.
  • Non mescolare logica e presentazione: calcola la data in una funzione e formatta il risultato solo alla fine.
  • Valida sempre l’input: se la data arriva da un form o da un database, controlla che sia nel formato atteso prima di usarla.

Riepilogo

In questo tutorial abbiamo visto come calcolare date future in PHP partendo da una data iniziale e aggiungendo un intervallo preciso. La soluzione base con DateTimeImmutable e DateInterval è perfetta per molti casi pratici, come promemoria, rinnovi e scadenze temporali.

Abbiamo anche affrontato un problema reale e spesso sottovalutato: l’aggiunta di mesi. Quando una data cade a fine mese, il comportamento standard può non essere sempre quello desiderato. In questi casi, una funzione dedicata come aggiungiMesiSicuri() aiuta a mantenere il controllo sulla logica di business.

Il messaggio principale è semplice: con le date non basta “far funzionare il codice”, bisogna anche assicurarsi che il risultato sia coerente con le regole del dominio applicativo. È proprio qui che PHP, usato con attenzione, diventa uno strumento affidabile e professionale.

Approfondisci con risorse ufficiali

  • PHP Manual - DateTimeImmutable: documentazione ufficiale sulla classe immutabile per date e orari.
  • PHP Manual - DateInterval: riferimento completo per creare intervalli temporali.
  • PHP Manual - DateTime::add(): dettagli sul comportamento dell’operazione di somma tra data e intervallo.
  • PHP Manual - cal_days_in_month(): funzione utile per calcolare il numero di giorni in un mese specifico.
  • PHP Manual - date_default_timezone_set(): impostazione del fuso orario predefinito dell’applicazione.

SHARE