java日历返回错误的一周

时间:2014-09-18 16:14:57

标签: java calendar

我有以下测试,我的理解应该通过。有没有我遗失的东西或者这是日历中的错误?

Calendar inputC = new GregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.US);  

// Sunday  
inputC.set(2014, Calendar.JUNE, 22, 0, 0, 0);  
inputC.set(Calendar.MILLISECOND, 0);  

// START code impl
// Given a date returns back the date with it's day being first day of week and resets time.  
Calendar dc = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.US);  
dc.set(inputC.get(Calendar.YEAR), inputC.get(Calendar.MONTH), inputC.get(Calendar.DAY_OF_MONTH), 0, 0 , 0);  
dc.set(Calendar.MILLISECOND, 0);  
// dc.getTimeInMillis();  
// dc.set(Calendar.WEEK_OF_YEAR, inputC.get(Calendar.WEEK_OF_YEAR));  
dc.set(Calendar.DAY_OF_WEEK, dc.getFirstDayOfWeek());  

Date output = dc.getTime();  
// END code impl

Calendar expectedSundayC = new GregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.US);  
expectedSundayC.set(2014, Calendar.JUNE, 22, 0, 0, 0);  
expectedSundayC.set(Calendar.MILLISECOND, 0);  

assertEquals(output, expectedSundayC.getTime());  

输出:2014-06- 15 T00:00:00Z
预计:2014-06- 22 T00:00:00Z

上述测试失败,除非我取消注释以下两行中的任何一行:

// dc.getTimeInMillis();  
// dc.set(Calendar.WEEK_OF_YEAR, inputC.get(Calendar.WEEK_OF_YEAR));

为什么dc.getTimeInMillis()影响输出?
取消注释上面的第2行似乎是多余的,因为我已经设置了年,月,日,小时,分钟,秒,毫秒字段,这应该是完整日期时间的足够数据。

1 个答案:

答案 0 :(得分:16)

你当然给了我一个关于这个的头像,但我明白了。

规格

From the JavaDoc:

  

可以通过调用set方法来设置日历字段值。在需要计算其时间值(Epoch的毫秒数)或日历字段的值之前,不会解释日历中设置的任何字段值。调用get,getTimeInMillis,getTime,add和roll涉及这样的计算。

这意味着,无论何时使用set更改值,都应该告诉它使用其中一种get方法来传播更改来更新自身。但是,除非取消注释代码,否则您的代码不会。因此,代码中的某些内容显然无法与computeTime中的set一起使用。

事情不好

所以...我们可以看看这个过时但仍然难以驾驭的 GregorianCalendar来源的副本,并准确分析你做的时候发生了什么使用所有其他chanes设置星期几之后的getTime()。

唉,抄袭是痛苦的。我会引导你完成它。

首先,我们基本知道getTimeInMillis()computeTime()行调用505

您声明了Calendar的实例。它具有预定义的值。今天的确切日期。而且我们也在九月的第三周。重要的是晚些时候!

现在我们在511行分配您的一天。这是您的一天设置为22的地方。

int day = fields[DAY_OF_MONTH];

523是令人兴奋的地方并打破你的约会!我们将其评估为false并转到第553行的其他位置。

if (! isSet[MONTH] && (! isSet[DAY_OF_WEEK] || isSet[WEEK_OF_YEAR]))
//evaluates to true

我们在555输入if语句,因为设置了DAY_OF_WEEK。我们有DAY_OF_WEEK_IN_MONTH,但我们没有自己设定。日历在实例化时执行了。还记得九月的第三周吗?是的,它在这里它咬我们回来。它计算6月的第3周的星期日,并且将我们的日期= 22 覆盖到15日,这是6月的第3个星期日。

    if (isSet[DAY_OF_WEEK]) { //yup we set this
        int first = getFirstDayOfMonth(year, month);

        // 3: YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
        if (isSet[DAY_OF_WEEK_IN_MONTH]) { //we didn't set this, but it's been set!
            if (fields[DAY_OF_WEEK_IN_MONTH] < 0) { 
                month++;
                first = getFirstDayOfMonth(year, month);
                day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH]);
            } else
                day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH] - 1); 
                 // 15 = 1 + 7 * (3 - 1)

            int offs = fields[DAY_OF_WEEK] - first;
            if (offs < 0)
                offs += 7;
            day += offs;
        }
    }

我们如何解决问题

检查您用年,月和日调用的set方法的源javadoc。

  

设置日历字段YEAR,MONTH,DAY_OF_MONTH,HOUR_OF_DAY和MINUTE的值。 保留其他字段的先前值。如果不需要,请先调用clear()。

果然,在你的套装之前添加dc.clear();会返回所需的结果。我没有通过它,但是鉴于目前经历的奇怪行为,每次更改后最好的选择是getTime()将确保您的时间不会成为意外结果。< / p>

所以基本上,造成这个问题的原因是你有半数据的数据,半新数据与自己相矛盾并覆盖了一些东西。如果这段代码已经在下周运行,你甚至都不会发现这个错误。对于一个不方便的时间依赖条件,这是怎么回事?