日历行为与 LocalDate 不兼容?
Calendar behavior is not compatible with LocalDate?
嗨,伙计们,我在尝试将行为从日历迁移到本地日期时遇到了麻烦。
payDate.set(Calendar.DAY_OF_MONTH,payDay)
假设 payDate
的当前日期是 2020-01-29
出于业务原因 payDay
可以具有 0
的值,因此,当使用前面的场景执行前面的代码行时,结果是 payDate
更新日期到 2019-12-31,
也就是说日期回到上个月的最后一天。
我不确定,这是技术原因,如果有人能向我解释一下,我将非常感激,我尝试查看 java 文档,但没有帮助。
所以我需要使用 LocalDate java 库复制该行为。在我看来,我的观点是; Calendar
中的 set
方法与 LocalDate 中 DAY_OF_MONTH
的值类似:
payDate.withDayOfMonth(payDay)
但是当出现以下情况并且 payDay
等于 0
时,我得到一个错误:
java.time.DateTimeException: Invalid value for DayOfMonth (valid values 1 - 28/31): 0
我也有一些想法,关于如何在规则生效时在 localDate 中获得相同的日历结果(如果 payDay 为 0,return 到上个月的最后一天),但是太冗长。
如果您知道 LocalDate 上的类似行为,请帮助我。谢谢。
您将无法仅调用一种方法来获得相同的结果。如果您 确定 将 DAY_OF_MONTH 设置为 0 应该会导致它回滚一个月(这是我 运行 过去的业务类型分析师或产品负责人进行健全性检查)然后你将不得不做这样的事情:
int payDay = 0;
LocalDate payDate = LocalDate.of(2020, Month.JANUARY, 29);
if(payDay == 0) {
payDate = payDate.minusMonths(1);
payDay = payDate.lengthOfMonth();
}
payDate = payDate.withDayOfMonth(payDay);
另一种方法:
int payDay = 0;
LocalDate payDate = LocalDate.of(2020, Month.JANUARY, 29);
if(payDay == 0) {
payDate = payDate.withDayOfMonth(1).minusDays(1);
} else {
payDate = payDate.withDayOfMonth(payDay);
}
TL;DR: 使用 payDate = payDate.plusDays(payDay - payDate.getDayOfMonth());
您描述的 Calendar
的行为记录在 javadoc 中:
Leniency
Calendar
has two modes for interpreting the calendar fields, lenient and non-lenient. When a Calendar
is in lenient mode, it accepts a wider range of calendar field values than it produces. When a Calendar
recomputes calendar field values for return by get()
, all of the calendar fields are normalized. For example, a lenient GregorianCalendar
interprets MONTH == JANUARY
, DAY_OF_MONTH == 32
as February 1.
When a Calendar
is in non-lenient mode, it throws an exception if there is any inconsistency in its calendar fields. For example, a GregorianCalendar
always produces DAY_OF_MONTH
values between 1 and the length of the month. A non-lenient GregorianCalendar
throws an exception upon calculating its time or calendar field values if any out-of-range field value has been set.
为显示此效果,请尝试将 Calendar
的日期设置为 2020 年 1 月 70 日:
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2020, Calendar.JANUARY, 70);
System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime()));
输出
2020-03-10
如果你这样做,你会得到相同的结果:
cal.set(2020, Calendar.JANUARY, 1);
cal.add(Calendar.DAY_OF_MONTH, 69);
LocalDate
始终是 non-lenient,因此您不能将日期值设置为超出范围的值。但是,通过将操作更改为“添加”而不是“设置”,您可以获得与 Calendar
相同的结果。
所以,如果你有一个特定的日期,例如问题中提到的 2020-01-29
日期,并且您想将日期值“设置”为 70 或 0,具有与 [=18] 相同的 lenient 溢出逻辑=] 有,这样做:
LocalDate date = LocalDate.parse("2020-01-29");
date = date.plusDays(70 - date.getDayOfMonth());
System.out.println(date);
LocalDate date = LocalDate.parse("2020-01-29");
date = date.plusDays(0 - date.getDayOfMonth());
System.out.println(date);
输出
2020-03-10
2019-12-31
如您所见,date.plusDays(dayToSet - date.getDayOfMonth())
会给您想要的结果。
以下是我的处理方式:
LocalDate payDate = LocalDate.now(); // or whatever
int payDay = 0;
if (payDay == 0) {
// simulate `GregorianCalendar` behaviour: day 0 is the day before day 1
payDate = payDate.withDayOfMonth(1).minusDays(1);
} else {
payDate = payDate.withDayOfMonth(payDay);
}
System.out.println(payDate);
当我运行刚才的片段时,输出的是你已经提到的日期:
2019-12-31
如果我们想要它更短,我们可以使用 payDate.withDayOfMonth(1).minusDays(1).plusDays(payDay)
或 Andreas 回答中的技巧,我们不需要 if
语句。不过我不会。 (1) 阅读困难。 (2) 它没有给出上面代码段中免费提供的 payDay
的验证。
Calendar
的混乱行为来自于 not 运行ge 检查 set()
的参数。因此,该月的第 0 天是该月第 1 天的前一天。第 -1 天将是前一天,依此类推。它在文档的这个片段中(或者至少应该是):
When a Calendar is in lenient mode, it accepts a wider range of
calendar field values than it produces. When a Calendar recomputes
calendar field values for return by get()
, all of the calendar
fields are normalized. For example, a lenient GregorianCalendar
interprets MONTH == JANUARY, DAY_OF_MONTH == 32
as February 1.
您可以使用 setLenient
方法文档中的此片段阅读它:
The default is lenient.
链接
嗨,伙计们,我在尝试将行为从日历迁移到本地日期时遇到了麻烦。
payDate.set(Calendar.DAY_OF_MONTH,payDay)
假设 payDate
的当前日期是 2020-01-29
出于业务原因 payDay
可以具有 0
的值,因此,当使用前面的场景执行前面的代码行时,结果是 payDate
更新日期到 2019-12-31,
也就是说日期回到上个月的最后一天。
我不确定,这是技术原因,如果有人能向我解释一下,我将非常感激,我尝试查看 java 文档,但没有帮助。
所以我需要使用 LocalDate java 库复制该行为。在我看来,我的观点是; Calendar
中的 set
方法与 LocalDate 中 DAY_OF_MONTH
的值类似:
payDate.withDayOfMonth(payDay)
但是当出现以下情况并且 payDay
等于 0
时,我得到一个错误:
java.time.DateTimeException: Invalid value for DayOfMonth (valid values 1 - 28/31): 0
我也有一些想法,关于如何在规则生效时在 localDate 中获得相同的日历结果(如果 payDay 为 0,return 到上个月的最后一天),但是太冗长。
如果您知道 LocalDate 上的类似行为,请帮助我。谢谢。
您将无法仅调用一种方法来获得相同的结果。如果您 确定 将 DAY_OF_MONTH 设置为 0 应该会导致它回滚一个月(这是我 运行 过去的业务类型分析师或产品负责人进行健全性检查)然后你将不得不做这样的事情:
int payDay = 0;
LocalDate payDate = LocalDate.of(2020, Month.JANUARY, 29);
if(payDay == 0) {
payDate = payDate.minusMonths(1);
payDay = payDate.lengthOfMonth();
}
payDate = payDate.withDayOfMonth(payDay);
另一种方法:
int payDay = 0;
LocalDate payDate = LocalDate.of(2020, Month.JANUARY, 29);
if(payDay == 0) {
payDate = payDate.withDayOfMonth(1).minusDays(1);
} else {
payDate = payDate.withDayOfMonth(payDay);
}
TL;DR: 使用 payDate = payDate.plusDays(payDay - payDate.getDayOfMonth());
您描述的 Calendar
的行为记录在 javadoc 中:
Leniency
Calendar
has two modes for interpreting the calendar fields, lenient and non-lenient. When aCalendar
is in lenient mode, it accepts a wider range of calendar field values than it produces. When aCalendar
recomputes calendar field values for return byget()
, all of the calendar fields are normalized. For example, a lenientGregorianCalendar
interpretsMONTH == JANUARY
,DAY_OF_MONTH == 32
as February 1.When a
Calendar
is in non-lenient mode, it throws an exception if there is any inconsistency in its calendar fields. For example, aGregorianCalendar
always producesDAY_OF_MONTH
values between 1 and the length of the month. A non-lenientGregorianCalendar
throws an exception upon calculating its time or calendar field values if any out-of-range field value has been set.
为显示此效果,请尝试将 Calendar
的日期设置为 2020 年 1 月 70 日:
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2020, Calendar.JANUARY, 70);
System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime()));
输出
2020-03-10
如果你这样做,你会得到相同的结果:
cal.set(2020, Calendar.JANUARY, 1);
cal.add(Calendar.DAY_OF_MONTH, 69);
LocalDate
始终是 non-lenient,因此您不能将日期值设置为超出范围的值。但是,通过将操作更改为“添加”而不是“设置”,您可以获得与 Calendar
相同的结果。
所以,如果你有一个特定的日期,例如问题中提到的 2020-01-29
日期,并且您想将日期值“设置”为 70 或 0,具有与 [=18] 相同的 lenient 溢出逻辑=] 有,这样做:
LocalDate date = LocalDate.parse("2020-01-29");
date = date.plusDays(70 - date.getDayOfMonth());
System.out.println(date);
LocalDate date = LocalDate.parse("2020-01-29");
date = date.plusDays(0 - date.getDayOfMonth());
System.out.println(date);
输出
2020-03-10
2019-12-31
如您所见,date.plusDays(dayToSet - date.getDayOfMonth())
会给您想要的结果。
以下是我的处理方式:
LocalDate payDate = LocalDate.now(); // or whatever
int payDay = 0;
if (payDay == 0) {
// simulate `GregorianCalendar` behaviour: day 0 is the day before day 1
payDate = payDate.withDayOfMonth(1).minusDays(1);
} else {
payDate = payDate.withDayOfMonth(payDay);
}
System.out.println(payDate);
当我运行刚才的片段时,输出的是你已经提到的日期:
2019-12-31
如果我们想要它更短,我们可以使用 payDate.withDayOfMonth(1).minusDays(1).plusDays(payDay)
或 Andreas 回答中的技巧,我们不需要 if
语句。不过我不会。 (1) 阅读困难。 (2) 它没有给出上面代码段中免费提供的 payDay
的验证。
Calendar
的混乱行为来自于 not 运行ge 检查 set()
的参数。因此,该月的第 0 天是该月第 1 天的前一天。第 -1 天将是前一天,依此类推。它在文档的这个片段中(或者至少应该是):
When a Calendar is in lenient mode, it accepts a wider range of calendar field values than it produces. When a Calendar recomputes calendar field values for return by
get()
, all of the calendar fields are normalized. For example, a lenientGregorianCalendar
interpretsMONTH == JANUARY, DAY_OF_MONTH == 32
as February 1.
您可以使用 setLenient
方法文档中的此片段阅读它:
The default is lenient.