field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

TeraCivilDateInput.swift (1970B)


      1 import Foundation
      2 
      3 /// Partial native component entry stays separate from a validated civil date.
      4 struct TeraCivilDateInput {
      5   let raw: String?
      6 
      7   func component(_ index: Int) -> String {
      8     let parts = raw?.split(separator: "-", omittingEmptySubsequences: false) ?? []
      9     return parts.indices.contains(index) ? String(parts[index]) : ""
     10   }
     11 
     12   var value: TeraCivilDate? {
     13     guard let raw, raw.utf8.count <= 64,
     14           raw.split(separator: "-", omittingEmptySubsequences: false).count == 3,
     15           let year = number(component(0), maximumDigits: 4),
     16           let month = number(component(1), maximumDigits: 2),
     17           let day = number(component(2), maximumDigits: 2),
     18           let typedYear = UInt16(exactly: year), let typedMonth = UInt8(exactly: month),
     19           let typedDay = UInt8(exactly: day)
     20     else { return nil }
     21     return try? TeraCivilDate(year: typedYear, month: typedMonth, day: typedDay)
     22   }
     23 
     24   func replacing(_ index: Int, with input: String) -> String? {
     25     guard (0 ... 2).contains(index) else { return raw }
     26     var parts = (0 ... 2).map(component)
     27     parts[index] = String(input.prefix(index == 0 ? 4 : 2))
     28     guard parts.contains(where: { !$0.isEmpty }) else { return nil }
     29     return parts.joined(separator: "-")
     30   }
     31 
     32   var canonicalOrRaw: String? {
     33     value?.canonical ?? raw
     34   }
     35 
     36   private func number(_ text: String, maximumDigits: Int) -> Int? {
     37     guard !text.isEmpty, text.count <= maximumDigits else { return nil }
     38     var value = 0
     39     for character in text {
     40       guard let digit = character.wholeNumberValue, (0 ... 9).contains(digit) else { return nil }
     41       value = value * 10 + digit
     42     }
     43     return value
     44   }
     45 }
     46 
     47 extension TeraCivilDate {
     48   var nextDay: Self? {
     49     if let next = try? Self(year: year, month: month, day: day + 1) {
     50       return next
     51     }
     52     if let next = try? Self(year: year, month: month + 1, day: 1) {
     53       return next
     54     }
     55     return try? Self(year: year + 1, month: 1, day: 1)
     56   }
     57 }