diff --git a/src/repositories/powerBoostingRepository.test.ts b/src/repositories/powerBoostingRepository.test.ts index 1fcb39afc..5b6cdfd79 100644 --- a/src/repositories/powerBoostingRepository.test.ts +++ b/src/repositories/powerBoostingRepository.test.ts @@ -988,12 +988,23 @@ function powerBoostingSnapshotTests() { const firstGivbackRoundTimeStamp = Number( process.env.FIRST_GIVBACK_ROUND_TIME_STAMP, ); - const givbackRoundLength = Number(process.env.GIVPOWER_ROUND_DURATION); - const startBoundary = - firstGivbackRoundTimeStamp + (round - 1) * givbackRoundLength; - const endBoundary = startBoundary + givbackRoundLength; - const snapshotTime = (snapshot.time.getTime() as number) / 1000; + // Calculate month-based boundaries (new logic) + const startDate = new Date(firstGivbackRoundTimeStamp * 1000); + + // Round boundaries: 1st of the round's month to 1st of next month + const roundStartDate = new Date(startDate); + roundStartDate.setUTCMonth(startDate.getUTCMonth() + (round - 1)); + roundStartDate.setUTCDate(1); + roundStartDate.setUTCHours(0, 0, 0, 0); + + const roundEndDate = new Date(roundStartDate); + roundEndDate.setUTCMonth(roundEndDate.getUTCMonth() + 1); + + const startBoundary = Math.floor(roundStartDate.getTime() / 1000); + const endBoundary = Math.floor(roundEndDate.getTime() / 1000); + const snapshotTime = Math.floor(snapshot.time.getTime() / 1000); + assert.isAtLeast(snapshotTime, startBoundary); assert.isBelow(snapshotTime, endBoundary); diff --git a/src/utils/powerBoostingUtils.ts b/src/utils/powerBoostingUtils.ts index 79a605676..bf7db125d 100644 --- a/src/utils/powerBoostingUtils.ts +++ b/src/utils/powerBoostingUtils.ts @@ -4,8 +4,25 @@ const firstGivbackRoundTimeStamp = Number( process.env.FIRST_GIVBACK_ROUND_TIME_STAMP, ); -const givbackRoundLength = Number(process.env.GIVPOWER_ROUND_DURATION); +// Not used for month-based calculation, kept for backwards compatibility +// const givbackRoundLength = Number(process.env.GIVPOWER_ROUND_DURATION); +/** + * Get the round number for a given date + * + * @description + * The round number is calculated based on the date. Each month is a new round. + * + * @example + * getRoundNumberByDate(new Date('2026-02-10')) // 1778 // 1st of February 2026 is the 1778th round + * + * @param date - The date to get the round number for + * @returns { + * round: number; + * fromTimestamp: number; + * toTimestamp: number; + * } + */ export const getRoundNumberByDate = ( date: Date, ): { @@ -13,14 +30,28 @@ export const getRoundNumberByDate = ( fromTimestamp: number; toTimestamp: number; } => { - const now = getTimestampInSeconds(date); + // Calendar month-based calculation for perfect 1st-of-month alignment + const startDate = new Date(firstGivbackRoundTimeStamp * 1000); + const currentDate = new Date(date); - const round = - Math.floor((now - firstGivbackRoundTimeStamp) / givbackRoundLength) + 1; + // Calculate months elapsed since start date + const monthsElapsed = + (currentDate.getFullYear() - startDate.getFullYear()) * 12 + + (currentDate.getMonth() - startDate.getMonth()); + + const round = monthsElapsed + 1; + + // Round boundaries: 1st of current month to 1st of next month + const roundStartDate = new Date( + Date.UTC(currentDate.getFullYear(), currentDate.getMonth(), 1, 0, 0, 0), + ); + + const roundEndDate = new Date(roundStartDate); + roundEndDate.setUTCMonth(roundEndDate.getUTCMonth() + 1); + + const fromTimestamp = getTimestampInSeconds(roundStartDate); + const toTimestamp = getTimestampInSeconds(roundEndDate); - const fromTimestamp = - (round - 1) * givbackRoundLength + firstGivbackRoundTimeStamp; - const toTimestamp = round * givbackRoundLength + firstGivbackRoundTimeStamp; return { round, fromTimestamp,