Mostrar fechas relativas estilo Facebook en nuestras aplicaciones

Post original en JASoft.org: http://www.jasoft.org/Blog/post/Mostrar-fechas-relativas-estilo-Facebook-en-nuestras-aplicaciones.aspx

Este es un truco sencillo pero útil que puede ayudar a hacer más amigables las fechas de cara a los usuarios.

Generalmente cuando mostramos una fecha y hora en una de nuestras aplicaciones tendemos a mostrar este dato de la forma habitual, es decir, visualizando la fecha y la hora en el formato que sea apropiado para el idioma y país actuales. Sin embargo para la mayoría de los usuarios esta información no les dice gran cosa y tienen que fijarse y hacer cálculos mentales para hacerse una idea de cuándo es exactamente ese evento, sea en el pasado o en el futuro.

En este sentido sería mucho más útil mostrarle al usuario una fecha indicada de un modo más amigable, relativo al momento actual y de forma que resulte más informativa para el usuario medio.

Así, en lugar de mostrarles simplemente la fecha, resultaría mucho más útil decirles cosas como "Hace 3 minutos", "Dentro de 2 días" o expresiones similares. Incluso podríamos mostrarle ambas cosas, por ejemplo expresiones como las anteriores y además la fecha y hora completas en un "Tooltip" por si quieren afinar.

Un gran ejemplo de ello es lo que hace Facebook:

IntervaloRelativoEnTexto

 

Si te fijas muestra la fecha en la que se produjo un evento (en este caso el envío de un post) en un formato amigable y fácil de entender para el usuario, y si dejas el cursor encima muestra la fecha y hora completas en un Tooltip.

Conseguirlo es muy sencillo y además gracias al uso de los métodos extensores es realmente fácil de usar en cualquier página ASPX o aplicación de escritorio, incluso en expresiones enlazadas a datos.

Para ello he construido un método extensor que extiende a la clase DateTime para añadirle un nuevo método DiferenciaEnTexto. Con él para obtener los intervalos de tiempo de manera similar a la de Facebook basta con escribir:

miFecha.DiferenciaEnTexto()

Y obtendremos una cadena igual.

El código necesario en C# es el siguiente:

   1: public static class DateTimeExt

   2: {

   3:     // Método extensor para obtener las fechas en formato relativo de texto (hace 3 segundos...)

   4:     public static string DiferenciaEnTexto(this DateTime fecha)

   5:     {

   6:         //Fecha y hora actuales

   7:         DateTime ahora = DateTime.Now;

   8:         

   9:         //Variables

  10:         TimeSpan diferencia;

  11:         string prefijo = "Hace {0} {1}";    // Para construir, por defecto, "Hace 5 minutos" o similares

  12:         

  13:         diferencia = ahora.Subtract(fecha);

  14:         //Compruebo si es un momento en el pasado o en el futuro

  15:         if (diferencia.Ticks < 0) // Momento en el pasado

  16:         {

  17:             prefijo = "Dentro de {0} {1}"; // para momentos en el futuro, y construir "Dentro de 5 minutos"

  18:             diferencia = diferencia.Negate();    // Lo dejo en positivo para las comprobaciones de rangos

  19:         }

  20:         

  21:         if (diferencia.Days >= 7)    //Hace una semana o más

  22:             return String.Format("El {0} a las {1}", fecha.ToLongDateString(), fecha.ToLongTimeString());

  23:         else if (diferencia.Days >= 2)    //Entre 1 día y una semana

  24:             return String.Format(prefijo, diferencia.Days, "días");

  25:         else if (diferencia.Days == 1) //Hace un día, o sea, ayer

  26:             return "ayer";

  27:         else if (diferencia.Hours >= 2)

  28:             return String.Format(prefijo, diferencia.Hours, "horas");

  29:         else if (diferencia.Minutes >= 2)

  30:             return String.Format(prefijo, diferencia.Minutes, "minutos");

  31:         else 

  32:             return String.Format(prefijo, Math.Floor(diferencia.TotalSeconds), "segundos");

  33:  

  34:     }

Como vemos se trata de un código muy sencillo.

Defino una clase estática que actuará de contenedora del método extensor. el método DiferenciaEnTexto es extensor porque es estático y su único parámetro lleva la palabra clave "this" delante y es de tipo DateTime, así que extiende a este tipo.

El código funciona indistintamente con fechas en el pasado y en el futuro. Si las fechas son negativas (en el futuro) hace uso del método Negate de la clase TimeSpan para actuar sobre el intervalo siempre en positivo, cambiando la frase mostrada.

Sólo se muestra la fecha y hora en formato habitual cuando hay más de 7 días de diferencia, aunque podríamos cambiarlo fácilmente para cualquier intervalo menos o mayor en el primer condicional. Para los segundos entre 0 y 119 (dos minutos) he utilizado la propiedad TotalSeconds de TimeSpan para expresar el intervalo concreto en segundos, y no sólo los segundos que sobran del minuto, que sería lo que obtendríamos usando simplemente la propiedad Seconds. Esto sólo lo hago en los segundos porque en este intervalo tan pequeño sí que se necesita más precisión, pero no así en otros más amplios como minutos y sucesivos. Además se redondea a la fracción mínima de segundo ya que TotalSeconds devuelve valores decimales pues es un cálculo con mucha precisión.

Si quieres puedes descargarte el ejemplo desde DiferenciaEnTexto.zip (894 bytes).

¡Espero que te sea útil!

Mi aplicación Web tarda muchísimo en arrancar: ¿Usas ensamblados firmados quizá?

Post original en JASoft.org: http://www.jasoft.org/Blog/post/Mi-aplicacion-Web-tarda-muchisimo-en-arrancar-191;Usas-ensamblados-firmados-quiza.aspx

Este es uno de esos problemas raros que, si nadie te lo cuenta, es dificilísimo que llegues a solucionar por tu cuenta. Y si lo haces vas a estar días rompiéndote la cabeza antes de descubrir qué pasa…

Seguramente conocerás (o te sonará al menos) la tecnología Authenticode de Microsoft. Se trata de una forma de firma digital que nos permite verificar el origen de un código ejecutable (EXE o DLL), un documento o cualquier otro tipo de archivo.

El uso de una firma Authenticode permite verificar el desarrollador de una aplicación, de modo que puedas estar seguro de que aquel programa que te bajaste de Internet es una copia legítima generada por su creador, y no una versión modificada  que contenga algún troyano u otros virus. Es decir, no prueba que el ejecutable sea inofensivo, sino que prueba que al menos lo ha creado alguien concreto. ahora ya es cosa tuya si confías o no en ese determinado fabricante.

Así, al ejecutar un programa que te has descargado de Internet y que está firmado por un fabricante válido verás un diálogo similar a este:

Authenticode_Bien

 

Sin embargo cuando el ensamblado no está firmado (o no tiene un certificado válido), el aviso cambia e indica el peligro de ejecutarlo:

Authenticode_Mal

 

También puedes ver si un ensamblado está firmado o no usando sus propiedades desde el explorador de Windows, ya que ofrece una pestaña "Certificados" para ello. Este, por ejemplo, es el certificado de Excel.exe en mi equipo:

Authenticode_Cert
Pulsa para aumentar

Firmar un ensamblado es muy sencillo puesto que sólo necesitamos un certificado digital válido (que se puede adquirir on-line en multitud de sitios, normalmente en los mismos en los que compras certificados SSL), y usar la utilidad signcode.exe que viene con .NET:

signcode.exe -spc miCertificado.spc -v miClavePrivada.pvk miEnsamblado.exe

Es importante firmar los ensamblados en algunas ocasiones porque nos ayudan a poder usarlos en empresas en las que existen políticas de seguridad y sólo admiten ensamblados firmados. Adicionalmente, si los distribuimos por Internet, nos aseguramos de que no van a ser modificados sin nuestro consentimiento y le ofrecemos a los usuarios una forma de verificar que son auténticos.

¿Qué pasa con las aplicaciones Web?

Es más habitual de lo que creemos usar ensamblados firmados con Authenticode en aplicaciones Web. Por ejemplo, muchas bibliotecas de controles comerciales vienen firmadas de esta manera.

Obviamente, cuando se ejecutan en una aplicación Web no se muestran los diálogos de confirmación que hemos visto, ya que son aplicaciones sin interfaz, o mejor dicho, cuya interfaz se muestra en remoto, en el navegador. Entonces ¿qué se hace con ellos?

Pues hasta la llegada de .NET 4.0, en la que ha cambiado el sistema de seguridad por completo, lo que hace la CLR cuando carga un ensamblado firmado con Authenticode es tratar de verificar la firma. Este proceso implica conectarse a la red o a Internet (dependiendo de si el certificado usado es privado o de una entidad certificadora pública, que es lo más común), descargarse listas de revocación de certificados y también seguir la cadena de certificación hasta el certificado raíz.

En resumen: puede ser un procedimiento un poco lento que lleve incluso varios segundos.

En el caso de IIS  este proceso se realiza para cada nuevo hilo de ejecución, por lo que el efecto se multiplica.

Y lo que es peor: si no hay conexión a Internet, al cabo de unos cuantos segundos (15 o 20), se desestimará la comprobación del ensamblado pero habremos demorado su carga bastante tiempo. En las siguientes peticiones respondidas con este hilo ya se ejecutará bien, pues sólo se carga una vez por proceso.

El resultado es que en servidores que no tengan conexión a Internet y usen ensamblados firmados con Authenticode veremos demoras enormes a la hora de lanzar la aplicación. Una pesadilla de rendimiento difícil de solucionar si no sabemos este detalle. Aunque tengamos conexión a Internet el proceso normal de comprobación puede tardar un par de segundos, por lo que incluso en ese caso sería interesante desactivar esta comprobación si estamos seguros de la identidad del ensamblado.

Desactivar la comprobación de la firma

Existe un ajuste que podemos hacer en el archivo Machine.config (ubicado en C:WindowsMicrosoft.NETFramework64v2.0.50727CONFIG) que se denomina generatePublisherEvidence.

Éste permite desactivar la comprobación de las firmas evitando el proceso y por lo tanto eliminando la demora en la carga de los ensamblados.

Basta con escribir dentro de machine.config lo siguiente:

   1: <configuration>

   2:     <runtime>

   3:         <generatePublisherEvidence enabled="false"/>

   4:     </runtime>

   5: </configuration>

Con esto eliminaremos el problema.

IMPORTANTE: Reitero que esto sólo es aplicable para aplicaciones previas a .NET 4.0, es decir, hasta .NET 3.5 SP1. El motivo es que .NET 4.0, comoe s sabido, ha cambiado el modo de funcionamiento del sistema de seguridad CAS.

¡Espero que te sea útil!