PostgreSQL, Java y fechas
Trabajando para un proyecto tuve que cambiar de mi querida MySQL a PosgreSQL y me encontré como era de esperar con unos cuantos problemas. Entre ellos, el que más dolores de cabeza me dio y sobre el que más variantes tuve que probar hasta encontrar con la más adecuada para las necesidades del momento fue el del almacenamiento y recuperación de fechas.
Evalué varias alternativas y vi mucha cosa al respecto y me quedé con la solución que a continuación presento, y que describo para salvar mis problemas de memoria y quizás para ayudar un poco a algún eventual lector que ande tras una solución para ese mismo problema.
La tabla
Las fechas son guardadas en campos de tipo timestamp without time zone. En el siguiente ejemplo solo se muestran dos columnas de la tabla y omití todo lo que no viene al caso.
CREATE TABLE trabajo ( nombre character varying(50) fecha timestamp without time zone );
La aplicación
La aplicación almacena las fechas en variables de tipo GregorianCalendar que es un objeto de Java muy flexible para el manejo de fechas.
class Trabajo {
private String nombre;
private GregorianCalendar fecha;
}
Además de los getters y setters habituales, escribí un par de métodos que permiten setear las fechas a partir de un String y recuperarlas como un String, lo cual es muy práctico al momento de mostrarlas o de setear fechas a partir del valor obtenido de un date picker.
public String getFechaAsString() {
SimpleDateFormat sdf = new SimpleDateFormat(“dd-MM-yyyy HH:mm”);
return( sdf.format( (this.fecha).getTime() ) );
}
El formato de fecha elegido está hardcoded pero en este caso no es una limitación. La clase SimpleDateFormat es muy flexible a la hora de parsear fechas con lo que el código anterior puede ser fácilmente modificable para soportar cualquier formato. En caso de quererse formatos más flexibles que se correspondan con las preferencias del usuario en el sistema operativo, tendrás que echar mano a la clase TimeZone. En el ejemplo no se manejan posibles errores al parsear la fecha (asumo que this.fecha siempre tiene un valor válido, el setter debería encargarse de eso).
public void setFechaAsString( String fecha ) {
if( fecha.length() != 0 ) {
SimpleDateFormat sdf = new SimpleDateFormat(“dd-MM-yyyy HH:mm”);
try {
java.util.Date d = sdf.parse( fecha );
(this.fecha) = new GregorianCalendar();
(this.fecha).setTime( d );
} catch ( ParseException e ) {
e.printStackTrace();
}
}
Las fechas son seteadas a partir de un String, el cual es parseado y en caso de no verificar exactamente el formato necesario, tira una excepción (en el ejemplo imprimo el stack trace, aunque en la realidad la excepción se maneja de manera un poquito más feliz).
La clase SimpleDateformat devuelve un objeto de tipo java.util.Date (no confundir con java.sql.Date) a partir del cual podemos establecer la fecha en nuestro GregorianCalendar.
Guardando fechas
Guardar las fechas es muy simple si echamos mano a nuestra clase getFechaAsString().
sql = “INSERT INTO trabajo (nombre,fecha) values(‘”+ this.nombre+”‘,’”+this.getFechaAsString()+”‘)”;
(Ojo con las comillas) y ese sql lo mandamos a la base de datos.
Recuperando fechas
Recuperar las fechas tiene tambien su truqito, veamos..
sql = “SELECT * FROM trabajo”;
ResultSet rs = st.executeQuery( sql ); donde st es un Statement valido conectado a la base de datos
Iteramos sobre el resultado
…
java.sql.Date d = rs.getDate(“fecha”);
if( d1 != null ) {
GregorianCalendar gc1 = new GregorianCalendar();
gc1.setTimeInMillis( d1.getTime() );
this.fecha = gc1;
…
Observar que getDate devuelve un objeto de java.sql.Date, pero igualmente podemos crear un GregorianCalendar a partir de él pasando la fecha a un timestamp.
Conclusiones
No es el más flexible de los métodos para almacenar y mostrar fechas, pero es lo suficientemente flexible si manejamos las fechas en un formato que no dependa de la configuración regional del usuario. Sin embargo, podemos mostrar las fechas en cualquier formato utilizando SimpleDateFormat para formatearlas de la manera que mas nos convenga.

Gracias, exactamente lo que estaba buscando.
Hola, lo he hecho como explicas y no va. Da un error al insertar en BD porque espera un tipo Date y le mandas un character varying (un string).
Lo que he hecho para que funcione es declarar en campo fecha en BD como un character varying
CREATE TABLE trabajo ( nombre character varying(50) fecha character varying );
a la hora de guardar la fecha queda tal cual, pero al recoger, al iterar sobre el resultSet no recogo un date, si no un string, y luego inicializo la fecha con el setFechaAsString
String d = rs.getString(“fecha”);
this.setFechaAsString(d);
De esta forma sí funciona. Haciendo lo que has descrito, la base de datos lanza una excepción, porque espera un date y le envías un string.
@Lobo Darbuko
Gracias por el comentario
Al cambiar la declaración del campo en la BD cambia todo. Cuando en la tabla es un timestamp, el tipo Date que se obtiene es un java.sql.Date y no un java.util.Date. Se me ocurre que por ahí puede estar la confusión.