Skip to content

Commit f310c7a

Browse files
authored
Merge pull request #655 from rajbos/rajbos/fix-30day-utc-cutoff
fix: use content timestamps for lastInteraction, not max(timestamp, mtime)
2 parents b46da13 + 7ecf838 commit f310c7a

1 file changed

Lines changed: 33 additions & 34 deletions

File tree

vscode-extension/src/extension.ts

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as vscode from 'vscode';
1+
import * as vscode from 'vscode';
22
import * as fs from 'fs';
33
import * as path from 'path';
44
import * as os from 'os';
@@ -161,7 +161,7 @@ type LocalViewRegressionCase = {
161161

162162
class CopilotTokenTracker implements vscode.Disposable {
163163
// Cache version - increment this when making changes that require cache invalidation
164-
private static readonly CACHE_VERSION = 39; // Cache-aware cost: track cachedReadTokens/cacheCreationTokens in ModelUsage
164+
private static readonly CACHE_VERSION = 40; // Fix lastInteraction: use content timestamps only, not max(timestamp, mtime)
165165
// Maximum length for displaying workspace IDs in diagnostics/customization matrix
166166
private static readonly WORKSPACE_ID_DISPLAY_LENGTH = 8;
167167

@@ -2161,7 +2161,17 @@ class CopilotTokenTracker implements vscode.Disposable {
21612161
continue;
21622162
}
21632163

2164+
// Derive lastActivity from session content timestamp (not mtime) to avoid
2165+
// date mis-classification when VS Code writes the file after midnight.
2166+
const lastActivity = sessionData.lastInteraction
2167+
? new Date(sessionData.lastInteraction)
2168+
: new Date(mtime);
2169+
21642170
// Add to last 30 days stats
2171+
if (lastActivity < last30DaysStart) {
2172+
processed++;
2173+
continue;
2174+
}
21652175
last30DaysStats.sessions++;
21662176
this.mergeUsageAnalysis(last30DaysStats, analysis);
21672177

@@ -2196,14 +2206,14 @@ class CopilotTokenTracker implements vscode.Disposable {
21962206
}
21972207
}
21982208

2199-
// Add to month stats if modified this calendar month
2200-
if (mtime >= monthStart.getTime()) {
2209+
// Add to month stats if activity falls in this calendar month
2210+
if (lastActivity >= monthStart) {
22012211
monthStats.sessions++;
22022212
this.mergeUsageAnalysis(monthStats, analysis);
22032213
}
22042214

2205-
// Add to today stats if modified today
2206-
if (mtime >= todayStart.getTime()) {
2215+
// Add to today stats if activity falls today
2216+
if (lastActivity >= todayStart) {
22072217
todayStats.sessions++;
22082218
this.mergeUsageAnalysis(todayStats, analysis);
22092219
}
@@ -2826,18 +2836,11 @@ class CopilotTokenTracker implements vscode.Disposable {
28262836
return undefined;
28272837
}
28282838

2829-
// Determine lastInteraction: use the more recent of cached timestamp or file mtime
2830-
// This handles cases where file was modified but content timestamps are older
2831-
let lastInteraction: string | null = cached.lastInteraction || null;
2832-
if (lastInteraction) {
2833-
const cachedLastInteraction = new Date(lastInteraction);
2834-
if (stat.mtime > cachedLastInteraction) {
2835-
lastInteraction = stat.mtime.toISOString();
2836-
}
2837-
} else {
2838-
// No cached lastInteraction, use file mtime
2839-
lastInteraction = stat.mtime.toISOString();
2840-
}
2839+
// Use the cached lastInteraction from session content directly.
2840+
// Do NOT fall back to file mtime here: mtime is updated whenever VS Code writes the
2841+
// session file (e.g. finalising a session just after midnight), which would shift
2842+
// yesterday's sessions into "today". Only use mtime when no content timestamp exists.
2843+
const lastInteraction: string | null = cached.lastInteraction || stat.mtime.toISOString();
28412844

28422845
// Reconstruct SessionFileDetails from cache.
28432846
// Prefer actualTokens (real API count) when available; fall back to estimated tokens.
@@ -3049,10 +3052,10 @@ class CopilotTokenTracker implements vscode.Disposable {
30493052
if (timestamps.length > 0) {
30503053
timestamps.sort((a, b) => a - b);
30513054
details.firstInteraction = new Date(timestamps[0]).toISOString();
3052-
const lastTimestamp = new Date(timestamps[timestamps.length - 1]);
3053-
details.lastInteraction = lastTimestamp > stat.mtime
3054-
? lastTimestamp.toISOString()
3055-
: stat.mtime.toISOString();
3055+
// Use the last content timestamp directly. Do NOT mix in stat.mtime: mtime is set
3056+
// when VS Code writes the file (e.g. after midnight), which would shift yesterday's
3057+
// session into 'today', breaking the 30-day/today cutoff boundaries.
3058+
details.lastInteraction = new Date(timestamps[timestamps.length - 1]).toISOString();
30563059
} else {
30573060
details.lastInteraction = stat.mtime.toISOString();
30583061
}
@@ -3116,12 +3119,10 @@ class CopilotTokenTracker implements vscode.Disposable {
31163119
if (timestamps.length > 0) {
31173120
timestamps.sort((a, b) => a - b);
31183121
details.firstInteraction = new Date(timestamps[0]).toISOString();
3119-
// Use the more recent of: extracted last timestamp OR file modification time
3120-
// This handles cases where new requests are added without timestamp fields
3121-
const lastTimestamp = new Date(timestamps[timestamps.length - 1]);
3122-
details.lastInteraction = lastTimestamp > stat.mtime
3123-
? lastTimestamp.toISOString()
3124-
: stat.mtime.toISOString();
3122+
// Use the last content timestamp directly. Do NOT mix in stat.mtime: mtime is set
3123+
// when VS Code writes the file (e.g. after midnight), which would shift yesterday's
3124+
// session into 'today', breaking the 30-day/today cutoff boundaries.
3125+
details.lastInteraction = new Date(timestamps[timestamps.length - 1]).toISOString();
31253126
} else {
31263127
// Fallback to file modification time if no timestamps in content
31273128
details.lastInteraction = stat.mtime.toISOString();
@@ -3191,12 +3192,10 @@ class CopilotTokenTracker implements vscode.Disposable {
31913192
if (timestamps.length > 0) {
31923193
timestamps.sort((a, b) => a - b);
31933194
details.firstInteraction = new Date(timestamps[0]).toISOString();
3194-
// Use the more recent of: extracted last timestamp OR file modification time
3195-
// This handles cases where new requests are added without timestamp fields
3196-
const lastTimestamp = new Date(timestamps[timestamps.length - 1]);
3197-
details.lastInteraction = lastTimestamp > stat.mtime
3198-
? lastTimestamp.toISOString()
3199-
: stat.mtime.toISOString();
3195+
// Use the last content timestamp directly. Do NOT mix in stat.mtime: mtime is set
3196+
// when VS Code writes the file (e.g. after midnight), which would shift yesterday's
3197+
// session into 'today', breaking the 30-day/today cutoff boundaries.
3198+
details.lastInteraction = new Date(timestamps[timestamps.length - 1]).toISOString();
32003199
} else {
32013200
// Fallback to file modification time if no timestamps in content
32023201
details.lastInteraction = stat.mtime.toISOString();

0 commit comments

Comments
 (0)