Fix skipping month (#745)
This commit is contained in:
parent
3f37113a2c
commit
d1296f1a90
2 changed files with 51 additions and 4 deletions
|
|
@ -145,12 +145,29 @@ const calculateMonthlyWeekdayRecurrence = (
|
|||
};
|
||||
|
||||
const calculateMonthlyLastDayRecurrence = (fromDate, interval) => {
|
||||
const nextDate = new Date(fromDate);
|
||||
nextDate.setUTCMonth(nextDate.getUTCMonth() + interval);
|
||||
// Calculate target year and month directly to avoid date overflow
|
||||
// (e.g., Jan 31 + 1 month via setUTCMonth would overflow to March)
|
||||
const currentMonth = fromDate.getUTCMonth();
|
||||
const currentYear = fromDate.getUTCFullYear();
|
||||
|
||||
nextDate.setUTCMonth(nextDate.getUTCMonth() + 1, 0);
|
||||
const totalMonths = currentMonth + interval;
|
||||
const targetYear = currentYear + Math.floor(totalMonths / 12);
|
||||
const targetMonth = totalMonths % 12;
|
||||
|
||||
return nextDate;
|
||||
// Get last day of target month by creating date at day 0 of following month
|
||||
const lastDayOfMonth = new Date(
|
||||
Date.UTC(
|
||||
targetYear,
|
||||
targetMonth + 1, // next month
|
||||
0, // day 0 = last day of previous month
|
||||
fromDate.getUTCHours(),
|
||||
fromDate.getUTCMinutes(),
|
||||
fromDate.getUTCSeconds(),
|
||||
fromDate.getUTCMilliseconds()
|
||||
)
|
||||
);
|
||||
|
||||
return lastDayOfMonth;
|
||||
};
|
||||
|
||||
const getFirstWeekdayOfMonth = (year, month, weekday) => {
|
||||
|
|
|
|||
|
|
@ -303,6 +303,36 @@ describe('Recurring Tasks', () => {
|
|||
expect(nextDate.getUTCDate()).toBe(expectedDate.getUTCDate());
|
||||
});
|
||||
|
||||
it('should not skip months for monthly_last_day when starting from 31st', async () => {
|
||||
// Bug fix: Jan 31 -> should go to Feb 28, not March 31
|
||||
const jan31 = new Date(Date.UTC(2025, 0, 31, 0, 0, 0, 0));
|
||||
|
||||
const taskData = {
|
||||
name: 'End of Month Task',
|
||||
recurrence_type: 'monthly_last_day',
|
||||
recurrence_interval: 1,
|
||||
due_date: jan31.toISOString().split('T')[0],
|
||||
};
|
||||
|
||||
const response = await agent.post('/api/task').send(taskData);
|
||||
const task = await Task.findByPk(response.body.id);
|
||||
|
||||
// First occurrence: Jan 31 -> Feb 28
|
||||
const nextDate1 = calculateNextDueDate(task, jan31);
|
||||
expect(nextDate1.getUTCMonth()).toBe(1); // February
|
||||
expect(nextDate1.getUTCDate()).toBe(28);
|
||||
|
||||
// Second occurrence: Feb 28 -> Mar 31
|
||||
const nextDate2 = calculateNextDueDate(task, nextDate1);
|
||||
expect(nextDate2.getUTCMonth()).toBe(2); // March
|
||||
expect(nextDate2.getUTCDate()).toBe(31);
|
||||
|
||||
// Third occurrence: Mar 31 -> Apr 30
|
||||
const nextDate3 = calculateNextDueDate(task, nextDate2);
|
||||
expect(nextDate3.getUTCMonth()).toBe(3); // April
|
||||
expect(nextDate3.getUTCDate()).toBe(30);
|
||||
});
|
||||
|
||||
it('should handle monthly recurrence when day does not exist in target month', async () => {
|
||||
// Create a task for Jan 31
|
||||
const jan31 = new Date(Date.UTC(2024, 0, 31, 0, 0, 0, 0));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue