gpt4 book ai didi

java - java.time 中 Calendar.roll 的等价物是什么?

转载 作者:搜寻专家 更新时间:2023-11-01 00:53:55 24 4
gpt4 key购买 nike

我正在研究旧的 Calendar API 看看它有多糟糕,我发现 Calendar 有一个 roll方法。与 add 方法不同,roll 不会更改较大日历字段的值。

例如,日历实例 c 表示日期 2019-08-31。调用 c.roll(Calendar.MONTH, 13) 将 13 添加到月份字段,但不会更改年份,因此结果为 2019-09-30。请注意,日期会发生变化,因为它是一个较小的字段。

Related

我试图在现代 java.time API 中找到这样的方法。我认为这样的方法必须在 LocalDateLocalDateTime 中,但我没有找到任何此类方法。

所以我尝试编写自己的roll方法:

public static LocalDateTime roll(LocalDateTime ldt, TemporalField unit, long amount) {
LocalDateTime newLdt = ldt.plus(amount, unit.getBaseUnit());
return ldt.with(unit, newLdt.get(unit));
}

但是,这仅适用于某些情况,而对其他情况无效。例如,它不适用于 documentation here 中描述的情况。 :

Consider a GregorianCalendar originally set to Sunday June 6, 1999. Calling roll(Calendar.WEEK_OF_MONTH, -1) sets the calendar to Tuesday June 1, 1999, whereas calling add(Calendar.WEEK_OF_MONTH, -1) sets the calendar to Sunday May 30, 1999. This is because the roll rule imposes an additional constraint: The MONTH must not change when the WEEK_OF_MONTH is rolled. Taken together with add rule 1, the resultant date must be between Tuesday June 1 and Saturday June 5. According to add rule 2, the DAY_OF_WEEK, an invariant when changing the WEEK_OF_MONTH, is set to Tuesday, the closest possible value to Sunday (where Sunday is the first day of the week).

我的代码:

System.out.println(roll(
LocalDate.of(1999, 6, 6).atStartOfDay(),
ChronoField.ALIGNED_WEEK_OF_MONTH, -1
));

输出 1999-07-04T00:00,而使用 Calendar:

Calendar c = new GregorianCalendar(1999, 5, 6);
c.roll(Calendar.WEEK_OF_MONTH, -1);
System.out.println(c.getTime().toInstant());

输出 1999-05-31T23:00:00Z,在我的时区是 1999-06-01

什么是 java.time API 中的 roll?如果没有,我怎么写一个方法来模仿它?

最佳答案

首先,我不记得曾见过 Calendar.roll 的任何有用应用。其次,我认为在极端情况下功能没有得到很好的指定。角落案例将是有趣的案例。如果没有 roll 方法,按 13 个月滚动月份并不难。类似的观察结果可能是 java.time 不提供此功能的原因。

相反,我认为我们将不得不求助于更多的手动滚动方式。对于您的第一个示例:

    LocalDate date = LocalDate.of(2019, Month.JULY, 22);
int newMonthValue = 1 + (date.getMonthValue() - 1 + 13) % 12;
date = date.with(ChronoField.MONTH_OF_YEAR, newMonthValue);
System.out.println(date);

输出:

2019-08-22

我使用的事实是,在 ISO 年表中,一年中总是有 12 个月。由于 % 始终给出基于 0 的结果,因此我在模运算之前从基于 1 的月份值中减去 1,然后将其加回去我假设滚动为正。如果要滚动的月数可能是负数,它会稍微复杂一些(留给读者)。

对于其他字段,我认为类似的方法适用于大多数情况:在给定较大字段的情况下找到字段的最小和最大可能值,然后进行一些模运算。

在某些情况下,这可能会成为一个挑战。例如,当夏令时 (DST) 结束并且时钟从凌晨 3 点向后拨到凌晨 2 点时,一天是 25 小时,您如何从早上 6 点开始滚动 37 小时?我相信这是可以做到的。而且我也确信该功能不是内置的。

对于滚动一周的示例,旧 API 和现代 API 之间的另一个区别开始发挥作用:GregorianCalendar 不仅定义了日历日期和时间,还定义了周计划由一周的第一天和第一周的最少天数组成。在 java.time 中,周方案由 WeekFields 对象定义。因此,虽然在 GregorianCalendar 中滚动一周可能是明确的,但在不知道周方案的情况下,它不适用于 LocalDateLocalDateTime。可能会尝试假设 ISO 周(从星期一开始,第一周是新月份至少有 4 天的那一天),但这可能并不总是用户想要的。

月中的周和年中的周是特殊的,因为周跨越月和年的界限。这是我尝试实现每月一周的滚动:

private static LocalDate rollWeekOfMonth(LocalDate date, int amount, WeekFields wf) {
LocalDate firstOfMonth = date.withDayOfMonth(1);
int firstWeekOfMonth = firstOfMonth.get(wf.weekOfMonth());
LocalDate lastOfMonth = date.with(TemporalAdjusters.lastDayOfMonth());
int lastWeekOfMonth = lastOfMonth.get(wf.weekOfMonth());
int weekCount = lastWeekOfMonth - firstWeekOfMonth + 1;
int newWeekOfMonth = firstWeekOfMonth
+ (date.get(wf.weekOfMonth()) - firstWeekOfMonth
+ amount % weekCount + weekCount)
% weekCount;
LocalDate result = date.with(wf.weekOfMonth(), newWeekOfMonth);
if (result.isBefore(firstOfMonth)) {
result = firstOfMonth;
} else if (result.isAfter(lastOfMonth)) {
result = lastOfMonth;
}
return result;
}

尝试一下:

    System.out.println(rollWeekOfMonth(LocalDate.of(1999, Month.JUNE, 6), -1, WeekFields.SUNDAY_START));
System.out.println(rollWeekOfMonth(LocalDate.of(1999, Month.JUNE, 6), -1, WeekFields.ISO));

输出:

1999-06-01
1999-06-30

解释:您引用的文档假定星期日是一周的第一天(它以“星期日是一周的第一天”结束;它可能是在美国写的)所以在六月星期日之前有一周6. 滚动-1周应该滚动到本周之前。我的第一行代码就是这样做的。

在 ISO 周计划中,6 月 6 日星期日属于从 5 月 31 日星期一到 6 月 6 日星期日的那一周,因此 6 月没有本周之前的一周。因此,我的第二行代码滚动到 6 月的最后一周,即 6 月 28 日到 7 月 4 日。由于我们不能超出 6 月,因此选择 6 月 30 日。

我还没有测试它的行为是否与 GregorianCalendar 相同。相比之下,GregorianCalendar.roll 实现使用 52 行代码来处理 WEEK_OF_MONTH 情况,而我的是 20 行。要么我遗漏了一些东西,要么 java.time 再次显示了它的优越性。

我对现实世界的建议是:明确您的要求并直接在 java.time 之上实现它们,而忽略旧 API 的行为方式。作为一项学术练习,您的问题很有趣。

关于java - java.time 中 Calendar.roll 的等价物是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57326072/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com