使用 LocalDateTIme 和 ZonedDateTime 从日期中减去一天后得到错误的结果
Getting wrong result after Subtracting a day from a date Using LocalDateTIme and ZonedDateTime
我正在使用 "2016-03-28T02:00:00+0200"
(UTC 秒为 1459123200)进行测试
减去 1 天后,应用 DST,输出应为:
"2016-03-27T03:00:00+0200"
但我得到这个:
2016-03-26T01:00+01:00[Europe/Stockholm]
代码:
public class DateFormatSampleCode {
public static void main(String[] args)
{
LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(1459123200, 0, ZoneOffset.UTC);
System.out.println(localDateTime);
localDateTime = localDateTime.minusDays(1);
System.out.println(localDateTime);
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.of("Europe/Stockholm"));
System.out.println(zonedDateTime);
}
}
请检查并指出我哪里出错了。
我想我可以回答我上面的问题。
这是代码。
public ZonedDateTime addDays(long myUTCTimeInSeconds, int days) {
Instant instant = Instant.ofEpochSecond(myUTCTimeInSeconds);
ZonedDateTime dateTimeWithOffSet = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault());
if (localDays >= 0) {
dateTimeWithOffSet = dateTimeWithOffSet.plusDays(localDays);
} else {
dateTimeWithOffSet = dateTimeWithOffSet.minusDays(abs(localDays));
}
return dateTimeWithOffSet;
}
如果时区与系统时区不同,我们可以设置默认时区,并在调用上述方法后将时区重置为:
TimeZone systemDefaultTimeZone = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone(timezone));
addDays(1459123200, 1);
TimeZone.setDefault(systemDefaultTimeZone);
很好,您找到了解决方案,我只想补充一些见解并提出一些改进建议 。
使用 TimeZone.setDefault
设置 JVM 默认时区并不是实现此目的的最佳方法。虽然它可能在大多数情况下都有效,但如果您在更复杂的环境中考虑此代码 运行,它会有点冒险且容易出错。
那是因为 TimeZone.setDefault
更改了 整个 JVM 的默认时区。同一 JVM 中的任何其他应用程序 运行 都会受其影响。同一应用程序的其他部分也会受到影响,甚至在多线程中使用相同的代码 运行 也可能会产生错误的结果(和 race conditions are hard to debug)。
我注意到您正在使用 TimeZone.setDefault(TimeZone.getTimeZone(timezone));
。这意味着您已经在使用特定时区,因此无需依赖 JVM 的默认设置。如果您有特定的时区名称,只需使用它而不是默认名称。所以我建议你 addDays
方法应该是这样的:
public ZonedDateTime addDays(long myUTCTimeInSeconds, int days, String timezone) {
// get the instant from the UTC seconds
Instant instant = Instant.ofEpochSecond(myUTCTimeInSeconds);
// get the instant at the specified timezone
ZonedDateTime z = instant.atZone(ZoneId.of(timezone));
// add days
return z.plusDays(days);
}
所做的改进:
如果您将 -1
传递给它,plusDays
已经减去了 1 天。无需检查值并使用 abs
方法。
- 不要使用 JVM 默认时区:不要使用
ZoneId.systemDefault()
,而是使用您已有的 timezone
(您在 setDefault
方法中使用的那个)
instant.atZone
等价于 ZonedDateTime.ofInstant
。 IMO,atZone
更像是 "readable",但在这种情况下,这是一个选择和代码风格的问题。这对最终结果没有影响。
有了这个,你可以做:
// call directly, no need to change the default timezone
System.out.println(addDays(1459123200, -1, "Europe/Stockholm"));
这将打印:
2016-03-27T03:00+02:00[Europe/Stockholm]
我正在使用 "2016-03-28T02:00:00+0200"
(UTC 秒为 1459123200)进行测试
减去 1 天后,应用 DST,输出应为:
"2016-03-27T03:00:00+0200"
但我得到这个:
2016-03-26T01:00+01:00[Europe/Stockholm]
代码:
public class DateFormatSampleCode {
public static void main(String[] args)
{
LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(1459123200, 0, ZoneOffset.UTC);
System.out.println(localDateTime);
localDateTime = localDateTime.minusDays(1);
System.out.println(localDateTime);
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.of("Europe/Stockholm"));
System.out.println(zonedDateTime);
}
}
请检查并指出我哪里出错了。
我想我可以回答我上面的问题。
这是代码。
public ZonedDateTime addDays(long myUTCTimeInSeconds, int days) {
Instant instant = Instant.ofEpochSecond(myUTCTimeInSeconds);
ZonedDateTime dateTimeWithOffSet = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault());
if (localDays >= 0) {
dateTimeWithOffSet = dateTimeWithOffSet.plusDays(localDays);
} else {
dateTimeWithOffSet = dateTimeWithOffSet.minusDays(abs(localDays));
}
return dateTimeWithOffSet;
}
如果时区与系统时区不同,我们可以设置默认时区,并在调用上述方法后将时区重置为:
TimeZone systemDefaultTimeZone = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone(timezone));
addDays(1459123200, 1);
TimeZone.setDefault(systemDefaultTimeZone);
很好,您找到了解决方案,我只想补充一些见解并提出一些改进建议
使用 TimeZone.setDefault
设置 JVM 默认时区并不是实现此目的的最佳方法。虽然它可能在大多数情况下都有效,但如果您在更复杂的环境中考虑此代码 运行,它会有点冒险且容易出错。
那是因为 TimeZone.setDefault
更改了 整个 JVM 的默认时区。同一 JVM 中的任何其他应用程序 运行 都会受其影响。同一应用程序的其他部分也会受到影响,甚至在多线程中使用相同的代码 运行 也可能会产生错误的结果(和 race conditions are hard to debug)。
我注意到您正在使用 TimeZone.setDefault(TimeZone.getTimeZone(timezone));
。这意味着您已经在使用特定时区,因此无需依赖 JVM 的默认设置。如果您有特定的时区名称,只需使用它而不是默认名称。所以我建议你 addDays
方法应该是这样的:
public ZonedDateTime addDays(long myUTCTimeInSeconds, int days, String timezone) {
// get the instant from the UTC seconds
Instant instant = Instant.ofEpochSecond(myUTCTimeInSeconds);
// get the instant at the specified timezone
ZonedDateTime z = instant.atZone(ZoneId.of(timezone));
// add days
return z.plusDays(days);
}
所做的改进:
-
如果您将
plusDays
已经减去了 1 天。无需检查值并使用abs
方法。- 不要使用 JVM 默认时区:不要使用
ZoneId.systemDefault()
,而是使用您已有的timezone
(您在setDefault
方法中使用的那个) instant.atZone
等价于ZonedDateTime.ofInstant
。 IMO,atZone
更像是 "readable",但在这种情况下,这是一个选择和代码风格的问题。这对最终结果没有影响。
-1
传递给它,有了这个,你可以做:
// call directly, no need to change the default timezone
System.out.println(addDays(1459123200, -1, "Europe/Stockholm"));
这将打印:
2016-03-27T03:00+02:00[Europe/Stockholm]