TalerDater.swift (5629B)
1 /* 2 * This file is part of GNU Taler, ©2022-25 Taler Systems S.A. 3 * See LICENSE.md 4 */ 5 /** 6 * @author Marc Stibane 7 */ 8 import Foundation 9 import taler_swift 10 11 public class TalerDater: DateFormatter { 12 public static let shared = TalerDater() 13 14 static func relativeDate(_ from: TimeInterval, _ minimalistic: Bool) -> String? { 15 if from > 0 { // transactions should always be in the past 16 let minute = from / 60.0 // from is in seconds 17 if minute < 1 { return minimalistic ? String(localized: "Now") 18 : String(localized: "Right now") } 19 if minute < 2 { return minimalistic ? String(localized: "1 min ago") 20 : String(localized: "1 minute ago") } 21 if minute < 55 { return minimalistic ? String(localized: "\(Int(minute)) min ago") 22 : String(localized: "\(Int(minute)) minutes ago") } 23 if minute < 60 { return minimalistic ? String(localized: "~ 1 hour ago") 24 : String(localized: "About an hour ago") } 25 if minute < 75 { return String(localized: "1 hour ago") } 26 if minute < 105 { return minimalistic ? String(localized: "~ 1½ hours ago") 27 : String(localized: "About 1½ hours ago") } 28 if minute < 135 { return minimalistic ? String(localized: "~ 2 hours ago") 29 : String(localized: "About 2 hours ago") } 30 let hour = minute / 60.0 31 let calendar = Calendar.current 32 let now = Date.now 33 let currHour = Double(calendar.component(.hour, from: now)) 34 let currMin = Double(calendar.component(.minute, from: now)) 35 let currTime = currHour + currMin/60 36 if hour < currTime { return String(localized: "\(Int(hour)) hours ago") } 37 if hour < currTime + 24 { return String(localized: "Yesterday") } 38 let day = (hour - currTime) / 24.0 39 if day < 7 { return String(localized: "\(Int(day+1)) days ago") } 40 if day < 14 { return String(localized: "More than a week ago") } 41 // will fall thru... 42 return nil 43 } else { // Yikes❗️ transaction date is in the future 44 return nil 45 } 46 } 47 48 /// produces a random date string between `now` and m+h+d (edit values after 60x) 49 public static func randomDateStr(_ minimalistic: Bool) -> String { 50 let m = 60*15 51 let h = 60*60*9 52 let d = 24*60*60*22 53 let t = m+h+d 54 let randomTime = Int.random(in:1...t) 55 if let randomDateStr = relativeDate(Double(randomTime), minimalistic) { 56 return randomDateStr 57 } else { // t is too large for a relative date 58 // return absolute date with random locale 59 let localeStr = (randomTime&1 == 1) ? "de_DE" : "en_US" 60 shared.locale = NSLocale(localeIdentifier: localeStr) as Locale 61 let randomDate = Date(timeIntervalSinceNow: Double(-t)) 62 return shared.string(from: randomDate) 63 } 64 } 65 66 // public static func date(from: Timestamp) -> Date { 67 // let milliseconds = try from.milliseconds() 68 // let date = Date(milliseconds: milliseconds) 69 // return date 70 // } 71 72 /// converts a timestamp into a formatted date string 73 public static func dateString(_ from: Timestamp, _ minimalistic: Bool, relative: Bool = false) -> (String, Date?) { 74 do { 75 let milliseconds = try from.milliseconds() 76 let date = Date(milliseconds: milliseconds) 77 78 if relative { 79 let now = Date.now 80 let timeInterval = now.timeIntervalSince(date) 81 if let relativeDate = relativeDate(timeInterval, minimalistic) { 82 return (relativeDate, date) 83 } 84 } 85 return (shared.string(from: date), date) 86 } catch { // Never 87 let never = String(localized: "No date", comment: "Timestamp missing or invalid") 88 return (never, nil) 89 } 90 } 91 92 public static func accessibilityDate(_ date: Date?) -> String? { 93 if let date { 94 let formatted = date.formatted(date: .long, time: .shortened) 95 // print(formatted) 96 return formatted 97 } 98 return nil 99 } 100 101 public static func dateString() -> String { 102 shared.string(from: Date()) 103 } 104 105 private override init() { 106 super.init() 107 self.setLocalizedDateFormatFromTemplate("EEEdMMM") // abbreviated day of week 108 self.dateStyle = .medium 109 self.timeStyle = .short 110 // self.timeZone = TimeZone(abbreviation: "UTC") // UTC prints GMT 111 // self.dateFormat = "z yyyy-MM-dd HH:mm" // "GMT 2022-11-09 18:00" 112 } 113 114 required init?(coder aDecoder: NSCoder) { 115 fatalError("init(coder:) has not been implemented") 116 } 117 } 118 119 extension Date { 120 static func - (lhs: Date, rhs: Date) -> TimeInterval { 121 lhs.timeIntervalSinceReferenceDate - rhs.timeIntervalSinceReferenceDate 122 } 123 124 var iso:String { 125 self.formatted(.iso8601 126 // .day().month().year().dateSeparator(.dash) 127 .timeSeparator(.omitted)) 128 } 129 } 130 extension TimeInterval { 131 var seconds: Int { 132 Int(self.rounded()) 133 } 134 135 var milliseconds: Int { 136 Int(self * 1000) 137 } 138 }