【问题标题】:Calendar.add changes time zoneCalendar.add 更改时区
【发布时间】:2018-02-23 23:48:37
【问题描述】:

在以下代码中,range 仅在 range 小于 30 时才等于 durationInDays。如果等于或大于 30,durationInDays 始终为 range - 1

Calendar c = Calendar.getInstance();
Date now = new Date();
c.setTime(now);

int range = 35;
c.add(Calendar.DATE, range);
Date then = c.getTime();

Duration duration = Duration.between(now.toInstant(), then.toInstant());
int durationInDays = (int)duration.toDays();

调试本例时,变量设置如下:

this = {CalTest@871} 
c = {GregorianCalendar@876} "java.util.GregorianCalendar[time=1522449516301,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/Dublin",offset=0,dstSavings=3600000,useDaylight=true,transitions=228,lastRule=java.util.SimpleTimeZone[id=Europe/Dublin,offset=0,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2018,MONTH=2,WEEK_OF_YEAR=13,WEEK_OF_MONTH=5,DAY_OF_MONTH=30,DAY_OF_YEAR=89,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=5,AM_PM=1,HOUR=11,HOUR_OF_DAY=23,MINUTE=38,SECOND=36,MILLISECOND=301,ZONE_OFFSET=0,DST_OFFSET=3600000]"
now = {Date@877} "Fri Feb 23 23:38:36 GMT 2018"
range = 35
then = {Date@878} "Fri Mar 30 23:38:36 IST 2018"
duration = {Duration@879} "PT839H"
durationInDays = 34

为什么then 有 IST 时区?这种差异导致duration 略小于 35 天,四舍五入为 34。

【问题讨论】:

  • 尝试打印Instant 值,你会明白为什么。
  • 你知道c.setTime(new Date()); 是多余的,因为Calendar.getInstance() 现在已经“现在”初始化它了,对吧?
  • 您的默认时区是什么?打印TimeZone.getDefault().getID()
  • 不要将可怕的旧遗留类 Date/Calendar 与现代 java.time 类混合。完全避免遗留类。仅使用 java.time。阅读Oracle Tutorial 并重新开始。并在发布前搜索 Stack Overflow。这些话题已经被处理过很多次了。

标签: java time calendar timezone


【解决方案1】:

首先,then 没有 IST 时区。 Date 没有任何时区。 Date.toString 选择一个时区,通常是 JVM 的时区设置,仅用于生成字符串。

查看您的两个Date 对象是如何在调试器中呈现的,您可能想知道它们似乎是在两个不同的时区呈现的。他们不是。两者都在欧洲/都柏林时区。您可能知道,拥有大部分欧盟国家的爱尔兰在 3 月的最后一个星期日切换到夏令时 (DST)。因此,您在 2 月的日期是标准时间,在爱尔兰与 GMT 重合,因此您的字符串以 GMT 呈现为“时区”。 3 月 30 日,夏令时生效,所以这次的时区呈现为 IST 代表爱尔兰夏令时。编辑:与您的预期持续时间 35 天(840 小时)相比,向夏季时间的过渡也说明了缺少的时间。

编辑:既然您可以使用现代 Java 日期和时间 API java.time,我建议您全力以赴,忘记老式的 Date 和 Calendar。现代 API 更易于使用,您不再需要所有转换:

    ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Europe/Dublin"));
    int range = 35;
    ZonedDateTime then = now.plusDays(range);
    Duration duration = Duration.between(now, then);
    int durationInDays = (int) duration.toDays();

结果仍然是 PT839H,因此是 34 天。

要获得与我们添加的天数一致的持续时间:

    int durationInDays = (int) ChronoUnit.DAYS.between(now, then);
    System.out.println(durationInDays);

打印出来

35

Duration 主要用于以小时、分钟和秒为单位的持续时间。它确实支持天数,但仅支持天数,每天 24 小时,因此在整个夏季时间转换中使用时会产生令人惊讶的结果。另一方面,ChronoUnit.DAYS 正好是几天的时间。它发现两个 ZonedDateTime 实例具有相同的时间,因此确认两者之间有整整 35 天。

【讨论】:

    猜你喜欢
    • 2012-11-23
    • 1970-01-01
    • 2011-03-27
    • 2017-02-13
    • 2020-08-04
    • 2016-08-30
    • 2013-08-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多