@@ -5,6 +5,12 @@ import * as os from 'os';
55import * as path from 'path' ;
66import * as fs from 'fs' ;
77import { v4 as uuidv4 } from 'uuid' ;
8+ import {
9+ createTimelineTestDates ,
10+ createUTCDate ,
11+ createUTCDateByHours ,
12+ validateTimezoneSafety ,
13+ } from '../utils/timezone-safe-dates' ;
814
915describe ( 'Enhanced Context Operations Integration Tests' , ( ) => {
1016 let dbManager : DatabaseManager ;
@@ -658,48 +664,34 @@ describe('Enhanced Context Operations Integration Tests', () => {
658664
659665 describe ( 'Enhanced context_timeline' , ( ) => {
660666 beforeEach ( ( ) => {
661- // Create items across different time periods
662- const now = new Date ( ) ;
667+ // TIMEZONE-SAFE: Use fixed UTC dates to ensure consistent behavior across all environments
668+ validateTimezoneSafety ( ) ;
663669
664- // Calculate proper timestamps for yesterday to ensure they fall within yesterday's date range
665- const yesterday = new Date ( ) ;
666- yesterday . setDate ( yesterday . getDate ( ) - 1 ) ;
667- yesterday . setHours ( 10 , 0 , 0 , 0 ) ; // 10 AM yesterday
670+ const { today : _today , yesterday : _yesterday } = createTimelineTestDates ( ) ;
668671
669- const yesterdayAfternoon = new Date ( ) ;
670- yesterdayAfternoon . setDate ( yesterdayAfternoon . getDate ( ) - 1 ) ;
671- yesterdayAfternoon . setHours ( 15 , 0 , 0 , 0 ) ; // 3 PM yesterday
672-
673- // Ensure we have items that are definitely "today" - create them at specific times today
674- // Use UTC to match the test query
675- const todayMorning = new Date ( ) ;
676- todayMorning . setUTCHours ( 9 , 0 , 0 , 0 ) ; // 9 AM today UTC
677-
678- const todayAfternoon = new Date ( ) ;
679- todayAfternoon . setUTCHours ( 14 , 0 , 0 , 0 ) ; // 2 PM today UTC
680-
681- // Create more "today" items ensuring they stay within today's boundaries
682- const todayEarlyMorning = new Date ( ) ;
683- todayEarlyMorning . setUTCHours ( 7 , 0 , 0 , 0 ) ; // 7 AM today UTC
684-
685- const todayEarlyMorning2 = new Date ( ) ;
686- todayEarlyMorning2 . setUTCHours ( 5 , 0 , 0 , 0 ) ; // 5 AM today UTC
672+ // Create timezone-safe timestamps at specific hours
673+ const yesterdayMorning = createUTCDate ( - 1 , 10 , 0 , 0 ) ; // 10 AM yesterday UTC
674+ const yesterdayAfternoon = createUTCDate ( - 1 , 15 , 0 , 0 ) ; // 3 PM yesterday UTC
675+ const todayEarlyMorning = createUTCDate ( 0 , 5 , 0 , 0 ) ; // 5 AM today UTC
676+ const todayEarlyMorning2 = createUTCDate ( 0 , 7 , 0 , 0 ) ; // 7 AM today UTC
677+ const todayMorning = createUTCDate ( 0 , 9 , 0 , 0 ) ; // 9 AM today UTC
678+ const todayAfternoon = createUTCDate ( 0 , 14 , 0 , 0 ) ; // 2 PM today UTC
687679
688680 const timeOffsets = [
689- // Use absolute timestamps for "today" items to ensure they're always in today
681+ // Use timezone-safe absolute timestamps
690682 { timestamp : todayMorning , key : 'recent_1' , category : 'task' } , // 9 AM today
691683 { timestamp : todayAfternoon , key : 'recent_2' , category : 'note' } , // 2 PM today
692- { timestamp : todayEarlyMorning , key : 'today_1' , category : 'task' } , // 7 AM today
693- { timestamp : todayEarlyMorning2 , key : 'today_2' , category : 'decision' } , // 5 AM today
694- { timestamp : yesterday , key : 'yesterday_1' , category : 'task' } , // Yesterday 10 AM
684+ { timestamp : todayEarlyMorning2 , key : 'today_1' , category : 'task' } , // 7 AM today
685+ { timestamp : todayEarlyMorning , key : 'today_2' , category : 'decision' } , // 5 AM today
686+ { timestamp : yesterdayMorning , key : 'yesterday_1' , category : 'task' } , // Yesterday 10 AM
695687 { timestamp : yesterdayAfternoon , key : 'yesterday_2' , category : 'note' } , // Yesterday 3 PM
696- { hours : - 72 , key : 'days_ago_1' , category : 'progress' } , // 3 days ago
697- { hours : - 168 , key : 'week_ago_1' , category : 'task' } , // 1 week ago
698- { hours : - 336 , key : 'weeks_ago_1' , category : 'error' } , // 2 weeks ago
688+ { timestamp : createUTCDateByHours ( - 72 ) , key : 'days_ago_1' , category : 'progress' } , // 3 days ago
689+ { timestamp : createUTCDateByHours ( - 168 ) , key : 'week_ago_1' , category : 'task' } , // 1 week ago
690+ { timestamp : createUTCDateByHours ( - 336 ) , key : 'weeks_ago_1' , category : 'error' } , // 2 weeks ago
699691 ] ;
700692
701- timeOffsets . forEach ( ( { hours , timestamp, key, category } ) => {
702- const createdAt = timestamp || new Date ( now . getTime ( ) + hours * 3600000 ) ;
693+ timeOffsets . forEach ( ( { timestamp, key, category } ) => {
694+ const createdAt = timestamp ;
703695 db . prepare (
704696 `
705697 INSERT INTO context_items (id, session_id, key, value, category, priority, created_at)
@@ -724,7 +716,7 @@ describe('Enhanced Context Operations Integration Tests', () => {
724716 ] ;
725717
726718 journalEntries . forEach ( ( { hours, entry, mood } ) => {
727- const createdAt = new Date ( now . getTime ( ) + hours * 3600000 ) ;
719+ const createdAt = createUTCDateByHours ( hours ) ;
728720 db . prepare (
729721 `
730722 INSERT INTO journal_entries (id, session_id, entry, mood, created_at)
@@ -736,9 +728,10 @@ describe('Enhanced Context Operations Integration Tests', () => {
736728
737729 describe ( 'Basic timeline functionality' , ( ) => {
738730 it ( 'should group items by time period' , ( ) => {
739- const now = new Date ( ) ;
740- const oneDayAgo = new Date ( now . getTime ( ) - 24 * 3600000 ) ;
741- const oneWeekAgo = new Date ( now . getTime ( ) - 7 * 24 * 3600000 ) ;
731+ // TIMEZONE-SAFE: Use UTC-based date calculations
732+ const { today : _today } = createTimelineTestDates ( ) ;
733+ const oneDayAgo = createUTCDateByHours ( - 24 ) ;
734+ const oneWeekAgo = createUTCDateByHours ( - 7 * 24 ) ;
742735
743736 // Get items from different periods
744737 const todayItems = db
@@ -885,13 +878,12 @@ describe('Enhanced Context Operations Integration Tests', () => {
885878
886879 describe ( 'relativeTime parameter' , ( ) => {
887880 it ( 'should handle "today" relative time' , ( ) => {
888- // Use UTC to ensure consistent behavior across timezones
889- const todayUTC = new Date ( ) ;
890- todayUTC . setUTCHours ( 0 , 0 , 0 , 0 ) ;
881+ // TIMEZONE-SAFE: Use UTC start of day
882+ const todayStart = createUTCDate ( 0 , 0 , 0 , 0 ) ;
891883
892884 const items = db
893885 . prepare ( 'SELECT * FROM context_items WHERE session_id = ? AND created_at >= ?' )
894- . all ( testSessionId , todayUTC . toISOString ( ) ) as any [ ] ;
886+ . all ( testSessionId , todayStart . toISOString ( ) ) as any [ ] ;
895887
896888 expect ( items . length ) . toBeGreaterThan ( 0 ) ;
897889 // Check for items we know are created "today" based on our test setup
@@ -902,28 +894,25 @@ describe('Enhanced Context Operations Integration Tests', () => {
902894 } ) ;
903895
904896 it ( 'should handle "yesterday" relative time' , ( ) => {
905- const yesterday = new Date ( ) ;
906- yesterday . setDate ( yesterday . getDate ( ) - 1 ) ;
907- yesterday . setHours ( 0 , 0 , 0 , 0 ) ;
908-
909- const today = new Date ( ) ;
910- today . setHours ( 0 , 0 , 0 , 0 ) ;
897+ // TIMEZONE-SAFE: Use UTC dates for day boundaries
898+ const yesterdayStart = createUTCDate ( - 1 , 0 , 0 , 0 ) ;
899+ const todayStart = createUTCDate ( 0 , 0 , 0 , 0 ) ;
911900
912901 const items = db
913902 . prepare (
914903 'SELECT * FROM context_items WHERE session_id = ? AND created_at >= ? AND created_at < ?'
915904 )
916- . all ( testSessionId , yesterday . toISOString ( ) , today . toISOString ( ) ) as any [ ] ;
905+ . all ( testSessionId , yesterdayStart . toISOString ( ) , todayStart . toISOString ( ) ) as any [ ] ;
917906
918907 expect ( items . some ( i => i . key . includes ( 'yesterday' ) ) ) . toBe ( true ) ;
919908 } ) ;
920909
921910 it ( 'should handle "X hours ago" format' , ( ) => {
922- // Create test items relative to current time for this specific test
923- const now = new Date ( ) ;
924- const oneHourAgo = new Date ( now . getTime ( ) - 1 * 3600000 ) ;
925- const twoHoursAgo = new Date ( now . getTime ( ) - 2 * 3600000 ) ;
926- const fiveHoursAgo = new Date ( now . getTime ( ) - 5 * 3600000 ) ;
911+ // TIMEZONE-SAFE: Use UTC-based hour calculations
912+ const { today : _today } = createTimelineTestDates ( ) ;
913+ const oneHourAgo = createUTCDateByHours ( - 1 ) ;
914+ const twoHoursAgo = createUTCDateByHours ( - 2 ) ;
915+ const fiveHoursAgo = createUTCDateByHours ( - 5 ) ;
927916
928917 // Add test items with specific relative timestamps
929918 db . prepare (
@@ -966,7 +955,7 @@ describe('Enhanced Context Operations Integration Tests', () => {
966955 ) ;
967956
968957 // Query for items created 2 hours ago or less
969- const queryTime = new Date ( now . getTime ( ) - 2.1 * 3600000 ) ; // 2.1 hours ago to ensure we catch 2 hour old items
958+ const queryTime = createUTCDateByHours ( - 2.1 ) ; // 2.1 hours ago to ensure we catch 2 hour old items
970959 const items = db
971960 . prepare ( 'SELECT * FROM context_items WHERE session_id = ? AND created_at >= ?' )
972961 . all ( testSessionId , queryTime . toISOString ( ) ) as any [ ] ;
@@ -982,8 +971,8 @@ describe('Enhanced Context Operations Integration Tests', () => {
982971 } ) ;
983972
984973 it ( 'should handle "X days ago" format' , ( ) => {
985- const threeDaysAgo = new Date ( ) ;
986- threeDaysAgo . setDate ( threeDaysAgo . getDate ( ) - 3 ) ;
974+ // TIMEZONE-SAFE: Use UTC date arithmetic
975+ const threeDaysAgo = createUTCDate ( - 3 ) ;
987976
988977 const items = db
989978 . prepare ( 'SELECT * FROM context_items WHERE session_id = ? AND created_at >= ?' )
@@ -1003,11 +992,10 @@ describe('Enhanced Context Operations Integration Tests', () => {
1003992 } ) ;
1004993
1005994 it ( 'should handle "this week" relative time' , ( ) => {
1006- const startOfWeek = new Date ( ) ;
1007- const day = startOfWeek . getDay ( ) ;
1008- const diff = startOfWeek . getDate ( ) - day ;
1009- startOfWeek . setDate ( diff ) ;
1010- startOfWeek . setHours ( 0 , 0 , 0 , 0 ) ;
995+ // TIMEZONE-SAFE: Calculate start of week in UTC
996+ const { today : _today } = createTimelineTestDates ( ) ;
997+ const dayOfWeek = _today . getUTCDay ( ) ;
998+ const startOfWeek = createUTCDate ( - dayOfWeek , 0 , 0 , 0 ) ;
1011999
10121000 const items = db
10131001 . prepare ( 'SELECT * FROM context_items WHERE session_id = ? AND created_at >= ?' )
@@ -1017,14 +1005,11 @@ describe('Enhanced Context Operations Integration Tests', () => {
10171005 } ) ;
10181006
10191007 it ( 'should handle "last week" relative time' , ( ) => {
1020- const startOfLastWeek = new Date ( ) ;
1021- const day = startOfLastWeek . getDay ( ) ;
1022- const diff = startOfLastWeek . getDate ( ) - day - 7 ;
1023- startOfLastWeek . setDate ( diff ) ;
1024- startOfLastWeek . setHours ( 0 , 0 , 0 , 0 ) ;
1025-
1026- const endOfLastWeek = new Date ( startOfLastWeek ) ;
1027- endOfLastWeek . setDate ( endOfLastWeek . getDate ( ) + 7 ) ;
1008+ // TIMEZONE-SAFE: Calculate last week's boundaries in UTC
1009+ const { today : _today } = createTimelineTestDates ( ) ;
1010+ const dayOfWeek = _today . getUTCDay ( ) ;
1011+ const startOfLastWeek = createUTCDate ( - dayOfWeek - 7 , 0 , 0 , 0 ) ;
1012+ const endOfLastWeek = createUTCDate ( - dayOfWeek , 0 , 0 , 0 ) ;
10281013
10291014 const items = db
10301015 . prepare (
@@ -1219,8 +1204,8 @@ describe('Enhanced Context Operations Integration Tests', () => {
12191204
12201205 describe ( 'Combining timeline parameters' , ( ) => {
12211206 it ( 'should combine categories and date filters' , ( ) => {
1222- const twoDaysAgo = new Date ( ) ;
1223- twoDaysAgo . setDate ( twoDaysAgo . getDate ( ) - 2 ) ;
1207+ // TIMEZONE-SAFE: Use UTC date calculations
1208+ const twoDaysAgo = createUTCDate ( - 2 ) ;
12241209
12251210 const items = db
12261211 . prepare (
@@ -1297,9 +1282,10 @@ describe('Enhanced Context Operations Integration Tests', () => {
12971282 } ) ;
12981283
12991284 it ( 'should work with only startDate and endDate as before' , ( ) => {
1300- const endDate = new Date ( ) ;
1301- const startDate = new Date ( ) ;
1302- startDate . setDate ( startDate . getDate ( ) - 7 ) ;
1285+ // TIMEZONE-SAFE: Use UTC date range
1286+ const { today : _today } = createTimelineTestDates ( ) ;
1287+ const startDate = createUTCDate ( - 7 ) ;
1288+ const endDate = _today ;
13031289
13041290 const items = db
13051291 . prepare (
0 commit comments