【问题标题】:Is calling Java's Calendar.getTime() necessary to update the object?是否需要调用 Java 的 Calendar.getTime() 来更新对象?
【发布时间】:2009-09-10 18:44:30
【问题描述】:

我在一些 java 代码中发现了一条注释,指出需要调用 getTime() 来更新 Calendar 对象。这是真的?我找不到任何说这是必要的。

代码如下:

Calendar cal = new GregorianCalendar();
cal.setFirstDayOfWeek(Calendar.SUNDAY);
cal.set(2009, 9 - 1, 10, 2, 30);
// Get Time needs to be called to update the Calendar object
cal.getTime();

【问题讨论】:

  • 另外,为了让你的代码更简洁,你应该使用这个版本的构造函数:GregorianCalendar(int year, int month, int dayOfMonth) .... 真的不需要其他调用.
  • 请检查我的答案。

标签: java calendar


【解决方案1】:

cal.getTime() 确实需要调用以重新计算其内部结构。 API 的行为非常奇怪,但 Calendar javadocs 明确指出:

获取和设置日历字段值

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

...

字段操作

可以使用三种方法更改日历字段:set()、add()、 和滚动()。 set(f, value) 将日历字段 f 更改为值。在 此外,它设置了一个内部成员变量来指示 日历字段 f 已更改。虽然日历字段 f 是 立即更改,日历的时间值(以毫秒为单位)不 重新计算直到下一次调用 get()、getTime()、getTimeInMillis()、 add() 或 roll()。因此,多次调用 set() 不会触发 多次,不必要的计算。由于更改日历 使用 set() 的字段,其他日历字段也可能更改,具体取决于 日历字段、日历字段值和日历系统。 另外,get(f) 不一定会返回调用设置的值 重新计算日历字段后设置方法。这 具体由具体的日历类决定。

该行为是意外的,并不总是发生,但以下单元测试应该举例说明这种行为并且总是发生。

/**
 * Fails the assertion due to missing getTime()
 * @throws ParseException 
 */
public class DateTest {

    @Test
    public void testNoGetTime() throws ParseException {

        DateFormat df = new SimpleDateFormat("MM/dd/yyyy");
        Date testDate = df.parse("04/15/2013");
        Calendar testCal = Calendar.getInstance();
        testCal.setTime(testDate);
        Date expectedDate = df.parse("04/04/2013");
        Date actualDate = null;

        testCal.set(Calendar.DAY_OF_MONTH, testCal.getMinimum(Calendar.DAY_OF_MONTH));
        //testCal.getTime();
        testCal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
        testCal.add(Calendar.DAY_OF_MONTH, -1);
        actualDate = testCal.getTime();
        assertEquals("Dates should be equal", expectedDate.toString(), actualDate.toString());
    }

    @Test
    public void testWithGetTime() throws ParseException {

        DateFormat df = new SimpleDateFormat("MM/dd/yyyy");
        Date testDate = df.parse("04/15/2013");
        Calendar testCal = Calendar.getInstance();
        testCal.setTime(testDate);
        Date expectedDate = df.parse("04/04/2013");
        Date actualDate = null;

        testCal.set(Calendar.DAY_OF_MONTH, testCal.getMinimum(Calendar.DAY_OF_MONTH));
        testCal.getTime();
        testCal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
        testCal.add(Calendar.DAY_OF_MONTH, -1);
        actualDate = testCal.getTime();
        assertEquals("Dates should be equal", expectedDate.toString(), actualDate.toString());
    }
}

【讨论】:

  • 简而言之:如果您多次设置相同的字段,例如通过 setTime(..) 和 set(Day of week),那么您应该在使用 getTime() 之间触发重新计算,否则您将有一些奇怪的效果。
【解决方案2】:

@skaffman 答案是不正确

这是一个证明我所说的例子。

//Create an Calendar object set to todays date & time
Calendar calendar = Calendar.getInstance();
Log.d(Tag, "Now : "+ calendar.toString());

//Set the Calendar to the first day of Month
calendar.set(Calendar.DAY_OF_MONTH,1);
Log.d(Tag, "Calendar.DAY_OF_MONTH,1: "+ calendar.toString());

这是 Log.d 输出:

Now : java.util.GregorianCalendar[time=1478834995641,areFieldsSet=true,lenient=true,zone=America/Chicago,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2016,MONTH=10,WEEK_OF_YEAR=46,WEEK_OF_MONTH=2,DAY_OF_MONTH=10,DAY_OF_YEAR=315,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=9,HOUR_OF_DAY=21,MINUTE=29,SECOND=55,MILLISECOND=641,ZONE_OFFSET=-21600000,DST_OFFSET=0] Calendar.DAY_OF_MONTH,1: 
Calendar.DAY_OF_MONTH,1: java.util.GregorianCalendar[time=?,areFieldsSet=false,lenient=true,zone=America/Chicago,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2016,MONTH=10,WEEK_OF_YEAR=46,WEEK_OF_MONTH=2,DAY_OF_MONTH=1,DAY_OF_YEAR=315,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=9,HOUR_OF_DAY=21,MINUTE=29,SECOND=55,MILLISECOND=641,ZONE_OFFSET=-21600000,DST_OFFSET=0]

