import Foundation struct DeviceTime { let date: Date let secondsSincePowerOn: Int let totalNumberOfMeasurements: Int let secondsUntilNextMeasurement: Int var nextMeasurement: Date { date.adding(seconds: secondsUntilNextMeasurement) } var deviceStartTime: Date { date.adding(seconds: -secondsSincePowerOn) } var estimatedMeasurementInterval: TimeInterval { guard totalNumberOfMeasurements > 0 else { return 60 } return Double(secondsSincePowerOn + secondsUntilNextMeasurement) / Double(totalNumberOfMeasurements) } func measurementStartTime(measurementInterval interval: TimeInterval) -> Date { nextMeasurement.addingTimeInterval(-Double(totalNumberOfMeasurements) * interval) } func measurementOffset(measurementInterval interval: TimeInterval) -> TimeInterval { measurementStartTime(measurementInterval: interval).timeIntervalSince(deviceStartTime) } } extension DeviceTime: Equatable { } extension DeviceTime: Codable { init(from decoder: Decoder) throws { var container = try decoder.unkeyedContainer() let time = try container.decode(Double.self) self.date = .init(timeIntervalSince1970: time) self.secondsSincePowerOn = try container.decode(Int.self) self.totalNumberOfMeasurements = try container.decode(Int.self) self.secondsUntilNextMeasurement = try container.decode(Int.self) } func encode(to encoder: Encoder) throws { var container = encoder.unkeyedContainer() try container.encode(date.timeIntervalSince1970) try container.encode(secondsSincePowerOn) try container.encode(totalNumberOfMeasurements) try container.encode(secondsUntilNextMeasurement) } } extension DeviceTime { static var mock: DeviceTime { .init( date: .now, secondsSincePowerOn: 125, totalNumberOfMeasurements: 3, secondsUntilNextMeasurement: 55) } }