- Disable cell animations for huge changes - Updating a cell keeps the old position whenever possible - Async `didChangeDateFilter` - Fixes bug where saving a recording would persist entries again - Small changes to `TimeFormat`, `AlertDeleteLogs` and `binTreeIndex()`
83 lines
2.5 KiB
Swift
83 lines
2.5 KiB
Swift
import Foundation
|
|
|
|
private let dateTimeFormat = DateFormatter(withFormat: "yyyy-MM-dd HH:mm:ss")
|
|
|
|
extension DateFormatter {
|
|
convenience init(withFormat: String) {
|
|
self.init()
|
|
dateFormat = withFormat
|
|
}
|
|
}
|
|
|
|
extension Timestamp {
|
|
/// Time string with format `yyyy-MM-dd HH:mm:ss`
|
|
func asDateTime() -> String {
|
|
dateTimeFormat.string(from: Date.init(timeIntervalSince1970: Double(self)))
|
|
}
|
|
|
|
/// Convert `Timestamp` to `Date`
|
|
func toDate() -> Date {
|
|
Date(timeIntervalSince1970: Double(self))
|
|
}
|
|
|
|
/// Current time as `Timestamp` (second accuracy)
|
|
static func now() -> Timestamp {
|
|
Timestamp(Date().timeIntervalSince1970)
|
|
}
|
|
|
|
/// Create `Timestamp` with `now() - minutes * 60`
|
|
static func past(minutes: Int) -> Timestamp {
|
|
now() - Timestamp(minutes * 60)
|
|
}
|
|
}
|
|
|
|
extension Timer {
|
|
/// Recurring timer maintains a strong reference to `target`.
|
|
@discardableResult static func repeating(_ interval: TimeInterval, call selector: Selector, on target: Any, userInfo: Any? = nil) -> Timer {
|
|
Timer.scheduledTimer(timeInterval: interval, target: target, selector: selector,
|
|
userInfo: userInfo, repeats: true)
|
|
}
|
|
}
|
|
|
|
// MARK: - TimeFormat
|
|
|
|
struct TimeFormat {
|
|
private var formatter: DateComponentsFormatter
|
|
|
|
/// Init new formatter with exactly 1 unit count. E.g., `61 min -> 1 hr`
|
|
/// - Parameter allowed: Default: `[.day, .hour, .minute, .second]`
|
|
init(_ style: DateComponentsFormatter.UnitsStyle, allowed: NSCalendar.Unit = [.day, .hour, .minute, .second]) {
|
|
formatter = DateComponentsFormatter()
|
|
formatter.maximumUnitCount = 1
|
|
formatter.allowedUnits = allowed
|
|
formatter.unitsStyle = style
|
|
}
|
|
|
|
/// Formatted duration string, e.g., `20 min` or `7 days`
|
|
func from(days: Int = 0, hours: Int = 0, minutes: Int = 0, seconds: Int = 0) -> String? {
|
|
formatter.string(from: DateComponents(day: days, hour: hours, minute: minutes, second: seconds))
|
|
}
|
|
|
|
// MARK: static
|
|
|
|
/// Time string with format `HH:mm`
|
|
static func from(_ duration: Timestamp) -> String {
|
|
String(format: "%02d:%02d", duration / 60, duration % 60)
|
|
}
|
|
|
|
/// Duration string with format `HH:mm` or `HH:mm.sss`
|
|
static func from(_ duration: TimeInterval, millis: Bool = false) -> String {
|
|
let t = Int(duration)
|
|
if millis {
|
|
let mil = Int(duration * 1000) % 1000
|
|
return String(format: "%02d:%02d.%03d", t / 60, t % 60, mil)
|
|
}
|
|
return String(format: "%02d:%02d", t / 60, t % 60)
|
|
}
|
|
|
|
/// Duration string with format `HH:mm` or `HH:mm.sss` since reference date
|
|
static func since(_ date: Date, millis: Bool = false) -> String {
|
|
from(Date().timeIntervalSince(date), millis: millis)
|
|
}
|
|
}
|