Date.FormatStyle.Symbol.Hour defaultDigits shows leading zero if amPM is set to .omitted

I'm trying to format a date to show the current time with the least amount of digits as possible. Using Date.FormatStyle and adding .hour() is the closest I can get. See the code below ↓

var formatStyle = Date.FormatStyle()
var hourMinute: Date.FormatStyle{
    formatStyle
        .hour()
        .minute()
}
// Output 3:15 PM
var hourMinuteDefaultDigits: Date.FormatStyle{
    formatStyle
        .hour(.defaultDigits(amPM: .omitted))
        .minute()
}
// Output 03:15

My goal is to output 3:15 without the amPm but that doesn't seem possible using Date.FormatStyle.

Based on the documentation this is the function signature

func hour(_ format: Date.FormatStyle.Symbol.Hour = .defaultDigits(amPM: .abbreviated)) -> Date.FormatStyle

// Output 3:15 PM

For some reason if I put .omitted for the amPM then defaultDigits and twoDigits return the same 2 digits for the hour

This is absolutely a bug in Foundation. The documentation and examples both say that the leading "0" shouldn't be included.

Until the bug is fixes, the solutions available would be to either do string manipulation or to use a different format style. Each one has their own drawbacks when it comes to internationalization, so deciding which solution to use is a matter of weighing your needs against the drawbacks.

1. Trim Characters

Simply check for the existence of a leading 0 character, and remove it if needed.

// https://www.hackingwithswift.com/example-code/strings/how-to-remove-a-prefix-from-a-string
extension String {
    func deletingPrefix(_ prefix: String) -> String {
        guard self.hasPrefix(prefix) else { return self }
        return String(self.dropFirst(prefix.count))
    }
}

let testDate = try! Date("2024-01-01T10:36:01Z", strategy: .iso8601)
let dateString = testDate.formatted(.dateTime.hour(.defaultDigits(amPM: .omitted)).minute()) // 03:36

dateString.deletingPrefix("0") // 3:36

This would only work for locales that use Arabic numerals of course. If you're relying on strict internationalization then you would need to be a bit more clever in detecting leading characters.

2: Date.VerbatimFormatStyle

This would let you build out a fully custom date string, but at the cost of having the hour minute separator be hardcoded to be a colon character.

let testDate = try! Date("2024-01-01T10:36:01Z", strategy: .iso8601)

let hourMinuteStyle = Date.VerbatimFormatStyle(
    format: "\(hour: .defaultDigits(clock: .twelveHour, hourCycle: .zeroBased)):\(minute: .defaultDigits)",
    timeZone: .autoupdatingCurrent,
    calendar: .autoupdatingCurrent
)

testDate.formatted(hourMinuteStyle) // 3:36

// OR

testDate.formatted(
    .verbatim(
        "\(hour: .defaultDigits(clock: .twelveHour, hourCycle: .zeroBased)):\(minute: .defaultDigits)",
        timeZone: .autoupdatingCurrent,
        calendar: .autoupdatingCurrent
    )
)
Date.FormatStyle.Symbol.Hour defaultDigits shows leading zero if amPM is set to .omitted
 
 
Q