2

我有一个日期作为输入 = 2021-03-12T10:42:01.000Z.... 我想转换成这种格式:

String pattern = "yyyy-MM-dd'T'HH:mm:ssZ";


public String getDate(XMLGregorianCalendar input) {
    DateFormat f = new SimpleDateFormat(pattern);
    input.toGregorianCalendar().setTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC));
    String output = f.format(input.toGregorianCalendar().getTime());
    System.out.println(output);
}

2021-03-12T12:42:01+0200

基本上,它增加了 2 小时。可能和时区有关,我没有在另一台电脑上测试过。我有两个问题:

  • 为什么会这样
  • 我能做些什么来避免它?这是一个遗留应用程序,所以我不想做大的改变

谢谢

4

2 回答 2

3

基本上,它增加了 2 小时

并不真地。它为您提供同一时刻的输出,但在您的系统本地时区 - 因为您正在创建一个SimpleDateFormat没有指定时区(或文化)的:

DateFormat f = new SimpleDateFormat(pattern);

我个人建议避免java.text.SimpleDateFormat完全使用,更喜欢java.time类型和格式化程序。但是,如果您肯定要使用SimpleDateFormat,只需确保将时区设置为 UTC(假设您始终需要 UTC)并最好也设置文化(例如,设置为Locale.ROOT)。

于 2021-06-02T06:39:23.997 回答
3

Jon Skeet的答案是正确且聪明的。您似乎只是看到了时区调整。你的两个字符串2021-03-12T10:42:01.000Z&2021-03-12T12:42:01+0200代表同一个时刻。中午 12 点(如果比 UTC 早两个小时)与上午 10 点相同,与 UTC 的偏移量为零时分秒。

而且,正如另一个答案中提到的那样,您确实应该避免使用与 Java 的最早版本捆绑在一起的糟糕的日期时间类。

tl;博士

myXMLGregorianCalendar     // Legacy class, representing a moment as seen in some time zone.
.toGregorianCalendar()     // Another legacy class, also representing a moment as seen in some time zone.
.toZonedDateTime()         // A modern *java.time* class, representing a moment as seen in some time zone.
.toInstant()               // Another *java.time* class, for representing a moment as seen in UTC.
.truncatedTo(              // Lopping off some smaller part of the date-time value.
    ChronoUnit.SECONDS     // Specifying whole seconds as our granularity of truncation, so lopping off any fractional second.
)                          // Returns another `Instant` object, rather than altering (mutating) the original, per immutable objects pattern.
.toString()                // Generating text representing the content of our `Instant` object, using standard ISO 8601 format.

java.time

现代方法使用java.time多年前取代SimpleDateFormat, XMLGregorianCalendar ,GregorianCalendar等的类。

转换遗留<——> 现代的

您可以轻松地将遗留类型转换为java.time。在旧类上寻找新to/方法。from

ZonedDateTime zdt = myXMLGregorianCalendar.toGregorianCalendar().toZonedDateTime() ;

调整到零偏移

通过提取Instant. 此类表示以 UTC 格式显示的时刻,始终以 UTC 格式表示。

Instant instant = zdt.toInstant() ; 

理解这一点zdtinstant两者都代表同一时刻,时间轴上的同一点,但它们的挂钟时间不同。

截断

鉴于您的问题中看到的格式模式,您似乎希望使用整秒的粒度。要截断任何小数秒,请截断为秒。

Instant truncated = instant.truncatedTo( ChronoUnit.SECONDS ) ;

ISO 8601

您所需的文本格式在ISO 8601标准中定义。该标准在java.time中默认用于解析/生成字符串。因此无需指定任何格式模式。

String output = truncated.toString() ;
于 2021-06-02T07:08:53.683 回答