Fechas bonitas y funcionales en Javascript

Fechas bonitas y funcionales en Javascript

Creo que no me equivoco cuando pienso que todos los que construimos webs, nos hemos peleado con la fecha en varias ocasiones. En ocasiones tenemos que lidiar con fechas en el lado del servidor, cogiéndolas de bases de datos, transformándolas a un formato bonito a la vista y transportándolas al ordenador cliente en html o Javascript. Pero otras veces la fecha la recogeremos en el lado del cliente, o creando directamente la fecha por código, o capturándola a través de un input que el usuario tecleará o seleccionará de un calendario. Si es así, no tiene mucho sentido subir la fecha al servidor, tratarla, y descargarla de nuevo. Es mejor tratarla directamente en el ordenador cliente (salvo en excepciones, que no se me ocurre ninguna).

Javascript trata las fechas en formato TimeStamp de Unix, osea en milisegundos desde la medianoche del 1 de Enero de 1970. Pero a la vez es muy flexible, y nos permite poner la fecha en casi cualquier formato y lo reconocerá, que guay…

Y no solo eso, sino que hasta nos corrige, por ejemplo si guardas una fecha como “Jueves 26 de Enero de 2015” Javascript hace caso omiso a “Jueves” y si le pedimos que nos muestre el día de la semana de esa fecha nos dirá “Lunes”, que es el día correcto.

Si creas una fecha sin parámetros obtienes la fecha y hora actuales:

JavaScript
var fecha = new Date(); // Mon Jan 26 2015 04:36:10 GMT+0100

y con parámetros:

JavaScript
var fecha = new Date("2015-01-26");
var fecha = new Date("01-26-2015");
var fecha = new Date("01/26/2015");
var fecha = new Date("January 26, 2015");

De todas las formas anteriores obtenemos el mismo resultado:

JavaScript
Mon Jan 26 2015 01:00:00 GMT+0100

Pero… igual que digo una cosa, digo la otra. Hay algunas cosillas peculiares del objeto Date() de Javascript que en mi opinión son un poco “raritas”, aunque trabajan bien, y solo es cuestión de acostumbrarnos a ello, por ejemplo:

Si creas una fecha de la siguiente forma dará error:

JavaScript
var fecha = new Date("26-01-2015");

No hay forma de decirle que es en formato “DD-MM-AAAA”, solo admite el formato americano “MM-DD-AAAA”, o incluso lo admite con el año con 2 dígitos, pero eso sí, siempre en formato americano.

Cuando extraes el día de la semana de una fecha con el método .getDay(), el valor resultante es entre 0 y 6, siendo 0 Domingo, y 6 Sábado. Que yo sepa no hay posibilidad de decirle que el primer día de la semana es Lunes y empieza por 1.

Cuando extraes el mes de una fecha con el método .getMonth(), el valor resultante es entre 0 y 11, siendo 0 Enero, y 11 Diciembre. Y al igual que con el día de la semana, no hay la posibilidad de decirle que empieze a contar por 1 en vez de cero.

En cambio, cuando extraemos el día del mes y el año sí que trabaja como esperamos, el día del mes con el método getDate() da entre 1 y 31. Y el año igual, con el método getFullYear() da el año correcto en cuatro cifras.

Dicho todo esto, vamos a trabajar un caso práctico. Supongamos que tenemos una fecha en cualquier formato, siempre que sea válido claro, y lo tenemos que transformar en un formato bonito a la vista como “Lunes, 1 de Enero de 2015” para mostrarlo después. Vamos al lío.

JavaScript
var fecha_recibida = "2015-01-26"; // La fecha que recibimos en formato string.

Primero hay que hacer dos arrays, con los días de la semana y los meses en letra.

JavaScript
var dia_semana = new Array();
dia_semana[] = "Domingo";
dia_semana[] = "Lunes";
dia_semana[] = "Martes";
dia_semana[] = "Miércoles";
dia_semana[] = "Jueves";
dia_semana[] = "Viernes";
dia_semana[] = "Sábado";

