Fechas, malditas fechas

Este flash viene con dedicatoria a mi socio Inge, el cual vive traumatizado con el manejo de las fechas.

Desde que la computación trascendió la barrera del idioma inglés han surgido nuevos retos para los que hacemos programas multilingües.  El fenómeno se conoce como internacionalización y localización (abreviado i18n).  Uno de sus aspectos es el tratamiento de las fechas de acuerdo a su representación en diferentes regiones; he visto programas colapsar dramáticamente debido a la asunción de un formato específico de fecha.  Aunque los motores actuales de bases de datos también cuentan con herramientas para i18n, es práctica común almacenar los datos en un formato específico y centrar los esfuerzos en la interfaz de usuario.  Pero ello requiere el dominio de las herramientas que proporcionan los lenguajes de programación para la conversión entre los formatos de datos en su recorrido bidireccional entre el punto de almacenamiento (memoria, base de datos, archivo de texto, etc) <-> procesamiento <-> interfaz de usuario.

Siguiendo con las fechas, C# cuenta con una función muy útil para convertir cadenas de caracteres que contienen fechas en un formato predeterminado a variables DateTime de .NET:  DateTime.ParseExact().  El gran problema con las fechas consiste en su variedad de representaciones con la consiguiente ambiguedad, ej.

  • Mes – Dia – Año
  • Mes / Día / Año
  • Dia / Mes / Año
  • Febrero 14, 2011
  • 3 / 4 / 2011  puede ser 3 de abril ó 4 de marzo en dependencia de si usamos formato español o norteamericano.
  • etc

Pues bien, ParseExact( ) permite que le indiquemos exactamente cual es el formato esperado, imagine Ud los problemas que pudiera traer confundir el mes con el día !!!  Su documentación completa se puede encontrar en http://msdn.microsoft.com/es-es/library/w2sa9yss.aspx.  A modo de resumen, acepta como parámetros una cadena que debe contener una fecha, una segunda cadena que especifica en qué formato esta esa fecha, y un objeto IFormatProvider.  Vista hace fe, veamos el siguiente ejemplo:

Obtenemos de una base de datos una cadena que contiene una fecha (fecha_original) en formato estándar Mes/Dia/Año y debemos calcular una fecha de expiración de 5 meses después y devolver el resultado nuevamente como cadena:

CultureInfo provider = CultureInfo.InvariantCulture;
DateTime fecha_original_datetime = DateTime.ParseExact(fecha_original, "M/d/yyyy", provider);
string fecha_expiracion = DateTime.Now.AddMonths(5).ToString("M/d/yyyy");

