@@ -19,7 +19,11 @@ export function findTodayIndex(days: ContributionDay[], timezone: string, now: D
1919
2020 const localTodayIndex = days . findIndex ( ( d ) => d . date === localTodayStr ) ;
2121
22- return localTodayIndex !== - 1 ? localTodayIndex : days . length - 1 ;
22+ // If today's date isn't present in the calendar, return -1 so callers can
23+ // decide whether falling back to the last available day is appropriate.
24+ // Previously we always returned the last index which could cause an
25+ // overstated current streak when the calendar is partial or stale.
26+ return localTodayIndex !== - 1 ? localTodayIndex : - 1 ;
2327}
2428
2529export function calculateStreak (
@@ -47,15 +51,40 @@ export function calculateStreak(
4751
4852 // 2. Calculate Current Streak (Backwards loop with Grace Period)
4953 const localTodayStr = new Intl . DateTimeFormat ( 'en-CA' , { timeZone : timezone } ) . format ( now ) ;
50- const todayIndex = findTodayIndex ( days , timezone , now ) ;
54+ let todayIndex = findTodayIndex ( days , timezone , now ) ;
5155
56+ // If the calendar doesn't contain today's date, only fall back to the
57+ // last available day when the local date is after the calendar's last
58+ // reported date (i.e. the calendar is stale). Otherwise, avoid guessing
59+ // and treat today's data as missing to prevent overstating the streak.
5260 if ( todayIndex < 0 ) {
53- return {
54- currentStreak : 0 ,
55- longestStreak : 0 ,
56- totalContributions : calendar . totalContributions ,
57- todayDate : localTodayStr ,
58- } ;
61+ const lastIndex = days . length - 1 ;
62+ if ( lastIndex < 0 ) {
63+ return {
64+ currentStreak : 0 ,
65+ longestStreak : 0 ,
66+ totalContributions : calendar . totalContributions ,
67+ todayDate : localTodayStr ,
68+ } ;
69+ }
70+
71+ const lastDateStr = days [ lastIndex ] . date ;
72+
73+ // Compare YYYY-MM-DD strings lexicographically — this works for ISO dates.
74+ if ( localTodayStr > lastDateStr ) {
75+ // Local date is after the last reported date → calendar is stale.
76+ todayIndex = lastIndex ;
77+ } else {
78+ // Calendar contains dates after (or unrelated to) local today, or
79+ // today is simply missing from a partial range — don't assume the
80+ // streak is alive based on the last day.
81+ return {
82+ currentStreak : 0 ,
83+ longestStreak,
84+ totalContributions : calendar . totalContributions ,
85+ todayDate : localTodayStr ,
86+ } ;
87+ }
5988 }
6089
6190 let isStreakAlive = false ;
0 commit comments