var mes = new Array();
mes[] = "Enero";
mes[] = "Febrero";
mes[] = "Marzo";
mes[] = "Abril";
mes[] = "Mayo";
mes[] = "Junio";
mes[] = "Julio";
mes[] = "Agosto";
mes[] = "Septiembre";
mes[] = "Octubre";
mes[] = "Noviembre";
mes[] = "Diciembre";

Luego convertimos fecha_recibida de string a tipo fecha.

JavaScript
var fecha = new Date(fecha_recibida);

Y seguimos con este código que creo que no hace falta explicar, lo comentaré.

JavaScript
var dia_sem_letras = dia_semana[fecha.getDay()]; // día de la semana.
var dia = fecha.getDate(); // día del mes.
var mes_letras = mes[fecha.getMonth()]; // mes.
var ano = fecha.getFullYear(); // año en cuatro dígitos.

Ya solo queda construir la fecha en el formato que queremos.

JavaScript
var fecha_formateada = dia_sem_letras + ", " + dia + " de " + mes_letras + " de " + ano; // “Lunes, 26 de Enero de 2015”

Y ya está, así de fácil. El código completo quedaría así:

JavaScript
var fecha_recibida = "2015-01-26"; // La fecha que recibimos en formato string.

var dia_semana = new Array();
dia_semana[] = "Domingo";
dia_semana[] = "Lunes";
dia_semana[] = "Martes";
dia_semana[] = "Miércoles";
dia_semana[] = "Jueves";
dia_semana[] = "Viernes";
dia_semana[] = "Sábado";

var mes = new Array();
mes[] = "Enero";
mes[] = "Febrero";
mes[] = "Marzo";
mes[] = "Abril";
mes[] = "Mayo";
mes[] = "Junio";
mes[] = "Julio";
mes[] = "Agosto";
mes[] = "Septiembre";
mes[] = "Octubre";
mes[] = "Noviembre";
mes[] = "Diciembre";

var fecha = new Date(fecha_recibida);

var dia_sem_letras = dia_semana[fecha.getDay()]; // día de la semana.
var dia = fecha.getDate(); // día del mes.
var mes_letras = mes[fecha.getMonth()]; // mes.
var ano = fecha.getFullYear(); // año en cuatro dígitos.

var fecha_formateada = dia_sem_letras + ", " + dia + " de " + mes_letras + " de " + ano; // “Lunes, 26 de Enero de 2015”

Ahora supongamos que queremos pasar la fecha del formato “Lunes, 26 de Enero de 2015” a “2015-01-26”, para subirla al servidor y con PHP y MySql guardarla en una base de datos.

JavaScript
var fecha_recibida = "Lunes, 26 de Enero de 2015";

Empezamos por crear una Array con los meses en letra en la clave, y su número correspondiente en el valor.

JavaScript
var mes = new Array();
mes["Enero"] = "01";
mes["Febrero"] = "02";
mes["Marzo"] = "03";
mes["Abril"] = "04";
mes["Mayo"] = "05";
mes["Junio"] = "06";
mes["Julio"] = "07";
mes["Agosto"] = "08";
mes["Septiembre"] = "09";
mes["Octubre"] = "10";
mes["Noviembre"] = "11";
mes["Diciembre"] = "12";

Ahora con el método .split() dividimos la fecha poniendo un espacio en el patrón de busqueda.

JavaScript
var fecha_div = fecha_recibida.split(" ");

Nos quedará un Array con estos valores:

JavaScript
fecha_div[0] = "Lunes,";
fecha_div[1] = "26";
fecha_div[2] = "de";
fecha_div[3] = "Enero";
fecha_div[4] = "de";
fecha_div[5] = "2015";

Y seguimos con este código que creo que no hace falta explicar, lo comentaré.

JavaScript
var dia = fecha_div[1]; // día del mes.
var mes_letra = fecha_div[3]; // mes en letras.
var mes_numero = mes[mes_letra]; // mes en números, lo cogemos del Array mes.
var ano = fecha_div[5]; // año.

Ya solo queda construir la fecha en el formato que queremos.

