【问题标题】:JAVA: Weird behaviour when changing TimeZone of Calendar object without changing the Date/TimeJAVA:更改日历对象的时区而不更改日期/时间时的奇怪行为
【发布时间】:2014-02-16 01:25:07
【问题描述】:

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

我看了this other question。在这里,接受的答案是单独设置新日历对象的每个字段。但是,我可以通过简单地更改原始日历对象副本的时区来使其工作。奇怪的是,这只有在我在重置时区之前对原始日历进行一些修改时才有效。下面的例子说明了不一致。

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 按预期打印,但下一行不是 正如预期的那样(它应该对应于 1/24/2014 2:33:58PM UTC,相反,它对应于 当前日期/时间,即 2014 年 1 月 24 日 2:33:58 MTN)。谁能告诉我这里发生了什么?

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

【问题讨论】:

标签: java datetime calendar timezone datetime-conversion


【解决方案1】:

设置时区不像设置字段那样起作用。当您设置字段时,内部毫秒数会重新计算以匹配字段。当您设置时区时,会重新计算字段,以便毫秒数保持不变! (也就是说,当您将一个 clanedar 评估为下午 2:00 山并将其设置为 UTC 时,它不会计算代表 2PM UTC 的毫秒数,它会将时间更改为晚上 9 点。)

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

所以场景1日历有文本值:

UTC 时间 2015 年 1 月 24 日星期六下午 2:33:58,因为 add 被称为“根据‘人类’值重新计算毫秒数”的标志已打开,它会为您提供预期的 #。

场景 2 日历有文本值:

UTC 时间 2015 年 1 月 24 日星期六下午 2:33:58,但表示重新计算毫秒数的标志未打开,因此设置时区打开的标志为“基于毫秒重新计算'人类值'”是打开,它改变它到晚上 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());
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-17
    • 1970-01-01
    • 2013-09-21
    • 2015-01-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多