Apuntes sobre Php

Lleva décadas en el obituario y sigue aquí.

Fechas en PHP: casos de uso habituales

En esta lección resolvemos las situaciones más frecuentes al trabajar con fechas: extraer componentes, parsear formatos, comparar, mostrar en español y guardar en base de datos.

¿Cómo extraer los componentes de una fecha?

getdate() devuelve un array asociativo con todos los componentes de un timestamp. Es la forma más directa de obtener día, mes, año, etc.:

<?php
$fecha = getdate(mktime(0, 0, 0, 6, 7, 2025));
echo $fecha['mday'];    // 7
echo $fecha['mon'];     // 6
echo $fecha['year'];    // 2025
echo $fecha['weekday']; // Saturday
echo $fecha['wday'];    // 6 (0 = domingo)
?>

Sin argumento, usa el timestamp actual. Las claves disponibles:

seconds  Segundos (0-59)
minutes  Minutos (0-59)
hours    Horas (0-23)
mday     Día del mes (1-31)
wday     Día de la semana (0=domingo, 6=sábado)
mon      Mes (1-12)
year     Año (ejemplo: 2025)
yday     Día del año (0-365)
weekday  Día completo en inglés (Saturday)
month    Mes completo en inglés (June)
0        Timestamp original

Para casos simples, date() con el modificador adecuado suele bastar. getdate() es más útil cuando necesitas varios componentes a la vez o cuando construyes fechas localizadas manualmente.

Nota: idate("Y"), idate("m"), etc. devuelven un componente individual como entero, sin necesidad de parsear un array. Útil para comparaciones directas.

¿Cómo parsear una fecha con formato conocido?

Cuando recibes una fecha en un formato concreto (de un formulario, de un CSV...) y quieres convertirla a DateTime, usa DateTime::createFromFormat(). Es más preciso que strtotime() porque tú controlas exactamente cómo se lee la cadena:

<?php
// Fecha en formato español dd/mm/yyyy
$fecha = DateTime::createFromFormat("d/m/Y", "07/06/2025");
if ($fecha) {
    echo $fecha->format("Y-m-d"); // 2025-06-07
} else {
    echo "Formato inválido";
}

// Fecha con hora
$fecha = DateTime::createFromFormat("d/m/Y H:i", "07/06/2025 18:15");
echo $fecha->format("Y-m-d H:i:s"); // 2025-06-07 18:15:00
?>

Si la cadena no coincide con el formato, devuelve false. Comprueba siempre el resultado antes de usar el objeto.

Para validar si una cadena es una fecha reconocible sin formato fijo, date_parse() devuelve los componentes y también los errores:

<?php
$resultado = date_parse("2025-06-07 14:30:00");
echo $resultado['year'];        // 2025
echo $resultado['error_count']; // 0 si es válida
?>

¿Cómo comparar fechas y calcular diferencias?

La forma más sencilla es comparar timestamps. Si las fechas ya son objetos DateTime, puedes comparar directamente con <, > o ==:

<?php
$hoy = new DateTime("2025-06-07");
$evento = new DateTime("2025-12-25");

if ($hoy < $evento) {
    echo "El evento aún no ha pasado\n";
}
?>

Para calcular la diferencia exacta entre dos fechas, usa diff():

<?php
$inicio = new DateTime("2025-06-01");
$fin    = new DateTime("2025-06-07");

$intervalo = $inicio->diff($fin);
echo $intervalo->days . " días";  // 6 días
echo $intervalo->format("%R%a días"); // +6 días (con signo)
?>

Con timestamps puedes hacer lo mismo de forma procedural:

<?php
$inicio = strtotime("2025-06-01");
$fin    = strtotime("2025-06-07");
$dias   = ($fin - $inicio) / 86400; // 86400 = segundos en un día
echo $dias . " días"; // 6 días
?>

¿Cómo mostrar fechas en español?

date() siempre produce nombres en inglés. Hay tres opciones según el entorno:

Opción 1 — Arrays manuales (funciona en cualquier servidor):