如果您花时间检查它: 开头的日历显示:

time=1478834995641
DAY_OF_MONTH=10  
DAY_OF_YEAR=315

但是,当您将其设置为该月的第一天时:

time=?
DAY_OF_MONTH=1  
DAY_OF_YEAR=315

是的,它按照我们的意愿更改了当月的第一天,但​​某些属性保持不变。例如,我们将日历设置为DAY_OF_MONTH=1,但我们仍然在DAY_OF_YEAR=315

如果我们使用以下任何函数,它将强制更新日历。

调用get、getTimeInMillis、getTime、add和roll。

来自 Javadocs 中的日历类检查:Getting and Setting Calendar Field Values

为了解决这个问题,我们添加了以下代码calendar.getTimeInMillis();,它强制日历更新其属性。

//Create an Calendar object set to todays date & time
Calendar calendar = Calendar.getInstance();
Log.d(Tag, "Now : "+ calendar.toString());

//Set the Calendar to the first day of Month
calendar.set(Calendar.DAY_OF_MONTH,1);
//UPDATE BY CALLING getTimeInMillis() or any of the previously mentioned functions
calendar.getTimeInMillis();
Log.d(Tag, "Calendar.DAY_OF_MONTH,1: "+ calendar.toString());

现在,让我们检查结果:

Now : java.util.GregorianCalendar[time=1478836452183,areFieldsSet=true,lenient=true,zone=America/Chicago,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2016,MONTH=10,WEEK_OF_YEAR=46,WEEK_OF_MONTH=2,DAY_OF_MONTH=10,DAY_OF_YEAR=315,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=9,HOUR_OF_DAY=21,MINUTE=54,SECOND=12,MILLISECOND=183,ZONE_OFFSET=-21600000,DST_OFFSET=0]
Calendar.DAY_OF_MONTH,1: java.util.GregorianCalendar[time=1478055252183,areFieldsSet=true,lenient=true,zone=America/Chicago,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2016,MONTH=10,WEEK_OF_YEAR=45,WEEK_OF_MONTH=1,DAY_OF_MONTH=1,DAY_OF_YEAR=306,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=1,HOUR=9,HOUR_OF_DAY=21,MINUTE=54,SECOND=12,MILLISECOND=183,ZONE_OFFSET=-21600000,DST_OFFSET=3600000]

因此,@skaffman 的答案是不正确的,至于你的问题Calendar.getTime(); 确实是需要的,如果你不想在某个时候得到奇怪的值。

【讨论】:

    【解决方案3】:

    您可能正在点击Bug ID 4851640

    在 Calendar 实例上调用 get(...) / getTime() 会使 isSet(...) 无用!

    【讨论】:

    • @eed3si9n - 很好的发现 - 我认为我们没有使用 isSet 但我会再看一遍。
    【解决方案4】:

    不,这不是真的。

    【讨论】:

    • 它曾经是在某些早期版本的 java 中吗?
    • 不,永远不会。现有行为永远不会以这种方式改变。
    • 如果是真的,这将是设计糟糕的 API 的一个很好的例子。 Getter 方法应该是只读的(这是我错过 C++ 中的 const 修饰符的时候)。
    • @abahgat - 我同意这就是我质疑它的原因。开发人员也没有将结果分配给任何东西。他们只是使用它,就好像它没有返回任何东西一样。
    • @abahgat: java.util.Calendar 一个设计糟糕的 API
    【解决方案5】:

    不,应该没有必要。

    日历是否在之后立即序列化?

    我遇到了旧 jvm 中日历序列化的错误。

    Bug parade bug #4328747

    在序列化之前调用 getTime 可能足以绕过该错误,尽管我没有安装足够旧的 JVM 来确认这一点。

    【讨论】:

    • 不,不是。该错误的解决方法不是调用 getTime(),而是调用 get/setGregorianChange()。我在想这可能是很久以前的一个错误(这是我正在查看的旧代码),但似乎情况并非如此。
    • 实际上有几种不同的解决方法,getGregorianChange 是其中之一,但 getTime 可能是其中的另一个。无论如何,如果它没有被序列化,它无关紧要
    猜你喜欢
    • 1970-01-01
    • 2020-07-21
    • 1970-01-01
    • 1970-01-01
    • 2014-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多