JavaScript
var fecha_formateada = ano + "-" + mes_numero + "-" + dia; // “2015-01-26”

Ya estaría, el código completo quedaría así:

JavaScript
var fecha_recibida = "Lunes, 26 de Enero de 2015";

var mes = new Array();
mes["Enero"] = "01";
mes["Febrero"] = "02";
mes["Marzo"] = "03";
mes["Abril"] = "04";
mes["Mayo"] = "05";
mes["Junio"] = "06";
mes["Julio"] = "07";
mes["Agosto"] = "08";
mes["Septiembre"] = "09";
mes["Octubre"] = "10";
mes["Noviembre"] = "11";
mes["Diciembre"] = "12";

var fecha_div = fecha_recibida.split(" ");

var dia = fecha_div[1]; // día del mes.
var mes_letra = fecha_div[3]; // mes en letras.
var mes_numero = mes[mes_letra]; // mes en números, lo cogemos del Array mes.
var ano = fecha_div[5]; // año.

Var fecha_formateada = ano + "-" + mes_numero + "-" + dia; // “2015-01-26”

Veremos ahora como comparar fechas en Javascript. Es tan fácil como comprobar si una fecha es menor, igual o mayor que otra.

JavaScript
var fecha_input = "2015-01-26"; // 26 de Enero de 2015.
var fecha_hoy = new Date(); // fecha del momento actual (hoy).

var f_input_ts = Date.parse(fecha_input); // Pasamos a milisegundos (TimeStamp)
var f_hoy_ts = Date.parse(fecha_hoy); // Pasamos a milisegundos (TimeStamp)

var resultado = "";
if (f_input_ts < f_hoy_ts) {
    resultado = "La fecha " + fecha_input + " del input es anterior a hoy";
} else if (f_input_ts == f_hoy_ts) {
    resultado = "La fecha " + fecha_input + " del input es hoy";
} else {
    resultado = "La fecha " + fecha_input + " del input aún está por llegar";
}

Este artículo se complementa con otro que hice del tratamiento de fechas en PHP, ya que entiendo que PHP y Javascript se complementan. Hay que programar en los dos lados del cable (allí en el cielo “PHP” como en la tierra “Javascript”) 🙂

Se acabó por hoy…

JavaScript
if (visitante == "amigo" || visitante == "amiga") {
    alert("Un abrazo.");
} else {
    alert("Un saludo.");
}

Fechas bonitas y funcionales en PHP

Fechas bonitas y funcionales en PHP

Primero quiero hacer mención a una nota que hay en el manual oficial de PHP «PHP.net».

Nota:

Las fechas en los formatos m/d/y o d-m-y no son ambiguas al observar el separador entre los distintos componentes: si el separador es una barra (/), se asume el formato norteamericano m/d/y, mientras que si el separador es un guión (-) o un punto (.), se asume el formato europeo d-m-y. Para evitar esta ambigüedad potencial es mejor usar fechas ISO 8601 (YYYY-MM-DD).

Con esto entiendo que si pones una fecha así:

PHP
$fecha = "01-02-15"; // 1 de Febrero de 2015 | d-m-y.

Al estar con guiones, PHP lo interpreta como formato Europeo, todo bien. Pero si esa misma fecha la pones en formato Europeo con barras, PHP la interpretará como formato Americano, osea que la habremos «liao parda».

PHP
$fecha = "01/02/15"; // 2 de Enero de 2015 | m/d/y.

Por lo tanto, hagamos caso al manual oficial de PHP, y siempre que podamos, usemos el formato ISO 8601 (AAAA-MM-DD). A menos que creas que es lo mismo «1 de Febrero de 2015» y «2 de Enero de 2015».

Empezamos…

Muchas veces necesitamos mostrar fechas en la web, y como no?, necesitamos que sea bonita, o agradable a la vista, algo como: “Viernes, 23 de Enero de 2015”. Pero nos encontramos con un pequeño problema, que al intentar tratar esa fecha no podemos con ese bonito formato, ya que PHP no lo reconoce como fecha, y tenemos que transformarla, y entonces podremos tratarla y almacenarla en bases de datos.

