diff options
| author | Ariel Costas Guerrero <ariel@costas.dev> | 2025-11-06 22:33:55 +0100 |
|---|---|---|
| committer | Ariel Costas Guerrero <ariel@costas.dev> | 2025-11-06 22:33:55 +0100 |
| commit | 0ac8ba208e0ad4d61cb82d6216c9cb34d43421a0 (patch) | |
| tree | 194c110dfb84170a39c73c2ae6f5bec4f6131385 /src/Costasdev.Busurbano.Backend/Controllers | |
| parent | 8258cf91dda99bc68731b7f285612fdf63a7781d (diff) | |
Refactor GetStopArrivalsMerged to use Europe/Madrid timezone and ensure unique trip matches
Diffstat (limited to 'src/Costasdev.Busurbano.Backend/Controllers')
| -rw-r--r-- | src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs | 34 |
1 files changed, 24 insertions, 10 deletions
diff --git a/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs b/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs index 4d57cc1..5b03b6d 100644 --- a/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs +++ b/src/Costasdev.Busurbano.Backend/Controllers/VigoController.cs @@ -93,8 +93,12 @@ public class VigoController : ControllerBase { StringBuilder outputBuffer = new(); - var realtimeTask = _api.GetStopEstimates(stopId); - var timetableTask = LoadTimetable(stopId.ToString(), DateTime.Today.ToString("yyyy-MM-dd")); + // Use Europe/Madrid timezone consistently to avoid UTC/local skew + var tz = TimeZoneInfo.FindSystemTimeZoneById("Europe/Madrid"); + var nowLocal = TimeZoneInfo.ConvertTime(DateTime.UtcNow, tz); + + var realtimeTask = _api.GetStopEstimates(stopId); + var timetableTask = LoadTimetable(stopId.ToString(), nowLocal.Date.ToString("yyyy-MM-dd")); await Task.WhenAll(realtimeTask, timetableTask); @@ -104,13 +108,14 @@ public class VigoController : ControllerBase .Where(c => c.StartingDateTime() != null && c.CallingDateTime() != null) .ToList(); - var now = DateTime.Now.AddSeconds(60 - DateTime.Now.Second); + var now = nowLocal.AddSeconds(60 - nowLocal.Second); // Define the scope end as the time of the last realtime arrival (no extra buffer) var scopeEnd = realTimeEstimates.Count > 0 ? now.AddMinutes(realTimeEstimates.Max(e => e.Minutes)) : now; - List<ConsolidatedCirculation> consolidatedCirculations = []; + List<ConsolidatedCirculation> consolidatedCirculations = []; + var usedTripIds = new HashSet<string>(); foreach (var estimate in realTimeEstimates) { @@ -192,6 +197,13 @@ public class VigoController : ControllerBase continue; } + // Ensure each scheduled trip is only matched once to a realtime estimate + if (usedTripIds.Contains(closestCirculation.TripId)) + { + _logger.LogInformation("Skipping duplicate realtime match for TripId {TripId}", closestCirculation.TripId); + continue; + } + consolidatedCirculations.Add(new ConsolidatedCirculation { Line = estimate.Line, @@ -209,16 +221,14 @@ public class VigoController : ControllerBase Distance = estimate.Meters } }); + + usedTripIds.Add(closestCirculation.TripId); } // Add scheduled-only circulations between now and the last realtime arrival if (scopeEnd > now) { - var matchedTripIds = new HashSet<string>( - consolidatedCirculations - .Where(c => c.Schedule != null) - .Select(c => c.Schedule!.TripId) - ); + var matchedTripIds = new HashSet<string>(usedTripIds); var scheduledWindow = timetable .Where(c => c.CallingDateTime()!.Value >= now && c.CallingDateTime()!.Value <= scopeEnd) @@ -246,8 +256,12 @@ public class VigoController : ControllerBase }); } } + // Sort by ETA (RealTime minutes if present; otherwise Schedule minutes) + var sorted = consolidatedCirculations + .OrderBy(c => c.RealTime?.Minutes ?? c.Schedule!.Minutes) + .ToList(); - return Ok(consolidatedCirculations); + return Ok(sorted); } private async Task<List<ScheduledStop>> LoadTimetable(string stopId, string dateString) |