<?php
function fecha_es($timestamp) {
    $meses = [
        1 => 'enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio',
        'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre'
    ];
    $dias = [
        1 => 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado', 'domingo'
    ];

    $f = getdate($timestamp);
    $wday = ($f['wday'] === 0) ? 7 : $f['wday']; // domingo = 0, lo pasamos a 7
    return "{$dias[$wday]}, {$f['mday']} de {$meses[$f['mon']]} de {$f['year']}";
}

echo fecha_es(strtotime("2025-06-07")); // sábado, 7 de junio de 2025
?>

Opción 2 — IntlDateFormatter (requiere extensión intl, disponible en la mayoría de servidores modernos):

<?php
$fmt = new IntlDateFormatter('es_ES', IntlDateFormatter::FULL, IntlDateFormatter::NONE);
echo $fmt->format(new DateTime("2025-06-07"));
// sábado, 7 de junio de 2025

// Con hora:
$fmt = new IntlDateFormatter('es_ES', IntlDateFormatter::LONG, IntlDateFormatter::SHORT, 'Europe/Madrid');
echo $fmt->format(new DateTime());
// 7 de junio de 2025, 11:21
?>

Opción 3 — strftime() + setlocale() — obsoleta desde PHP 8.1, evítala en código nuevo:

<?php
setlocale(LC_TIME, "es_ES.UTF-8");
echo strftime("%A, %e de %B de %Y"); // Obsoleto, no usar
?>

¿Qué formato usar para MySQL?

MySQL espera fechas en formato ISO. Guarda siempre en UTC, convierte a zona local solo al mostrar:

<?php
date_default_timezone_set("UTC");

$datetime = date("Y-m-d H:i:s"); // Para columnas DATETIME
$date     = date("Y-m-d");       // Para columnas DATE
$time     = date("H:i:s");       // Para columnas TIME
?>

Zonas horarias

Para trabajar con zonas horarias diferentes en el mismo script, usa DateTimeZone:

<?php
$utc    = new DateTime("now", new DateTimeZone("UTC"));
$madrid = new DateTime("now", new DateTimeZone("Europe/Madrid"));

echo $utc->format("H:i");    // hora UTC
echo $madrid->format("H:i"); // hora de Madrid
?>

Errores comunes

1. No fijar la zona horaria. Sin date_default_timezone_set(), PHP usa la del servidor, que puede variar:

<?php
date_default_timezone_set("UTC"); // Siempre al inicio del script
?>

2. Comparar fechas como strings. "2025-6-7" == "2025-06-07" es false aunque sean el mismo día. Usa timestamps o DateTime.

3. No validar fechas de formularios. El usuario puede enviar "32" como día. Usa checkdate():

<?php
$dia = (int)$_POST['dia'];
$mes = (int)$_POST['mes'];
$ano = (int)$_POST['ano'];

if (checkdate($mes, $dia, $ano)) {
    $timestamp = mktime(0, 0, 0, $mes, $dia, $ano);
} else {
    echo "Fecha inválida";
}
?>

4. Usar strtotime() con texto en español. Solo entiende inglés: usa "tomorrow", "next Monday", "+1 week".

5. Asumir que febrero tiene 28 días. Usa checkdate(2, 29, $año) para detectar años bisiestos.

Recapitulación

  • getdate($timestamp) descompone un timestamp en un array asociativo con día, mes, año, día de la semana, etc.
  • DateTime::createFromFormat($formato, $cadena) parsea fechas con formato conocido y devuelve false si no coincide.
  • Para comparar fechas usa < / > entre objetos DateTime, o resta timestamps. Para la diferencia exacta, DateTime::diff().
  • Para mostrar en español: arrays manuales (siempre funciona), IntlDateFormatter (requiere extensión intl). Evita strftime().
  • Guarda fechas en MySQL en UTC con formato Y-m-d H:i:s. Convierte a zona local solo al mostrar.
  • Fija siempre la zona horaria al inicio del script. Valida fechas de formularios con checkdate(). No compares fechas como strings.

En la próxima lección: manejo de ficheros en PHP: leer, escribir, copiar y borrar archivos.

TOP