FraserHamilton / dayjs-recur

Day.js plugin for matching and generating recurring dates.
https://www.npmjs.com/package/dayjs-recur
MIT License
10 stars 2 forks source link

Duplicated recurrence when date is end of month #17

Open emileond opened 2 years ago

emileond commented 2 years ago

Hi, first of all thank you for building this plugin - it's great.

I've found a bug, whenever I create recurring dates with monthly recurrence and the initial date is at the end of the month (30, 31) it duplicates a date.

Example:

// Create a date to start from (end of a month)
const myDate = dayjs("30/11/2022");

// Pass recurrence in months
const recurrence = myDate.recur().every(3, "months");

// Output
Feb 28, 2023 ✅
May 30, 2023 ✅
May 31, 2023 ❌

For some reason it duplicates a date every 2 months or so. Hope this helps to find a fix.

alianza commented 4 months ago

Same issue here; With leap days it's even worse.

const recurrence = dayjs().recur(dayjs(new Date('2024-02-29T12:00:00.000Z')), dayjs(new Date('2025-02-29T12:00:00.000Z'))).every(1).month();

Results in:

 2024-02-29T12:00:00.000Z, 2024-03-29T12:00:00.000Z,
  2024-03-30T12:00:00.000Z, 2024-03-31T12:00:00.000Z,
  2024-04-29T12:00:00.000Z, 2024-04-30T12:00:00.000Z,
  2024-05-29T12:00:00.000Z, 2024-05-30T12:00:00.000Z,
  2024-05-31T12:00:00.000Z, 2024-06-29T12:00:00.000Z,
  2024-06-30T12:00:00.000Z, 2024-07-29T12:00:00.000Z,
  2024-07-30T12:00:00.000Z, 2024-07-31T12:00:00.000Z,
  2024-08-29T12:00:00.000Z, 2024-08-30T12:00:00.000Z,
  2024-08-31T12:00:00.000Z, 2024-09-29T12:00:00.000Z,
  2024-09-30T12:00:00.000Z, 2024-10-29T12:00:00.000Z,
  2024-10-30T12:00:00.000Z, 2024-10-31T12:00:00.000Z,
  2024-11-29T12:00:00.000Z, 2024-11-30T12:00:00.000Z,
  2024-12-29T12:00:00.000Z, 2024-12-30T12:00:00.000Z,
  2024-12-31T12:00:00.000Z, 2025-01-29T12:00:00.000Z,
  2025-01-30T12:00:00.000Z, 2025-01-31T12:00:00.000Z,
  2025-02-28T12:00:00.000Z

Up to 3 dates per month.

Fix I implemented for now is (after calling recurrence.all() and converting every recurrence to a Date if the interval frequency is monthly):

        // if dates contains the same month multiple times in a row, we need to remove the duplicates and only keep the last one
        dates = dates.reduce((acc, date) => {
            if (acc.length === 0 || acc[acc.length - 1].getMonth() !== date.getMonth()) {
                acc.push(date);
            } else {
                acc[acc.length - 1] = date;
            }
            return acc;
        }, [] as Date[]);

moment-recur has the same issue: https://jsfiddle.net/dx8pr10w/2/