martes, 22 de mayo de 2012

Conversión de una fecha a una cadena

Cuando es importante el que el tiempo de respuesta sea mínimo, la conversión a cadenas de tipos complejos, pero comunes, puede volverse importante. Un ejemplo de esto, es la conversión de fechas a cadenas. La mayoría de los APIs de generación de bitácoras, tocan el tema como un factor importante en el desempeño de una aplicación, puesto que la generación de las bitácoras, pueden influir drásticamente en tiempo de respuesta, dada la cantidad de las mismas que pueden generarse.

Para convertir una fecha a una cadena, en un formato determinado, particularmente sin utilizar los nombres de los días o de los meses, se puede realizar utilizando un arreglo de caracteres y utilizando funciones matemáticas simples.

01: package org.test.util;
02: 
03: import java.util.Calendar;
04: 
05: public final class DateTimeUtil {
06: 
07:         private static final Calendar calendar;
08:         private static final char[] buffer;
09: 
10:         static {
11:                 calendar = Calendar.getInstance();
12:                 buffer = new char[23];
13:         }
14: 
15:         private DateTimeUtil() {
16:         }
17: 
18:         /**
19:          * formato equivalente a "yyyy/MM/dd HH:mm:ss,SSS"
20:          */
21:         public static String formatDate(long time) {
22:                 int year;
23:                 int month;
24:                 int day;
25:                 int hour;
26:                 int minute;
27:                 int second;
28:                 int millisecond;
29:                 synchronized (calendar) {
30:                         calendar.setTimeInMillis(time);
31:                         year = calendar.get(Calendar.YEAR);
32:                         month = calendar.get(Calendar.MONTH);
33:                         day = calendar.get(Calendar.DAY_OF_MONTH);
34:                         hour = calendar.get(Calendar.HOUR_OF_DAY);
35:                         minute = calendar.get(Calendar.MINUTE);
36:                         second = calendar.get(Calendar.SECOND);
37:                         millisecond = calendar.get(Calendar.MILLISECOND);
38:                 }
39:                 synchronized (buffer) {
40:                         buffer[0] = (char) ('0' + year / 1000);
41:                         year %= 1000;
42:                         buffer[1] = (char) ('0' + year / 100);
43:                         year %= 100;
44:                         buffer[2] = (char) ('0' + year / 10);
45:                         year %= 10;
46:                         buffer[3] = (char) ('0' + year);
47:                         buffer[4] = '/';
48:                         buffer[5] = (char) ('0' + month / 10);
49:                         month %= 10;
50:                         buffer[6] = (char) ('0' + month);
51:                         buffer[7] = '/';
52:                         buffer[8] = (char) ('0' + day / 10);
53:                         day %= 10;
54:                         buffer[9] = (char) ('0' + day);
55:                         buffer[10] = ' ';
56:                         buffer[11] = (char) ('0' + hour / 10);
57:                         hour %= 10;
58:                         buffer[12] = (char) ('0' + hour);
59:                         buffer[13] = ':';
60:                         buffer[14] = (char) ('0' + minute / 10);
61:                         minute %= 10;
62:                         buffer[15] = (char) ('0' + minute);
63:                         buffer[16] = ':';
64:                         buffer[17] = (char) ('0' + second / 10);
65:                         second %= 10;
66:                         buffer[18] = (char) ('0' + second);
67:                         buffer[19] = ',';
68:                         buffer[20] = (char) ('0' + millisecond / 100);
69:                         millisecond %= 100;
70:                         buffer[21] = (char) ('0' + millisecond / 10);
71:                         millisecond %= 10;
72:                         buffer[22] = (char) ('0' + millisecond);
73:                         return new String(buffer);
74:                 }
75:         }
76: 
77: }

Cómo se puede observar, se utiliza una sola instancia de java.util.Calendar, así como una sola instancia de un arreglo de caracteres, con el fin de minimizar el uso de memoria. Debido a esto, es necesario sincronizar el código que extrae el año, mes, día, hora, minuto, segundo y milisegundo. Posteriormente se libera el objeto calendar y se sincroniza el código que realiza el formateo los datos extraídos previamente, esto utilizando las operaciones división, suma y modulo. Finalmente, se construye una cadena con el arreglo de caracteres.

El formato es fijo, por lo que no hay necesidad de tratar de detectar posibles opciones. No se utilizan formatos que dependan del lenguaje, por lo que la longitud, también es fija. Esto en conjunto ayuda a minimizar el tiempo de respuesta, dando una opción al uso de la clase java.util.SimpleDateFormat, la cual al ser una clase genérica, puede tardar un poco más, debido a la necesidad de resolver las situaciones con el idioma.

jueves, 17 de mayo de 2012

Constantes dinámicas

En ocasiones es neceasario cargar valores constantes de un archivo de configuración. Estos archivos de configuración se cargan en memoria parseando el archivo y posteriormente obteniendo la propiedad respectiva de objeto que resulta del parseo del archivo. Incluso puede exiatir la neceaidad de realizar la conversión de la esta a número, fecha, booleano, etc..

El siguiente código es común encontrarlo en cualquier desarrollo (se presume que la variable properties es del tipo java.util.Properties):


 while (condition) {

  System.out.println(Float.parseFloat(properties
    .getProperty("percent")));
  System.out.println(currentData.getCurrentAmount()
    * Float.parseFloat(properties.getProperty("percent")));
  float amount = currentData.getCurrentAmount()
    * Float.parseFloat(properties.getProperty("percent"));
  save(amount);

 }

Donde, como se puede ver, se parsea hasta 3 veces la misma propiedad. Para solventar este problema, es posible declarar en la clase (o en una clase auxiliar) esta propiedad, incluso realizando la conversión previamente.


  private static final float PERCENT = Float.parseFloat(properties
   .getProperty("percent"));

Y en el código anterior: 

  while (condition) {
    System.out.println(PERCENT);
    System.out.println(currentData.getCurrentAmount() * PERCENT);
    float amount = currentData.getCurrentAmount() * PERCENT;
    save(amount);
  }

Con lo cual se evita el procesamiento adiciónal de recuperar la propiedad y posteriormente realizar la conversión.