commit 29be035ec7271405e9b2762c4620d11ccc019925
parent 0c7c77acb70c2414977190c00b3d0776666a1363
Author: Michael Camilleri <[email protected]>
Date: Thu, 14 May 2026 08:24:07 +0900
Display log events in-app using local time
Diffstat:
1 file changed, 35 insertions(+), 20 deletions(-)
diff --git a/Crossmate/Views/DiagnosticsView.swift b/Crossmate/Views/DiagnosticsView.swift
@@ -1,13 +1,20 @@
import CloudKit
import SwiftUI
-struct DiagnosticsView: View {
- @Environment(\.syncEngine) private var syncEngine
- @Environment(SyncMonitor.self) private var syncMonitor
+private enum TimestampTimeZone {
+ case local
+ case utc
+}
- @State private var isSyncing = false
+private enum TimestampFormatter {
+ private static let localFormatter: DateFormatter = {
+ let formatter = DateFormatter()
+ formatter.dateStyle = .none
+ formatter.timeStyle = .medium
+ return formatter
+ }()
- private static let timestampFormatter: DateFormatter = {
+ private static let utcFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .medium
@@ -15,6 +22,22 @@ struct DiagnosticsView: View {
return formatter
}()
+ static func string(from date: Date, in timeZone: TimestampTimeZone) -> String {
+ switch timeZone {
+ case .local:
+ return localFormatter.string(from: date)
+ case .utc:
+ return utcFormatter.string(from: date)
+ }
+ }
+}
+
+struct DiagnosticsView: View {
+ @Environment(\.syncEngine) private var syncEngine
+ @Environment(SyncMonitor.self) private var syncMonitor
+
+ @State private var isSyncing = false
+
var body: some View {
List {
Section("Status") {
@@ -23,7 +46,7 @@ struct DiagnosticsView: View {
row("Pending Changes", syncMonitor.snapshot.map { String($0.pendingChangesCount) } ?? "Unknown")
row(
"Last Success",
- syncMonitor.lastSuccessAt.map(Self.timestampFormatter.string(from:)) ?? "None"
+ syncMonitor.lastSuccessAt.map { TimestampFormatter.string(from: $0, in: .local) } ?? "None"
)
row("Last Error Phase", syncMonitor.lastErrorPhase ?? "None")
row("Last Error Domain", syncMonitor.lastErrorDomain ?? "None")
@@ -67,7 +90,7 @@ struct DiagnosticsView: View {
ForEach(syncMonitor.entries.reversed()) { entry in
VStack(alignment: .leading, spacing: 4) {
Text(
- "\(Self.timestampFormatter.string(from: entry.timestamp)) [\(entry.level.uppercased())]"
+ "\(TimestampFormatter.string(from: entry.timestamp, in: .local)) [\(entry.level.uppercased())]"
)
.font(.caption.monospaced())
.foregroundStyle(.secondary)
@@ -170,7 +193,7 @@ struct DiagnosticsView: View {
lines.append("Engine Running: \(boolText(syncMonitor.snapshot?.engineRunning))")
lines.append("Pending Changes: \(syncMonitor.snapshot.map { String($0.pendingChangesCount) } ?? "Unknown")")
lines.append(
- "Last Success: \(syncMonitor.lastSuccessAt.map(Self.timestampFormatter.string(from:)) ?? "None")"
+ "Last Success: \(syncMonitor.lastSuccessAt.map { TimestampFormatter.string(from: $0, in: .utc) } ?? "None")"
)
lines.append("Last Error Phase: \(syncMonitor.lastErrorPhase ?? "None")")
lines.append("Last Error Domain: \(syncMonitor.lastErrorDomain ?? "None")")
@@ -181,7 +204,7 @@ struct DiagnosticsView: View {
lines.append("Recent Events (UTC):")
for entry in syncMonitor.entries {
lines.append(
- "\(Self.timestampFormatter.string(from: entry.timestamp)) [\(entry.level.uppercased())] \(entry.message)"
+ "\(TimestampFormatter.string(from: entry.timestamp, in: .utc)) [\(entry.level.uppercased())] \(entry.message)"
)
}
return lines.joined(separator: "\n")
@@ -191,14 +214,6 @@ struct DiagnosticsView: View {
struct ShareDiagnosticsView: View {
@Environment(SyncMonitor.self) private var syncMonitor
- private static let timestampFormatter: DateFormatter = {
- let formatter = DateFormatter()
- formatter.dateStyle = .none
- formatter.timeStyle = .medium
- formatter.timeZone = TimeZone(secondsFromGMT: 0)
- return formatter
- }()
-
private var shareEntries: [SyncDiagnosticEntry] {
syncMonitor.entries.filter {
$0.message.localizedCaseInsensitiveContains("share")
@@ -222,7 +237,7 @@ struct ShareDiagnosticsView: View {
ForEach(shareEntries.reversed()) { entry in
VStack(alignment: .leading, spacing: 4) {
Text(
- "\(Self.timestampFormatter.string(from: entry.timestamp)) [\(entry.level.uppercased())]"
+ "\(TimestampFormatter.string(from: entry.timestamp, in: .local)) [\(entry.level.uppercased())]"
)
.font(.caption.monospaced())
.foregroundStyle(.secondary)
@@ -267,10 +282,10 @@ struct ShareDiagnosticsView: View {
lines.append("Last Share Error Code: \(syncMonitor.lastErrorCode.map(String.init) ?? "None")")
lines.append("Last Share Error Description: \(syncMonitor.lastErrorDescription ?? "None")")
lines.append("")
- lines.append("Share Events:")
+ lines.append("Share Events (UTC):")
for entry in shareEntries {
lines.append(
- "\(Self.timestampFormatter.string(from: entry.timestamp)) [\(entry.level.uppercased())] \(entry.message)"
+ "\(TimestampFormatter.string(from: entry.timestamp, in: .utc)) [\(entry.level.uppercased())] \(entry.message)"
)
}
return lines.joined(separator: "\n")