假设2000年1月31日是闰年,以下两种方式增加一个月会给我不同的结果。我做错了什么,还是处理闰年的两种不同的哲学?而且,如果这两种方法只是一种哲学差异,你怎么知道选择哪种方法呢?
方法1:使用LocalDate plusMonths()
:
LocalDate atestDate = LocalDate.parse("2000-01-31");
System.out.println("One month in future using LocalDate.addMonths() " + atestDate.plusMonths(1));
输出:
One month in future using LocalDate.addMonths() 2000-02-29
方法2:使用Calendar
:
Calendar zcal = Calendar.getInstance();
zcal.set(Calendar.DAY_OF_MONTH, 31);
zcal.set(Calendar.MONTH, 1);
zcal.set(Calendar.YEAR, 2000);
zcal.add(Calendar.MONTH, 0);
System.out.println("ONE MONTH IN FUTURE using Calendar: "
+ zcal.getTime());
输出:
ONE MONTH IN FUTURE using Calendar: Thu Mar 02 2000
为什么这两个日期'输出不一样?
感谢。
答案 0 :(得分:3)
zcal.set(Calendar.DAY_OF_MONTH, 31);
zcal.set(Calendar.MONTH, 1);
zcal.set(Calendar.YEAR, 2000);
这定义了2000年2月31日的日期,由于测量宽松,它等于2000年3月2日。月值为零索引。
严格设定:
zcal.setLenient(false);
我们得到一个例外:
Exception in thread "main" java.lang.IllegalArgumentException: MONTH: 1 -> 2
at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2829)
at java.util.Calendar.updateTime(Calendar.java:3393)
at java.util.Calendar.getTimeInMillis(Calendar.java:1782)
at java.util.Calendar.getTime(Calendar.java:1755)
at Employee.Tester.main(Tester.java:19)
所以你可以看到插值转移到了三月。如果您希望硬编码日期以用于测试目的,我建议使用严格验证来避免这些情况。还有其他与设置值相关的文档问题会使事情变得混乱。
答案 1 :(得分:1)
我在上面的帖子中遇到的问题原来是一个错字。
Calendar zcal = Calendar.getInstance();
zcal.set(Calendar.DAY_OF_MONTH, 31);
zcal.set(Calendar.MONTH, 1);
zcal.set(Calendar.YEAR, 2000);
zcal.add(Calendar.MONTH, 0);
System.out.println("ONE MONTH IN FUTURE using Calendar: "
+ zcal.getTime());
应该是:
Calendar zcal = Calendar.getInstance();
zcal.set(Calendar.DAY_OF_MONTH, 31);
zcal.set(Calendar.MONTH, Calendar.JANUARY);
zcal.set(Calendar.YEAR, 2000);
zcal.add(Calendar.MONTH, 1);
System.out.println("ONE MONTH IN FUTURE using Calendar: "
+ zcal.getTime());
然后我得到预期的匹配日期。
感谢所有回答的人! :)
答案 2 :(得分:1)
只需使用LocalDate
,忘记所有Calendar
(麻烦遗留类)。
LocalDate.parse( "2000-01-31" ).plusMonths( 1 ).toString()
2000-02-29
方法1:使用LocalDate plusMonths():
LocalDate
课程文档解释说,它首先添加月份编号,然后单独留下日期。如果该月中的某一天在该月(29,30或31)无效,则会向后调整到最后一个有效的日期。
此方法分三个步骤将指定的金额添加到月份字段:
- 将输入月份添加到年度字段
- 检查结果日期是否无效
- 如有必要,将日期调整为最后一个有效日
醇>例如,2007-03-31加上一个月会导致2007-04-31无效日期。而不是返回无效结果,而是选择该月的最后一个有效日期,即2007-04-30。
对我来说似乎是一种聪明的态度。
在您的示例中,LocalDate
首先将2000-01-31
更改为2000-02-31
。 2月份没有第31届,所以它又回到了30年。但也没有2月30日。所以它进一步回到了29.宾果!在那个月的那一年,确实有第29天,因为2000年是Leap Year。所以答案是2000-02-29
。
LocalDate.parse( "2000-01-31" )
.plusMonths( 1 )
.toString()
2000-02-29
方法2:使用日历:
不要打扰。
这个非常麻烦的旧Calendar
类现在已经遗留下来,完全由JSR 310中定义的 java.time 类取代。具体而言,由ZonedDateTime
取代。
永远不要再触摸Calendar
课程。为自己保留一些痛苦和头痛,并假装这门课程从未存在过。
此外,Calendar
是与时间同步的值。不适合像问题一样的仅限日期的值。
如果您必须与尚未更新到 java.time 的代码进行互操作,请通过调用添加到 java.time 的新转换方法在旧版类和 java.time 之间进行转换老班。有关详情,请参阅问题Convert java.util.Date to what “java.time” type?
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和& SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。