Asigne la columna de marca de tiempo de la base de datos al calendario UTC (JPA) y páselo como fecha UTC a través del servicio web (jax-ws)

StackOverflow https://stackoverflow.com/questions/1821104

  •  10-07-2019
  •  | 
  •  

Pregunta

Esto suena como una tarea simple.
Obtenga el valor de marca de tiempo UTC de DB y páselo como fecha UTC a través del servicio web.

Tenemos la columna de fecha y hora DATE_COLUMN y almacenamos la hora en la zona horaria UTC.

Con JPA llegamos esta vez con

@Column(name = "DATE_COLUMN")
private java.sql.Timestamp dateValue;

Y como tenemos que pasar este tiempo a través del servicio web en UTC (Jax-ws 2.0) tenemos los métodos getDate y setDate.
Estamos interesados ??en getDate.

public Calendar getDate()
{
   Calendar calendar = Calendar.getInstance(utcTimeZone);
   calendar.setTimeInMillis(dateValue.getTime());

   return calendar;
}

Esto no funciona como crees que debería.
Y esto se debe a que la zona horaria predeterminada de la aplicación no es 'UTC'.

Aquí hay un ejemplo de aclaración.
El valor en DATE_COLUMN es igual a "30.11.09 16: 34: 48,833045000", cuando lo traduzco a UTC obtengo "2009-11-30T14: 34: 48.833Z".
La diferencia es de 2 horas. Y esto se debe a que mi zona horaria predeterminada es "Europa / Helsinki".

Mismo problema si solo desea asignar 'DATE_COLUMN' a Calendar<

@Column(name = "DATE_COLUMN")
@Temporal(TemporalType.TIMESTAMP)
private Calendar dateValue;

public Calendar getDate()
{
   calendar.setTimeZone(utcTimeZone);
   return calendar;
}

No quiero cambiar la zona horaria de la aplicación porque no parece la solución.

Por ahora solo tenemos dos opciones.

Primero . Calcule la compensación entre la zona horaria de la aplicación y UTC y agréguela manualmente después de la sustracción automática en calendar.setTimeZone.

public Calendar getDate()
{
   Calendar calendar = Calendar.getInstance(utcTimeZone);
   calendar.setTimeInMillis(dateValue.getTime());

   int offset = TimeZone.getDefault().getOffset(dateValue.getTime());

   calendar.add(Calendar.MILLISECOND, offset);

   return calendar;
}

Segundo . Pase dateValue como Long a través del servicio web. Lo que no está mal, excepto que perdemos el tipo real del campo en wsdl.

Mi solución imaginaria es

@Column(name = "DATE_COLUMN")
@Temporal(type = TemporalType.TIMESTAMP, timezone = 'UTC')
private Calendar dateValue;

Pero tiendo a pensar que existe el verdadero en alguna parte. Y espero que puedas señalarlo.

¿Fue útil?

Solución

Decidimos usar la siguiente solución.
Use Date para recuperar la fecha de la base de datos. Esto se debe a que Date es un tipo sin zona horaria.

@Column(name = "DATE_COLUMN")
@Temporal(TemporalType.TIMESTAMP)
private Date dateValue;

public Date getDate()
{
   return dateValue;
}

Y para enviarlo a través de WebService en UTC (jax-ws) creamos UtcTimestampAdapter para cambiar la zona del valor predeterminado de la aplicación a UTC en la fase de clasificación.

public class UtcTimestampAdapter extends XmlAdapter<XMLGregorianCalendar, Date>
{
   @Override
   public XMLGregorianCalendar marshal(Date date) throws Exception
   {
      GregorianCalendar calendar = new GregorianCalendar();
      calendar.setTime(date);

      DatatypeFactory dataTypeFactory = DatatypeFactory.newInstance();
      XMLGregorianCalendar xmlCalendar = 
         dataTypeFactory.newXMLGregorianCalendar(calendar);

      //Reset time zone to UTC
      xmlCalendar.setTimezone(0);

      return xmlCalendar;
   }

   @Override
   public Date unmarshal(XMLGregorianCalendar calendar) throws Exception
   {
      return calendar.toGregorianCalendar().getTime();
   }
}

Luego, para habilitar esta regla en todos los campos de Data en el módulo, agregamos la configuración específica del paquete de esta manera.

@XmlJavaTypeAdapter(value = UtcTimestampAdapter.class, type = Date.class)
@XmlSchemaType(name = "dateTime", type = XMLGregorianCalendar.class)
package com.companyname.modulename;

Eso es todo. Ahora tenemos una solución genérica que encapsula toda la lógica en un solo lugar. Y si queremos enviar fechas sin zona horaria como UTC a través del servicio web en algún otro módulo, simplemente anotaremos cierto paquete.

Otros consejos

Si necesita que el proceso de Java se ejecute en la zona horaria UTC, la forma más fácil de hacerlo es agregando el siguiente parámetro JVM:

-Duser.timezone=UTC

TimeZone.setDefault (TimeZone.getTimeZone (" UTC ")); parece estar afectando a toda la JVM.

Esto hará que otras aplicaciones fallen si esperaban la hora local

Otra solución es establecer la zona horaria predeterminada para la aplicación solo en un @StartupBean :

import java.util.TimeZone;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;

@Startup
@Singleton
public class StartupBean {

    @PostConstruct
    public void initializeTheServer() {
        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
    }
}

A partir de entonces, toda la interpretación de los objetos Date se basará en UTC. Esto incluye el cálculo de referencias XML.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top