A la hora de trabajar con fechas en nuestras bases de datos, solemos complicarnos la vida cuando en realidad, las funciones integradas de PHP hacen que sea muy fácil validar y dar formato a todo tipo de fechas.
Bien implementado, el código PHP es capaz de realizar todo el trabajo pesado para nosotros. En esta nota, veremos de qué manera podemos utilizarlo para nuestro beneficio.
Almacenando la fecha y hora en una base de datos
Antes de cubrir la forma de manejar las fechas en PHP, es necesario hablar un poco acerca de cómo se deben almacenar fechas en una base de datos. En concreto hablaremos de MySQL.
Muchas personas improvisan su propia forma de almacenamiento de fechas en la base de datos, lo que inevitablemente les da algunos problemas más adelante. Pueden, por ejemplo, tener un tipo de campo VARCHAR y almacenar fechas en un formato DD/MM/AAAA. Esta es la manera equivocada de hacerlo.
Hay un gran número de maneras diferentes para almacenar fechas correctamente en una base de datos. Una forma es tener un campo entero y almacenar un timestamp de Unix. Pero este enfoque te hace incapaz de utilizar las funciones de tiempo y fecha MySQL, y estos son los problemas potenciales con el tema del problema Y2K38.
MySQL tiene un tipo de datos llamado TIMESTAMP. Esto muestra la fecha y la hora en un formato ISO 8601, es decir, desde la parte más significativa (año) a la parte menos significativa (segundo). Un ejemplo podrÃa ser 2009-08-18 9:17:21. El formateo extra no es realmente necesario cuando se tipea, por lo que 2009081891721 serÃa la misma cosa.
Hay también un tipo de datos llamado DATETIME. Al principio, parece idéntico a la marca de tiempo, pero no es el caso. En primer lugar, TIMESTAMP todavÃa tiene problemas con el Y2K38, por lo que sólo puede almacenar las fechas que se pueden representar con un entero de 32 bits sin signo a partir de la época Unix (1970-01-01 00:00:00). Esto le da un lÃmite superior 2038-01-19 03:14:07. DATETIME no tiene este problema, ya que puede almacenar fechas del 1000-01-01 al 9999-12-31. Por último, hay algunos tipos de datos llamados DATE y TIME. Son por si sólo necesitas o la fecha o la parte del tiempo.
Mi recomendación es utilizar DATETIME, DATE o TIME en función de sus necesidades. En realidad, hay un tipo YEAR también. Utilizar los tipos de datos de almacenamiento incorporados de MySQL para la fecha y hora nos permite utilizar sus funciones de fecha y hora.
No vamos a usar bases de datos para el resto de este tutorial. Los ejemplos suponen que ya tiene alguna cadena que contenga una fecha.
Las zonas horarias
Este es un problema muy frecuente. Cuando se utiliza la función Date () en PHP es utilizar siempre la hora del servidor para generar la fecha y hora. A menos que estés en una zona horaria diferente a tu servidor, este no es un problema. Pero si tengo un VPS (Virtual Private Server) en Inglaterra, y yo vivo en Dinamarca. esa es una diferencia de tiempo de una hora. ¿Cómo puedo lograr que PHP muestre la hora adecuada para mi? Como soy el administrador, simplemente puedo cambiar el reloj del servidor. Pero la mayorÃa de gente no puede hacer eso, asà que tendremos que trabajar algunas alternativas de solución a este problema.
Si tienes acceso a php.ini (o puedes cambiar sus directrices usando un archivo .htaccess de Apache) se puede modificar la directiva date.timezone (en realidad, esto debe ser establecido en something si no desea errores de PHP ). De lo contrario, puedes utilizar la función date_default_timezone_set (). El manual tiene una lista de zonas horarias válidas que puede utilizar para estas dos cosas. Siguiendo con el ejemplo de Dinamarca, elegirÃa Europa / Copenhague.
Probémoslo:
- echo date('H:i:s');
El tiempo local de mi computadora es 09:34:08, por lo que eso también es lo que se muestra. Ahora intentemos cambiar la zona horaria:
- date_default_timezone_set('America/New_York');
- echo date('H:i:s');
Y obtendremos 03:34:08. Asà que pareciera funcionar. Esta es una buena forma de lograrlo.
¿Qué pasa si necesitamos fechas múltiples pero en diferentes zonas horarias, en una misma página? Bueno, es bastante simple. Considera seguir este ejemplo:
- date_default_timezone_set('Europe/Copenhagen');
- echo 'The time in Copenhagen is: ' . date('H:i:s') . PHP_EOL;
- date_default_timezone_set('America/New_York');
- echo 'The time in New York is: ' . date('H:i:s') . PHP_EOL;
- date_default_timezone_set('Europe/Moscow');
- echo 'The time in Moscow is: ' . date('H:i:s') . PHP_EOL;
La exteriorización de eso serÃa:
- The time in Copenhagen is: 09:45:56
- The time in New York is: 03:45:56
- The time in Moscow is: 11:45:56
¿Qué pasa si tienes usuarios de múltiples zonas horarias? Sigue siendo fácil. Pregúntales cuál es su zona horaria y prográmala en función a esto. La función timezone_identifiers_list() te brinda una lista de todos los identificadores de zonas horarias, por lo que puedes usarla para generar una lista desplegable en la cual ellos puedan seleccionar su zona. También puedes probar la geolocalización, pero eso ya es otra cosa.
Finalmente, PHP posee una función llamada gmdate(), que siempre formatea la fecha en GMT. Si conoces el desplazamiento en segundos puedes hacer como:
- gmdate('H:i:s', time() + $offset);
Por lo que podrÃas hacer:
- gmdate('H:i:s', time() + 3600);
Para un desplazamiento de una hora. Sin embargo, esto requiere que tomes DST en consideración. No todos los paÃses observan DST, y algunos lo hacen de forma diferente a los otros. Por eso es recomendable usar el soporte de zonas horarias PHP incorporado.
Convirtiendo de un formato al otro
“Tengo una fecha en DD/MM/AAAA pero la necesito en MM-DD-AAAA. ¡Ayuda!”
Analicemos la situación. Cada fecha posee tres componentes: el dÃa, el mes y el año. Asà que esencialmente sólo necesitamos reordenar estas partes y utilizar guiones en lugar de barras. Hay algunas maneras de lograr esto. Primero probemos dividiéndolas. Tenemos una función llamada explode() para esto.
- $oldDate = '18/08/2009'; // DD/MM/YYYY
- $parts = explode('/', $oldDate);
- /**
- * Now we have:
- * $parts[0]: the day
- * $parts[1]: the month
- * $parts[2]: the year
- * We could also have done:
- * list($day, $month, $year) = explode('/', $oldDate);
- */
- $newDate = "{$parts[1]}-{$parts[0]}-{$parts[2]}"; // MM-DD-YYYY
- echo $newDate; // 08-18-2009
Esto parece funcionar bastante bien. También podemos usar algo llamado expresiones regulares. Será esencialmente una sola lÃnea:
- $oldDate = '18/08/2009';
- $newDate = preg_replace('#^(\d{2})/(\d{2})/(\d{4})$#', '$2-$1-$3', $oldDate);
Como suele suceder con la programación, hay muchas maneras de hacer una misma cosa. Suponiendo que ya hemos dividido nuestra fecha en tres variables $ dia, $ mes y $ año podemos utilizar una función llamada mktime () para generar una marca de tiempo Unix y luego usar date () para darle formato de esta manera:
- $timestamp = mktime(0, 0, 0, $month, $day, $year);
- echo date('m-d-Y', $timestamp);
Otra forma es usar strtotime() para convertir un string formateado en un timestamp de Unix. Pero, strtotime() no reconoce DD/MM/AAAA como un formato válido, por lo que devuelve false. También hay problemas con la ambigüedad. Considera el string 07-08-2009. ¿Se trata del 7 de Agosto, o del 8 de Julio? Será la primera.
Validación de fecha
Si tomas una fecha como entrada (por ejemplo un cumpleaños), serÃa muy útil saber si en realidad es una fecha válida. Hay dos condiciones que deben cumplirse antes de que una fecha puede ser considerada válida:
1. Debe ajustarse a un formato especifico (puede que sólo deseemos AAAA-MM-DD, por ejemplo).
2. La fecha debe existir en concreto (2009-02-29 no existe, pero 2008-02-29 si).
La primera condición es bastante simple, y podemos lograrlo usando las expresiones regulares. El segundo es un poco más complicado. Tendremos que tomar la duración de un mes en consideración. ¿Tal vez es un año bisiesto, tal vez no lo es? Afortunadamente, no tenemos que preocuparnos de eso, PHP tiene una función llamada checkdate () que, precisamente, controla la fecha. AsÃ, expresiones regulares y checkdate () es lo que necesitamos. Un pequeño ejemplo:
- <?php
- if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['date'])) {
- if (preg_match('#^(\d{4})-(\d{2})-(\d{2})$#', $_POST['date'], $matches) && checkdate($matches[2], $matches[3], $matches[1])) {
- echo '<p>The date is <strong>valid</strong>!</p>';
- }
- else {
- echo '<p>The date is <strong>invalid</strong>!</p>';
- }
- echo '<hr>';
- }
- ?>
- <form action="<?php echo $_SERVER['PHP_SELF'] ?>" method="post">
- <label for="date">Enter an ISO 8601 date:</label>
- <input type="text" name="date" id="date"<?php if (!empty($_POST['date'])): ?> value="<?php echo htmlentities($_POST['date']) ?>"<?php endif ?>>
- <button type="submit">Check validity</button>
- </form>
Este ejemplo deberÃa ser bastante sencillo. Primero, comprobar el formato de la cadena mediante preg_match () donde también se extrae el mes, dÃa y año en backreferences. preg_match () devuelve true si la cadena coincide con el patrón. Por lógica, preg_match () se evalúa en primer lugar, y el checkdate () sólo se evalúa si el primer operando es verdadero (si el operando de la izquierda es false, la expresión regular no podrÃa devolver true, por lo que evaluar el operando de la derecha serÃa redundante, he ahà la razón por la que no lo es).
Si has hecho campos separados para el dÃa, mes y año es aún más sencillo y se puede omitir la comprobación del formato.
Puedes programar condiciones adicionales que deben cumplirse antes de considerar una fecha válida. Puedes pedir un intervalo de fecha, lo que quiere decir que la primera debe ser anterior a la última. Puede que sólo quieras fechas en el pasado o en el futuro, o que estén dentro de un intervalo predeterminado en particular. La forma más sencilla de hacerlo serÃa convertir las fechas a las marcas de tiempo Unix. De aquà en adelante se trata de comparación integer rudimentaria.
Como esperamos que hayan notado, las fechas no son tan difÃciles de tratar como parece. ¿O no?
Fuente: PHP Freaks
Lunes, 7 de septiembre de 2009 a las 22.05
En mi humilde opinion de un programador dinosaurio es un EXcelente articulo que los que trabajamos con bitacoras de control y procesos agradeceremos el poder utilizar en su momento, gracias
Martes, 8 de septiembre de 2009 a las 11.52
Buenos dias me parece muy bueno el articulo, agrego que tambien estan las funciones de fecha hora de la base de datos tales como CURRENT_TIMESTAMP para obtener la fecha y hora actual sin tener que ponerla el usuario tambien podemos trabajar con la fecha de la siguiente manera:
(2009-year(empleado.fecha_ingreso)) AS años esta nos sirve para sacar los años que tenga un empleado en nuestra empresa o simplemente si queremos sacar por separado el dia mes y el año
(year(empleado.fecha_ingreso)) AS año,
(month(empleado.fecha_ingreso)) AS mes,
(day(empleado.fecha_ingreso)) AS dia
alli ya tienes para buscar por campos select de otro formulario con las variables
$dia, $mes, $anio
ejemplo en el where de sql
where
(year(empleado.fecha_ingreso))=$anio and
(month(empleado.fecha_ingreso))=$mes and
(day(empleado.fecha_ingreso))=$dia
Saludos espero aportatrles otras ideas a los campos fecha para php y mysql.
Martes, 8 de septiembre de 2009 a las 13.47
impresionante man ! im pre sio nan te
la verdad que el tema de als fechas es todo un tema y lo acabas de excplicar de una manera muy sencilla y didactica.
gracias !
Martes, 8 de septiembre de 2009 a las 15.20
Para mi la manera más fácil de trabajar con fechas es:
$fecha = 15/09/2009;
$formateada = date(‘Y-m-d’, strtotime($fecha));
De esta forma puede pasarla a cualquier formato y no necesitas trabajar con arrays.
Un saludo.
Miércoles, 9 de septiembre de 2009 a las 09.28
PErfecto el articulo justo lo que querÃa, dado que tengo un VPS en EEUU y vivo en España, me viene bien dado que obtenemos la fecha según donde uno vive…
Gracias..
Miércoles, 15 de junio de 2011 a las 16.42
Lo explicaste sencillo y grandioso a la vez muchas gracias por el aporte
Viernes, 9 de noviembre de 2012 a las 03.10
Simplemente excelente, de gran ayuda!…Muchas gracias por el aporte.
Lunes, 28 de enero de 2013 a las 21.55
Muchas gracias por tu aporte de validacion de fechas,me has salvado