Skip to content

Instantly share code, notes, and snippets.

@esnssr
Created December 5, 2025 12:15
Show Gist options
  • Select an option

  • Save esnssr/b1a21175add1d179855b9ba47bdb2228 to your computer and use it in GitHub Desktop.

Select an option

Save esnssr/b1a21175add1d179855b9ba47bdb2228 to your computer and use it in GitHub Desktop.
FormatStyle
public extension FormatStyle where Self == TimeFormatStyle<FloatingPointFormatStyle<Double>> {
static var time: TimeFormatStyle<FloatingPointFormatStyle<Double>> {
TimeFormatStyle(baseStyle: .number, countDirection: nil, formatter: .timeFormatter)
}
static func time(formatter: DateComponentsFormatter) -> TimeFormatStyle<FloatingPointFormatStyle<Double>> {
TimeFormatStyle(baseStyle: .number, countDirection: nil, formatter: formatter)
}
static func time(
countDirection: Self.CountDirection,
formatter: DateComponentsFormatter = .timeFormatter.zeroFormattingBehavior(.pad)
) -> TimeFormatStyle<FloatingPointFormatStyle<Double>> {
TimeFormatStyle(baseStyle: .number, countDirection: countDirection, formatter: formatter)
}
}
public struct TimeFormatStyle<BaseStyle: FormatStyle>: FormatStyle, Codable where BaseStyle.FormatInput: BinaryFloatingPoint,
BaseStyle.FormatInput: Codable,
BaseStyle.FormatInput: Hashable,
BaseStyle.FormatOutput == String,
BaseStyle: Codable {
public enum CountDirection: Codable {
case forward
case backward
}
let base: BaseStyle
let countDirection: CountDirection?
let formatter: DateComponentsFormatter
init(baseStyle: BaseStyle, countDirection: CountDirection?, formatter: DateComponentsFormatter) {
self.base = baseStyle
self.countDirection = countDirection
self.formatter = formatter
}
// MARK: - Codable Implementation
private enum CodingKeys: String, CodingKey {
case base
case countDirection
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let base = try container.decode(BaseStyle.self, forKey: .base)
let countDirection = try container.decodeIfPresent(CountDirection.self, forKey: .countDirection)
self.init(
baseStyle: base,
countDirection: countDirection,
formatter: .timeFormatter
)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(base, forKey: .base)
try container.encodeIfPresent(countDirection, forKey: .countDirection)
// ignore encoding formatter
}
public func format(_ value: Double) -> String {
var formattedValue = formatter.string(from: value) ?? fallbackFormat(value)
if let countDirection {
formattedValue = countDirection == .forward ? formattedValue : "-" + formattedValue
}
return formattedValue
}
public func locale(_ locale: Locale) -> TimeFormatStyle {
TimeFormatStyle(baseStyle: base.locale(locale), countDirection: countDirection, formatter: formatter)
}
private func fallbackFormat(_ value: Double) -> String {
let hours = value / 3600
let minutes = value.truncatingRemainder(dividingBy: 3600) / 60
let seconds = value.truncatingRemainder(dividingBy: 60)
return String(format: "%d:%02d:%02d", hours, minutes, seconds)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment