繁体   English   中英

JAVA:更改日历对象的时区而不更改日期/时间时的奇怪行为

[英]JAVA: Weird behaviour when changing TimeZone of Calendar object without changing the Date/Time

我试图更改日历对象的时区,同时保持实际日期/时间,以便可以在更新的日历对象上调用getTimeInMilliseconds()。

我看着另一个问题 在这里,可接受的答案是单独设置新Calendar对象的每个字段。 但是,我能够通过简单地更改原始Calendar对象的副本的时区来使其工作。 奇怪的是,仅当我在重置timeZone之前对原始Calendar进行了一些修改时,此方法才有效。 以下示例说明了不一致之处。

public void TestCalendar()
{
   Calendar nextYear = Calendar.getInstance();
   nextYear.add(Calendar.YEAR, 1);
   log.info("Next Year: {}", getUTCMilliseconds(nextYear));

   Calendar now = Calendar.getInstance();
   log.info("Now: {}", getUTCMilliseconds(now));
}

protected String getUTCMilliseconds(Calendar cal)
{
   // Create a new calendar so we don't modify input
   Calendar expectedDbTime = (Calendar) cal.clone();
   // Change the TimeZone the contained date is interpreted in
   expectedDbTime.setTimeZone(TimeZone.getTimeZone("UTC"));
   // Return millisecond value of this date in the UTC timezone
   return String.valueOf(expectedDbTime.getTimeInMillis());
}

我在2015年1月24日下午2:33运行了该程序,并得到以下输出:

Next Year: 1422110038529 //(Corresponding UTC Date: Sat Jan 24 2015 2:33:58 PM)
Now: 1390599238531 //(Corresponding UTC Date: Fri Jan 24 2014 9:33:58 PM)

如您所见,nextYear打印出预期的结果,但是下一行却不符合预期(它应该对应于UTC的1/24/2014 2:33:58 PM UTC,相反,它对应的是当前日期/时间1/24 / 2014 2:33:58 MTN)。 有人可以告诉我这是怎么回事吗?

编辑:刚刚更新了一些格式。

设置时区不能像设置字段那样工作。 设置字段时,将重新计算内部的毫秒数以匹配字段。 设置时区时,将重新计算字段,以使毫秒数保持不变! (也就是说,当您将clanedar评估为2:00 PM峰并将其设置为UTC时,它不会计算代表2PM UTC的毫秒数,因此会将时间更改为9PM。)

现在这是横set的地方,当您有一个待处理的set (或在您的情况下add )时,告诉Calendar重新计算毫秒数(由add启用)的标志优先于指示是否重新计算文本字段的标志。关闭毫秒(由setTimeZone打开)

因此,方案1日历具有文本值:

2015年1月24日,星期六UTC,并且因为add被称为标志,说“基于“人类”值重新计算毫秒”被打开,它为您提供了您期望的#。

方案2日历具有文本值:

2015年1月24日,星期六UTC,但表示重新计算毫秒数的标志未打开,因此将设置时区设置为“基于毫秒重新计算“人类值”的标志”打开,并且它将更改为晚上9点。

如果将代码更改为:

public void TestCalendar() {
    Calendar nextYear = Calendar.getInstance();
    nextYear.add(Calendar.YEAR, 1);
    nextYear.getTime();
    log.info("Next Year: {}", getUTCMilliseconds(nextYear));

    Calendar now = Calendar.getInstance();
    log.info("Now: {}", getUTCMilliseconds(now));
}

您将看到两者之间的行为一致,因为调用getTime()会清除该标志,以便在设置时区之前重新计算毫秒。

现在您知道为什么股票对此类问题的答案是“ omg use jodatime”!

获得您期望发生的事情的“安全”方法是:

protected String getUTCMilliseconds(Calendar cal) {
    Calendar utcCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    utcCal.set(Calendar.YEAR, cal.get(Calendar.YEAR));
    utcCal.set(Calendar.DAY_OF_YEAR, cal.get(Calendar.DAY_OF_YEAR)); 
    utcCal.set(Calendar.HOUR_OF_DAY, cal.get(Calendar.HOUR_OF_DAY));
    utcCal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE));
    utcCal.set(Calendar.SECOND, cal.get(Calendar.SECOND));
    utcCal.set(Calendar.MILLISECOND, cal.get(Calendar.MILLISECOND));
    return String.valueOf(utcCal.getTimeInMillis());
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM