如何避免将日期转换为时区格式?

时间:2019-01-16 15:05:05

标签: java date jaxb

我有一个DateAdapter

public class DateAdapter extends XmlAdapter<String, Date> {
    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS Z");

    @Override
    public String marshal(Date v) throws Exception {
        synchronized (dateFormat) {
            return dateFormat.format(v);
        }
    }

    @Override
    public Date unmarshal(String v) throws Exception {
        synchronized (dateFormat) {
            return dateFormat.parse(v);
        }
    }
}

因此,当将字符串传输到日期时,我输入“ 2009-12-31T23:59:59.999 +0000”

但是,当尝试从日期解析为字符串-> 2010-01-01T02:59:59.999 +0300(小时数和时区也发生了变化)

那么,如何禁用时间传输?

3 个答案:

答案 0 :(得分:1)

tl; dr

始终使用 java.time 类,绝不要使用诸如java.util.Date之类的可怕的旧类。要进行互操作,请转换。

我怀疑您实际上打算使用标准的ISO 8601格式,例如2009-12-31T23:59:59.999Z

请勿被Date::toString中的反功能绊倒,该功能会动态将时区应用于UTC实际值。

java.time 类是通过thread-safe设计的immutable objects。因此,不需要synchronized调用。

public class DateAdapter extends XmlAdapter< String, Date > {

    @Override
    public String marshal( Date v ) throws Exception {      // `Instant` generates standard ISO 8601 strings by default. No need to specify a formatting pattern. 
        return v.toInstant().toString() ;                   // Convert from legacy `Date` to modern `Instant`. 
    }

    @Override
    public Date unmarshal( String v ) throws Exception {    // `Instant` parses standard ISO 8601 strings by default. No need to specify a formatting pattern. 
        return java.util.Date.from( Instant.parse( v ) ) ;  // Convert from modern `Instant` to legacy `Date`. 
    }
}

ISO 8601

您的输入字符串接近标准ISO 8601格式。我强烈建议尽可能使用该标准。在从UTC偏移之前省略SPACE。

而且,最好在小时和分钟之间的偏移量中包含COLON-虽然在标准中是可选的,但某些库希望使用该字符。

String input = "2009-12-31T23:59:59.999+00:00" ;

顺便说一句,从UTC偏移量为零的常见缩写是字母Z,发音为“ Zulu”。

String input = "2009-12-31T23:59:59.999Z"  ;

java.time

您正在使用与最早的Java版本捆绑在一起的可怕的日期时间类。这些遗留类完全由 java.time 类取代。

选中see if your libraries have been updated to work with java.time。如果没有,您可以通过调用添加到旧类中的新方法来回转换。

java.util.Date对象是一个时刻,是UTC时间轴上的一点。现在由java.time.Instant类代替。

Instant instant = myJavaUtilDate.toInstant() ;   // Convert by calling new method added to the old class.

要使用Z生成标准格式的字符串,只需调用toString

String output = instant.toString() ;

使用Instant.parse可以很容易地解析标准格式的字符串。无需指定格式设置模式。

Instant instant = Instant.parse( "2009-12-31T23:59:59.999Z" ) ;

使用添加到旧类中的另一种新方法进行转换。

java.util.Date d = java.util.Date.from( instant ) ;

如果无法更改为使用标准ISO 8601文本格式,请使用DateTimeFormatter对象来解析并生成其他格式的字符串。搜索堆栈溢出,因为已经有很多次了。

Date::toString说谎

请注意, java.util.Date::toString是您的谎言。该方法动态地应用JVM的当前默认时区。内部值以UTC表示,但是此方法以其他方式报告。尽管有很好的意图,但这种反功能并未引起Java程序员的困惑和痛苦。相反, java.time 类直接报告其值,而无需进行此类自以为是的注入。


关于 java.time

java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

答案 1 :(得分:0)

我认为您的系统将时区设置为系统时区。对于输出,您可以使用没有时区的另一个SimpleDateFormat,即

private final SimpleDateFormat noTimeZoneDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");

如果根本不需要时区,也可以使用此日期格式进行输入

答案 2 :(得分:0)

您得到的结果是绝对正确的(但显然不是您想要的)。它与您解析的时间完全相同,只是表示方式不同。如何记录给定时刻的时间不计其数,有不计其数的字符串,其中一个参数是首选时区。

您似乎生活在+0300时区中,因此无论输入时区是什么,SimpleDateFormat都愿意根据该时区来代表任何给定的Date

如果您不仅要保留原始时间,还要保留有关源表示的信息(包括指定的时区),那么Date不能建模(除非您想使用API的过时,不推荐使用的部分),则您必须寻找其他表示形式类。

使用DateSimpleDateFormatSimpleDateFormat的属性决定结果的表示形式,一个属性是格式字符串,另一个属性是首选时区。您可以通过在setTimeZone()实例上调用SimpleDateFormat来设置后者。这样,您可以决定在格式化时使用哪个(固定)时区,例如您要根据UTC表示所有日期。

相关问题