我正在使用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
答案 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
给予 ZoneId
比 ZoneOffset
更高的优先级。 documentation 清楚地提到:
在设计方面,这个类应该主要被视为
LocalDateTime
和 ZoneId
的组合。 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
是可变计算的。
可能对您和未来访问者有用的几件事:
2016-05-04T12:58:22+01:00[Europe/Paris]
已经采用 ZonedDateTime#parse
使用的默认格式,因此,您无需将 DateTimeFormatter
作为参数传递给它。立>
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);
}
}