java—获取指定时间段后的下一个有效日期,并相应地调整月份的日期

mfuanj7w  于 2021-07-05  发布在  Java
关注(0)|答案(4)|浏览(238)

我有一个类似这样的枚举

enum Period{DAY, WEEK, MONTH, YEAR}

我需要的是一个函数,它将给定周期的指定次数添加到 today 设置月份的日期,使其等于开始日期(如果结果有效)。
或者像这样更容易理解:假设你每个月31号拿到薪水(如果适用的话)。函数返回下一个有效日期(从今天开始),即您将收到下一份工资的日期。函数可以区分是否每天、每周、每月、每年以及在指定间隔内多久获取一次。它还处理无效日期
我们来看一个例子:

public static Date getNextDate(Date startDate, Period period, int times){
/* 
Examples:
getNextDate(31.08.2020, MONTH, 1) -> 30.09.2020
getNextDate(31.08.2020, MONTH, 2) -> 31.10.2020
getNextDate(30.05.2020, MONTH, 2) -> 30.09.2020
getNextDate(30.06.2020, MONTH, 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, YEAR, 1) -> 28.02.2021 <- Edge case, as 2020 is a gap year
getNextDate(29.02.2020, YEAR, 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, DAY, 3) -> 05.09.2020
getNextDate(29.02.2020, WEEK, 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, MONTH, 1) -> 03.09.2020 (No change here, the date matches today)

* /

}

实际上,我更喜欢使用现代的localdateapi(只是输入目前是一个旧的date对象,但稍后会更改)
我希望我没有忘记任何边缘案件。
更新我所做的

//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;
    }

我的 getNextDate() 方法有效。这个 getNextDateAfterToday() 也可以,但不会返回边缘情况的有效日期。例子 31.06.2020, MONTH,1 在每个月的30号会立即停止,即使这个月有31天也不会向后跳。对于2020年9月30日,这是正确的。但到了2020年10月31日就不会了

wnavrhmk

wnavrhmk1#

…(虽然对于我真正想要达到的目标来说,这似乎是一种,一种,一种复杂的方式)…
你自己的解决方案还不错。我就是不能让挑战停下来,所以我来了。我觉得简单一点。
我将全神贯注于java.time,这是一个现代的java日期和时间api。我也跳过了你的课 Period 自预定义 ChronoUnit enum实现了这个目的。只是它还包括小时、分钟和其他在这里没有意义的单位,所以我们需要拒绝这些单位。
这个 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, TemporalUnit period, int times) {
    LocalDate nextDate = getNextDate(startDate, period, times);
    System.out.format("getNextDate(%s, %s, %d) -> %s%n", startDate, period, times, nextDate);
}

现在让我们看看:

demo(LocalDate.of(2020, Month.AUGUST, 31), ChronoUnit.MONTHS, 1);
    demo(LocalDate.of(2020, Month.AUGUST, 31), ChronoUnit.MONTHS, 2);
    demo(LocalDate.of(2020, Month.MAY, 30), ChronoUnit.MONTHS, 2);
    demo(LocalDate.of(2020, Month.JUNE, 30), ChronoUnit.MONTHS, 2);

    System.out.println();

    demo(LocalDate.of(2020, Month.FEBRUARY, 28), ChronoUnit.YEARS, 1);
    demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.YEARS, 1);
    demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.YEARS, 4);

    System.out.println();

    demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.DAYS, 1);
    demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.DAYS, 3);
    demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.WEEKS, 2);

    System.out.println();

    demo(LocalDate.of(2020, Month.SEPTEMBER, 4), ChronoUnit.MONTHS, 1);

刚才运行时,输出是:

getNextDate(2020-08-31, Months, 1) -> 2020-09-30
getNextDate(2020-08-31, Months, 2) -> 2020-10-31
getNextDate(2020-05-30, Months, 2) -> 2020-09-30
getNextDate(2020-06-30, Months, 2) -> 2020-10-30

getNextDate(2020-02-28, Years, 1) -> 2021-02-28
getNextDate(2020-02-29, Years, 1) -> 2021-02-28
getNextDate(2020-02-29, Years, 4) -> 2024-02-29

getNextDate(2020-02-29, Days, 1) -> 2020-09-04
getNextDate(2020-02-29, Days, 3) -> 2020-09-05
getNextDate(2020-02-29, Weeks, 2) -> 2020-09-12

getNextDate(2020-09-04, Months, 1) -> 2020-09-04

我应该说,这与你在这个问题上的例子是一致的。
如果你不能避免有一个老式的 Date 对象和自己的示例 Period 枚举和/或你需要一个老式的 Date 返回,您可以将我的方法 Package 成一个执行必要转换的方法。首先,我将扩展枚举以了解其对应的 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;
    }
}

现在 Package 器方法可能是这样的;

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);
}
cmssoen2

cmssoen22#

你可以用这个班 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(), Calendar.MONTH, 3));
System.out.println(getNextDate(new Date(), Calendar.YEAR, 1));

如果你真的需要 enum ,你能做到的!

jucafojl

jucafojl3#

最好不要使用十年前的DateAPI,因为它编写得很糟糕,而且通常不安全,使用起来很痛苦。使用 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));
nwlls2ji

nwlls2ji4#

我终于想出了一个办法(尽管对于我真正想达到的目标来说,似乎是一个办法,一个办法,一个办法)。我改变了主意 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,但将来会这样做。这适用于我上面发布的所有测试用例。虽然我希望我没有错过重要的

相关问题