2010-11-24 17 views
5

Estoy buscando una clase de fecha única serializable adecuada. Tengo un servidor en el huso horario central, quiero que los usuarios en el este ingresen la fecha como 2010-11-23, y los usuarios en el Pacífico lo vean como 2010-11-23 (y viceversa).Joda LocalDate, ¿aún un instante?

java.util.Date es un instante sensible al tiempo, por lo que no funciona para mí. No queriendo reinventar la rueda, decidí darle una oportunidad a Joda Time. Según la descripción general, LocalDate es una "clase que representa una fecha local sin tiempo (sin zona horaria)", justo lo que necesito.

Desafortunadamente, no parece ser zona horaria independiente. En la base de datos tengo 2010-11-23. En el servidor, lo transformo en LocalDate con new LocalDate(java.sql.Date). date.toString() prints 2010-11-23.

Después de deserializar que en el cliente, date.ToString() impresiones 2010-11-22 date.getDayOfMonth() y es 22. Pero date.toDateTimeAtStartOfDay() produce 2010-11-23T00: 00: 00,000 a 08: 00.

¿Estoy haciendo algo mal? ¿Hay una clase insensible a la zona horaria apropiada solo para la fecha en Joda?

EDIT: En la base de datos estoy usando una columna insensible a la zona horaria (DATE en Postgres). El servidor está en la misma zona horaria que la base de datos, por lo que lee bien la fecha. No es un problema. Estoy buscando un tipo de Java que una vez instanciado en una zona horaria, tenga el mismo valor de fecha civil (parcial) en todas las zonas horarias.

EDIT 2: en el servidor, todo está cargado correctamente y tiene zona horaria UTC. Después de la investigación, creo que es un error en ISOChronology. En el cliente, se desempaqueta como America/Los_Angeles. La razón es que la serialización se hace con:

class ISOChronology { 
// ... 
    private Object writeReplace() { 
     return new Stub(getZone()); 
    } 

    private static final class Stub implements Serializable { 
     private static final long serialVersionUID = -6212696554273812441L; 

     private transient DateTimeZone iZone; 

     Stub(DateTimeZone zone) { 
      iZone = zone; 
     } 

     private Object readResolve() { 
      return ISOChronology.getInstance(iZone); 
     } 

     private void writeObject(ObjectOutputStream out) throws IOException { 
      out.writeObject(iZone); 
     } 

     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 
      iZone = (DateTimeZone)in.readObject(); 
     } 
    } 
} 

Buena suerte en la serialización con un campo transitorio.

+1

Si quiere ignorar la zona horaria, ¿puede usar UTC/GMT en todos los lugares? –

+0

¿Qué versión de Joda estás usando y cómo se serializan los objetos? Para la serialización Java estándar, la clase Stub tiene los métodos writeObject/readObject adecuados para el manejo "manual" del campo iZone, pero esto puede fallar con otras implementaciones de serialización como IIOP, SOAP o cualquier otra cosa que intente extraer automáticamente valores de campo de instancias de objeto . – jarnbjo

+0

@jarnbjo 1.6.2. El serializador está bien, el problema como señalé anteriormente es que 'Stub.iZone' es transitorio. –

Respuesta

3

La zona horaria trasient en la clase de stub que se muestra arriba es un error. Es sorprendente que no haya sido atrapado en decenas de miles de pruebas o 5 años de uso generalizado. Informe de error https://sourceforge.net/tracker/index.php?func=detail&aid=3117678&group_id=97367&atid=617889

La zona horaria debe ser UTC, tal como se define en Javadoc - http://joda-time.sourceforge.net/apidocs/org/joda/time/LocalDate.html. Deserializar para obtener cualquier otra zona horaria romperá el estado interno de la clase.

JSR-310 utiliza un diseño interno mucho mejor que no sufre este tipo de problema.

+0

Gracias, eso tiene sentido. ¿Entiendo correctamente que estás diciendo que siempre debe ser UTC para que sea absolutamente insensible a la zona horaria? –

+0

Internamente, una vez que se ha completado la construcción, la zona horaria en la cronología debe ser UTC o las invariantes de clase fallan. – JodaStephen

1

Sospecho que usar el constructor new LocalDate(java.sql.Date) puede ser el culpable.

En su lugar, intente utilizar new LocalDate(int year, int month, int dayOfMonth).

Consulte LocalDateJavaDoc y manual para obtener más información.

+0

en ese caso, nueva LocalDate (date.getTime()) debería funcionar. El constructor LocalDate (Object) observa que intenta tomar la zona horaria si puede obtenerse. –

+0

Voy a dar una oportunidad a todas estas opciones. Si funcionó, sin embargo, significaría que 'LocalDate' todavía es sensible a la zona horaria y necesito ser extremadamente cuidadoso con eso. Eso es lo que esperaba evitar. –

1

Tienes que verificar qué es actualmente en tu base de datos. Si realmente usa marcas de tiempo absolutas y todas están en una zona horaria, entonces debe analizar los valores en esa zona horaria y solo luego convertirlos a LocalDate. Si tiene una cadena en su base de datos, entonces analice esa cadena y use el constructor para LocalDate que toma la información que realmente tiene. Hagas lo que hagas, no te mezcles entre los instantes y las fechas; como has descubierto por tus problemas, no son intercambiables.

+0

Ver descripción actualizada. –

Cuestiones relacionadas