5

我已经搜索过这个问题的答案,但似乎找不到。有人能解释一下为什么下面的代码没有给我每个月的 1,而是跳到 31 几个月吗?它用于需要确定下个月日期的报告事件。

DateFormat formatter = new SimpleDateFormat("dd-MMM-yy"); 
Calendar cal = Calendar.getInstance( TimeZone
    .getTimeZone( "America/Los_Angeles" ) );
DateFormat formatter = new SimpleDateFormat("dd-MMM-yy"); 
Date date;
try
{
  date = (Date)formatter.parse("01-JUN-12");
  cal.setTime(date);
  //cal.set(2012, 05, 01);

  long now = new Date().getTime();
  int frequency = 1;

  System.out.println("Current calendar time=" + cal.getTime().toString()) ;

  while (cal.getTime().getTime() < now)
  {
      cal.add( Calendar.MONTH, frequency );
      System.out.println("In loop - current calendar time=" + cal.getTime().toString()) ;
  }
}
catch (ParseException e)
{
  // TODO Auto-generated catch block
  e.printStackTrace();
} 

输出是:

Current calendar time=Fri Jun 01 00:00:00 EDT 2012

In loop - current calendar time=Sun Jul 01 00:00:00 EDT 2012

In loop - current calendar time=Tue Jul 31 00:00:00 EDT 2012

In loop - current calendar time=Fri Aug 31 00:00:00 EDT 2012

In loop - current calendar time=Mon Oct 01 00:00:00 EDT 2012

In loop - current calendar time=Wed Oct 31 00:00:00 EDT 2012

注意它是如何跳到 31,然后又回到 1。如果我使用 Calendar.set() 代替,输出是正确的:

Current calendar time=Fri Jun 01 15:14:26 EDT 2012

In loop - current calendar time=Sun Jul 01 15:14:26 EDT 2012

In loop - current calendar time=Wed Aug 01 15:14:26 EDT 2012

In loop - current calendar time=Sat Sep 01 15:14:26 EDT 2012

In loop - current calendar time=Mon Oct 01 15:14:26 EDT 2012

In loop - current calendar time=Thu Nov 01 15:14:26 EDT 2012

这似乎是 1) 日历 API 的错误,或者 2) 缺乏对日历 API 工作原理的理解。在任何一种情况下,我只想要下个月(同一天),除非某些月份和日期当然有问题。但是上面的场景让我很困惑。这个月的任何其他日子都不会发生这种情况,只有第一天才会发生。

4

4 回答 4

3

而不是cal.add( Calendar.MONTH, frequency );,试试这个:

cal.add(Calendar.MONTH, frequency);
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH));
于 2012-10-23T19:28:01.690 回答
3

这是时区问题。将现有的设置行替换为以下内容,以便日历和格式化程序都使用相同的时区:

    TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
    formatter.setTimeZone(tz);
    Calendar cal = Calendar.getInstance(tz);

一切正常:

Current calendar time=Fri Jun 01 08:00:00 BST 2012
In loop - current calendar time=Sun Jul 01 08:00:00 BST 2012
In loop - current calendar time=Wed Aug 01 08:00:00 BST 2012
In loop - current calendar time=Sat Sep 01 08:00:00 BST 2012
In loop - current calendar time=Mon Oct 01 08:00:00 BST 2012
In loop - current calendar time=Thu Nov 01 07:00:00 GMT 2012

您可能希望将toString()呼叫替换为呼叫,formatter.format()以便输出也位于正确的时区(如果您在 EDT,它可能看起来不错,但我在英国时区,如您所见)。

于 2012-10-23T19:35:54.713 回答
2

您正在以系统默认时区打印时间。使用格式化程序显示它。

为格式化程序设置相同的时区:

DateFormat formatter = new SimpleDateFormat("dd-MMM-yy");
formatter.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));

并用它来显示时间:

while (cal.getTime().getTime() < now) { 
    cal.add(Calendar.MONTH, frequency);
    System.out.println("In loop - current calendar time=" + formatter.format(cal.getTime()));
}
于 2012-10-23T19:38:44.073 回答
0

来自 Javadoc日历

add(f, delta) 将 delta 添加到字段 f。这相当于调用 set(f, get(f) + delta) 并进行两次调整:

  • 添加规则 1。调用后字段 f 的值减去调用前字段 f 的值是 delta,以字段 f 中发生的任何溢出为模。当字段值超出其范围时会发生溢出,因此,下一个较大的字段会递增或递减,并且字段值会调整回其范围内。

  • 增加规则2。如果一个较小的字段期望是不变的,但是由于字段f改变后其最小值或最大值的变化或其他约束,例如时区偏移量变化,它不可能等于它的先前值,然后将其值调整为尽可能接近其预期值。较小的字段表示较小的时间单位。HOUR 是比 DAY_OF_MONTH 小的字段。不对预期不会保持不变的较小字段进行任何调整。日历系统确定哪些字段预计是不变的。

此外,与 set() 不同,add() 强制立即重新计算日历的毫秒数和所有字段。

Example: Consider a GregorianCalendar originally set to August 31, 1999. Calling add(Calendar.MONTH, 13) sets the calendar to September 30, 2000. Add rule 1 sets the MONTH field to September, since adding 13 months to August gives September of the next year. Since DAY_OF_MONTH cannot be 31 in September in a GregorianCalendar, add rule 2 sets the DAY_OF_MONTH to 30, the closest possible value. Although it is a smaller field, DAY_OF_WEEK is not adjusted by rule 2, since it is expected to change when the month changes in a GregorianCalendar.

So in your case considering the time that you have set if calendar API add month to Jul then it is possible that it is getting overflown more than a month so it is being adjusted back Jul Midnight

于 2012-10-23T19:42:43.470 回答