We format times in a localized way using the DateFormatter APIs.
DateFormatter.localizedString(from: date, dateStyle: .none, timeStyle: .short)
However if the result of this localization is say "22:00" VoiceOver just reads this out as "22". The VoiceOver user is given no indication that this numeric value is a time of the day.
I've read online that one approach is to use DateComponentsFormatter and the spellOut unitsStyle e.g.
let hourComponents = Calendar.current.dateComponents([.hour, .minute], from: date)
cell.accessibilityLabel = DateComponentsFormatter.localizedString(from: hourComponents, unitsStyle: .spellOut)
But again for a time "22:00" this reads out "22 hours". Sure if gives more context that this is related to time but it doesn't really indicate it is a time of the day i.e. it could be an interval (22 hours until something happens).
We've trawled through the Apple documenation but we can't find anything that covers this.
The best possible workaround we've found would be to force the accessibility label to be read out in 12 hour time i.e.
let dateFormatter = DateFormatter()
dateFormatter.setLocalizedDateFormatFromTemplate("hh:mm")
accessibilityLabel =dateFormatter.string(from: date)
that way it is clear to the user that it is a time of the day. Although this approach has the downside that ignores the user's preference of a 24 hour or 12 hour time display.
The VoiceOver readout is handled perfectly in the Calendar app. It reads out "10 o'clock" and "22 hundred hours" in English and it is fully localized so for example it reads out "midi" for 12:00pm in French. It is possible that this is being done with private APIs though.
So my question is, what is the recommended approach for formatting times for VoiceOver?