Note que la conversión inversa se realiza con la función ToString( ) y un parámetro que también especifica el formato deseado.  Los formatos pueden ser predeterminados de .NET (http://msdn.microsoft.com/es-es/library/az4se3k1.aspx) o definidos por nosotros según las reglas en http://msdn.microsoft.com/es-es/library/8kb3ddd4.aspx .  Ojo pues como su nombre lo indica, los formatos deben ser EXACTOS, no es lo mismo dia 01 que día 1.

Fácil cierto?  Feliz programación con fechas 🙂

Cuando los milisegundos cuentan: conversión de hexadecimal a bytes y viceversa

El progreso de la industria y la ciencia de la computación coloca a nuestra disposición cada día un número mayor de herramientas y frameworks de alto nivel, que nos permiten concentrar nuestros esfuerzos en la modelización y conceptualización de las aplicaciones que desarrollamos. Ello sin duda es muy bueno, recuerdo que hace veinte años para construir un software había que estar al tanto de hasta el último pixel de la interfaz.  Sin embargo, percibo que los nuevos programadores, excepto quizás en algunos círculos académicos han ido perdiendo todo aquel bagaje de pequeños y útiles algoritmos de la época en que el hardware obligaba a optimizar el código desde la misma concepción.

El desarrollo de aplicaciones servidores que atienden miles o millones de clientes simultáneamente obliga a rescatar aquellos “trucos” en aras de un uso óptimo de los recursos de hardware. Su empleo puede marcar la diferencia por ejemplo entre un servidor que responde y uno “muerto”.  A continuación les dejo un ejemplo en C# que ilustra el planteamiento anterior, se trata de convertir una cadena de caracteres hexadecimales al correspondiente arreglo de bytes y viceversa.

Dada la cadena:

string hex = “AABBCCDDEEFF00112233445566778899”;

construiremos una función ConvertHexStringToByteArray que retorne el arreglo de bytes:

{byte[16]}    [0]: 170    [1]: 187    [2]: 204    [3]: 221    [4]: 238    [5]: 255    [6]: 0    [7]: 17    [8]: 34    [9]: 51    [10]: 68    [11]: 85    [12]: 102    [13]: 119    [14]: 136    [15]: 153

y otra ConvertByteArrayToString que tomando como parámetro el arreglo retorne la misma cadena en hex.

Usando las primitivas de C# quedarían:

public static byte[] ConvertHexStringToByteArray_Method1(string hex)
        {
            if (hex.Length % 2 != 0)
                throw new Exception("La cadena no puede ser impar");

            byte[] result = new byte[hex.Length / 2];
            for (int i = 0; i < hex.Length / 2; i++)
            {
                result[i] = byte.Parse(hex.Substring(2 * i, 2), System.Globalization.NumberStyles.HexNumber);
            }
            return result;
        }

        public static string ConvertByteArrayToHexString_Method1(byte[] b)
        {
            StringBuilder sb = new StringBuilder(b.Length * 2);

            for (int i = 0; i < b.Length; i++)
            {
                sb.Append(b[i].ToString("X2"));
            }
            return sb.ToString();
        }

Pero sucede que en la práctica realizar miles o millones de iteraciones de estas funciones resulta extremadamente costoso debido principalmente a la implementación de la función Parse de .NET y a la conversión de un carácter a hexadecimal usando ToString(“X2”).  Las versiones a continuación, basadas en técnicas simples de la aritmética binaria, cumplen la misma funcionalidad y resultan cientos de milisegundos más eficientes que las originales.

public static string ConvertByteArrayToHexString_Method2(byte[] b)
        {
            char[] c = new char[b.Length * 2];
            byte curByte;

            for (int y = 0, x = 0; y < b.Length; ++y, ++x)
            {
                curByte = ((byte)(b[y] >> 4));
                c[x] = (char)(curByte > 9 ? curByte + 0x37 : curByte + 0x30);
                curByte = ((byte)(b[y] & 0xF));
                c[++x] = (char)(curByte > 9 ? curByte + 0x37 : curByte + 0x30);
            }

            return new string(c);
        }

        public static byte[] ConvertHexStringToByteArray_Method2(string hex)
        {
            if (hex.Length % 2 != 0)
                throw new Exception("La cadena no puede ser impar");

            byte c1, c2;
            byte[] result = new byte[hex.Length / 2];
            int l = hex.Length;

            for (int i = 0, cur = 0; i < l; i++, cur++)
            {
                c1 = (byte)hex[i];
                if (c1 > 0x60) c1 -= 0x57;
                else if (c1 > 0x40) c1 -= 0x37;
                else c1 -= 0x30;
                c2 = (byte)hex[++i];
                if (c2 > 0x60) c2 -= 0x57;
                else if (c2 > 0x40) c2 -= 0x37;
                else c2 -= 0x30;
                result[cur] = (byte)((c1 << 4) + c2);
            }
            return result;
        }

Estos son los resultados obtenidos en un mini benchmarking de 10000 iteraciones con ambos métodos:

Byte 2 Hex: Method 1: 469 ms – Method 2: 100 ms

Hex 2 Byte: Method 1: 44 ms – Method 2: 5 ms

Finalmente, tampoco se trata de sacrificar claridad por optimización, al menos en las primeras etapas del desarrollo pero, cuando los milisegundos cuentan …

.NET 4 y Web Semántica: el inicio de un romance

Uno de los Principios de Diseño (para una arquitectura web) de Tim Berners-Lee es el Principio de la Menor Potencia (Principle of Least Power). Referente a los lenguajes de programación plantea que “… En la actualidad debemos tomar en consideración una razón para escoger la solución menos potente en lugar de la más poderosa: mientras menos potente sea el lenguaje, más puedes lograr con los datos almacenados en el lenguaje …”.

Durante muchos años ha ocurrido cierta “discriminación” en círculos elitistas de programación hacia los lenguajes de tipificación dinámica (ej. Visual Basic anterior a VB.NET, ASP, Javascript), aquellos en los cuales el chequeo de tipos ocurre en tiempo de ejecución, opuesto a los de tipificación estática (ej. C, C++, C#, Java), para los que el chequeo de tipos se realiza durante la compilación. Lo mismo sucede con otras clasificaciones de los lenguajes como tipificación fuerte vs débil, interpretados vs compilados, entre otras. En estas élites se consideran “verdaderos programadores” a los que trabajan en lenguajes compilados y tipificados estáticamente, leáse cualquier variante con llaves { }. Para muchos, los lenguajes sin tipos o con un comodín como el Variant de Visual Basic, son algo así como demonios malignos, y realmente la asignación de un tipo de datos a cada variable y el chequeo en el proceso de compilación de su uso correcto, evita un gran número de problemas y errores durante la ejecución. Un comentarista del post I like strong typing and compilation errors plantea muy acertadamente que “la tipificación fuerte y los errores de compilación (entre ellos los relativos a la tipificación estática) son unidades de prueba (unit tests) implícitas que nunca será necesario escribir, opuesto a cuando se usa un lenguaje con tipificación dinámica, en el cual es obligatorio construir estas unidades de prueba si deseamos cierto nivel de protección contra errores de regresión”.

Pero bueno, estos debates casi que apasionados han perdurado durante mucho tiempo y seguirán así por mucho más. En la práctica, la elección del lenguaje para un proyecto depende mucho de las preferencias y/o dominio del equipo y de la misma naturaleza del trabajo en cuestión. He visto muchos proyectos implementados en Visual Basic con una calidad y diseño impecables y otros hechos en C# realmente vergonzozos.

Finalmente, aparece la version 4 de .NET y con ella la posibilidad de usar variables dinámicas en C# y el resto de los lenguajes compatibles. Muchos interpretan esto como un paso atrás, un regreso al viejo Variant del viejo Visual Basic.

Si ha leído hasta aquí, ya se estará preguntando: qué tiene que ver todo lo anterior con el Principio de la Menor Potencia y con el título del post? Muy sencillo, actualmente las tecnologías semánticas se encuentran muy cerca de alcanzar la “masa crítica” de popularidad y con ello los “lenguajes discriminados” o mejor, los conceptos en los están basados, se convierten en poderosas herramientas para el tratamiento de las estructuras de datos semánticas. Pensemos en XML, RDF, OWL, que permiten representar objetos eminentemente dinámicos; resulta bastante díficil encasillarlos en un sistema de tipos estáticos. Con la introducción de las características dinámicas en .NET 4, evidentemente Microsoft está abriendo el camino a los desarrolladores hacia la Web Semántica (además de otras aplicaciones, por supuesto). Sin embargo, creo que aún queda mucho camino por recorrer.

En los últimos días he estado experimentando un poco con el “nuevo dinamismo” de C#, en parte motivado por la “asignatura pendiente” de mi post Consultando RDF con SPARQL en C# (2da parte), y con la esperanza de que el problema presentado allí sería ahora mucho más simple de resolver. Concretamente, se trata de construir progresivamente una lista genérica de objetos de una clase diferente en cada ejecución; por tanto debe ser una clase dinámica. La definición de la clase en si, proviene del resultado de una consulta SPARQL a un almacén RDF y consiste de un grupo de nombres de variables (definidas por el usuario en el texto de la consulta). La ejecución de la consulta retorna en cada paso los nombres de estas variables (miembros de la clase dinámica) y sus valores, conformando ambos un nuevo objeto para agregar a la lista. Una vez obtenidos todos los resultados se desea mostrarlos en una tabla (DataGridView) empleando las capacidades de enlace de datos (data binding).

Leer más de esta entrada