Centralize lateness logic

- I finally put the app in the Travel category. xD
- I also moved the train lateness and arrival calculations into AmtrakDateFormatting from ContentView. I also renamed the timezone formatting parameters for clarity.
This commit is contained in:
Tony Bark 2026-08-24 21:05:11 -04:00
parent 79176585b4
commit e0077ec13b
3 changed files with 38 additions and 34 deletions

View file

@ -1,3 +1,4 @@
# https://swiftbundler.dev/documentation/swift-bundler/configuration
format_version = 2 format_version = 2
[apps.AmtrakStatus] [apps.AmtrakStatus]
@ -5,6 +6,7 @@ identifier = 'com.tonybark.AmtrakStatus'
product = 'AmtrakStatus' product = 'AmtrakStatus'
version = '0.2.103' version = '0.2.103'
icon = "./assets/icon.png" icon = "./assets/icon.png"
category = "public.app-category.travel"
[[apps.AmtrakStatus.overlays]] [[apps.AmtrakStatus.overlays]]

View file

@ -26,21 +26,21 @@ struct AmtrakDateFormatting {
return isoFormatter.date(from: isoString) return isoFormatter.date(from: isoString)
} }
func time(from isoString: String?, timeZone: TimeZone?, relativeTo otherZone: TimeZone? = nil) -> String? { func time(from isoString: String?, in zone: TimeZone?, comparedTo originZone: 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 timeFormatter.timeZone = zone
let formatted = timeFormatter.string(from: date) let formatted = timeFormatter.string(from: date)
guard guard
let timeZone, let zone,
let otherZone, let originZone,
timeZone.identifier != otherZone.identifier, zone.identifier != originZone.identifier,
let abbreviation = timeZone.abbreviation(for: date) let abbreviation = zone.abbreviation(for: date)
else { else {
return formatted return formatted
} }
@ -48,6 +48,29 @@ struct AmtrakDateFormatting {
return "\(formatted) \(abbreviation)" return "\(formatted) \(abbreviation)"
} }
func isOnTime(actual: String?, scheduled: String?) -> TrainLateness {
guard
let actualDate = parsedDate(from: actual),
let scheduledDate = parsedDate(from: scheduled)
else {
return .unknown
}
let differenceInMinutes = Int(actualDate.timeIntervalSince(scheduledDate) / 60)
switch differenceInMinutes {
case -1...1:
return .onTime
case ..<0:
return .early(minutes: -differenceInMinutes)
default:
return .late(minutes: differenceInMinutes)
}
}
func localTime(timeZone: TimeZone?) -> String { func localTime(timeZone: TimeZone?) -> String {
let now = Date() let now = Date()
timeFormatter.timeZone = timeZone timeFormatter.timeZone = timeZone

View file

@ -169,27 +169,6 @@ struct ContentView: View {
} }
} }
func isOnTime(arr: String?, schArr: String?) -> TrainLateness {
guard
let actualDate = amtrakDate.parsedDate(from: arr),
let scheduledDate = amtrakDate.parsedDate(from: schArr)
else {
return .unknown
}
let differenceInMinutes = Int(actualDate.timeIntervalSince(scheduledDate) / 60)
switch differenceInMinutes {
case -1...1:
return .onTime
case ..<0:
return .early(minutes: -differenceInMinutes)
default:
return .late(minutes: differenceInMinutes)
}
}
func trainStatus( func trainStatus(
for train: Train, for train: Train,
atStationCode code: String, atStationCode code: String,
@ -210,12 +189,12 @@ struct ContentView: View {
let time = amtrakDate.time( let time = amtrakDate.time(
from: leg?.arr, from: leg?.arr,
timeZone: timeZone, in: timeZone,
relativeTo: originZone comparedTo: originZone
) ?? amtrakDate.time( ) ?? amtrakDate.time(
from: leg?.schArr, from: leg?.schArr,
timeZone: timeZone, in: timeZone,
relativeTo: originZone comparedTo: originZone
) )
currentStation = stationName currentStation = stationName
@ -229,9 +208,9 @@ struct ContentView: View {
train: train.routeName, train: train.routeName,
to: train.destName, to: train.destName,
from: train.origName, from: train.origName,
status: isOnTime( status: amtrakDate.isOnTime(
arr: leg?.arr, actual: leg?.arr,
schArr: leg?.schArr scheduled: leg?.schArr
), ),
track: track.isEmpty ? "" : track track: track.isEmpty ? "" : track
) )