Change strings to date time offset
This commit is contained in:
parent
cc81cacda8
commit
3ad9e48fcd
11 changed files with 79 additions and 71 deletions
|
|
@ -4,11 +4,11 @@ namespace ShiftScheduler.Services
|
|||
{
|
||||
public interface ITransportService
|
||||
{
|
||||
Task<TransportConnection?> GetConnectionAsync(DateTime shiftStartTime);
|
||||
Task<TransportConnection?> GetConnectionAsync(DateTimeOffset shiftStartTime);
|
||||
}
|
||||
|
||||
public interface ITransportApiService
|
||||
{
|
||||
Task<TransportConnection?> GetConnectionAsync(DateTime shiftStartTime);
|
||||
Task<TransportConnection?> GetConnectionAsync(DateTimeOffset shiftStartTime);
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ namespace ShiftScheduler.Services
|
|||
var summary = $"{shift.Name} (Morning)";
|
||||
var description = "";
|
||||
|
||||
if (shiftWithTransport.MorningTransport != null && !string.IsNullOrEmpty(shiftWithTransport.MorningTransport.DepartureTime))
|
||||
if (shiftWithTransport.MorningTransport != null)
|
||||
{
|
||||
var transportSummary = FormatTransportInfo(shiftWithTransport.MorningTransport);
|
||||
description = $"Transport: {transportSummary}";
|
||||
|
|
@ -47,7 +47,7 @@ namespace ShiftScheduler.Services
|
|||
var summary = $"{shift.Name} (Afternoon)";
|
||||
var description = "";
|
||||
|
||||
if (shiftWithTransport.AfternoonTransport != null && !string.IsNullOrEmpty(shiftWithTransport.AfternoonTransport.DepartureTime))
|
||||
if (shiftWithTransport.AfternoonTransport != null)
|
||||
{
|
||||
var transportSummary = FormatTransportInfo(shiftWithTransport.AfternoonTransport);
|
||||
description = $"Transport: {transportSummary}";
|
||||
|
|
@ -68,9 +68,8 @@ namespace ShiftScheduler.Services
|
|||
|
||||
private string FormatTransportInfo(TransportConnection transport)
|
||||
{
|
||||
var departure = DateTime.TryParse(transport.DepartureTime, out var dep) ? dep.ToString("HH:mm") : transport.DepartureTime;
|
||||
var arrival = DateTime.TryParse(transport.ArrivalTime, out var arr) ? arr.ToString("HH:mm") : transport.ArrivalTime;
|
||||
|
||||
var departure =transport.DepartureTime.ToString("HH:mm");
|
||||
var arrival = transport.ArrivalTime.ToString("HH:mm");
|
||||
return $"{departure} → {arrival}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,13 +176,13 @@ namespace ShiftScheduler.Services
|
|||
|
||||
var transportLines = new List<string>();
|
||||
|
||||
if (shiftWithTransport.MorningTransport != null && !string.IsNullOrEmpty(shiftWithTransport.MorningTransport.DepartureTime))
|
||||
if (shiftWithTransport.MorningTransport != null)
|
||||
{
|
||||
var morningInfo = FormatTransportConnection(shiftWithTransport.MorningTransport);
|
||||
transportLines.Add(morningInfo);
|
||||
}
|
||||
|
||||
if (shiftWithTransport.AfternoonTransport != null && !string.IsNullOrEmpty(shiftWithTransport.AfternoonTransport.DepartureTime))
|
||||
if (shiftWithTransport.AfternoonTransport != null)
|
||||
{
|
||||
var afternoonInfo = FormatTransportConnection(shiftWithTransport.AfternoonTransport);
|
||||
transportLines.Add(afternoonInfo);
|
||||
|
|
@ -193,9 +193,8 @@ namespace ShiftScheduler.Services
|
|||
|
||||
private static string FormatTransportConnection(TransportConnection transport)
|
||||
{
|
||||
var departure = DateTime.TryParse(transport.DepartureTime, out var dep) ? dep.ToString("HH:mm") : transport.DepartureTime;
|
||||
var arrival = DateTime.TryParse(transport.ArrivalTime, out var arr) ? arr.ToString("HH:mm") : transport.ArrivalTime;
|
||||
|
||||
var departure = transport.DepartureTime.ToString("HH:mm");
|
||||
var arrival = transport.ArrivalTime.ToString("HH:mm");
|
||||
return $"🚂 {departure}→{arrival}";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ShiftScheduler.Shared;
|
||||
|
|
@ -6,7 +7,7 @@ namespace ShiftScheduler.Services
|
|||
{
|
||||
public class TransportApiService(HttpClient httpClient, IConfigurationService configurationService, ILogger<TransportApiService> logger) : ITransportApiService
|
||||
{
|
||||
public async Task<TransportConnection?> GetConnectionAsync(DateTime shiftStartTime)
|
||||
public async Task<TransportConnection?> GetConnectionAsync(DateTimeOffset shiftStartTime)
|
||||
{
|
||||
var config = configurationService.GetTransportConfiguration();
|
||||
|
||||
|
|
@ -45,11 +46,12 @@ namespace ShiftScheduler.Services
|
|||
if (apiConnection == null)
|
||||
return new TransportConnection();
|
||||
|
||||
var formats = new[] { @"dd'd'hh\:mm\:ss", @"d'd'hh\:mm\:ss" };
|
||||
return new TransportConnection
|
||||
{
|
||||
DepartureTime = apiConnection.From?.Departure ?? string.Empty,
|
||||
ArrivalTime = apiConnection.To?.Arrival ?? string.Empty,
|
||||
Duration = apiConnection.Duration,
|
||||
DepartureTime = DateTimeOffset.Parse(apiConnection.From!.Departure),
|
||||
ArrivalTime = DateTimeOffset.Parse(apiConnection.To!.Arrival),
|
||||
Duration = TimeSpan.ParseExact(apiConnection.Duration, formats, CultureInfo.InvariantCulture),
|
||||
Platform = apiConnection.From?.Platform ?? string.Empty,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
using System.Globalization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ShiftScheduler.Shared;
|
||||
|
||||
namespace ShiftScheduler.Services;
|
||||
|
||||
public record ConnectionPickArgument(
|
||||
DateTime ShiftStartTime,
|
||||
DateTimeOffset ShiftStartTime,
|
||||
int SafetyBufferMinutes,
|
||||
int MaxEarlyArrivalMinutes,
|
||||
int MaxLateArrivalMinutes)
|
||||
|
|
@ -35,16 +34,13 @@ public static class TransportConnectionCalculator
|
|||
|
||||
foreach (var connection in connections)
|
||||
{
|
||||
if (DateTime.TryParse(connection.ArrivalTime, out var arrivalTime))
|
||||
if (connection.ArrivalTime <= latestArrivalTime)
|
||||
{
|
||||
if (arrivalTime <= latestArrivalTime)
|
||||
{
|
||||
validConnections.Add(connection);
|
||||
}
|
||||
else if (arrivalTime <= latestAcceptableTime)
|
||||
{
|
||||
lateValidConnections.Add(connection);
|
||||
}
|
||||
validConnections.Add(connection);
|
||||
}
|
||||
else if (connection.ArrivalTime <= latestAcceptableTime)
|
||||
{
|
||||
lateValidConnections.Add(connection);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,18 +48,17 @@ public static class TransportConnectionCalculator
|
|||
if (validConnections.Count > 0)
|
||||
{
|
||||
var sortedValid = validConnections
|
||||
.OrderBy(c => DateTime.Parse(c.ArrivalTime ?? "00:00"))
|
||||
.OrderBy(c => c.ArrivalTime)
|
||||
.ToList();
|
||||
|
||||
var bestValidConnection = sortedValid.Last();
|
||||
var bestValidArrivalTime = DateTime.Parse(bestValidConnection.ArrivalTime!);
|
||||
|
||||
// Check if the best valid connection arrives too early (more than maxEarlyArrivalMinutes before shift)
|
||||
if (bestValidArrivalTime < earliestAcceptableTime && lateValidConnections.Count > 0)
|
||||
if (bestValidConnection.ArrivalTime < earliestAcceptableTime && lateValidConnections.Count > 0)
|
||||
{
|
||||
// Return the earliest connection that arrives after latest arrival time but within acceptable range
|
||||
var sortedLateValid = lateValidConnections
|
||||
.OrderBy(c => DateTime.Parse(c.ArrivalTime ?? "23:59"))
|
||||
.OrderBy(c => c.ArrivalTime)
|
||||
.ToList();
|
||||
|
||||
return sortedLateValid.First();
|
||||
|
|
@ -76,7 +71,7 @@ public static class TransportConnectionCalculator
|
|||
if (lateValidConnections.Count > 0)
|
||||
{
|
||||
var sortedLateValid = lateValidConnections
|
||||
.OrderBy(c => DateTime.Parse(c.ArrivalTime ?? "23:59"))
|
||||
.OrderBy(c => c.ArrivalTime)
|
||||
.ToList();
|
||||
|
||||
return sortedLateValid.First();
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ namespace ShiftScheduler.Services
|
|||
{
|
||||
public class TransportService(ITransportApiService transportService, IConfigurationService configurationService, IMemoryCache cache) : ITransportService
|
||||
{
|
||||
public async Task<TransportConnection?> GetConnectionAsync(DateTime shiftStartTime)
|
||||
public async Task<TransportConnection?> GetConnectionAsync(DateTimeOffset shiftStartTime)
|
||||
{
|
||||
var config = configurationService.GetTransportConfiguration();
|
||||
var searchDate = shiftStartTime.ToString("yyyy-MM-dd");
|
||||
|
|
|
|||
|
|
@ -19,10 +19,8 @@ namespace ShiftScheduler.Shared
|
|||
|
||||
private string FormatTransportSummary(TransportConnection connection)
|
||||
{
|
||||
if (string.IsNullOrEmpty(connection.DepartureTime)) return string.Empty;
|
||||
|
||||
var departure = DateTime.TryParse(connection.DepartureTime, out var dep) ? dep.ToString("HH:mm") : connection.DepartureTime;
|
||||
var arrival = DateTime.TryParse(connection.ArrivalTime, out var arr) ? arr.ToString("HH:mm") : connection.ArrivalTime;
|
||||
var departure = connection.DepartureTime.ToString("HH:mm");
|
||||
var arrival = connection.ArrivalTime.ToString("HH:mm");
|
||||
return $"{departure}→{arrival}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ namespace ShiftScheduler.Shared
|
|||
|
||||
public TransportConnection(string arrivalTime)
|
||||
{
|
||||
ArrivalTime = arrivalTime;
|
||||
ArrivalTime = DateTimeOffset.Parse(arrivalTime);
|
||||
}
|
||||
|
||||
public string DepartureTime { get; set; } = string.Empty;
|
||||
public string ArrivalTime { get; set; } = string.Empty;
|
||||
public string Duration { get; set; } = string.Empty;
|
||||
public DateTimeOffset DepartureTime { get; set; }
|
||||
public DateTimeOffset ArrivalTime { get; set; }
|
||||
public TimeSpan Duration { get; set; }
|
||||
public string? Platform { get; set; } = string.Empty;
|
||||
|
||||
public override string ToString()
|
||||
|
|
|
|||
|
|
@ -58,9 +58,9 @@ public class TransportApiServiceTests
|
|||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.DepartureTime.ShouldBe("2023-12-15T06:45:00");
|
||||
result.ArrivalTime.ShouldBe("2023-12-15T07:30:00");
|
||||
result.Duration.ShouldBe("00:45:00");
|
||||
result.DepartureTime.ShouldBe(T("2023-12-15T06:45:00"));
|
||||
result.ArrivalTime.ShouldBe(T("2023-12-15T07:30:00"));
|
||||
result.Duration.ShouldBe(TimeSpan.Parse("00:45:00"));
|
||||
result.Platform.ShouldBe("5");
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ public class TransportApiServiceTests
|
|||
// Latest acceptable arrival: 8:00 - 30 min = 7:30
|
||||
// Valid connections: 07:15 and 07:25 (both arrive before 7:30)
|
||||
// Algorithm should return the latest valid: 07:25
|
||||
result.ArrivalTime.ShouldBe("2023-12-15T07:25:00");
|
||||
result.ArrivalTime.ShouldBe(T("2023-12-15T07:25:00"));
|
||||
}
|
||||
|
||||
private void SetupHttpMockResponse(HttpStatusCode statusCode, string content)
|
||||
|
|
@ -151,7 +151,7 @@ public class TransportApiServiceTests
|
|||
Arrival = "2023-12-15T07:30:00",
|
||||
Platform = "3"
|
||||
},
|
||||
Duration = "00:45:00",
|
||||
Duration = "00d00:45:00",
|
||||
Sections = new List<TransportApiSection>
|
||||
{
|
||||
new TransportApiSection
|
||||
|
|
@ -202,7 +202,7 @@ public class TransportApiServiceTests
|
|||
Arrival = "2023-12-15T07:15:00",
|
||||
Platform = "3"
|
||||
},
|
||||
Duration = "00:45:00"
|
||||
Duration = "00d00:45:00"
|
||||
},
|
||||
// Later valid connection - should be selected as best
|
||||
new TransportApiConnection
|
||||
|
|
@ -219,7 +219,7 @@ public class TransportApiServiceTests
|
|||
Arrival = "2023-12-15T07:25:00",
|
||||
Platform = "2"
|
||||
},
|
||||
Duration = "00:45:00"
|
||||
Duration = "00d00:45:00"
|
||||
},
|
||||
// Too late connection - invalid (arrives after 07:30 which is shift start - 30 min buffer)
|
||||
new TransportApiConnection
|
||||
|
|
@ -236,9 +236,14 @@ public class TransportApiServiceTests
|
|||
Arrival = "2023-12-15T07:35:00",
|
||||
Platform = "1"
|
||||
},
|
||||
Duration = "00:45:00"
|
||||
Duration = "00d00:45:00"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private DateTimeOffset T(string dateTimeString)
|
||||
{
|
||||
return DateTimeOffset.Parse(dateTimeString);
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ public class TransportConnectionCalculatorTests
|
|||
new(arrivalTime: "2023-12-15T07:25:00"), // Should be selected (latest valid)
|
||||
new(arrivalTime: "2023-12-15T08:05:00") // Too late
|
||||
};
|
||||
var shiftStartTime = new DateTime(2023, 12, 15, 8, 0, 0);
|
||||
var shiftStartTime = T("2023-12-15T08:00:00");
|
||||
|
||||
// Act
|
||||
var result = TransportConnectionCalculator.FindBestConnection(
|
||||
|
|
@ -30,7 +30,7 @@ public class TransportConnectionCalculatorTests
|
|||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.ArrivalTime.ShouldBe("2023-12-15T07:25:00");
|
||||
result.ArrivalTime.ShouldBe(T("2023-12-15T07:25:00"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -42,7 +42,7 @@ public class TransportConnectionCalculatorTests
|
|||
new(arrivalTime: "2023-12-15T06:45:00"), // Too early (more than 60 min before 8:00)
|
||||
new(arrivalTime: "2023-12-15T08:05:00") // Within acceptable late range (15 min after 8:00)
|
||||
};
|
||||
var shiftStartTime = new DateTime(2023, 12, 15, 8, 0, 0);
|
||||
var shiftStartTime = T("2023-12-15T08:00:00");
|
||||
|
||||
// Act
|
||||
var result = TransportConnectionCalculator.FindBestConnection(
|
||||
|
|
@ -52,7 +52,7 @@ public class TransportConnectionCalculatorTests
|
|||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.ArrivalTime.ShouldBe("2023-12-15T08:05:00");
|
||||
result.ArrivalTime.ShouldBe(T("2023-12-15T08:05:00"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -65,7 +65,7 @@ public class TransportConnectionCalculatorTests
|
|||
new(arrivalTime: "2023-12-15T08:05:00"), // Should be selected (earliest late)
|
||||
new(arrivalTime: "2023-12-15T08:12:00")
|
||||
};
|
||||
var shiftStartTime = new DateTime(2023, 12, 15, 8, 0, 0);
|
||||
var shiftStartTime = T("2023-12-15T08:00:00");
|
||||
|
||||
// Act
|
||||
var result = TransportConnectionCalculator.FindBestConnection(
|
||||
|
|
@ -75,7 +75,7 @@ public class TransportConnectionCalculatorTests
|
|||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.ArrivalTime.ShouldBe("2023-12-15T08:05:00");
|
||||
result.ArrivalTime.ShouldBe(T("2023-12-15T08:05:00"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -88,7 +88,7 @@ public class TransportConnectionCalculatorTests
|
|||
new(arrivalTime: "2023-12-15T08:25:00"),
|
||||
new(arrivalTime: "2023-12-15T08:30:00")
|
||||
};
|
||||
var shiftStartTime = new DateTime(2023, 12, 15, 8, 0, 0);
|
||||
var shiftStartTime = T("2023-12-15T08:00:00");
|
||||
|
||||
// Act
|
||||
var result = TransportConnectionCalculator.FindBestConnection(
|
||||
|
|
@ -109,7 +109,7 @@ public class TransportConnectionCalculatorTests
|
|||
new(arrivalTime: "2023-12-15T07:20:00"), // 40 min before shift - acceptable
|
||||
new(arrivalTime: "2023-12-15T08:05:00") // 5 min after shift
|
||||
};
|
||||
var shiftStartTime = new DateTime(2023, 12, 15, 8, 0, 0);
|
||||
var shiftStartTime = T("2023-12-15T08:00:00");
|
||||
|
||||
// Act
|
||||
var result = TransportConnectionCalculator.FindBestConnection(
|
||||
|
|
@ -119,14 +119,14 @@ public class TransportConnectionCalculatorTests
|
|||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.ArrivalTime.ShouldBe("2023-12-15T07:20:00");
|
||||
result.ArrivalTime.ShouldBe(T("2023-12-15T07:20:00"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindBestConnectionEnhanced_WithEmptyConnections_ShouldReturnNull()
|
||||
{
|
||||
// Arrange
|
||||
var shiftStartTime = new DateTime(2023, 12, 15, 8, 0, 0);
|
||||
var shiftStartTime = T("2023-12-15T08:00:00");
|
||||
|
||||
// Act
|
||||
var result = TransportConnectionCalculator.FindBestConnection(
|
||||
|
|
@ -149,7 +149,7 @@ public class TransportConnectionCalculatorTests
|
|||
new(arrivalTime: "2023-12-15T08:05:00"), // Late but acceptable
|
||||
new(arrivalTime: "2023-12-15T08:20:00") // Too late
|
||||
};
|
||||
var shiftStartTime = new DateTime(2023, 12, 15, 8, 0, 0);
|
||||
var shiftStartTime = T("2023-12-15T08:00:00");
|
||||
|
||||
// Act - MaxEarlyArrivalMinutes = 30, so earliest acceptable is 7:30
|
||||
var result = TransportConnectionCalculator.FindBestConnection(
|
||||
|
|
@ -159,6 +159,11 @@ public class TransportConnectionCalculatorTests
|
|||
|
||||
// Assert - 7:25 arrives at 7:25, which is 35 min before shift (more than 30 min), so should prefer late connection
|
||||
result.ShouldNotBeNull();
|
||||
result.ArrivalTime.ShouldBe("2023-12-15T08:05:00");
|
||||
result.ArrivalTime.ShouldBe(T("2023-12-15T08:05:00"));
|
||||
}
|
||||
|
||||
private DateTimeOffset T(string dateTimeString)
|
||||
{
|
||||
return DateTimeOffset.Parse(dateTimeString);
|
||||
}
|
||||
}
|
||||
|
|
@ -46,9 +46,9 @@ public class TransportServiceTests
|
|||
var shiftStartTime = new DateTime(2023, 12, 15, 8, 0, 0);
|
||||
var connection = new TransportConnection
|
||||
{
|
||||
DepartureTime = "2023-12-15T06:45:00",
|
||||
ArrivalTime = "2023-12-15T07:30:00",
|
||||
Duration = "00:45:00",
|
||||
DepartureTime = T("2023-12-15T06:45:00"),
|
||||
ArrivalTime = T("2023-12-15T07:30:00"),
|
||||
Duration = TimeSpan.Parse("00:45:00"),
|
||||
Platform = "5"
|
||||
};
|
||||
|
||||
|
|
@ -100,11 +100,11 @@ public class TransportServiceTests
|
|||
public async Task GetConnectionAsync_WithDifferentDates_ShouldCreateSeparateCacheEntries()
|
||||
{
|
||||
// Arrange
|
||||
var shiftStartTime1 = new DateTime(2023, 12, 15, 8, 0, 0);
|
||||
var shiftStartTime2 = new DateTime(2023, 12, 16, 8, 0, 0);
|
||||
var shiftStartTime1 = T("2023-12-15T08:00:00");
|
||||
var shiftStartTime2 = T("2023-12-16T08:00:00");
|
||||
|
||||
var connection1 = new TransportConnection { ArrivalTime = "2023-12-15T07:30:00" };
|
||||
var connection2 = new TransportConnection { ArrivalTime = "2023-12-16T07:30:00" };
|
||||
var connection1 = new TransportConnection { ArrivalTime = T("2023-12-15T07:30:00") };
|
||||
var connection2 = new TransportConnection { ArrivalTime = T("2023-12-16T07:30:00") };
|
||||
|
||||
_transportServiceMock
|
||||
.Setup(x => x.GetConnectionAsync(shiftStartTime1))
|
||||
|
|
@ -121,10 +121,15 @@ public class TransportServiceTests
|
|||
// Assert
|
||||
result1.ShouldNotBeNull();
|
||||
result2.ShouldNotBeNull();
|
||||
result1.ArrivalTime.ShouldBe("2023-12-15T07:30:00");
|
||||
result2.ArrivalTime.ShouldBe("2023-12-16T07:30:00");
|
||||
result1.ArrivalTime.ShouldBe(T("2023-12-15T07:30:00"));
|
||||
result2.ArrivalTime.ShouldBe(T("2023-12-16T07:30:00"));
|
||||
|
||||
// Verify that transport service was called twice (different cache keys)
|
||||
_transportServiceMock.Verify(x => x.GetConnectionAsync(It.IsAny<DateTime>()), Times.Exactly(2));
|
||||
_transportServiceMock.Verify(x => x.GetConnectionAsync(It.IsAny<DateTimeOffset>()), Times.Exactly(2));
|
||||
}
|
||||
|
||||
private DateTimeOffset T(string dateTimeString)
|
||||
{
|
||||
return DateTimeOffset.Parse(dateTimeString);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue