问题描述
enum Period{DAY,WEEK,MONTH,YEAR}
我需要的功能是在给today
添加指定时间段的同时给public static Date getNextDate(Date startDate,Period period,int times){
/*
Examples:
getNextDate(31.08.2020,1) -> 30.09.2020
getNextDate(31.08.2020,2) -> 31.10.2020
getNextDate(30.05.2020,2) -> 30.09.2020
getNextDate(30.06.2020,2) -> 30.10.2020 (This is the next valid date after today)
Years are pretty simple i guess (Okay,there is at least one edge case):
getNextDate(28.02.2020,YEAR,1) -> 28.02.2021
getNextDate(29.02.2020,1) -> 28.02.2021 <- Edge case,as 2020 is a gap year
getNextDate(29.02.2020,4) -> 29.02.2024 <- gap year to gap year
For weeks and days there are no edge cases,are there?
getNextDate(29.02.2020,DAY,1) -> 03.09.2020
getNextDate(29.02.2020,3) -> 05.09.2020
getNextDate(29.02.2020,2) -> 12.09.2020 (Same as DAY,14)
Important: If today is already a payment day,this already is the solution
getNextDate(03.09.2020,1) -> 03.09.2020 (No change here,the date matches today)
*/
}
设置月份中的某天,使其等于开始日期(如果结果有效)。
或者也许这样更容易理解: 想象一下,您每月31号得到薪水(如适用)。该函数将返回您下一个薪水的下一个有效日期(从今天开始)。函数在何处可以区分您是每天,每周,每月,每年还是在指定间隔内获取频率。 它还可以处理无效日期
让我们看一个例子:
//This is a method of the enum mentioned
public Date getNextDate(Date baseDate,int specific) {
Date result = null;
switch (this) {
case DAY:
result = DateTimeUtils.addDays(baseDate,specific);
break;
case WEEK:
result = DateTimeUtils.addWeeks(baseDate,specific);
break;
case MONTH:
result = DateTimeUtils.addMonths(baseDate,specific);
break;
case YEAR:
result = DateTimeUtils.addYears(baseDate,specific);
break;
}
return result;
}
public Date getNextDateAfterToday(Date baseDate) {
today = new Date();
while(!baseDate.equals(today ) && !baseDate.after(today)){
baseDate= getNextDate(baseDate,1);
}
return startHere;
}
我实际上更喜欢使用现代的LocalDate API(只是输入目前是一个旧的日期对象,但稍后会更改)
我希望我不会忘记任何边缘情况。
更新我所做的事情
getNextDate()
我的getNextDateAfterToday()
方法有效。 31.06.2020,1
也可以使用,但不会返回边缘情况的有效日期。示例{{1}}将在每个月的30日立即显示为stuc,即使该月有31天,也绝不会跳回。对于30.09.2020,这是正确的。但是对于2020年10月31日则不会
解决方法
我终于找到了一种方法(尽管对于我真正想要实现的目标来说,它似乎很复杂)。我将getNextDateAfterToday
更改为此
public Date getNextValidFutureDate(Date entryDate,Date startDate,int specific) {
Date result = new Date(startDate.getTime());
while (!result.equals(entryDate) && !result.after(entryDate)) {
result = getNextDate(result,true,specific);
}
LocalDate ldStart = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate ldResult = result.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
if (ldResult.getDayOfMonth() < ldStart.getDayOfMonth() && this != DAY && this != WEEK && this != YEAR) {
if (ldResult.lengthOfMonth() >= ldStart.getDayOfMonth()) {
ldResult = ldResult.with(ChronoField.DAY_OF_MONTH,ldStart.getDayOfMonth());
} else {
ldResult = ldResult.with(ChronoField.DAY_OF_MONTH,ldResult.lengthOfMonth());
}
}
return Date.from(ldResult.atStartOfDay(ZoneId.systemDefault()).toInstant());
}
我没有更改其他方法来使用LocalDate,但是将来会这样做。 这适用于我上面发布的所有测试用例。虽然我希望我不要错过必不可少的东西
, Not using the decade old date api which is badly written and generally unsafe and painful to use might be the best idea.在这里可能更适合使用java.time
。您只需要做的就是将方法签名更改为此:
import java.time.LocalDate;
import java.time.Period;
...
public static LocalDate getNextDate(LocalDate startDate,Period period) {
return startDate.plus(period);
}
然后可以这样称呼:
LocalDate startDate = LocalDate.of(3,9,2020);
LocalDate nextDate = getNextDate(startDate,Period.ofDays(20)); // 2020-09-23
或者只是将您的辅助函数放在首位并直接使用它:
LocalDate nextDate = startDate.plus(Period.ofDays(20));
,
…(尽管对于我真正的东西来说似乎很复杂) 想要实现)...
您自己的解决方案还不错。我只是无法解决挑战,所以这是我的努力。我相信这会更简单。
我全力以赴java.time,这是现代的Java日期和时间API。由于预定义的Period
枚举已达到目的,因此我也跳过了您的ChronoUnit
枚举。只有它还包括小时,分钟和其他在这里没有意义的单位,因此我们需要拒绝这些。
Date
类的设计很差,而且已经过时了。如果可以的话,请避免使用它(如果您无法避免的话,我最终会为您提供解决方案)。
public static LocalDate getNextDate(LocalDate startDate,TemporalUnit period,int times) {
if (! period.isDateBased()) {
throw new IllegalArgumentException("Cannot add " + period + " to a date");
}
LocalDate today = LocalDate.now(ZoneId.of("America/Eirunepe"));
if (startDate.isBefore(today)) {
// Calculate how many times we need to add times units to get a future date (or today).
// We need to round up; the trick for doing so is count until yesterday and add 1.
LocalDate yesterday = today.minusDays(1);
long timesToAdd = period.between(startDate,yesterday) / times + 1;
return startDate.plus(timesToAdd * times,period);
} else {
return startDate;
}
}
为了演示该方法,我使用了这个小实用程序方法:
public static void demo(LocalDate startDate,int times) {
LocalDate nextDate = getNextDate(startDate,period,times);
System.out.format("getNextDate(%s,%s,%d) -> %s%n",startDate,times,nextDate);
}
现在让我们看看:
demo(LocalDate.of(2020,Month.AUGUST,31),ChronoUnit.MONTHS,1);
demo(LocalDate.of(2020,2);
demo(LocalDate.of(2020,Month.MAY,30),Month.JUNE,2);
System.out.println();
demo(LocalDate.of(2020,Month.FEBRUARY,28),ChronoUnit.YEARS,29),4);
System.out.println();
demo(LocalDate.of(2020,ChronoUnit.DAYS,3);
demo(LocalDate.of(2020,ChronoUnit.WEEKS,Month.SEPTEMBER,4),1);
目前运行时,输出为:
getNextDate(2020-08-31,Months,1) -> 2020-09-30 getNextDate(2020-08-31,2) -> 2020-10-31 getNextDate(2020-05-30,2) -> 2020-09-30 getNextDate(2020-06-30,2) -> 2020-10-30 getNextDate(2020-02-28,Years,1) -> 2021-02-28 getNextDate(2020-02-29,4) -> 2024-02-29 getNextDate(2020-02-29,Days,1) -> 2020-09-04 getNextDate(2020-02-29,3) -> 2020-09-05 getNextDate(2020-02-29,Weeks,2) -> 2020-09-12 getNextDate(2020-09-04,1) -> 2020-09-04
我应该说这与您在问题中所举的例子相符。
如果您无法避免使用老式的Date
对象和您自己的Period
枚举的实例,并且/或者您必不可少地需要老式的Date
,则可以将其包装起来将我的方法转换为执行必要的转换的方法。首先,我将扩展您的枚举以了解其对应的ChronoUnit
常量:
enum Period {
DAY(ChronoUnit.DAYS),WEEK(ChronoUnit.WEEKS),MONTH(ChronoUnit.MONTHS),YEAR(ChronoUnit.YEARS);
private final ChronoUnit unit;
private Period(ChronoUnit unit) {
this.unit = unit;
}
public ChronoUnit getUnit() {
return unit;
}
}
现在,包装方法可能看起来像这样;
public static Date getNextDate(Date startDate,Period period,int times) {
ZoneId zone = ZoneId.of("America/Eirunepe");
LocalDate startLocalDate = startDate.toInstant().atZone(zone).toLocalDate();
LocalDate nextDate = getNextDate(startLocalDate,period.getUnit(),times);
Instant startOfDay = nextDate.atStartOfDay(zone).toInstant();
return Date.from(startOfDay);
}
,
您可以使用类Calendar
来解决您的问题,
public static Date getNextDate(Date startDate,int period,int times) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(startDate);
calendar.add(period,times);
return calendar.getTime();
}
句点是Calendar类中定义的一个int,您可以这样调用函数:
System.out.println(getNextDate(new Date(),Calendar.MONTH,1));
System.out.println(getNextDate(new Date(),3));
System.out.println(getNextDate(new Date(),Calendar.YEAR,1));
如果您确实需要使用enum
,可以做到!