Java Web服务中使用了错误的时区

时间:2009-10-01 17:04:12

标签: java web-services date marshalling

我有一个JAX-B java web服务,我用它来更新数据库。我正在更新的表中的每一行都由类似于下面的对象表示: -

public class Item {
    private String id;
    private Date startDate;
    private Date endDate;

    public Item() { }

    ...

}

这个类在一个单独的程序中实例化,然后通过类似于下面的消息通过SOAP传递: -

...

<item>
    <id>D001IAAC030</id>
    <startDate>2009-09-17T00:00:00.000+01:00</startDate>
    <endDate>2009-10-01T00:00:00.000+01:00</endDate>
</item>

...

如您所见,由于BST,UTC时间偏移为+01:00。但是,当对象在服务器上进行编组时(也在我的本地计算机上),它将恢复为GMT并从日期中扣除1小时。

你能告诉我怎么做: -

  1. 将我的Glassfish服务器设置为正确的区域设置,以便将日期识别为BST。
  2. 告诉我如何拦截网络服务端的编组,以便我可以在设置日期之前自己设置时区。
  3. TIA,

    URF

4 个答案:

答案 0 :(得分:5)

您只需要记住,Date对象(始终)将日期/时间存储为UTC / GMT时区中自纪元以来的毫秒数。让人们兴奋的是Date.toString()方法在JVM的默认时区(通过内部Calendar对象)返回文本表示。 (看看JDK源代码。)

例如在我的机器上

Date now = new Date();
System.out.println(now.toString());
System.out.println(now.getTime())

将给出

Fri Oct 02 06:56:24 EST 2009
1254430584531

毫秒数是GMT / UTC时区中自纪元以来的实际毫秒数。

操作/使用Date对象时,应始终使用Date格式化程序或Calendar实例。例如:

Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:MM:ss zzz yyyy");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(now.toString());
System.out.println(sdf.format(now));

给出

Fri Oct 02 06:56:24 EST 2009
Thu Oct 01 20:56:24 UTC 2009

总结:始终将Date对象视为仅数据,自纪元以来的毫秒数。 (不要使用任何Deprecated方法,除非你理解它显示的内容,否则不要使用toString()。)要显示,格式化,转换(添加减去时间等)日期/时间总是使用Calendar实例或DateFormat实施,很难出错。

正如Javadoc所说的日期:

'在JDK 1.1之前,Date类有两个附加功能。它允许将日期解释为年,月,日,小时,分钟和秒值。它还允许格式化和解析日期字符串。不幸的是,这些功能的API不适合国际化。从JDK 1.1开始,Calendar类应该用于在日期和时间字段之间进行转换,而DateFormat类应该用于格式化和解析日期字符串。不推荐使用Date中的相应方法。

使用Date,Calendars和Formatters对自己进行实验并阅读Javadoc,它会变得更加清晰。

对于问题的第一部分,您不需要设置Glassfish服务器的时区来容纳您的数据。如果要使用数据/时间值存储时区数据,请在对象中使用日历而不是日期。或者像我通常那样,所有内容都存储为UTC时间(在对象中的db和Date实例中),时区仅在显示/输出或解析数据时使用。因此,当收到您的数据时,使用DateFormat或等效的时间区域设置为+01:00解析它(如果它附加了时区,它可以自动从时间字符串中选择,如示例所示)。

我不清楚你的问题的第二部分,但是如果你的web服务端的实现正确处理日期并正确解析它,它应该在没有你干预的情况下处理它。

答案 1 :(得分:4)

更改glassfish服务器中使用的时间:

C:\ glassfish4 \的glassfish \ bin)中

asadmin create-jvm-options -Duser.timezone=UTC
asadmin restart-domain

答案 2 :(得分:2)

我现在解决了我的问题,并打算帮助其他开发人员,按照一步一步的步骤

输入你的玻璃鱼

c: \ glassfish3 \ bin \> asadmin list-jvm-options

查看列表是否显示

<jvm-options>-Duser.timezone=UTC</jvm-options>

没有出现的情况转到domain.xml文件并搜索jvm-options并添加命令行

<jvm-options>-Duser.timezone = Brazil / East </ jvm-options> substituting the Brazil / East timezone by country

然后重启服务器并开心

答案 3 :(得分:1)

Locale无关

Locale与时区无关。对于日期时间工作,Locale确定两件事:(a)用于翻译日期名称,月份名称等的人类语言,以及(b)句点与逗号等问题的文化规范,元素的排序(日期之前/之后等)。

不要更改默认时区

请勿按照另一个答案的建议更改JVM的当前默认时区。更改会影响该JVM中所有应用程序的所有线程中的所有代码,并在运行时期间立即执行

java.time

您正在使用旧的过时类。与最早版本的Java捆绑在一起的旧日期时间类已被证明设计糟糕,令人困惑且麻烦。避免他们。现在取代了Java 8及更高版本中内置的java.time框架。这些类取代了旧的麻烦的日期时间类,如java.util.Date。见Oracle Tutorial。许多功能已经被后端移植到Java 6&amp; ThreeTen-Backport中的7,并在ThreeTenABP中进一步适应Android。

ISO 8601

您的日期时间字符串恰好符合ISO 8601标准。在解析/生成表示日期时间值的字符串时,默认情况下会在java.time类中使用此标准定义的格式。

String input = "2009-09-17T00:00:00.000+01:00";
OffsetDateTime odt = OffsetDateTime.parse( input );

如果您想在时间轴上使用相同的时刻,但需要UTC,请提取Instant个对象。最好坚持使用UTC和本课程来完成大部分业务逻辑和数据交换以及数据存储。

Instant instant = odt.toInstant();

要生成ISO 8601格式的字符串,请调用toString

String output = instant.toString();  // 2009-09-16T23:00:00Z

要查看调整到某个时区wall-clock time的相同值,请应用ZoneId获取ZonedDateTime个对象。时区是用于处理Daylight Saving Time (DST)等异常的UTC 规则的偏移量。

ZoneId zoneId = ZoneId.of( "Europe/London" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

此外,如果您需要捕获UTC中的当前时刻:

Instant instant = Instant.now();

JAXB适配器

Java Architecture for XML Binding (JAXB)可能尚未直接支持java.time类型。

在添加直接支持之前,您可以使用适用于java.time类型的适配器,如问题this Answer by Blaise Doughan中所讨论的Can JAXB handle java.time objects?。您可以使用this implementation或通过建模一个或此similar one for Joda-Time库来编写自己的文件。

LocalDate

00:00:00的那些时间值让我想知道您是否真的想要表示仅限日期的值。在仅限日期的值中,您不关心时间或时区。如果是这种情况,请考虑使用LocalDate类。