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

Это звучит как простая задача.
Получить значение метки времени в формате 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;

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


person Mykola Golubyev    schedule 30.11.2009    source источник


Ответы (4)


Мы решили использовать следующее решение.
Используйте 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();
   }
}

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

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

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

person Mykola Golubyev    schedule 02.12.2009
comment
Николай, ваше решение JPA не подойдет. См. stackoverflow.com / questions / 508019 /. Зачем вам нужен JAX-WS для передачи только дат по Гринвичу? Клиент, зная свой часовой пояс, может (и должен) переводить дату из любого удаленного часового пояса в свой локальный часовой пояс. - person Derek Mahar; 07.11.2010
comment
Проблема что-то с клиентом C #. Мы использовали старую версию. Это решение отлично работает. - person Mykola Golubyev; 08.11.2010
comment
Существует множество проблем при попытке сохранить / интерпретировать информацию о времени, если нет абсолютной базовой линии. UTC является хорошей базой и не меняется из-за перехода на дневное время, будет согласованным, может быть переведено на местное время и т. Д. Просто рассмотрите возможность сохранения времени транзакции в GMT + 5, а затем произвольного изменения на GMT + 6 из-за дневного света. сбережения правила смены часов. Теперь клиент должен каким-то образом знать, что транзакция №1 была GMT + 5 до определенной даты, тогда транзакция №2 должна интерпретироваться как GMT + 6, когда они находятся в часовом поясе, не наблюдая таких изменений. Катастрофа - не использовать UTC. - person Darrell Teague; 23.02.2013

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

-Duser.timezone=UTC
person David Rabinowitz    schedule 30.11.2009
comment
Но мне это не нужно. И все происходит на сервере приложений. Я не хочу менять его часовой пояс. - person Mykola Golubyev; 01.12.2009
comment
Насколько я понимаю, вам нужно указать время в формате UTC. В таком случае почему бы не запустить приложение (и базу данных) в формате UTC? Я делаю это очень успешно последние 5 лет. - person David Rabinowitz; 01.12.2009
comment
Потому что на сервере приложений очень много приложений. Не только мой. - person Mykola Golubyev; 01.12.2009
comment
И это действительно похоже на взлом, а не на решение. - person Mykola Golubyev; 01.12.2009
comment
Я согласен с тем, что если вы размещаете много приложений, это не решение. Однако почему это взлом? Это системная настройка, аналогичная настройке часового пояса на уровне ОС. - person David Rabinowitz; 01.12.2009
comment
Привет, @David и @Mykola, я придерживаюсь того же решения, о котором говорят ваши комментарии. Я хочу сохранить дату, используя столбец часового пояса db. Если я изменю сопоставление JPA на «Дата» или «Календарь», а мой часовой пояс JVM НЕ находится в UTC, я сохраню дату НЕ в UTC. Уникальный способ хранения данных в базе данных в реальном времени в формате UTC - это изменение моей JVM для запуска в формате UTC, точно так же, как @David Rabinowitz упоминает в этом посте! - person rafa.ferreira; 20.05.2011

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

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

person freewill    schedule 18.08.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.

person Hank    schedule 14.06.2011