El formato de fecha en MySql es AAAA-MM-DD, algo como “2015-01-23”, y si le damos a MySql algo que no sea así, nos insertará esto 0000-00-00 en la base de datos.

PHP nos provee de funciones para poder tratar las fechas de forma sencilla, pero como ya he comentado antes, estas tienen que estar en un formato reconocible. Ahora veremos como podemos transformar la fecha de bonita a funcional y viceversa.

Podemos empezar por transformar una fecha recogida de una base de datos, en este formato bonito para mostrarla.

Supongamos que la tenemos en la variable $fecha, como la acabamos de coger de una base de datos, su formato será “2015-01-23”, así que para convertirla a “Viernes, 23 de Enero de 2015” usaremos la función date() de PHP para saber el día de la semana que corresponde a esa fecha, esa misma función nos dará el día de mes y el año, y con un array con los días de la semana y otro con los meses en letra transformaremos fácilmente ese “01” a “Enero” en los meses y el “1” a “Lunes” en los días.

PHP
$fecha = "2015-01-23";

Empezamos por crear el array de los días de la semana.

PHP
$dia_semana[1] = "Lunes";
$dia_semana[2] = "Martes";
$dia_semana[3] = "Miércoles";
$dia_semana[4] = "Jueves";
$dia_semana[5] = "Viernes";
$dia_semana[6] = "Sábado";
$dia_semana[7] = "Domingo";

Y seguimos con el array de los meses.

PHP
$mes[1] = "Enero";
$mes[2] = "Febrero";
$mes[3] = "Marzo";
$mes[4] = "Abril";
$mes[5] = "Mayo";
$mes[6] = "Junio";
$mes[7] = "Julio";
$mes[8] = "Agosto";
$mes[9] = "Septiembre";
$mes[10] = "Octubre";
$mes[11] = "Noviembre";
$mes[12] = "Diciembre";

En realidad la clave no es necesaria, pero si no la ponemos tenemos que tener en cuenta que los arrays siempre comienzan por cero, así que sería algo así:

PHP
$mes[] = "Enero"; // equivale a $mes[0] = "Enero";
$mes[] = "Febrero"; // equivale a $mes[1] = "Febrero";

Así que el mes “01” nos devolverá “Febrero”, por lo que para que funcione correctamente le tendríamos que restar 1 a la clave, algo así:

PHP
$mes[clave – 1]; // ----> la clave "01" menos 1 es 0 y ya correspondería con el valor "Enero".

Aún así, nos podemos encontrar con el problema de que el array $mes ya exista, entonces al crear un nuevo elemento sin la clave, este se indexará con el número que le corresponda según su posición en el array, por ejemplo, supongamos que tenemos el array $mes con cuatro elementos:

PHP
$mes[0] = "Enero";
$mes[1] = "Febrero";
$mes[2] = "Marzo";
$mes[3] = "Abril";

Ahora insertamos un nuevo elemento sin clave:

PHP
$mes[] = "Enero";

Este se indexará igual que si hubiéramos puesto:

PHP
$mes[4] = "Enero";

Por eso usaremos claves numéricas al crear el array, así evitaremos estos pequeños inconvenientes. En muchas ocasiones nos irá muy bien insertar elementos en nuestros arrays sin poner clave, ya que así no importa cuantos elementos tenga, siempre se insertará al final de este, pero este no es el caso.

Entonces, ya tenemos creados los arrays $dia_semana y $mes, y ahora las siguientes líneas serían algo así:

PHP
$ts_fecha = strtotime($fecha); // Convertimos la fecha en TimeStamp para poder trabajarla con date(). 
$numero_dia_semana = date("N", $ts_fecha); // Extraemos el número de día de la semana, de 1 a 7.
$numero_mes = date("m", $ts_fecha); // Extraemos el número del mes sin cero inicial, de 1 a 12.
$fecha_formateada = $dia_semana[$numero_dia_semana] . ", " . date("j", $ts_fecha) . " de " . $mes[$numero_mes] . " de " . date("Y", $ts_fecha); // Lo juntamos todo para formar la cadena.

