Java 添加年份的日历问题
Java Calendar issue with adding year
我正在尝试添加一个日期,但它为不同的输入提供了相同的日期。 输入日期为 28/Feb/2020 和 29/Feb/2020,两者的输出与 28/Feb/2021 相同。请帮我看看这里有什么问题。
更新:
我希望输出为 28/Feb/2021 和 01/Mar/2021。
public static Date dateAdd(Date newDate, int field, int amount) {
Calendar aCalendar = Calendar.getInstance();
aCalendar.setTime(newDate);
aCalendar.add(field, amount);
return aCalendar.getTime();
}
public static void main(String[] args) throws Exception {
System.out.println(dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("28/02/2020"),Calendar.YEAR,1));
System.out.println(dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("29/02/2020"),Calendar.YEAR,1));
// Console output below
//Sun Feb 28 00:00:00 IST 2021
//Sun Feb 28 00:00:00 IST 2021
}
使用:
System.out.println(dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("28/02/2020"),Calendar.DAY_OF_YEAR,366));
System.out.println(dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("29/02/2020"),Calendar.DAY_OF_YEAR,366));
你可以先加一年再加一天:
Date d = dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("28/02/2020"),Calendar.YEAR,1); // add a year
System.out.println(d);
d = dateAdd(d, Calendar.DATE, 1); // add a day
System.out.println(d);
没有理由期待 01/Mar/2021 的结果。事实上,有些人会质疑为什么在两个日期 29/Feb/2020 和 01/Mar/2020 上加上一年会为它们输出相同的结果 01/Mar/2021 !
相反,Java 遵循一个清晰、一致且简单的规则:如果日期算术的结果(例如添加月份或年份)导致无效日期,则返回的答案将是最后一天该结果的月份。
例如,来自:https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html#plusMonths-long-
- 将输入的月份添加到月份字段
- 检查结果日期是否无效
- 如有必要,将日期调整为最后一个有效日期
例如,2007-03-31 加上一个月将导致无效日期 2007-04-31。没有返回无效结果,而是选择了该月的最后一个有效日期 2007-04-30。
没有错,就看“加一年”的解释了,对于java.util.Calendar#add
,就是:
add(f, delta) adds delta
to field f
. This is equivalent to calling set(f, get(f) + delta)
with two adjustments:
Add rule 1. The value of field f after the call minus the value of field f before the call is delta, modulo any overflow that has occurred in field f. Overflow occurs when a field value exceeds its range and, as a result, the next larger field is incremented or decremented and the field value is adjusted back into its range.
Add rule 2. If a smaller field is expected to be invariant, but it is impossible for it to be equal to its prior value because of changes in its minimum or maximum after field f is changed or other constraints, such as time zone offset changes, then its value is adjusted to be as close as possible to its expected value. A smaller field represents a smaller unit of time. HOUR is a smaller field than DAY_OF_MONTH. No adjustment is made to smaller fields that are not expected to be invariant. The calendar system determines what fields are expected to be invariant.
In addition, unlike set(), add() forces an immediate recomputation of the calendar's milliseconds and all fields.
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.
来源:https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html
这听起来与所描述的行为一致,您期望什么以及为什么?也许有 better/more 个专门的解决方案。
阅读 add and roll 的文档后。很明显,你要找的是在roll中找不到的。
进一步对该主题进行一些研究后,我发现给定的场景也取决于业务并且取决于你想做什么,因为 add 和 roll 让您可以选择其中之一。
我在搜索我遇到的类似问题时发现 this。
需要修改后返回您的代码
aCalendar.roll(field, amount);
这里的专家将能够帮助清理这个答案。 JavaDocs 对我来说不是很清楚,所以 following 问题有一些有趣的答案
我正在尝试添加一个日期,但它为不同的输入提供了相同的日期。 输入日期为 28/Feb/2020 和 29/Feb/2020,两者的输出与 28/Feb/2021 相同。请帮我看看这里有什么问题。
更新:
我希望输出为 28/Feb/2021 和 01/Mar/2021。
public static Date dateAdd(Date newDate, int field, int amount) {
Calendar aCalendar = Calendar.getInstance();
aCalendar.setTime(newDate);
aCalendar.add(field, amount);
return aCalendar.getTime();
}
public static void main(String[] args) throws Exception {
System.out.println(dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("28/02/2020"),Calendar.YEAR,1));
System.out.println(dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("29/02/2020"),Calendar.YEAR,1));
// Console output below
//Sun Feb 28 00:00:00 IST 2021
//Sun Feb 28 00:00:00 IST 2021
}
使用:
System.out.println(dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("28/02/2020"),Calendar.DAY_OF_YEAR,366));
System.out.println(dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("29/02/2020"),Calendar.DAY_OF_YEAR,366));
你可以先加一年再加一天:
Date d = dateAdd(new SimpleDateFormat("dd/MM/yyyy").parse("28/02/2020"),Calendar.YEAR,1); // add a year
System.out.println(d);
d = dateAdd(d, Calendar.DATE, 1); // add a day
System.out.println(d);
没有理由期待 01/Mar/2021 的结果。事实上,有些人会质疑为什么在两个日期 29/Feb/2020 和 01/Mar/2020 上加上一年会为它们输出相同的结果 01/Mar/2021 !
相反,Java 遵循一个清晰、一致且简单的规则:如果日期算术的结果(例如添加月份或年份)导致无效日期,则返回的答案将是最后一天该结果的月份。
例如,来自:https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html#plusMonths-long-
- 将输入的月份添加到月份字段
- 检查结果日期是否无效
- 如有必要,将日期调整为最后一个有效日期
例如,2007-03-31 加上一个月将导致无效日期 2007-04-31。没有返回无效结果,而是选择了该月的最后一个有效日期 2007-04-30。
没有错,就看“加一年”的解释了,对于java.util.Calendar#add
,就是:
add(f, delta) adds
delta
to fieldf
. This is equivalent to callingset(f, get(f) + delta)
with two adjustments:Add rule 1. The value of field f after the call minus the value of field f before the call is delta, modulo any overflow that has occurred in field f. Overflow occurs when a field value exceeds its range and, as a result, the next larger field is incremented or decremented and the field value is adjusted back into its range.
Add rule 2. If a smaller field is expected to be invariant, but it is impossible for it to be equal to its prior value because of changes in its minimum or maximum after field f is changed or other constraints, such as time zone offset changes, then its value is adjusted to be as close as possible to its expected value. A smaller field represents a smaller unit of time. HOUR is a smaller field than DAY_OF_MONTH. No adjustment is made to smaller fields that are not expected to be invariant. The calendar system determines what fields are expected to be invariant.
In addition, unlike set(), add() forces an immediate recomputation of the calendar's milliseconds and all fields.
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.
来源:https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html
这听起来与所描述的行为一致,您期望什么以及为什么?也许有 better/more 个专门的解决方案。
阅读 add and roll 的文档后。很明显,你要找的是在roll中找不到的。
进一步对该主题进行一些研究后,我发现给定的场景也取决于业务并且取决于你想做什么,因为 add 和 roll 让您可以选择其中之一。
我在搜索我遇到的类似问题时发现 this。
需要修改后返回您的代码
aCalendar.roll(field, amount);
这里的专家将能够帮助清理这个答案。 JavaDocs 对我来说不是很清楚,所以 following 问题有一些有趣的答案