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

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

我有一个类似这样的枚举

  1. enum Period{DAY, WEEK, MONTH, YEAR}

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

  1. public static Date getNextDate(Date startDate, Period period, int times){
  2. /*
  3. Examples:
  4. getNextDate(31.08.2020, MONTH, 1) -> 30.09.2020
  5. getNextDate(31.08.2020, MONTH, 2) -> 31.10.2020
  6. getNextDate(30.05.2020, MONTH, 2) -> 30.09.2020
  7. getNextDate(30.06.2020, MONTH, 2) -> 30.10.2020 (This is the next valid date after today)
  8. Years are pretty simple i guess (Okay, there is at least one edge case):
  9. getNextDate(28.02.2020, YEAR, 1) -> 28.02.2021
  10. getNextDate(29.02.2020, YEAR, 1) -> 28.02.2021 <- Edge case, as 2020 is a gap year
  11. getNextDate(29.02.2020, YEAR, 4) -> 29.02.2024 <- gap year to gap year
  12. For weeks and days there are no edge cases, are there?
  13. getNextDate(29.02.2020, DAY, 1) -> 03.09.2020
  14. getNextDate(29.02.2020, DAY, 3) -> 05.09.2020
  15. getNextDate(29.02.2020, WEEK, 2) -> 12.09.2020 (Same as DAY,14)
  16. Important: If today is already a payment day, this already is the solution
  17. getNextDate(03.09.2020, MONTH, 1) -> 03.09.2020 (No change here, the date matches today)
  18. * /
  19. }

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

  1. //This is a method of the enum mentioned
  2. public Date getNextDate(Date baseDate, int specific) {
  3. Date result = null;
  4. switch (this) {
  5. case DAY:
  6. result = DateTimeUtils.addDays(baseDate, specific);
  7. break;
  8. case WEEK:
  9. result = DateTimeUtils.addWeeks(baseDate, specific);
  10. break;
  11. case MONTH:
  12. result = DateTimeUtils.addMonths(baseDate, specific);
  13. break;
  14. case YEAR:
  15. result = DateTimeUtils.addYears(baseDate, specific);
  16. break;
  17. }
  18. return result;
  19. }
  20. public Date getNextDateAfterToday(Date baseDate) {
  21. today = new Date();
  22. while(!baseDate.equals(today ) && !baseDate.after(today)){
  23. baseDate= getNextDate(baseDate,1);
  24. }
  25. return startHere;
  26. }

我的 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 课程设计很差,而且已经过时很久了。如果你能避免它(如果你不能避免它,我会给你最后的解决办法)。

  1. public static LocalDate getNextDate(LocalDate startDate, TemporalUnit period, int times) {
  2. if (! period.isDateBased()) {
  3. throw new IllegalArgumentException("Cannot add " + period + " to a date");
  4. }
  5. LocalDate today = LocalDate.now(ZoneId.of("America/Eirunepe"));
  6. if (startDate.isBefore(today)) {
  7. // Calculate how many times we need to add times units to get a future date (or today).
  8. // We need to round up; the trick for doing so is count until yesterday and add 1.
  9. LocalDate yesterday = today.minusDays(1);
  10. long timesToAdd = period.between(startDate, yesterday) / times + 1;
  11. return startDate.plus(timesToAdd * times, period);
  12. } else {
  13. return startDate;
  14. }
  15. }

为了演示我使用的这个小实用方法:

  1. public static void demo(LocalDate startDate, TemporalUnit period, int times) {
  2. LocalDate nextDate = getNextDate(startDate, period, times);
  3. System.out.format("getNextDate(%s, %s, %d) -> %s%n", startDate, period, times, nextDate);
  4. }

现在让我们看看:

  1. demo(LocalDate.of(2020, Month.AUGUST, 31), ChronoUnit.MONTHS, 1);
  2. demo(LocalDate.of(2020, Month.AUGUST, 31), ChronoUnit.MONTHS, 2);
  3. demo(LocalDate.of(2020, Month.MAY, 30), ChronoUnit.MONTHS, 2);
  4. demo(LocalDate.of(2020, Month.JUNE, 30), ChronoUnit.MONTHS, 2);
  5. System.out.println();
  6. demo(LocalDate.of(2020, Month.FEBRUARY, 28), ChronoUnit.YEARS, 1);
  7. demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.YEARS, 1);
  8. demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.YEARS, 4);
  9. System.out.println();
  10. demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.DAYS, 1);
  11. demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.DAYS, 3);
  12. demo(LocalDate.of(2020, Month.FEBRUARY, 29), ChronoUnit.WEEKS, 2);
  13. System.out.println();
  14. demo(LocalDate.of(2020, Month.SEPTEMBER, 4), ChronoUnit.MONTHS, 1);

刚才运行时,输出是:

  1. getNextDate(2020-08-31, Months, 1) -> 2020-09-30
  2. getNextDate(2020-08-31, Months, 2) -> 2020-10-31
  3. getNextDate(2020-05-30, Months, 2) -> 2020-09-30
  4. getNextDate(2020-06-30, Months, 2) -> 2020-10-30
  5. getNextDate(2020-02-28, Years, 1) -> 2021-02-28
  6. getNextDate(2020-02-29, Years, 1) -> 2021-02-28
  7. getNextDate(2020-02-29, Years, 4) -> 2024-02-29
  8. getNextDate(2020-02-29, Days, 1) -> 2020-09-04
  9. getNextDate(2020-02-29, Days, 3) -> 2020-09-05
  10. getNextDate(2020-02-29, Weeks, 2) -> 2020-09-12
  11. getNextDate(2020-09-04, Months, 1) -> 2020-09-04

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

  1. enum Period {
  2. DAY(ChronoUnit.DAYS),
  3. WEEK(ChronoUnit.WEEKS),
  4. MONTH(ChronoUnit.MONTHS),
  5. YEAR(ChronoUnit.YEARS);
  6. private final ChronoUnit unit;
  7. private Period(ChronoUnit unit) {
  8. this.unit = unit;
  9. }
  10. public ChronoUnit getUnit() {
  11. return unit;
  12. }
  13. }

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

  1. public static Date getNextDate(Date startDate, Period period, int times) {
  2. ZoneId zone = ZoneId.of("America/Eirunepe");
  3. LocalDate startLocalDate = startDate.toInstant().atZone(zone).toLocalDate();
  4. LocalDate nextDate = getNextDate(startLocalDate, period.getUnit(), times);
  5. Instant startOfDay = nextDate.atStartOfDay(zone).toInstant();
  6. return Date.from(startOfDay);
  7. }
展开查看全部
cmssoen2

cmssoen22#

你可以用这个班 Calendar 要这样解决问题:

  1. public static Date getNextDate(Date startDate, int period, int times) {
  2. Calendar calendar = Calendar.getInstance();
  3. calendar.setTime(startDate);
  4. calendar.add(period, times);
  5. return calendar.getTime();
  6. }

句点是calendar类中定义的int,可以这样调用函数:

  1. System.out.println(getNextDate(new Date(), Calendar.MONTH, 1));
  2. System.out.println(getNextDate(new Date(), Calendar.MONTH, 3));
  3. System.out.println(getNextDate(new Date(), Calendar.YEAR, 1));

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

展开查看全部
jucafojl

jucafojl3#

最好不要使用十年前的DateAPI,因为它编写得很糟糕,而且通常不安全,使用起来很痛苦。使用 java.time 可能对你有利。将方法签名更改为以下内容,就是您所要做的:

  1. import java.time.LocalDate;
  2. import java.time.Period;
  3. ...
  4. public static LocalDate getNextDate(LocalDate startDate, Period period) {
  5. return startDate.plus(period);
  6. }

然后可以这样称呼:

  1. LocalDate startDate = LocalDate.of(3, 9, 2020);
  2. LocalDate nextDate = getNextDate(startDate, Period.ofDays(20)); // 2020-09-23

或者干脆在第一时间删除助手函数并直接使用它:

  1. LocalDate nextDate = startDate.plus(Period.ofDays(20));
展开查看全部
nwlls2ji

nwlls2ji4#

我终于想出了一个办法(尽管对于我真正想达到的目标来说,似乎是一个办法,一个办法,一个办法)。我改变了主意 getNextDateAfterToday 为了这个

  1. public Date getNextValidFutureDate(Date entryDate, Date startDate, int specific) {
  2. Date result = new Date(startDate.getTime());
  3. while (!result.equals(entryDate) && !result.after(entryDate)) {
  4. result = getNextDate(result, true, specific);
  5. }
  6. LocalDate ldStart = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
  7. LocalDate ldResult = result.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
  8. if (ldResult.getDayOfMonth() < ldStart.getDayOfMonth() && this != DAY && this != WEEK && this != YEAR) {
  9. if (ldResult.lengthOfMonth() >= ldStart.getDayOfMonth()) {
  10. ldResult = ldResult.with(ChronoField.DAY_OF_MONTH, ldStart.getDayOfMonth());
  11. } else {
  12. ldResult = ldResult.with(ChronoField.DAY_OF_MONTH, ldResult.lengthOfMonth());
  13. }
  14. }
  15. return Date.from(ldResult.atStartOfDay(ZoneId.systemDefault()).toInstant());
  16. }

我没有将另一个方法更改为使用localdate,但将来会这样做。这适用于我上面发布的所有测试用例。虽然我希望我没有错过重要的

展开查看全部

相关问题