Esto sería en cuatro líneas, pero podemos hacerlo en unas cuantas líneas más, que aunque tiene más código, se ve más claro:

PHP
$ts_fecha = strtotime($fecha); // Convertimos la fecha en TimeStamp para poder trabajarla con date().
$numero_dia_semana = date("N", $ts_fecha); // Extraemos el número de día de la semana, de 1 a 7.
$dia_mes = date("j", $ts_fecha); // Extraemos el número de día del mes sin cero inicial, de 1 a 31.
$numero_mes = date("m", $ts_fecha); // Extraemos el número del mes sin cero inicial, de 1 a 12.
$ano = date("Y", $ts_fecha); // Extraemos el número del año en cuatro dígitos.
$fecha_formateada = $dia_semana[$numero_dia_semana] . ", " . $dia_mes . " de " . $mes[$numero_mes] . " de " . $ano; // Lo juntamos todo para formar la cadena.

En los dos casos anteriores el resultado es exactyamente el mismo:

PHP
$fecha_formateada = "Viernes, 23 de Enero de 2015";

Tengo que reconocer que yo suelo usar el primer método, para no usar tantas variables intermedias, pero creo que es más inteligente usar el segundo, ya que todo lo que sea facilitar la lectura se agradece enormemente cuando tienes que repasar el código una y otra vez buscando ese error que te deja en ocasiones clavado varias horas (Tomo nota).

En definitiva, el código para transformar una fecha tomada de una base de datos MySQL en algo agradable a la vista (2015-01-23 —-> Viernes, 23 de Enero de 2015) quedaría en esto:

PHP
$dia_semana[1] = "Lunes";
$dia_semana[2] = "Martes";
$dia_semana[3] = "Miércoles";
$dia_semana[4] = "Jueves";
$dia_semana[5] = "Viernes";
$dia_semana[6] = "Sábado";
$dia_semana[7] = "Domingo";

$mes[1] = "Enero";
$mes[2] = "Febrero";
$mes[3] = "Marzo";
$mes[4] = "Abril";
$mes[5] = "Mayo";
$mes[6] = "Junio";
$mes[7] = "Julio";
$mes[8] = "Agosto";
$mes[9] = "Septiembre";
$mes[10] = "Octubre";
$mes[11] = "Noviembre";
$mes[12] = "Diciembre";

$ts_fecha = strtotime($fecha); // Convertimos la fecha en TimeStamp para poder trabajarla con date().
$numero_dia_semana = date("N", $ts_fecha); // Extraemos el número de día de la semana, de 1 a 7.
$dia_mes = date("j", $ts_fecha); // Extraemos el número de día del mes sin cero inicial, de 1 a 31.
$numero_mes = date("m", $ts_fecha); // Extraemos el número del mes sin cero inicial, de 1 a 12.
$ano = date("Y", $ts_fecha); // Extraemos el número del año en cuatro dígitos.
$fecha_formateada = $dia_semana[$numero_dia_semana] . ", " . $dia_mes . " de " . $mes[$numero_mes] . " de " . $ano; // Lo juntamos todo para formar la cadena.

Ahora vamos a hacer lo contrario, imaginemos que tenemos la fecha en ese formato bonito y queremos guardarla en una base de datos MySQL. Veamos como transformarla.

Supongamos que en la variable $fecha tenemos esto:

PHP
$fecha = "Viernes, 23 de Enero de 2015";

No podemos usar la función date() ya que la fecha en formato bonito no es reconocida como tal por PHP, así que usaremos la funcion explode(), que lo que hace es dividir una cadena y agregar los elementos resultantes en un array, las divisiones las hace a través de un patrón definido, me explico:

PHP
$array_fecha = explode(" ", $fecha); //Patrón de búsqueda, un espacio en blanco.

Nos daría como resultado esto:

PHP
$array_fecha[0] = "Viernes,";
$array_fecha[1] = "23";
$array_fecha[2] = "de";
$array_fecha[3] = "Enero";
$array_fecha[4] = "de";
$array_fecha[5] = "2015";

