添加月份时的Java日历/日期问题

时间:2013-06-05 13:27:51

标签: java

如果我们在当前日期加上1个月(2013年5月31日星期五18:33:00),它会产生:

Sun Jun 30 18:33:00 IST 2013

如果我们减去1个月它会产生:

Thu May 30 18:33:00 IST 2013

这是一个错误还是任何人都可以提供推理?

请找到相同的代码:

Calendar c1 = Calendar.getInstance()
System.out.println(c1.getTime());
c1.add(Calendar.MONTH, 1);
System.out.println(c1.getTime());
c1.add(Calendar.MONTH, -1);
System.out.println(c1.getTime());

输出

Fri May 31 18:33:00 IST 2013
Sun Jun 30 18:33:00 IST 2013
Thu May 30 18:33:00 IST 2013

4 个答案:

答案 0 :(得分:6)

更改日期是预期的行为,在此处记录为“添加规则2”:http://docs.oracle.com/javase/6/docs/api/java/util/Calendar.html

  

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

     
    

添加规则1。调用后字段f的值减去调用前的字段f的值为delta,以模拟字段f中发生的任何溢出为模。当字段值超出其范围时发生溢出,结果,下一个更大的字段递增或递减,字段值调整回其范围。

         

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

  

根据这些规则,如果你加1个月,那就没有办法保留月份的日期,然后加上-1个月。

答案 1 :(得分:1)

不,这是一个功能并且有记录(抱歉在这里很挑剔,但你真的在问之前阅读了文档吗?)。阅读docs中关于Field Manipulation方法的add()部分。相关部分:

  

如果预期较小的场不变,但这是不可能的   因为它的变化,它等于它的先前值   字段f改变后的最小值或最大值或其他约束条件   随着时区偏移的变化,其值将被调整为接近   尽可能达到预期值。

     

示例:考虑最初设置为8月31日的GregorianCalendar,   调用add(Calendar.MONTH,13)将日历设置为2000年9月30日。添加规则1将MONTH字段设置为9月,因为添加   截至8月的13个月给了明年9月。以来   添加时,DAY_OF_MONTH在9月的GregorianCalendar中不能是31   规则2将DAY_OF_MONTH设置为30,即最接近的可能值。   虽然它是一个较小的字段,但规则2不会调整DAY_OF_WEEK,   因为当月份变化时,预计会发生变化   GregorianCalendar的。

答案 2 :(得分:0)

这是正确的,因为它是设计的。

当您从5月31日起添加一个月后,您将获得6月30日(该月的最后一天)。

当您从6月30日减去一个月后,您将获得5月31日(一个月前)。

当您使用getTime()获得时间时,计算完成。因此,我在这里看不到任何错误。

答案 3 :(得分:0)

使用java.time类替换现代时代,这些类取代了麻烦的旧遗留日期时间类,包括Calendar

时区

定义您的时区。以continent/region格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用诸如ESTIST之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。我猜你的印度时间是IST,但也许是爱尔兰标准时间或其他?

final ZoneId z = ZoneId.of ( "Asia/Kolkata" );

ZonedDateTime类表示调整到特定时区的时间轴上的一个时刻。

final ZonedDateTime zdt = ZonedDateTime.of ( 2013, 5, 31, 18, 33, 0, 0, z );

plusMonths | minusMonths

final ZonedDateTime zdtMonthPlus = zdt.plusMonths ( 1 );
final ZonedDateTime zdtMonthMinus = zdtMonthPlus.minusMonths ( 1 );

转储到控制台。

System.out.println ( "zdt.toString(): " + zdt );
System.out.println ( "zdtMonthPlus.toString(): " + zdtMonthPlus );
System.out.println ( "zdtMonthMinus.toString(): " + zdtMonthMinus );

我们看到与旧版Calendar类相同的行为。当从5月31日开始添加一个月时,没有6月31日,所以它会回到6月30日这个月的最后一天。从6月30日减去时,该类会尝试匹配同一天的月份,因此它使用5月30.

  

zdt.toString():2013-05-31T18:33 + 05:30 [亚洲/加尔各答]

     

zdtMonthPlus.toString():2013-06-30T18:33 + 05:30 [亚洲/加尔各答]

     

zdtMonthMinus.toString():2013-05-30T18:33 + 05:30 [亚洲/加尔各答]

plus( Duration) | minus( Duration )

如果您想要其他行为,请使用其他方法代码。例如,如果按“月”表示“通常的24小时工作日30天(忽略夏令时等异常情况)”,则加上/减去30天的持续时间。

Duration thirtyDays = Duration.ofDays( 30 );  // 30 days of generic 24-hour length. Ignoring anomalies such as DST. Ignoring calendar months.
final ZonedDateTime zdtMonthPlusDuration = zdt.plus ( thirtyDays );
final ZonedDateTime zdtMonthMinusDuration = zdtMonthPlusDuration.minus ( thirtyDays );
  

thirtyDays.toString():PT720H

     

zdtMonthPlusDuration.toString():2013-06-30T18:33 + 05:30 [亚洲/加尔各答]

     

zdtMonthMinusDuration.toString():2013-05-31T18:33 + 05:30 [亚洲/加尔各答]

关于java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和& SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore