0becf7829e
- Package.swift: AI (S2-owned) + DSKit (models/client/fixtures) + DSKitTests, tools 6.2, .swiftLanguageMode(.v6), .macOS(.v26) - JSONValue (Sendable AnyCodable), DSDate (value-type ISO8601FormatStyle cascade, date-only UTC), explicit-CodingKeys decoder - Models: Auth/Document(+Detail flat-compose, MD-first)/Catalog/Search+Ask/Memo/Digest; non-optional limited to id/file_type/created+updated_at/total - DSClient protocol + FixtureDSClient (Bundle.module, zero backend) + DSError + DSConfig + DownloadURL (?token= query) - Tests: 14-fixture contract acceptance (value asserts) + JSONValue number trap + Ask round-trip + AI router fallback/explicit-unavailable swift build + swift test green (19 tests). Sources/AI untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
34 lines
1.7 KiB
Swift
34 lines
1.7 KiB
Swift
import Foundation
|
|
|
|
/// Date parsing for the contract's mixed shapes. Two ISO-8601 datetime shapes (with / without
|
|
/// fractional seconds) coexist with date-only strings in the SAME response (digest), so a single
|
|
/// JSONDecoder.dateDecodingStrategy is impossible. Dates are decoded as `String?` raw and parsed here.
|
|
///
|
|
/// Concurrency (B-1 review): uses value-type `Date.ISO8601FormatStyle` (Sendable) as static lets —
|
|
/// no shared reference formatter, so no `nonisolated(unsafe)`.
|
|
///
|
|
/// Date-only (B-1 r2 review): `digest_date` should be DISPLAYED from its raw string (no Date conversion)
|
|
/// to avoid KST off-by-one; `parse` here fixes date-only to UTC and is for sort/window math only.
|
|
public enum DSDate {
|
|
private static let isoFractional = Date.ISO8601FormatStyle(includingFractionalSeconds: true)
|
|
private static let isoPlain = Date.ISO8601FormatStyle(includingFractionalSeconds: false)
|
|
|
|
public static func parse(_ s: String?) -> Date? {
|
|
guard let s, !s.isEmpty else { return nil }
|
|
if let d = try? isoFractional.parse(s) { return d }
|
|
if let d = try? isoPlain.parse(s) { return d }
|
|
return parseDateOnly(s)
|
|
}
|
|
|
|
/// Date-only "YYYY-MM-DD" fixed to UTC (avoids timezone off-by-one). Value-type Calendar, Sendable-safe.
|
|
private static func parseDateOnly(_ s: String) -> Date? {
|
|
let p = s.split(separator: "-")
|
|
guard p.count == 3, let y = Int(p[0]), let m = Int(p[1]), let d = Int(p[2]) else { return nil }
|
|
var comps = DateComponents()
|
|
comps.year = y; comps.month = m; comps.day = d
|
|
var cal = Calendar(identifier: .gregorian)
|
|
cal.timeZone = TimeZone(identifier: "UTC")!
|
|
return cal.date(from: comps)
|
|
}
|
|
}
|