El patrón a buscar para dividir por ahí es un espacio, y el resultado nos deja bastante claro como tenemos que continuar. Igual que antes, haremos un array para convertir los meses de texto a números. Para el día y el año no hace falta, ya los tenemos en $array_fecha[1] y $array_fecha[5].

PHP
$mes["Enero"] = "01";
$mes["Febrero"] = "02";
$mes["Marzo"] = "03";
$mes["Abril"] = "04";
$mes["Mayo"] = "05";
$mes["Junio"] = "06";
$mes["Julio"] = "07";
$mes["Agosto"] = "08";
$mes["Septiembre"] = "09";
$mes["Octubre"] = "10";
$mes["Noviembre"] = "11";
$mes["Diciembre"] = "12";

Pues ya acabamos igual que antes:

PHP
$dia = $array_fecha[1];
$texto_mes = $array_fecha[3];
$ano = $array_fecha[5];

if ($dia >= 1 && $dia <= 9) { // Si es del 1 al 9 añade un cero delante.
  $dia = "0" . $dia;
}

$fecha_formateada = $ano . "-" . $mes[$texto_mes] . "-" . $dia;

El condicional “if” solo es necesario si en la fecha bonita los nueve primeros días los trae sin el cero, que es lo más lógico. Eso lo hacemos para mantener el formato de dos dígitos.

Con esto ya hemos transformado la fecha (Viernes, 23 de Enero de 2015 —-> 2015-01-23), y podremos insertarla en una base de datos, o compararla con otras fechas.

El código al completo sería el siguiente:

PHP
$mes["Enero"] = "01";
$mes["Febrero"] = "02";
$mes["Marzo"] = "03";
$mes["Abril"] = "04";
$mes["Mayo"] = "05";
$mes["Junio"] = "06";
$mes["Julio"] = "07";
$mes["Agosto"] = "08";
$mes["Septiembre"] = "09";
$mes["Octubre"] = "10";
$mes["Noviembre"] = "11";
$mes["Diciembre"] = "12";
$dia = $array_fecha[1];
$texto_mes = $array_fecha[3];
$ano = $array_fecha[5];

if ($dia >= 1 && $dia <= 9) { // Si es del 1 al 9 añade un cero delante.
  $dia = "0" . $dia;
}

$fecha_formateada = $ano . "-" . $mes[$texto_mes] . "-" . $dia;

Para comparar fechas es tan fácil como comprobar si una fecha es menor, igual o mayor que la otra. Por ejemplo:

PHP
$fecha_bd = "2015-01-05"; // 5 de Enero de 2015.
$fecha_hoy = date("Y-m-d"); // fecha del momento actual (hoy).
$ts_fecha_bd = strtotime($fecha_bd); // TimeStamp de $fecha_bd.
$ts_fecha_hoy = strtotime($fecha_hoy); //TimeStamp de $fecha_hoy.

if ($ts_fecha_bd < $ts_fecha_hoy) {
  echo "La fecha $fecha_bd de la base de datos es anterior a hoy";
elseif ($ts_fecha_bd == $ts_fecha_hoy) {
  echo "La fecha $fecha_bd de la base de datos es hoy";
else {
  echo "La fecha $fecha_bd de la base de datos aún está por llegar";
}

Esto es todo por hoy. En este artículo hemos dado un repaso a una de las muchísimas formas que hay en PHP de formatear fechas y compararlas. Tengo pendiente hacer otro para lo mismo pero en Javascript, ya que muchas veces tenemos que comparar fechas y formatearlas en el lado del cliente. Sí, se pueden subir al servidor las fechas vía Ajax, trabajarlas en PHP y devolverlas al cliente, pero si no es del todo necesario me parece una pérdida de tiempo y recursos, es mucho más eficiente hacerlo en Javascript.

Si has llegado hasta aquí abajo, entiendo que te ha parecido interesante, así que me alegro, por ti, y por mí.

PHP
<?php

if ($visitante == "amigo" || $visitante == "amiga") {
  echo "Un abrazo.";
} else {
  echo "Un saludo.";
}

?>