Skip to content

fix(nodes): clamp traffic reset day to month length#360

Open
TopPro104 wants to merge 1 commit into
remnawave:devfrom
TopPro104:fix/node-reset-day-month-overflow
Open

fix(nodes): clamp traffic reset day to month length#360
TopPro104 wants to merge 1 commit into
remnawave:devfrom
TopPro104:fix/node-reset-day-month-overflow

Conversation

@TopPro104

Copy link
Copy Markdown

Что было не так

Утилиты getNodeResetDaysUtil и getNodeResetPeriodUtil показывают на карточке ноды сколько дней осталось до сброса трафика и сам период сброса. Они должны были откатываться на последний день месяца, если заданного дня в текущем месяце нет, например 31 в апреле или 30 в феврале.

Проверка использовала dayjs().date(targetDay).isValid(), но dayjs при переполнении дня молча переносит дату на следующий месяц и всё равно считает её валидной. Поэтому откат на конец месяца никогда не срабатывал.

Из-за этого у нод с днём сброса 29, 30 или 31 в коротких месяцах счётчик дней до сброса и период показывались неверно, с ошибкой на один или два дня, а период заезжал в следующий месяц.

Как исправлено

Теперь день обрезается до количества дней в месяце через dayjs().daysInMonth(), поэтому дата всегда попадает на последний день месяца и никуда не уезжает.

Проверка

Февраль с днём сброса 31, период был 3 Feb по 3 Mar, стало 28 Jan по 28 Feb. Апрель с днём сброса 31, дней до сброса было 21, стало 20.

@greptile-apps

greptile-apps Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a bug in getNodeResetDaysUtil and getNodeResetPeriodUtil where the previous dayjs().date(n).isValid() guard never triggered overflow protection because dayjs silently rolls overflowing day numbers into the next month while keeping isValid() truthy. The fix replaces that guard with Math.min(targetDay, daysInMonth()) clamping, correctly landing on the last day of short months.

  • getNodeResetDaysUtil: clamping is applied independently to both the current and next month using their respective daysInMonth() values — correctly handles e.g. April (30) and May (31) for targetDay = 31.
  • getNodeResetPeriodUtil: clamping is applied to the current month reset date; the endDate path is correct, but the startDate for the period (correctedThisMonth.subtract(1, 'month')) can still land on the wrong day when the prior month has more days than the current one (see comment).

Confidence Score: 4/5

Safe to merge — the core isValid() overflow bug is correctly fixed for both utilities and no regressions were found in edge-case analysis.

The clamping logic in getNodeResetDaysUtil is correct across all tested month-length combinations. In getNodeResetPeriodUtil the end-date path is also correct, but the start-date is derived by subtracting one month from the clamped current-month date rather than independently clamping the target day for the previous month, so it can be off by a few days when the prior month is longer than the current one.

Only get-node-reset-days.util.ts changed; the start-date computation in getNodeResetPeriodUtil is worth a follow-up.

Important Files Changed

Filename Overview
src/shared/utils/time-utils/get-node-reset-days/get-node-reset-days.util.ts Replaces broken isValid() overflow check with Math.min(targetDay, daysInMonth()) clamping for both reset-day utility functions; one residual edge case in period start-date calculation

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[targetDay input] --> B{targetDay greater than daysInMonth?}
    B -- No --> C[clampedDay = targetDay]
    B -- Yes --> D[clampedDay = daysInMonth]
    C --> E[correctedDate = today.date clampedDay]
    D --> E

    subgraph getNodeResetDaysUtil
        E --> G[daysToThisMonth = correctedThisMonth.diff today]
        E --> H[daysToNextMonth = correctedNextMonth.diff today using nextMonth.daysInMonth]
        G --> I{daysToThisMonth less than 0?}
        I -- Yes --> J[return daysToNextMonth]
        I -- No --> K{thisMonth less than nextMonth?}
        K -- Yes --> L[return daysToThisMonth]
        K -- No --> J
    end

    subgraph getNodeResetPeriodUtil
        E --> M{today.date greater than targetDay?}
        M -- isPast --> N[startDate = correctedThisMonth endDate = correctedThisMonth plus 1 month]
        M -- isNotPast --> O[startDate = correctedThisMonth minus 1 month endDate = correctedThisMonth]
        O --> P[startDate may be off by 1-3 days when prior month is longer]
    end
Loading

Comments Outside Diff (1)

  1. src/shared/utils/time-utils/get-node-reset-days/get-node-reset-days.util.ts, line 31-35 (link)

    P2 The start-date of the period is computed by subtracting one month from the already-clamped current-month date rather than independently clamping the target day for the previous month. This means when the previous month has more days than the current month, the reported start is slightly wrong. For example, with targetDay = 31 and today = February 15, correctedThisMonth = Feb 28, then startDate = Feb 28 − 1 month = Jan 28, but the actual previous reset was on Jan 31.

Reviews (1): Last reviewed commit: "fix(nodes): clamp traffic reset day to m..." | Re-trigger Greptile

getNodeResetDaysUtil/getNodeResetPeriodUtil tried to fall back to the
end of the month when the configured trafficResetDay does not exist in
the current month (e.g. 31 in April, 30 in February). The guard relied
on dayjs().date(targetDay).isValid(), but dayjs silently rolls an
overflowing day into the next month and still reports isValid() === true,
so the fallback never ran.

As a result nodes with a reset day of 29/30/31 showed an incorrect
'days until reset' countdown and reset period in short months (off by
1-2 days, and the period leaked into the next month).

Clamp targetDay to daysInMonth() so it lands on the last day of the
month instead. In getNodeResetPeriodUtil the previous and next month
reset dates are clamped independently as well, otherwise deriving them
by subtracting/adding a month can land on the wrong day when the
adjacent month has a different number of days.
@TopPro104 TopPro104 force-pushed the fix/node-reset-day-month-overflow branch from 326092f to 4a7cb96 Compare June 2, 2026 19:53
@TopPro104

Copy link
Copy Markdown
Author

Поправил в getNodeResetPeriodUtil начало и конец периода. Теперь даты предыдущего и следующего месяца клампятся независимо, каждая по своему daysInMonth, а не выводятся прибавлением или вычитанием месяца от текущей даты.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant