将数据库时间戳列映射到 UTC 日历 (JPA) 并通过 WebService (jax-ws) 将其作为 UTC 日期传递

发布于 2024-08-12 22:04:10 字数 1694 浏览 4 评论 0原文

这听起来像是一个简单的任务。
从数据库获取 UTC 时间戳值,并通过 Web 服务将其作为 UTC 日期传递。

我们有时间戳列 DATE_COLUMN 并以 UTC 时区存储时间。

通过 JPA,我们可以得到这个时间,

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

并且由于我们必须通过 UTC (Jax-ws 2.0) 中的 Web 服务传递这个时间,所以我们有 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”映射到日历,也会出现同样的问题,

@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;
}

第二。通过 Web 服务将 dateValue 作为 Long 传递。除了我们丢失了 wsdl 中字段的真实类型之外,这还不错。

我想象的解决方案是

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

但我倾向于认为某个地方有真正的解决方案。我希望你能指出这一点。

This sounds like a simple task.
Get UTC timestamp value from DB and pass it as UTC date via Web Service.

We have timestamp column DATE_COLUMN and store there time in UTC time zone.

With JPA we get this time with

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

And as we have to pass this time via Web Service in UTC (Jax-ws 2.0) we have getDate and setDate methods.
We are interested in getDate.

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

   return calendar;
}

This doesn't work as you may think it should.
And this is because application's default time zone is not 'UTC'.

Here is an example for clarification.
Value in the DATE_COLUMN equals to "30.11.09 16:34:48,833045000", when I translate it to UTC I get "2009-11-30T14:34:48.833Z".
The difference is 2 hours. And this is because my default time zone is "Europe/Helsinki".

Same problem if you just want to map 'DATE_COLUMN' to Calendar

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

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

I don't want to change application's time zone because it doesn't look like the solution.

By now we have only two options.

First. Calculate offset between application's time zone and UTC and add it manually after automatic subtraction in the 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;
}

Second. Pass dateValue as Long via Web Service. Which is not bad except that we lose real type of the field in wsdl.

My imaginary solution is

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

But I tend to think that there is the real one somewhere. And I hope you can point it out.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

走过海棠暮 2024-08-19 22:04:10

我们决定使用以下解决方案。
使用 Date 从数据库中检索日期。这是因为 Date 是无时区类型。

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

public Date getDate()
{
   return dateValue;
}

为了通过 UTC (jax-ws) 中的 WebService 发送它,我们创建了 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();
   }
}

然后,为了对该模块中的所有数据字段启用此规则,我们添加了包特定设置,如下所示。

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

就是这样。现在我们有了将所有逻辑封装在一处的通用解决方案。如果我们想通过其他模块中的 Web 服务以 UTC 形式发送无时区日期,我们只需注释某个包即可。

We decided to use following solution.
Use Date for retrieving date from database. It is because Date is timezoneless type.

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

public Date getDate()
{
   return dateValue;
}

And to send it via WebService in UTC (jax-ws) we created UtcTimestampAdapter to change zone from application's default to UTC in the marshaling phase.

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

Then to enable this rule to all Datas fields in the module we added package specific setting like so.

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

That's it. Now we have generic solution which encapsulate all logic in one place. And if we want to send timezoneless dates as UTC via web service in some other module we will just annotate certain package.

孤芳又自赏 2024-08-19 22:04:10

如果您需要 java 进程在 UTC 时区运行,最简单的方法是添加以下 JVM 参数:

-Duser.timezone=UTC

If you need the java process to run at the UTC timezone, the easiest way to do so is by adding the following JVM parameter:

-Duser.timezone=UTC
信仰 2024-08-19 22:04:10

TimeZone.setDefault(TimeZone.getTimeZone("UTC")); 似乎正在影响整个 JVM。

如果其他应用程序期待当地时间,这将导致其他应用程序失败

TimeZone.setDefault(TimeZone.getTimeZone("UTC")); seems to be affecting the entire JVM.

This will cause other applications fail if they were expecting local time

z祗昰~ 2024-08-19 22:04:10

另一个解决方案是仅在 @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 编组。

Another solution is to set the default timezone for the application only in a @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"));
    }
}

From then on, all interpretation of Date objects will be based on UTC. This includes XML marshalling.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文