diff --git a/server/db.go b/server/db.go index 6f2f0b9..b71ab25 100644 --- a/server/db.go +++ b/server/db.go @@ -6,7 +6,6 @@ import ( "log" "net/url" "os" - "time" _ "github.com/go-sql-driver/mysql" ) @@ -25,13 +24,15 @@ func InitDB() { return } - log.Printf("[DB] Using timezone: %s (Local=%s)", os.Getenv("TZ"), time.Local.String()) - // Default to UTC if TZ is empty, otherwise use the encoded TZ name - tzParam := "UTC" - if os.Getenv("TZ") != "" { - tzParam = url.QueryEscape("'" + os.Getenv("TZ") + "'") + // Use TZ env var if set, otherwise fallback to Local + location := "Local" + tz := os.Getenv("TZ") + + dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true&loc=%s", user, pass, host, port, name, location) + if tz != "" { + dsn += fmt.Sprintf("&time_zone='%s'", url.QueryEscape(tz)) } - dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true&loc=Local&time_zone=%s", user, pass, host, port, name, tzParam) + var err error db, err = sql.Open("mysql", dsn) if err != nil { @@ -42,21 +43,7 @@ func InitDB() { log.Fatalf("Error connecting to database: %v", err) } - log.Println("Connected to MariaDB successfully") - - // Verify and enforce timezones - var dbNow, dbTZ string - err = db.QueryRow("SELECT NOW(), @@session.time_zone").Scan(&dbNow, &dbTZ) - if err == nil { - log.Printf("[DB] Before setting: NOW()=%s, session.time_zone=%s", dbNow, dbTZ) - } - - if _, err := db.Exec("SET time_zone = SYSTEM"); err != nil { - log.Printf("[DB] Warning: Failed to set session time_zone to SYSTEM: %v", err) - } else { - db.QueryRow("SELECT NOW(), @@session.time_zone").Scan(&dbNow, &dbTZ) - log.Printf("[DB] After setting: NOW()=%s, session.time_zone=%s", dbNow, dbTZ) - } + log.Printf("[DB] Connected to MariaDB successfully (using loc=%s, tz=%s)", location, tz) createTables() } @@ -108,8 +95,8 @@ func createTables() { start_day_initial_steps INT, previous_total_steps INT, target_total_steps INT, - last_sync_time DATETIME, - next_sync_time DATETIME, + last_sync_time TIMESTAMP NULL, + next_sync_time TIMESTAMP NULL, FOREIGN KEY (user_id) REFERENCES users(fitbit_user_id) ON DELETE CASCADE )`, `CREATE TABLE IF NOT EXISTS daily_steps ( @@ -126,5 +113,10 @@ func createTables() { log.Fatalf("Error creating table: %v\nQuery: %s", err, query) } } + + // Migrations: Ensure trips table uses TIMESTAMP for sync times (breaking change for DATETIME -> TIMESTAMP) + db.Exec("ALTER TABLE trips MODIFY last_sync_time TIMESTAMP NULL") + db.Exec("ALTER TABLE trips MODIFY next_sync_time TIMESTAMP NULL") + log.Println("Database tables initialized") } diff --git a/server/step_manager.go b/server/step_manager.go index d71fb22..ec4ee92 100644 --- a/server/step_manager.go +++ b/server/step_manager.go @@ -93,10 +93,6 @@ func (sm *StepManager) SaveTripState() { } func (sm *StepManager) saveTripStateLocked() { - fmt.Printf("[StepManager] Saving trip state for %s: StartTime=%v (%s), LastSync=%v (%s)\n", - sm.userID, sm.tripState.StartTime, sm.tripState.StartTime.Location(), - sm.lastSyncTime, sm.lastSyncTime.Location()) - _, err := db.Exec(` INSERT INTO trips (user_id, start_date, start_time, start_day_initial_steps, previous_total_steps, target_total_steps, last_sync_time, next_sync_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?) @@ -248,6 +244,20 @@ func (sm *StepManager) performSync(interval time.Duration) { sm.lastSyncTime = time.Now() sm.nextSyncTime = time.Now().Add(interval) + // Save final state with explicit NOW() for last_sync_time to ensure DB consistency + // next_sync_time is also calculated in SQL relative to the same NOW() + _, err = db.Exec(` + UPDATE trips SET + last_sync_time = NOW(), + next_sync_time = DATE_ADD(NOW(), INTERVAL ? SECOND), + previous_total_steps = ?, + target_total_steps = ? + WHERE user_id = ? + `, interval.Seconds(), sm.previousTotalSteps, sm.targetTotalSteps, sm.userID) + if err != nil { + fmt.Printf("Error saving sync completion: %v\n", err) + } + fmt.Printf("Sync Complete. Total Trip Steps: %d\n", sm.targetTotalSteps) }