Сопоставьте столбец отметки времени базы данных с календарем UTC (JPA) и передайте его как дату UTC через WebService (jax-ws) - PullRequest
2 голосов
/ 30 ноября 2009

Звучит как простое задание.
Получите значение метки времени UTC из БД и передайте его как дату UTC через веб-сервис.

У нас есть столбец отметки времени DATE_COLUMN и мы сохраняем там время в часовом поясе UTC.

С JPA мы получаем это время с

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

И так как мы должны передать это время через веб-сервис в UTC (Jax-ws 2.0), у нас есть методы getDate и setDate.
Мы заинтересованы в getDate.

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

   return calendar;
}

Это не работает, как вы думаете, должно.
И это потому, что часовой пояс приложения по умолчанию не «UTC».

Вот пример для разъяснения.
Значение в DATE_COLUMN равно «30.11.09 16: 34: 48,833045000», при переводе в UTC я получаю «2009-11-30T14: 34: 48.833Z».
Разница составляет 2 часа. И это потому, что мой часовой пояс по умолчанию - «Европа / Хельсинки».

Та же проблема, если вы просто хотите сопоставить 'DATE_COLUMN' с Calendar

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

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

Я не хочу менять часовой пояс приложения, потому что это не похоже на решение.

К настоящему времени у нас есть только два варианта.

Первый . Вычислить смещение между часовым поясом приложения и UTC и добавить его вручную после автоматического вычитания в 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;
}

Второй . Передайте dateValue как Long через веб-сервис. Что неплохо, за исключением того, что мы теряем реальный тип поля в wsdl.

Мое воображаемое решение

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

Но я склонен думать, что где-то есть настоящий. И я надеюсь, что вы можете указать на это.

Ответы [ 4 ]

4 голосов
/ 02 декабря 2009

Мы решили использовать следующее решение.
Используйте Date для получения даты из базы данных. Это потому, что Date - это тип без часового пояса.

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

public Date getDate()
{
   return dateValue;
}

И чтобы отправить его через WebService в UTC (jax-ws), мы создали UtcTimestampAdapter для изменения зоны со значения по умолчанию приложения на UTC на этапе маршалинга.

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();
   }
}

Затем, чтобы включить это правило для всех Data s полей в модуле, мы добавили настройки, специфичные для пакета, вот так:

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

Вот и все. Теперь у нас есть общее решение, которое заключает в себе всю логику в одном месте. И если мы хотим отправлять даты без часовых поясов в формате UTC через веб-сервис в каком-либо другом модуле, мы просто аннотируем определенный пакет.

3 голосов
/ 19 августа 2011

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));, похоже, влияет на всю JVM.

Это приведет к сбою других приложений, если они ожидают местное время

3 голосов
/ 30 ноября 2009

Если вам нужен процесс java для запуска в часовом поясе UTC, самый простой способ сделать это - добавить следующий параметр JVM:

-Duser.timezone=UTC
0 голосов
/ 14 июня 2011

Другое решение состоит в том, чтобы установить часовой пояс по умолчанию для приложения только в @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"));
    }
}

С этого момента вся интерпретация объектов Date будет основываться на UTC. Это включает в себя XML маршаллинг.

...