Buenas tardes, en esta entrada vamos a ver un poco el uso de Date, Numbers y Currency en Java.
La API de Java provee un extenso set de clases para ayudarnos a la hora de trabajar con fechas, números y monedas. Cuando finalicemos esta sección deberíamos tener un sólido conocimiento en tareas como crear una nueva fecha y un objeto DateFormat, convertir Strings a Dates y de nuevo, realizar una función Calendar, imprimiendo apropiadamente los valores de moneda formateadas, y hacer todo esto para cualquier localización del globo.
-
Trabajando con Dates, Numbers y Currencies
Si queremos trabajar con fechas en los diferentes lugares del mundo, tenemos que estar familiarizados con al menos 4 clases del package java.text y java.util. Aquí vamos a ver las 4 clases relacionadas con fechas que tenemos que entender:
- java.util.Date: La mayoría de los métodos de esta clase ya están obsoletos, pero podemos usar esta clase para trabajar junto a las clases Calendar y DateFormat. Un ainstancia de un Date representa una fecha y el tiempo, hasta milisegundos.
- java.util.Calendar: Esta clase provee una gran variedad de métodos que nos ayudan a convertir y manipular fechas y horas. Por ejemplo, si queremos añadir un mes a una fecha dada, o buscar que dia de la semana será el día 1 de Enero, los métodos de esta clase nos ayudarán.
- java.text.DateFormat: Esta clase es usada para formatear fechas no solo de estilos como “01/01/70” o “January 1, 1970”, sino tambien como formatear fechas para diferentes localizaciones en el mundo.
- java.text.NumberFormat: Esta clase es usada para formatear números y currencies para diferentes localizaciones en el mundo.
- java.util.Locale: Esta clase es usada en conjunción con DateFormat y numberFormat para formatear fechas, números y monedas para una localización específica. Con la ayuda de la clase Locale seremos capaces de convertir una fecha como “10/10/2005” a “Segunda-feira, 10 de Outubro de 2005”. Si ueremos manipular fechas sin producir una salida formateada, podemos usar la clase Locale directamente con la clase Calendar.
-
Clases relacionadas con Date y Number
Cuando trabajamos con fechas y números, a menudo usaremos clases entre ellas. Es importante entender como las clases que hemos vito encima se relacionan entre ellas, y cuando usarlas en combinacion las unas con las otras. Por ejemplo, vamos a necesitar saber que si queremos hacer un formato específico para una localización, tenemos que crear primero el objeto Locale antes de hacer el objeto DateFormat, ya que necesitamos el objeto Locale como argumento para el método de DateFormat. En la siguiente tabla vamos a proveer un pequeño resumen de los casos de uso relacionados con las fechas y los números usando estas clases. Esta tabla tambien nos traerá preguntas específicas sobre las clases individuales, y veremos casos específicos de cada clase después.
Caso de uso Pasos Obtener la fecha y hora actual 1.Creamos un Date: Date d = new Date();
2.Obtenemos el valor: String s = d.toString();Obtener un objeto que nos permita realizar calculos de fecha y hora en nuestra localización. 1.Creamos un objeto de la clase Calendar: Calendar c = Calendar.getInstance();
2.Usamos c.add(…) y c.roll(…) para realizar manipulaciones de datos.Obtener un objeto que nos permita realizar calculos en una localización diferente. 1.Creamos un objeto Locale:
Locale loc = new Locale(language);
Locale loc = new Locale(language, country);
2.Creamos un Calendar para ese Locale:
Calendar c = Calendar.getInstance(loc);
3.Usamos c.add(…) y c.roll(…) para realizar manipulaciones de fechas y horasObtener un objeto que nos permita realizar calculos de fecha y hora, y entonces formatear la salida en diferentes localizaciones con diferentes estilos de fecha. 1.Creamos un Calendar:
Calendar c = Calendar.getInstance();
2.Creamos un Locale para cada localización:
Locale loc = new Locale(…);
3.Convertimos Calendar a Date:
Date d = c.getTime();
4.Creamos un DateFormat para cada Locale:
DateFormat df = DateFormat.getDateInstance(style, loc);
5.Usamos los métodos de format() para crear fechas formateadas:
String s = df.format(d);Obtener un objeto que nos permita formatear numeros y monedas en los diferentes localizaciones: 1.Creamos un Locale para cada localizacion:
Locale loc = new Locale(…);
2.Creamos un NumberFormat:
NumberFormat nf = NumberFormat.getInstance(loc); o
NumberFormat nf = NumberFormat.getCurrencyInstance(loc);
3.Usamos el método format() para crear una salida formateada:
String s = nf.format(someNumber); -
La Clase Date
La clase Date tiene un pasado muy comprobado. El diseño de su API no hacía un buen trabajo del manejo de internacionalización y situaciones de localización. En su estado actual, la mayoría de sus métodos ya están obsoletos, y para la mayoría de los propósitos querremos usar la clase Calendar en vez de la clase Date.
Como hemos mencionado anteriormente, una instancia de la clase Date representa una fecha y una hora. Internamente, la fecha y la hora es guardada en un tipo de dato primitivo long. Específicamente, el long almacena el número de milisegundos (1000 por cada segundo), entre la fecha dada y el 1 de Enero de 1970.
Vamos a usar la clase Date para ver como de largo puede ser pasar un trillón de milisegundos, empezando desde el 1 de Enero de 1970:
import java.util.*; public class TestDates { public static void main(String[] args){ Date d1 = new Date(1000000000000L); // Un trillon System.out.println("1st Date " + d1.toString()); } }
En mi JVM, la salida es:
1st Date Sun Sep 09 03:46:40 CEST 2001
Aunque la mayoría de los métodos de Date están ya obsoletos, es aún aceptable usar los métodos getTime y setTime, aunque como veremos mas tarde, es un poco doloroso. Vamos a agregar una hora a nuestra instancia de Date d1, en el ejemplo anterior:
import java.util.*; public class TestDates { public static void main(String[] args){ Date d1 = new Date(1000000000000L); // Un trillon System.out.println("1st Date " + d1.toString()); d1.setTime(d1.getTime() + 3600000); // 3600000 milisegundos / hora System.out.println("new time " + d1.toString()); } }
La salida en mi JVM es:
1st Date Sun Sep 09 03:46:40 CEST 2001 new time Sun Sep 09 04:46:40 CEST 2001
Vemos que tanto setTime() como getTime() usamos una escala de milisegundos. Si queremos manipular fechas con la clase Date, esta es nuestra única elección. Si esto ya era dificil, imaginemos cuando se tenían que agregar, por así decirlo, un año a una fecha dada.
Volveremos a visitar la clase Date mas tarde, pero por ahora la otra cosa que necesitamos saber es que si queremos crear una instancia de Date para representar la hora y fecha actual, tenemos que usar el constructor sin argumentos de Date:
Hora actual Thu Jan 24 16:56:58 CET 2013
-
La Clase Calendar
Acabamos de ver como manipular fechas usando la clase Date, y que es bastante dificil. La clase Calendar está diseñada para hacer la manipulación de fechas mas fácil. Mientras que la clase Calendar tiene millones de campos y métodos, una vez que sepamos como manejar unos cuantos, el resto será fácil.
Cuando intentamos hacer un primer uso de la clase Calendar podemos ver que es una clase abstracta. No podemos decir:
Calendar c = new Calendar(); // Ilegal, Calendar es abstract
Para crear una instancia de Calendar, tenemos que usar uno de los métodos sobrecargados getInstance():
Calendar cal = Calendar.getInstance();
Cuando obtenemos una referencia de Calendar como cal, nuestra referencia de Calendar está actualmente refiriendose a una instancia de una subclase concreta de Calendar. No podemos saber con certeza que subclase vamos a obtener (java.util.GregorianCalendar es lo que normalmente obtendremos), pero no nos importa. Estaremos usando la API de Calendar.
Bien, ahora que tenemos una instancia de Calendar, vamos a ir a nuestro ejemplo anterior, y encontrar en que dia de la semana cae nuestro trillón de milisegundos, y vamos a agregar un mes a esa fecha:
public class TestDates { public static void main(String[] args) { Date d1 = new Date(1000000000000L); System.out.println("1st date " + d1.toString()); Calendar c = Calendar.getInstance(); c.setTime(d1); // #1 if (Calendar.MONDAY == c.getFirstDayOfWeek()) { // #2 System.out.println("Monday is the first day of the week"); } System.out.println("trillionth milli day of the week is " + c.get(Calendar.DAY_OF_WEEK)); // #3 c.add(Calendar.MONTH, 1); // #4 Date d2 = c.getTime(); // #5 System.out.println("new date " + d2.toString()); } }
Vamos a echar un vistazo al programa, enfocandonos en las lineas comentadas:
- Asignamos el Date d1 a la instancia de Calendar c.
- Usamos el campo de MONDAY para determinar si en nuestra JVM, es considerado como el primer día de la semana. La clase Calendar provee campos similares para días de la semana, meses, el día del mes, el día del año, y así con el resto.
- Usamos el campo DAY_OF_THE_WEEK para encontrar el cia de la semana en el que cae nuestro trillón de milisegundos.
- Hemos usado métodos setter y getter para guiarnos de forma intuitiva. Ahora usamos el método de Calendar add(). Estos métodos son realmente potentes y nos permiten añadir o quitar unidades de tiempo apropiadamente para el campo de Calendar que hemos especificado. Por ejemplo:
c.add(Calendar.HOUR, -4); // Extrae 4 horas del valor de C c.add(Calendar.YEAR, 2); // Añade 2 años al valor de c c.add(Calendar.DAY_OF_WEEK, -2); // Extrae 2 dias del valor de c
- Convierte el valor de c de nuevo a una instancia de Date.
Otro de los métodos de Calendar que tenemos que saber es el método roll(). Este método actúa como el método add(), excepto que cuando una parte de Date es incrementada o decrementada, las partes mayores de Date no serán incrementadas o decrementadas. Por ejemplo:
public class TestDates { public static void main(String[] args) { Calendar c = Calendar.getInstance(); c.roll(Calendar.MONTH, 13); Date d4 = c.getTime(); System.out.println("new date " + d4.toString()); } }
Vemos que el año no cambia, incluso aunque hayamos agregado 13 meses a una fecha de Enero. De una manera similar, invocar roll() con HOUR no cambiará la fecha, el mes, o el año.
-
La clase DateFormat
Ya que hemos aprendido a crear fechas y manipularlas, vamos a ver como podemos formatearlas. Aquí vamos a ver un ejemplo de como una fecha puede ser formateada de diferentes maneras:
import java.text.DateFormat; import java.util.Date; public class DateFormatTest { public static void main (String[] args){ Date d1 = new Date(1000000000000L); DateFormat[] dfa = new DateFormat[6]; dfa[0] = DateFormat.getInstance(); dfa[1] = DateFormat.getDateInstance(); dfa[2] = DateFormat.getDateInstance(DateFormat.SHORT); dfa[3] = DateFormat.getDateInstance(DateFormat.MEDIUM); dfa[4] = DateFormat.getDateInstance(DateFormat.LONG); dfa[5] = DateFormat.getDateInstance(DateFormat.FULL); for (DateFormat df : dfa){ System.out.println(df.format(d1)); } } }
En nuestra JVM produce la salida:
9/09/01 3:46 09-sep-2001 9/09/01 09-sep-2001 9 de septiembre de 2001 domingo 9 de septiembre de 2001
Examinando este código vemos un par de cosas. Lo primero, parece que DateFormat es otro tipo de clase abstract, por lo que no podemos usar new para crear instancias de DateFormat. En este caso hemos usado 2 métodos de factoría, getInstance() y getDateInstance(). Vemos que getDateInstance() está sobrecargado, cuando discutamos los Locale, veremos otra versión de getDateInstance() que tenemos que entender.
Lo siguiente, hemos usado campos static de la clase DateFormat para personalizar nuestras diferentes instancias de DateFormat. Cada uno de estos campos static representan un estilo de formateo. En este cao parece que la versión sin argumentos de getDateInstance() nos dá el mismo estilo que la version de MEDIUM del método, pero no es una regla dificil y rápida. Finalmente, hemos usado el método format() para crear Strings que representan la versión formateada apropiadamente de Date con la que hemos estado trabajando.
El último método con el que tendríamos que estar familiarizados es el método parse(). El método parse() coge un String formateado con un estilo de la instancia de DateFormat que está siendo usado, y convierte el String en un objeto Date. Como podemos imaginar, el método parse() tiene muchos riesgos ya que puede recibir un String mal formado.Por esto, el método parse() puede lanzar un ParseException. El siguiente código crea una instancia de Date, usando DateFormat.format() para convertirlo en un String, y entonces usa DateFormat.parse() para cambiarlo de nuevo a Date.
public class DateFormatTest { public static void main (String[] args){ Date d1 = new Date(1000000000000L); System.out.println("d1 = " + d1.toString()); DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT); String s = df.format(d1); System.out.println(s); try{ Date d2 = df.parse(s); System.out.println("parsed = " + d2.toString()); }catch (ParseException pe){ System.out.println("Parse exc"); } } }
En mi JVM produce lo siguiente:
d1 = Sun Sep 09 03:46:40 CEST 2001 9/09/01 parsed = Sun Sep 09 00:00:00 CEST 2001
-
La Clase Locale
Antes dijimos que una gran parte de esta parte consistía en la habilidad de hacer diferentes tareas de internacionalización. La clase Locale es nuestro ticket a la dominación del mundo. Ambas clases, Dateformat y NumberFormat pueden usar instancias de Locale para personalizar el formato de salida para que sea específico para una localización. ¿Como define Java un Locale?. La API dice que un Locale es “Una región específica geográficamente, politica o cultural”. Los 2 constructores de Locale que tenemos que entender para usar con mucha mas frecuencia son:
Locale (String language); Locale(String language, String country);
El argumento language representa el Código de Lenguaje ISO 639, por ejemplo si queremos dar formato a nuestras fechas o números en Walloon (lengua usada algunas veces en el sur de Bélgica), usaríamos “wa” como argumento. Hay mas de 500 Códigos de Lenguaje ISO, incluyendo el Klingon (“tlh”), aunque por desgracia Java aún no soporta el Klingon.
Vamos a ver como podemos usar estos códigos. Si queremos representar el Italiano básico en nuestra aplicación, todo lo que necesitamos es el código del lenguaje. Si, por otro lado, queremos representar el Italiano usado en Suiza, deberíamos indicar que el pais es Suiza (el código de pais es “CH”), pero el lenguaje es Italiano:
Locale locPT = new Locale("it"); // Italiano Locale locBR = new Locale("it", "CH"); // Suiza
Usando estas 2 localizaciones en una fecha podríamos obtener la siguiente salida:
sabato 1 ottobre 2005 sabato, 1. ottobre 2005
Ahora vamos a poner todo esto en un objeto Calendar, establecemos una fecha, y entonces lo convertimos a Date. Después de esto tendremos el objeto Date y lo imprimiremos usando algunas localizaciones del mundo:
public class LocaleTest { public static void main (String[] args){ Calendar c = Calendar.getInstance(); c.set(2013, 0, 24); // 24 Enero de 2013 Date d2 = c.getTime(); Locale locIT = new Locale("it", "IT"); // Italia Locale locPT = new Locale("pt"); // Portugal Locale locBR = new Locale("pt", "BR"); // Brasil Locale locIN = new Locale("hi", "IN"); // India Locale locJA = new Locale("ja"); // Japon DateFormat dfUS = DateFormat.getInstance(); System.out.println("US " + dfUS.format(d2)); DateFormat dfUSfull = DateFormat.getDateInstance(DateFormat.FULL); System.out.println("US full " + dfUSfull.format(d2)); DateFormat dfIT = DateFormat.getDateInstance(DateFormat.FULL, locIT); System.out.println("Italy " + dfIT.format(d2)); DateFormat dfPT = DateFormat.getDateInstance(DateFormat.FULL, locPT); System.out.println("Portugal " + dfPT.format(d2)); DateFormat dfBR = DateFormat.getDateInstance(DateFormat.FULL, locBR); System.out.println("Brasil " + dfBR.format(d2)); DateFormat dfIN = DateFormat.getDateInstance(DateFormat.FULL, locIN); System.out.println("India " + dfIN.format(d2)); DateFormat dfJA = DateFormat.getDateInstance(DateFormat.FULL, locJA); System.out.println("Japon " + dfJA.format(d2)); } }
Esto nos dá la salida en mi JVM:
US 24/01/13 18:33 US full jueves 24 de enero de 2013 Italy giovedì 24 gennaio 2013 Portugal Quinta-feira, 24 de Janeiro de 2013 Brasil Quinta-feira, 24 de Janeiro de 2013 India ???????, ?? ?????, ???? Japon 2013?1?24?
Hay un par de métodos en Locale (getDisplayCountry() y getDisplayLanguage()) que tenemos que conocer. Estos métodos nos permiten crear Strings que representan un pais y un lenguaje en términos de localización por defecto y otra localización:
public class LocaleTest { public static void main (String[] args){ Calendar c = Calendar.getInstance(); c.set(2013, 0, 24); Date d2 = c.getTime(); Locale locBR = new Locale("pt", "BR"); // Brasil Locale locDK = new Locale("da", "DK"); // Danés Locale locIT = new Locale("it", "IT"); // Italia System.out.println("def " + locBR.getDisplayCountry()); System.out.println("loc " + locBR.getDisplayCountry(locBR)); System.out.println("def " + locDK.getDisplayLanguage()); System.out.println("loc " + locDK.getDisplayLanguage(locDK)); System.out.println("D>I " + locDK.getDisplayLanguage(locIT)); } }
En mi JVM el resultado es:
def Brasil loc Brasil def danés loc Dansk D>I danese
-
La Clase NumberFormat
Vamos a terminar esta entrada con la clase NumberFormat. Como la clase DateFormat, NumberFormat es abstracta, por lo que podemos usar tanto getInstance() como getCurrencyInstance() para crear un objeto NumberFormat. Podemos usar esta clase para dar formato a numeros o valores de moneda:
public class NumberFormatTest { public static void main (String[] args){ float f1 = 123.4567f; Locale locFR = new Locale("fr"); // France NumberFormat[] nfa = new NumberFormat[4]; nfa[0] = NumberFormat.getInstance(); nfa[1] = NumberFormat.getInstance(locFR); nfa[2] = NumberFormat.getCurrencyInstance(); nfa[3] = NumberFormat.getCurrencyInstance(locFR); for (NumberFormat nf : nfa){ System.out.println(nf.format(f1)); } } }
En mi JVM produce la salida:
123,457 123,457 123,46 123,46 €
No nos tenemos que preocupar si no se nos muestran los simbolos de los francos, las libras, los drachmas etc. No tenemos porque conocer los simbolos para cada moneda. Aquí hay un pequeño código que usa los métodos getMaximumFractionDigits(), setMaximumFractionDigits(), parse(), y setParseIntegerOnly():
public class NumberFormatTest { public static void main (String[] args){ float f1 = 123.45678f; NumberFormat nf = NumberFormat.getInstance(); System.out.print(nf.getMaximumFractionDigits() + " "); System.out.print(nf.format(f1) + " "); nf.setMaximumFractionDigits(5); System.out.println(nf.format(f1) + " "); try{ nf.setParseIntegerOnly(false); System.out.println(nf.parse("1234,567")); nf.setParseIntegerOnly(true); System.out.println(nf.parse("1234.567")); }catch(ParseException pe){ System.out.println("parse exc"); } } }
En mi JVM la salida es:
3 123,457 123,4567 1234.567 1234567
Vemos que en este caso, el número inicial de los fígitos fraccionarios por defecto de NumberFormat es 3: y entonces el método format() redondea el valor de f1, no lo trunca.Despues de cambiar los digitos fraccionales de nf, el valor entero de f1 es mostrado. Luego, vemos que el método parse() debe ejecutarse en un bloque try/catch y el método setParseIntegerOnly() usa un boolean y en este caso, retorna solo la parte entera de Strings formateada como numeros punto-flotante.
Como hemos visto, las diferentes clases que hemos cubierto en este objetivo son abstract. En adición, para todas estas clases, la funcionalidad de cada instancia es establecida en tiempo de creación. EN la tabla siguiente vamos a ver los métodos y constructores usados para crear instancias de todas las clases que hemos discutido en esta sección.
Clase Opciones de Creacion Clave de la Instancia util.date new Date();
new Date(long millisecondsSince010170);util.Calendar Calendar.getInstance();
Calendar.getInstance(Locale);util.Locale Locale.getDefault();
new Locale(String language);
new Locale(String language, String country);text.DateFormat DateFormat.getInstance();
DateFormat.getDateInstance();
DateFormat.getDateInstance(style);
DateFormat.getDateInstance(stryle, Locale);text.NumberFormat NumberFormat.getInstance();
NumberFormat.getInstance(Locale);
NumberFormat.getNumberInstance();
NumberFormat.getNumberInstance(Locale)
NumberFormat.getCurrencyInstance();
NumberFormat.getCurrencyInstance(Locale);
Tras esta larga sección hemos visto el uso básico de las clases mas importantes para tratar con Fechas, el formato de fechas, formato de numeros, formato de moneda etc. Es algo importante saber como reaccionar en cada caso y como saber aplicarlo.
Sin mas, cualquier aporte o corrección es bienvenido.
Saludos!!!