Сопоставьте столбец метки времени базы данных с календарем UTC (JPA) и передайте его как дату UTC через веб-сервис (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;
Но я склонен думать, что где-то есть настоящий.И я надеюсь, что вы сможете указать на это.
Решение
Мы решили использовать следующее решение.
Использование Date
для получения даты из базы данных.Это потому, что Date
является типом без временных зон.
@Column(name = "DATE_COLUMN")
@Temporal(TemporalType.TIMESTAMP)
private Date dateValue;
public Date getDate()
{
return dateValue;
}
И чтобы отправить его через веб-сервис в 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
для полей в модуле мы добавили настройку, специфичную для конкретного пакета, вот так.
@XmlJavaTypeAdapter(value = UtcTimestampAdapter.class, type = Date.class)
@XmlSchemaType(name = "dateTime", type = XMLGregorianCalendar.class)
package com.companyname.modulename;
Вот и все.Теперь у нас есть универсальное решение, которое инкапсулирует всю логику в одном месте.И если мы хотим отправлять даты без часового пояса в формате UTC через веб-сервис в каком-либо другом модуле, мы просто пометим определенный пакет.
Другие советы
Если вам нужно, чтобы процесс Java выполнялся в часовом поясе UTC, самый простой способ сделать это - добавить следующий параметр JVM:
-Duser.timezone=UTC
TimeZone.setDefault (TimeZone.getTimeZone (" UTC "));
влияет на всю JVM.
Это приведет к сбою других приложений, если они ожидают местное время
Другое решение - установить часовой пояс по умолчанию для приложения только в @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.