ZonedDateTime成功解析但输出字符串不同

时间:2016-05-06 08:31:50

标签: java java-8 zoneddatetime

我正在使用ZonedDateTime来解析并根据时区获取当前时间。

当我解析以下String时,解析成功但输出String不同。那是为什么?

String dateTimeString = "2016-05-04T12:58:22+01:00[Europe/Paris]";
ZonedDateTime dateTime = ZonedDateTime.parse(dateTimeString, ISO_ZONED_DATE_TIME);
System.out.println(dateTimeString);
System.out.println(dateTime.toString());

输出

2016-05-04T12:58:22+01:00[Europe/Paris]
2016-05-04T12:58:22+02:00[Europe/Paris]

它决定将+1更改为+2以及为什么没有抛出异常?

我知道括号[Europe/Paris]中的参数是可选的,但这里它优先于偏移量。

另一方面,以下代码

String dateTimeString = "2016-05-04T12:58:22+01:00";
ZonedDateTime dateTime = ZonedDateTime.parse(dateTimeString, ISO_ZONED_DATE_TIME);
System.out.println(dateTimeString);
System.out.println(dateTime.toString());

产生输出

2016-05-04T12:58:22+01:00
2016-05-04T12:58:22+01:00

2 个答案:

答案 0 :(得分:6)

问题是,2016-05-04T12:58:22+01:00[Europe/Paris]不是正确的时间,因为我们在5月的CEST(中欧夏令时,夏令时)开始,从3月的最后一个星期日开始。与UTC相比,它是+ 2小时。确实2016-05-04T12:58:22+02:00[Europe/Paris]确实是正确的。

如你所说,[Europe/Paris]似乎取得了优势。不确定是否应该有关于规范的例外,但我对此表示怀疑。

换句话说2016-05-04T12:58:22+01:00不能在时区Europe/Paris

答案 1 :(得分:1)

功能;不是错误。

ZonedDateTime#parse 给予 ZoneIdZoneOffset 更高的优先级。 documentation 清楚地提到:

<块引用>

在设计方面,这个类应该主要被视为 LocalDateTimeZoneId 的组合。 ZoneOffset 是一个 重要但次要的信息,用于确保 class 代表一个瞬间,尤其是在夏令时 重叠。

查看反编译的源代码可以进一步理解:

class ZonedDateTime

public static ZonedDateTime parse(CharSequence text) {
    return parse(text, DateTimeFormatter.ISO_ZONED_DATE_TIME);
}

public static ZonedDateTime parse(CharSequence text, DateTimeFormatter formatter) {
    Objects.requireNonNull(formatter, "formatter");
    return formatter.parse(text, ZonedDateTime::from);
}

public static ZonedDateTime from(TemporalAccessor temporal) {
    if (temporal instanceof ZonedDateTime) {
        return (ZonedDateTime) temporal;
    }
    try {
        ZoneId zone = ZoneId.from(temporal);
        if (temporal.isSupported(INSTANT_SECONDS)) {
            long epochSecond = temporal.getLong(INSTANT_SECONDS);
            int nanoOfSecond = temporal.get(NANO_OF_SECOND);
            return create(epochSecond, nanoOfSecond, zone);
        } else {
            LocalDate date = LocalDate.from(temporal);
            LocalTime time = LocalTime.from(temporal);
            return of(date, time, zone);
        }
    } catch (DateTimeException ex) {
        throw new DateTimeException("Unable to obtain ZonedDateTime from TemporalAccessor: " +
                temporal + " of type " + temporal.getClass().getName(), ex);
    }
}

类 ZoneId

public static ZoneId from(TemporalAccessor temporal) {
    ZoneId obj = temporal.query(TemporalQueries.zone());
    if (obj == null) {
        throw new DateTimeException("Unable to obtain ZoneId from TemporalAccessor: " +
                temporal + " of type " + temporal.getClass().getName());
    }
    return obj;
}

类 TemporalQuery

public static TemporalQuery<ZoneId> zone() {
    return TemporalQueries.ZONE;
}

static final TemporalQuery<ZoneId> ZONE = new TemporalQuery<>() {
    @Override
    public ZoneId queryFrom(TemporalAccessor temporal) {
        ZoneId zone = temporal.query(ZONE_ID);
        return (zone != null ? zone : temporal.query(OFFSET));// <----- Look at this line
    }

    @Override
    public String toString() {
        return "Zone";
    }
};

此外,ZonedDateTime 的全部意义在于您将 ZoneId 包含在其中,以便考虑到夏令时/夏令时 例如如果没有 ZoneId 信息,以下代码将为您提供两个打印语句的相同输出:

import java.time.ZonedDateTime;

public class Main {
    public static void main(String[] args) {
        String dateTimeString = "2016-05-04T12:58:22+01:00";
        ZonedDateTime dateTime = ZonedDateTime.parse(dateTimeString);
        System.out.println(dateTimeString);
        System.out.println(dateTime.toString());
    }
}

输出:

2016-05-04T12:58:22+01:00
2016-05-04T12:58:22+01:00

再说一次,这是一个功能;不是错误,因为您要求系统使用 fixed ZoneOffset 信息解析日期时间字符串。对于一个简单的类比,您可以认为 ZoneOffset 是按字面计算的,而 ZoneId 是可变计算的。

可能对您和未来访问者有用的几件事:

  1. 日期时间字符串 2016-05-04T12:58:22+01:00[Europe/Paris] 已经采用 ZonedDateTime#parse 使用的默认格式,因此,您无需将 DateTimeFormatter 作为参数传递给它。
  2. System.out.println 自动打印参数对象的 toString 方法返回的字符串,因此您不需要显式调用 toString

这样你的代码,

import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME;

import java.time.ZonedDateTime;

public class Main {
    public static void main(String[] args) {
        String dateTimeString = "2016-05-04T12:58:22+01:00[Europe/Paris]";
        ZonedDateTime dateTime = ZonedDateTime.parse(dateTimeString, ISO_ZONED_DATE_TIME);
        System.out.println(dateTime.toString());
    }
}

可以简单地写成

import java.time.ZonedDateTime;

public class Main {
    public static void main(String[] args) {
        String dateTimeString = "2016-05-04T12:58:22+01:00[Europe/Paris]";
        ZonedDateTime dateTime = ZonedDateTime.parse(dateTimeString);
        System.out.println(dateTime);
    }
}