Как заставить дату повторяться каждый год в последний день февраля в Java для iCal google-rfc-2445

Мне нужно повторять дату каждый последний день февраля, я использую время Joda, чтобы использовать правило для генерации дат дней, недель, месяцев и т. Д. Для Months работает нормально, но когда идет год, я получаю неправильные результаты.

private static List<DateTime> calculateRecurrentDates(DateTime startDate, String recurrentRule) {
    List<DateTime> dates = new ArrayList<DateTime>();
    TimeStamp startDate = Timestamp.valueOf("2011-02-28 10:10:10");
    String recurrentRule = "RRULE:FREQ=YEARLY;COUNT=6;INTERVAL=1;";
    String leapYearRule = "RRULE:FREQ=YEARLY;COUNT=6;INTERVAL=1;BYMONTHDAY=28,29;BYSETPOS=-1";

    try {
        DateTimeIterable range = DateTimeIteratorFactory.createDateTimeIterable(recurrentRule, startDate, DateTimeZone.UTC, true);
        for (DateTime date : range) {
           dates.add(date);
        }
    } catch (ParseException e) {
        dates = null;
        logger.error(e.getMessage());
    }
    return dates;
}

Но я получаю это:

2011-02-28T10: 10: 10.000Z
2012-02-28T10: 10: 10.000Z
2013-02-28T10: 10: 10.000Z
2014-02-28T10: 10: 10.000Z
2015-02-28T10: 10: 10.000Z
2016-02-28T10: 10: 10.000Z

и в случае високосного года это:

2012-02-29T10: 10: 10.000Z
2012-12-29T10: 10: 10.000Z
2013-12-29T10: 10: 10.000Z
2014-12-29T10: 10: 10.000Z
2015-12-29T10: 10: 10.000Z
2016-12-29T10: 10: 10.000Z
2017-12-29T10: 10: 10.000Z

Как я пишу одно правило, чтобы получить последний день февраля каждый год?

4 ответа

Решение

Мое решение состоит в том, чтобы считать от последнего дня года до дня до 1 марта. Так что правило будет таким:

"RRULE:FREQ=YEARLY;COUNT=6;INTERVAL=1;BYYEARDAY=-307"

Если вы хотите убедиться, что вы получаете событие на последний день месяца, вы должны использовать BYMONTHDAY=-1 (См. RRULE в RFC), чтобы убедиться, что это произойдет только в феврале BYMONTH=2 который затем дает:

RRULE:FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=-1

Затем, так как вы упоминаете COUNT свойство, которое вы могли бы также определить UNTIL который указывает до тех пор, пока вы не хотите, чтобы ваш RRULE подлежит оценке:

RRULE:FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=-1;UNTIL=20200302T070000Z

НИКОГДА не используйте DateTimes, если вы хотите манипулировать только датами, LocalDate - правильный класс.

Если вы действительно хотите получить последние дни февраля:

public static List<LocalDate> getLastDaysOfFebruary(LocalDate start, int n) {
    ArrayList<LocalDate> list = new ArrayList<>();
    LocalDate firstMar = start.withMonthOfYear(3).withDayOfMonth(1);
    if(! firstMar.isAfter(start)) firstMar = firstMar.plusYears(1);
    for(int i=0;i<n;i++) {
        list.add(firstMar.minusDays(1));
        firstMar = firstMar.plusYears(1);
    }
    return list;
}

Вычтите один день с первого марта.

DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime firstOfMarch = new DateTime( "2014-03-01", timeZone ).withTimeAtStartOfDay();
DateTime lastOfFebruary = firstOfMarch.minusDays( 1 ).withTimeAtStartOfDay();

На firstOfMarch объект, вызов plusYears(1) продолжать дальше.

Другие вопросы по тегам