如果我们在当前日期加上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
答案 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/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用诸如EST
或IST
之类的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 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和& SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。