mirror of
https://github.com/tonytins/amtkstat.git
synced 2026-10-24 05:32:54 -04:00
Add timezone-aware station lookup and formatting
These changes ensure displayed times respect the station's timezone. Something I mistakenly overlooked when creating this. Luckily, my screenshot with Ashland isn't affected by this change.
This commit is contained in:
parent
fc45303761
commit
dd50d7cd4a
3 changed files with 42 additions and 19 deletions
|
|
@ -7,6 +7,12 @@ enum AmtrakError: Error {
|
||||||
case stationNotFound(String)
|
case stationNotFound(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct TrainStationLookUp: Sendable {
|
||||||
|
var trainNumbers: [String]
|
||||||
|
var timeZone: TimeZone?
|
||||||
|
}
|
||||||
|
|
||||||
struct AmtrakClient {
|
struct AmtrakClient {
|
||||||
var decoder = JSONDecoder()
|
var decoder = JSONDecoder()
|
||||||
|
|
||||||
|
|
@ -41,15 +47,21 @@ struct AmtrakClient {
|
||||||
.first
|
.first
|
||||||
.map(String.init) ?? trainId
|
.map(String.init) ?? trainId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func fetchStationTrainNumbers(stationCode code: String) async throws -> [String] {
|
func trainStationLookup(stationCode code: String) async throws -> TrainStationLookUp {
|
||||||
let reponse = try await fetchStation(code: code)
|
let reponse = try await fetchStation(code: code)
|
||||||
|
|
||||||
guard let meta = reponse[code] else {
|
guard let meta = reponse[code] else {
|
||||||
throw AmtrakError.stationNotFound(code)
|
throw AmtrakError.stationNotFound(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array(Set(meta.trains.map(trainNumber(fromTrainId:))))
|
let trainNumbers = Array(Set(meta.trains.map(trainNumber(fromTrainId:))))
|
||||||
|
let timeZone = meta.tz.flatMap(TimeZone.init(identifier:))
|
||||||
|
return TrainStationLookUp(
|
||||||
|
trainNumbers: trainNumbers,
|
||||||
|
timeZone: timeZone
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func get<T: Decodable>(_ urlString: String) async throws -> T {
|
func get<T: Decodable>(_ urlString: String) async throws -> T {
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,12 @@ struct AmtrakDateFormatting {
|
||||||
return isoFormatter.date(from: isoString)
|
return isoFormatter.date(from: isoString)
|
||||||
}
|
}
|
||||||
|
|
||||||
func time(from isoString: String?) -> String? {
|
func time(from isoString: String?, timeZone: TimeZone? = nil) -> String? {
|
||||||
guard let isoString, !isoString.isEmpty, let date = isoFormatter.date(
|
guard let isoString, !isoString.isEmpty, let date = isoFormatter.date(
|
||||||
from: isoString) else {
|
from: isoString) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
timeFormatter.timeZone = timeZone
|
||||||
return timeFormatter.string(from: date)
|
return timeFormatter.string(from: date)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ struct ContentView: View {
|
||||||
Task {
|
Task {
|
||||||
await loadTrainStatus(forStationCode: normalizedStationCode)
|
await loadTrainStatus(forStationCode: normalizedStationCode)
|
||||||
}
|
}
|
||||||
}.disabled(normalizedStationCode.isEmpty || isLoading)
|
}.disabled(normalizedStationCode.isEmpty)
|
||||||
}.padding(10)
|
}.padding(10)
|
||||||
|
|
||||||
// Most popular Virtual Railfan stations
|
// Most popular Virtual Railfan stations
|
||||||
|
|
@ -103,6 +103,12 @@ struct ContentView: View {
|
||||||
return .white
|
return .white
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadStationStatus(forStationCode code: String) async {
|
||||||
|
guard !code.isEmpty else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func loadTrainStatus(forStationCode code: String) async {
|
func loadTrainStatus(forStationCode code: String) async {
|
||||||
guard !code.isEmpty else {
|
guard !code.isEmpty else {
|
||||||
|
|
@ -115,25 +121,32 @@ struct ContentView: View {
|
||||||
defer { isLoading = false }
|
defer { isLoading = false }
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let trainNumbers = try await client.fetchStationTrainNumbers(
|
let lookup = try await client.trainStationLookup(
|
||||||
stationCode: code,
|
stationCode: code,
|
||||||
)
|
)
|
||||||
|
|
||||||
guard !trainNumbers.isEmpty else {
|
guard !lookup.trainNumbers.isEmpty else {
|
||||||
errorMessage = "\(code) has no trains listed right now"
|
errorMessage = "\(code) has no trains listed right now"
|
||||||
rows = []
|
rows = []
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var newRows: [TrainStatusRow] = []
|
var newRows: [TrainStatusRow] = []
|
||||||
for number in trainNumbers {
|
for number in lookup.trainNumbers {
|
||||||
let trains = try await client.fetchAllTrainStatus(num: number)
|
let trains = try await client.fetchAllTrainStatus(num: number)
|
||||||
for train in trains {
|
for train in trains {
|
||||||
let platform = train.stations?.first(
|
let platform = train.stations?.first(
|
||||||
where: { $0.code == code
|
where: { $0.code == code
|
||||||
},
|
},
|
||||||
)?.platform
|
)?.platform
|
||||||
newRows.append(trainStatus(for: train, atStationCode: code))
|
newRows
|
||||||
|
.append(
|
||||||
|
trainStatus(
|
||||||
|
for: train,
|
||||||
|
atStationCode: code,
|
||||||
|
timeZone: lookup.timeZone
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,18 +183,16 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func trainStatus(for train: Train, atStationCode code: String) -> TrainStatusRow {
|
func trainStatus(for train: Train, atStationCode code: String, timeZone: TimeZone?) -> TrainStatusRow {
|
||||||
let leg = train.stations?.first { $0.code == code }
|
let leg = train.stations?.first { $0.code == code }
|
||||||
let amtrakDate = AmtrakDateFormatting()
|
let amtrakDate = AmtrakDateFormatting()
|
||||||
let stationName = leg?.name ?? unknown
|
let stationName = leg?.name ?? unknown
|
||||||
let arrival = amtrakDate.time(from: leg?.arr) ?? amtrakDate.time(
|
let arrival = amtrakDate.time(from: leg?.arr, timeZone: timeZone) ?? amtrakDate.time(
|
||||||
from: leg?.schArr
|
from: leg?.schArr,
|
||||||
)
|
timeZone: timeZone)
|
||||||
let depature = amtrakDate.time(from: leg?.dep) ?? amtrakDate.time(
|
let depature = amtrakDate.time(from: leg?.dep, timeZone: timeZone) ?? amtrakDate.time(
|
||||||
from: leg?.schDep
|
from: leg?.schDep,
|
||||||
)
|
timeZone: timeZone)
|
||||||
|
|
||||||
currentStation = "\(stationName) station"
|
|
||||||
|
|
||||||
return TrainStatusRow(
|
return TrainStatusRow(
|
||||||
trainID: train.trainID,
|
trainID: train.trainID,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue