Skip to content

Commit e3787f1

Browse files
authored
test(calculate): verify streak formulas for timezone shifts around midnight timeline (JhaSourav07#2015)
## Description Fixes JhaSourav07#1480 ## Pillar * [ ] 🎨 Pillar 1 — New Theme Design * [ ] 📐 Pillar 2 — Geometric SVG Improvement * [x] 🕐 Pillar 3 — Timezone Logic Optimization * [x] 🛠️ Other (Bug fix, refactoring, docs) ## Visual Preview Not applicable. This PR only adds test coverage for streak calculation logic and does not modify UI or SVG output. ## Checklist before requesting a review: * [x] I have read the `CONTRIBUTING.md` file. * [x] I have tested these changes locally (`localhost:3000/api/streak?user=PiyushRajan2007`). * [x] I have run `npm run format` and `npm run lint` locally and resolved all errors (CI will fail otherwise). * [x] My commits follow the Conventional Commits format (e.g., `feat(themes): ...`, `fix(calculate): ...`). * [ ] I have updated `README.md` if I added a new theme or URL parameter. * [x] I have started the repo. * [x] I have made sure that I have only one commit to merge in this PR. * [ ] The SVG output matches the CommitPulse "premium quality" aesthetic standard (no raw elements, smooth animations, correct fonts). * [x] (Recommended) I joined the CommitPulse Discord community for contributor discussions, mentorship, and faster PR support. ## Summary Fixes JhaSourav07#1480 Adds a test case covering contributions occurring around a UTC midnight boundary (23:59 and 00:01) across different timezone offsets. The test validates both `currentStreak` and `longestStreak` calculations and helps detect off-by-one errors caused by timezone/date-boundary handling.
2 parents eb1bedc + 3df3d47 commit e3787f1

1 file changed

Lines changed: 47 additions & 0 deletions

File tree

lib/calculate.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,53 @@ describe('calculateStreak — timezone awareness', () => {
968968
expect(result.currentStreak).toBe(3);
969969
});
970970

971+
it('handles contributions at 23:59 and 00:01 UTC across timezone boundaries', () => {
972+
// Simulate two commits that occur around the UTC midnight boundary:
973+
// - One commit at 2024-07-10T23:59:00Z (falls on 2024-07-10 UTC)
974+
// - Another commit at 2024-07-11T00:01:00Z (falls on 2024-07-11 UTC)
975+
// The flattened calendar only stores dates; these two commits appear on
976+
// consecutive dates (2024-07-10 and 2024-07-11). Depending on the
977+
// caller's timezone, the local "today" may be either 2024-07-11 or
978+
// still 2024-07-10 which can expose off-by-one errors.
979+
const calendar = {
980+
totalContributions: 2,
981+
weeks: [
982+
{
983+
contributionDays: [
984+
{ contributionCount: 0, date: '2024-07-09' },
985+
{ contributionCount: 1, date: '2024-07-10' }, // 23:59 UTC commit
986+
{ contributionCount: 1, date: '2024-07-11' }, // 00:01 UTC commit
987+
],
988+
},
989+
],
990+
};
991+
992+
// Use a UTC moment shortly after the second commit.
993+
const nowUTC = new Date('2024-07-11T00:01:00.000Z');
994+
995+
// In UTC the local date is 2024-07-11 — both days are in scope → streak=2
996+
const utcResult = calculateStreak(calendar, 'UTC', nowUTC);
997+
expect(utcResult.todayDate).toBe('2024-07-11');
998+
expect(utcResult.currentStreak).toBe(2);
999+
expect(utcResult.longestStreak).toBe(2);
1000+
1001+
// In a timezone ahead of UTC by 1 hour (Etc/GMT-1), local date is also 2024-07-11
1002+
// and the streak remains 2 (no split occurs).
1003+
const aheadResult = calculateStreak(calendar, 'Etc/GMT-1', nowUTC);
1004+
expect(aheadResult.todayDate).toBe('2024-07-11');
1005+
expect(aheadResult.currentStreak).toBe(2);
1006+
expect(aheadResult.longestStreak).toBe(2);
1007+
1008+
// In a timezone behind UTC by 1 hour (Etc/GMT+1), local date is still 2024-07-10
1009+
// at the same instant — only the earlier day's commit is considered "today",
1010+
// so currentStreak should be 1 while longestStreak across the whole calendar
1011+
// remains 2.
1012+
const behindResult = calculateStreak(calendar, 'Etc/GMT+1', nowUTC);
1013+
expect(behindResult.todayDate).toBe('2024-07-10');
1014+
expect(behindResult.currentStreak).toBe(1);
1015+
expect(behindResult.longestStreak).toBe(2);
1016+
});
1017+
9711018
it('falls back to the last available day when the local date is ahead of the calendar data', () => {
9721019
const futureNow = new Date('2024-06-16T12:00:00.000Z');
9731020
const result = calculateStreak(tzCalendar, 'Etc/GMT-14', futureNow);

0 commit comments

Comments
 (0)