调用Java的Calendar.getTime()是否需要更新对象?

我在一些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(); 

不,这不是真的。

确实需要调用cal.getTime()来重新计算其内部结构。 这是一个非常奇怪的API行为,但Calendar javadocs明确说明了这一点:

获取和设置日历字段值

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

野外操纵

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

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

 /** * 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()); } } 

你可能会遇到Bug ID 4851640

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

不,它不应该是必要的。

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

我遇到过旧jvms中日历序列化的错误。

Bug游行bug#4328747

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

@skaffman答案不正确

这是一个certificate我在说什么的例子。

 //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

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

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

从Javadocs中的Calendar类检查: 获取和设置日历字段值

要修复它,我们添加以下代码calendar.getTimeInMillis(); 这会强制Calendar更新其属性。

 //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] 

因此,对于你的问题Calendar.getTime(); @skaffman Answer是不正确的Calendar.getTime(); 如果你不想在某些时候获得奇怪的价值,确实需要。