PHP, date e orari: validare e analizzare correttamente le date inserite dall’utente

by theArchitect
SHARE
PHP, date e orari: validare e analizzare correttamente le date inserite dall’utente
© Guida-HTML5.it

Introduzione

Quando un’applicazione riceve una data da un modulo HTML, da un’API o da un file CSV, non è sufficiente convertirla automaticamente in un oggetto DateTime. Una stringa come 31/02/2026 è formalmente simile a una data, ma non rappresenta un giorno valido. Inoltre, PHP può correggere automaticamente alcune date non valide, producendo risultati inattesi.

Un caso pratico è un modulo per registrare una data di nascita, una data di consegna o una scadenza. L’utente potrebbe inserire il valore nel formato italiano dd/mm/YYYY, mentre il database potrebbe richiedere il formato standard YYYY-mm-dd. È quindi necessario:

  • controllare che il formato sia esattamente quello atteso;
  • verificare che la data esista davvero;
  • gestire eventuali errori senza interrompere l’applicazione;
  • convertire la data in un formato coerente per il salvataggio.

In questo tutorial utilizzeremo DateTimeImmutable, preferibile quando vogliamo evitare modifiche accidentali allo stesso oggetto data durante l’elaborazione.

Codice completo

<?php

/**
 * Analizza una data nel formato italiano gg/mm/aaaa.
 *
 * @return array{
 *     valida: bool,
 *     data: ?DateTimeImmutable,
 *     errori: string[]
 * }
 */
function analizzaDataItaliana(string $valore): array
{
    $formato = ´!d/m/Y´;

    // Crea una data usando esclusivamente il formato indicato.
    $data = DateTimeImmutable::createFromFormat($formato, $valore);

    // Recupera gli errori e gli avvisi generati dal parser.
    $problemi = DateTimeImmutable::getLastErrors();

    // Nelle versioni recenti di PHP, se non ci sono problemi,
    // getLastErrors() può restituire false.
    if ($problemi === false) {
        $problemi = [
            ´warning_count´ => 0,
            ´warnings´ => [],
            ´error_count´ => 0,
            ´errors´ => [],
        ];
    }

    $errori = [];

    foreach ($problemi[´warnings´] as $messaggio) {
        $errori[] = $messaggio;
    }

    foreach ($problemi[´errors´] as $messaggio) {
        $errori[] = $messaggio;
    }

    // Il confronto impedisce a PHP di accettare date "corrette"
    // automaticamente, come 31/02/2026.
    $formatoNormalizzato = $data !== false
        ? $data->format(´d/m/Y´)
        : null;

    if ($data === false || $formatoNormalizzato !== $valore) {
        $errori[] = ´La data non rispetta il formato o non è valida.´;
    }

    return [
        ´valida´ => $errori === [],
        ´data´ => $errori === [] ? $data : null,
        ´errori´ => $errori,
    ];
}

$input = $_POST[´data´] ?? ´29/02/2028´;

$risultato = analizzaDataItaliana($input);

if (!$risultato[´valida´]) {
    echo ´<p>Data non valida:</p>´;
    echo ´<ul>´;

    foreach ($risultato[´errori´] as $errore) {
        echo ´<li>´ . htmlspecialchars($errore, ENT_QUOTES, ´UTF-8´) . ´</li>´;
    }

    echo ´</ul>´;
    exit;
}

$data = $risultato[´data´];

echo ´<p>Data accettata: <strong>´
    . htmlspecialchars($data->format(´d/m/Y´), ENT_QUOTES, ´UTF-8´)
    . ´</strong></p>´;

echo ´<p>Formato per il database: <strong>´
    . htmlspecialchars($data->format(´Y-m-d´), ENT_QUOTES, ´UTF-8´)
    . ´</strong></p>´;

echo ´<p>Formato ISO 8601: <strong>´
    . htmlspecialchars($data->format(DateTimeInterface::ATOM), ENT_QUOTES, ´UTF-8´)
    . ´</strong></p>´;

?>

Spiegazione

Usare createFromFormat()

Il metodo DateTimeImmutable::createFromFormat() permette di indicare esplicitamente il formato atteso. Nel nostro caso:

<?php

$data = DateTimeImmutable::createFromFormat(´!d/m/Y´, ´15/08/2026´);

echo $data->format(´Y-m-d´);
// 2026-08-15

?>

La ! iniziale è importante: azzera i campi non presenti nella stringa, come ora, minuti e secondi. Senza di essa, alcuni valori potrebbero essere ereditati dalla data corrente o dal contesto del parser.

Perché controllare anche getLastErrors()

La creazione dell’oggetto non è l’unico controllo necessario. Il parser può generare avvisi per date come 31/02/2026. Inoltre, può tentare di correggere il valore trasformandolo in una data successiva. Per questo leggiamo il risultato di getLastErrors() e rifiutiamo ogni input che produca errori o avvisi.

Il confronto finale tra la stringa originale e quella riformattata è una seconda protezione:

<?php

$originale = ´31/02/2026´;
$data = DateTimeImmutable::createFromFormat(´!d/m/Y´, $originale);

if ($data !== false && $data->format(´d/m/Y´) === $originale) {
    echo ´Data valida´;
} else {
    echo ´Data non valida´;
}

?>

Una data come 29/02/2028, invece, viene accettata perché il 2028 è un anno bisestile. La stessa data con l’anno 2027 deve essere rifiutata.

Separare visualizzazione e salvataggio

È consigliabile mostrare all’utente il formato locale dd/mm/YYYY, ma salvare nel database il formato YYYY-mm-dd. In questo modo i dati sono più semplici da ordinare, confrontare e filtrare tramite SQL.

Per esempio, con PDO si può usare una query parametrizzata:

<?php

$sql = ´INSERT INTO ordini (data_consegna) VALUES (:data_consegna)´;

$stmt = $pdo->prepare($sql);
$stmt->execute([
    ´data_consegna´ => $data->format(´Y-m-d´),
]);

?>

Best practice

  • Non affidarti a strtotime() per input ambigui: stringhe come 03/04/2026 possono essere interpretate in modi diversi. Usa un formato esplicito.
  • Valida sempre lato server: i controlli JavaScript migliorano l’esperienza utente, ma non sostituiscono la validazione PHP.
  • Usa DateTimeImmutable: ogni operazione come modify() restituisce un nuovo oggetto, riducendo gli effetti collaterali.
  • Salva date e orari in formato standard: per le sole date usa Y-m-d; per data e ora considera ISO 8601 o un formato coerente con il database.
  • Gestisci l’output con htmlspecialchars(): i messaggi derivati dall’input dell’utente non devono essere stampati direttamente in HTML.
  • Non confondere data e orario: una data di nascita non necessita necessariamente di un fuso orario, mentre un appuntamento online sì.
  • Conserva il valore originale solo per la visualizzazione degli errori: per l’elaborazione usa esclusivamente dati già validati.

Riepilogo

La gestione affidabile delle date in PHP richiede più di una semplice conversione. Con createFromFormat() possiamo definire il formato ammesso; con getLastErrors() possiamo intercettare errori e avvisi; confrontando la stringa originale con quella riformattata impediamo al parser di correggere silenziosamente date impossibili.

Il flusso consigliato è quindi: ricevere la stringa, validarla, creare un DateTimeImmutable, convertire il valore nel formato necessario e solo dopo utilizzarlo o salvarlo nel database.

Approfondisci con risorse ufficiali

SHARE