使用java.time

时间:2016-08-19 19:20:15

标签: java date java-time

使用LocalDateTimeLocalDate在指定日期添加月份的最干净解决方案是什么?

好的答案是In java.time, how is the result of adding a month calculated?Find next occurrence of a day-of-week in JSR-310,所以我希望通过类似的清洁解决方案解决这个问题。

让我们假设用户选择一个月中的某一天(任何从1到31)进行某些定期任务。选择时间戳生成后,每月增加。仅存储当前月份的时间戳(没有上个月的跟踪,每月更新时间戳)和选定的某一天。

如果选择的日期 31日,我们从 1月31日开始,使用dateTime.plusMonths(1)方法添加一个月,在2016年为 2月29日 。如果再次使用dateTime.plusMonths(1)方法,则结果为 3月29日,而预期为 3月31日 。但是,从1日到28日,它可以工作几天。

每月31日的解决方法可能是使用dateTime.with(TemporalAdjusters.lastDayOfMonth()),但这不包括从28日到30日的日期,也可能是一个月的最后几天。

lastDayOfMonth()所选日期 30日 1月30日 2月29日的示例(plusMonth方法行为选择最后一天如果前一天比前一个月更高,则 3月31日(预计 30th 这是每月的选定日期)。此方法未考虑月中的选定日期。

当然这个问题有许多潜在的解决方案,其中包括一些检查和计算月和日之间的差异,但我正在寻找一种只有在可能的情况下使用java.time功能的解决方案。

澄清

我正在寻找一种方法,将日期移至下个月的选定日期。像dateTime.plusMonths(1).withDayOfMonth(selectedDayOfMonth)一样。这会因为withDayOfMonth覆盖plusMonths对上一个有效日期的调整而对2月31日这样的日期抛出异常。

使用plusMonth方法的迭代示例。在迭代中,从前一个值开始 - 想象递归。

+-----------+------------------+---------------+
| iteration | Day-of-month: 31 |   Expected    |
+-----------+------------------+---------------+
|         1 | January 31st     | January 31st  |
|         2 | February 29th    | February 29th |
|         3 | March 29th       | March 31st    |
|         4 | April 29th       | April 30th    |
+-----------+------------------+---------------+

另一个工作示例,从1日到28日的月份。

+-----------+-------------------+--------------+
| iteration | Day-of-month: 4th |   Expected   |
+-----------+-------------------+--------------+
|         1 | January 4th       | January 4th  |
|         2 | February 4th      | February 4th |
|         3 | March 4th         | March 4th    |
|         4 | April 4th         | April 4th    |
+-----------+-------------------+--------------+
一个月的第29天对于闰年是可以的,但是对于普通年份不是。

+-----------+--------------------+---------------+
| iteration | Day-of-month: 29th |   Expected    |
+-----------+--------------------+---------------+
|         1 | January 29th       | January 29th  |
|         2 | February 28th      | February 28th |
|         3 | March 28th         | March 29th    |
|         4 | April 28th         | April 29th    |
+-----------+--------------------+---------------+

5 个答案:

答案 0 :(得分:4)

将月份日期设置为min(selectedDayOfMonth, lastDayOfNextMonth)

public static LocalDate next(LocalDate current, int selectedDayOfMonth) {
    LocalDate next = current.plusMonths(1);
    return next.withDayOfMonth(Math.min(selectedDayOfMonth, next.lengthOfMonth()));
}

用法:

public static void test(int selectedDayOfMonth) {
    LocalDate date = LocalDate.of(2001, Month.JANUARY, selectedDayOfMonth);
    System.out.println(date);
    for (int i = 0; i < 5; i++) {
        date = next(date, selectedDayOfMonth);
        System.out.println(date);
    }
    System.out.println();
}

test(4)的输出:

2001-01-04
2001-02-04
2001-03-04
2001-04-04
2001-05-04
2001-06-04

test(29)的输出:

2001-01-29
2001-02-28
2001-03-29
2001-04-29
2001-05-29
2001-06-29

test(31)的输出:

2001-01-31
2001-02-28
2001-03-31
2001-04-30
2001-05-31
2001-06-30

答案 1 :(得分:0)

你可以尝试

myDate.plusDays(YearMonth.of(myDate.getYear, myDate.getMonth + 1).lengthOfMonth())

将下个月的天数添加到当前日期。这仍然会导致一些奇怪的行为,尽管像1月1日那样 - > 1月29日 - &gt; 3月1日,但在可能的情况下,它仍会一直恢复。

添加额外的支票,例如

myDate.plusDays(YearMonth.of(myDate.getYear, myDate.getMonth + (myDate.getDayOfMonth() > 15 ? 1 : 0)).lengthOfMonth())

会解决这个问题并使其更加一致。现在它将添加当月的天数,如果它在上半部分或下个月的天数,如果它在下半部分应该工作我认为。

答案 2 :(得分:0)

一种解决方案可能是花费最后一天并添加一天:

firstDayOfNextMonth = lastDayOfThisMonth.plusDays(1);

然后添加一个月:

firstDayOfNextNextMonth = firstDayOfNextMonth.plusMonths(1);

然后减去一天:

lastDayOfNextMonth = firstDayOfNextNextMonth.minusDays(1);

所以,你得到:

lastDayOfNextMonth = lastDayOfThisMonth.plusDays(1).plusMonths(1).minusDays(1);

您可以将其转换为方法:

LocalDate addMonthToDate(LocalDate date) {
    return date.plusDays(1).plusMonths(1).minusDays(1);
}

答案 3 :(得分:0)

YearMonth

如果您希望将月末作为目标,请考虑月份而非特定日期。

YearMonth类代表任何特定月份。

YearMonth ym = YearMonth.of( 2016 , 1 );  // January

您可以为该对象添加月份。

YearMonth nextMonth = ym.plusMonths( 1 );  // February

当您需要该月末的日期时,请致电atEndOfMonth询问。

LocalDate ld = ym.atEndOfMonth();

顺便说一下,Month enum有预定义的对象来代表一年中十二个月中的每一个,即一月到十二月。

YearMonth ym = YearMonth.of( 2016 , Month.JANUARY );

如果您想申请某个月(第29-31天)可能无效的特定日期,请尝试申请此类日期。如果抛出异常,则回到要求月末。

int dayOfMonth = 30;
LocalDate ld = null;
try {
    ld = ym.atDay( dayOfMonth );
} catch ( DateTimeException e ) {
    ld = ym.atEndOfMonth();
}

执行if( dayOfMonth > 28 )而不是捕获异常。

答案 4 :(得分:-1)

我只是不得不自己解决此问题,尽管输入的内容略有不同,最终得到以下解决方案。正如其他人指出的那样,在这种情况下,YearMonth可能会有所帮助,但我认为它的真正威力在于将其用作TemporalAdjuster。就我而言,我需要保留从初始日期开始的每月的某天:

public static LocalDate next(LocalDate initial, LocalDate current) {
    return initial.with(YearMonth.from(current.plusMonths(1)));
}

这将向前调整日期,同时保留从初始日期起的每月日期,但如有必要,将使用该月的最后一天。

如果只有一个月的日期,则可以从31天的任何月份开始:

public static LocalDate next(LocalDate current, int selectedDayOfMonth) {
    return LocalDate.of(2020, 1, selectedDayOfMonth).with(YearMonth.from(current.plusMonths(1)));
}

或使用org.threeten的DayOfMonth

public static LocalDate next(LocalDate current, int selectedDayOfMonth) {
    return DayOfMonth.of(selectedDayOfMonth).atYearMonth(YearMonth.from(current.plusMonths(1)));
}