diff options
| author | Ariel Costas Guerrero <ariel@costas.dev> | 2025-12-12 18:40:12 +0100 |
|---|---|---|
| committer | Ariel Costas Guerrero <ariel@costas.dev> | 2025-12-12 18:40:12 +0100 |
| commit | c3363ee0e3808d826c4e4797ffa7207647435e08 (patch) | |
| tree | 9797a7d9e91bedc229df48635d5d45cf5cb632bc /src/Costasdev.Busurbano.Backend | |
| parent | ffb8ee87898bffe5fee706abb047133585bb5d0d (diff) | |
feat: add comfort parameters to AppConfiguration and update OTP service for Madrid timezone handling
Diffstat (limited to 'src/Costasdev.Busurbano.Backend')
| -rw-r--r-- | src/Costasdev.Busurbano.Backend/Configuration/AppConfiguration.cs | 5 | ||||
| -rw-r--r-- | src/Costasdev.Busurbano.Backend/Services/OtpService.cs | 47 |
2 files changed, 39 insertions, 13 deletions
diff --git a/src/Costasdev.Busurbano.Backend/Configuration/AppConfiguration.cs b/src/Costasdev.Busurbano.Backend/Configuration/AppConfiguration.cs index 3224515..db0b573 100644 --- a/src/Costasdev.Busurbano.Backend/Configuration/AppConfiguration.cs +++ b/src/Costasdev.Busurbano.Backend/Configuration/AppConfiguration.cs @@ -14,6 +14,11 @@ public class AppConfiguration public int MaxWalkTime { get; set; } = 20; public int NumItineraries { get; set; } = 4; + // Comfort/Slack Parameters + public int TransferSlackSeconds { get; set; } = 120; // Extra buffer for transfers + public int MinTransferTimeSeconds { get; set; } = 120; // Minimum transfer time + public double WalkReluctance { get; set; } = 2.0; // Slightly penalize walking to add slack + // Fare Configuration public double FareCashPerBus { get; set; } = 1.63; public double FareCardPerBus { get; set; } = 0.67; diff --git a/src/Costasdev.Busurbano.Backend/Services/OtpService.cs b/src/Costasdev.Busurbano.Backend/Services/OtpService.cs index 87895d3..6b01c5c 100644 --- a/src/Costasdev.Busurbano.Backend/Services/OtpService.cs +++ b/src/Costasdev.Busurbano.Backend/Services/OtpService.cs @@ -98,9 +98,26 @@ public class OtpService { try { - var date = time ?? DateTime.Now; - var dateStr = date.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture); - var timeStr = date.ToString("h:mm tt", CultureInfo.InvariantCulture); + // Convert the provided time to Europe/Madrid local time and pass explicit offset to OTP + var tz = TimeZoneInfo.FindSystemTimeZoneById("Europe/Madrid"); + DateTime utcReference; + if (time.HasValue) + { + var t = time.Value; + if (t.Kind == DateTimeKind.Unspecified) + t = DateTime.SpecifyKind(t, DateTimeKind.Utc); + utcReference = t.Kind == DateTimeKind.Utc ? t : t.ToUniversalTime(); + } + else + { + utcReference = DateTime.UtcNow; + } + + var localMadrid = TimeZoneInfo.ConvertTimeFromUtc(utcReference, tz); + var offsetSeconds = (int)tz.GetUtcOffset(localMadrid).TotalSeconds; + + var dateStr = localMadrid.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture); + var timeStr = localMadrid.ToString("HH:mm", CultureInfo.InvariantCulture); var queryParams = new Dictionary<string, string> { @@ -116,9 +133,15 @@ public class OtpService { "walkSpeed", _config.WalkSpeed.ToString(CultureInfo.InvariantCulture) }, { "maxWalkDistance", _config.MaxWalkDistance.ToString() }, // Note: OTP might ignore this if it's too small { "optimize", "QUICK" }, - { "wheelchair", "false" } + { "wheelchair", "false" }, + { "timeZoneOffset", offsetSeconds.ToString(CultureInfo.InvariantCulture) } }; + // Add slack/comfort parameters + queryParams["transferSlack"] = _config.TransferSlackSeconds.ToString(); + queryParams["minTransferTime"] = _config.MinTransferTimeSeconds.ToString(); + queryParams["walkReluctance"] = _config.WalkReluctance.ToString(CultureInfo.InvariantCulture); + var queryString = string.Join("&", queryParams.Select(kvp => $"{kvp.Key}={Uri.EscapeDataString(kvp.Value)}")); var url = $"{_config.OtpPlannerBaseUrl}/plan?{queryString}"; @@ -140,14 +163,12 @@ public class OtpService private RoutePlan MapToRoutePlan(OtpPlan otpPlan) { - // Compute time offset: OTP's "date" field is the server time in millis - var otpServerTime = DateTimeOffset.FromUnixTimeMilliseconds(otpPlan.Date); - var now = DateTimeOffset.Now; - var timeOffsetSeconds = (long)(otpServerTime - now).TotalSeconds; + // OTP times are already correct when requested with explicit offset + var timeOffsetSeconds = 0L; return new RoutePlan { - Itineraries = otpPlan.Itineraries.Select(MapItinerary).ToList(), + Itineraries = otpPlan.Itineraries.Select(MapItinerary).OrderBy(i => i.DurationSeconds).ToList(), TimeOffsetSeconds = timeOffsetSeconds }; } @@ -185,8 +206,8 @@ public class OtpService return new Itinerary { DurationSeconds = otpItinerary.Duration, - StartTime = DateTimeOffset.FromUnixTimeMilliseconds(otpItinerary.StartTime).LocalDateTime, // Assuming local time or handling timezone - EndTime = DateTimeOffset.FromUnixTimeMilliseconds(otpItinerary.EndTime).LocalDateTime, + StartTime = DateTimeOffset.FromUnixTimeMilliseconds(otpItinerary.StartTime).UtcDateTime, + EndTime = DateTimeOffset.FromUnixTimeMilliseconds(otpItinerary.EndTime).UtcDateTime, WalkDistanceMeters = otpItinerary.WalkDistance, WalkTimeSeconds = otpItinerary.WalkTime, TransitTimeSeconds = otpItinerary.TransitTime, @@ -211,8 +232,8 @@ public class OtpService RouteTextColor = otpLeg.RouteTextColor, From = MapPlace(otpLeg.From), To = MapPlace(otpLeg.To), - StartTime = DateTimeOffset.FromUnixTimeMilliseconds(otpLeg.StartTime).LocalDateTime, - EndTime = DateTimeOffset.FromUnixTimeMilliseconds(otpLeg.EndTime).LocalDateTime, + StartTime = DateTimeOffset.FromUnixTimeMilliseconds(otpLeg.StartTime).UtcDateTime, + EndTime = DateTimeOffset.FromUnixTimeMilliseconds(otpLeg.EndTime).UtcDateTime, DistanceMeters = otpLeg.Distance, Geometry = DecodePolyline(otpLeg.LegGeometry?.Points), Steps = otpLeg.Steps.Select(MapStep).ToList(), |
