-4

How can I express hour and minute integer values as a String formatted like "9:30 am"?

Currently, I have:

let hour: Int = 9
let minute: Int = 30

var dateComponents = DateComponents()
dateComponents.hour = hour
dateComponents.minute = minute

let time: String = DateComponentsFormatter.localizedString(from: dateComponents, unitsStyle: DateComponentsFormatter.UnitsStyle.positional)

print(time)     // Prints "9:30", not "9:30 am"

I know I can manually concatenate the time meridian at the end, but I'm hoping there's a built in function for this. Perhaps a different UnitsStyle?

Eric33187
  • 1,006
  • 7
  • 13
  • 1
    `DateComponentsFormatter` displays the number of hours not the time of the day. And `DateComponents` are 24 hour based anyway. – vadian Mar 25 '21 at 19:27
  • 1
    @JoakimDanielson DateComponents hour component value range is 0-23 – Leo Dabus Mar 25 '21 at 21:42
  • @JoakimDanielson there is no am pm component – Leo Dabus Mar 25 '21 at 21:49
  • @JoakimDanielson The OP is trying to convert the hour component to string. If he would like to result in 9PM he would pass 21 not 9. He is not parsing a string. – Leo Dabus Mar 25 '21 at 21:51
  • The main point here is that OP should never choose which time format the user will use. – Leo Dabus Mar 25 '21 at 21:52
  • @JoakimDanielson OP said `"Prints "9:30", not "9:30 am""` Does it look like he was expecting the result to be `"9:00 pm"`? – Leo Dabus Mar 25 '21 at 21:53
  • @JoakimDanielson You should never do that. When displaying a date/time to the user you should always respect the device locale and settings https://stackoverflow.com/a/28347285/2303865 – Leo Dabus Mar 25 '21 at 21:55
  • I was referring to am because of the 24 hour time scale. It would say `hour: Int = 21` for 9 pm. Sorry for not clarifying, and I understand that confusion. – Eric33187 Mar 25 '21 at 22:10
  • @LeoDabus I'm not sure I understand why I would need to express the time "locally". I just want to express the string "9:30 am" from the integer `9` and `30`. It doesn't matter where the user is located... – Eric33187 Mar 25 '21 at 22:12
  • @Eric33187 you should never try to enforce a certain time format to the user. You should respect their settings. Use the aproach shown at this [post](https://stackoverflow.com/a/28347285/2303865) to display it localized – Leo Dabus Mar 25 '21 at 22:13
  • @Eric33187 If you need to convert the date component to a date you need to specify a calendar as well. If you don't specify a timezone it is fine but be aware that it would be using the device's current timezone as default. Btw not every date has 24 hours so the conversion might not be successful if you use the wrong hour component value at certain locales when they are on a daylight savings tiime transition date. – Leo Dabus Mar 25 '21 at 22:15
  • You need also to specify a date otherwise it will be on a completely different date. – Leo Dabus Mar 25 '21 at 22:18
  • @Leo Dabus What do you mean "not every date has 24 hours". Does not every country tell time with hours and minutes?... – Eric33187 Mar 27 '21 at 00:40
  • 1
    @Eric33187 Some places around the world have daylight savings transition. They change the clock back and forward to save energy during the summer. So there is two daylight savings transition dates. One of them the date will have less than 24 hours and the other will have more. So you might try to set a time that doesn't exist or even one that might occur twice in the same day. – Leo Dabus Mar 27 '21 at 00:43
  • 1
    Oh I see! So in other words, to set a time, I should always use dates, (so that time is localized and "correct"), and never just grab an hour and minute value which could be wrong at times. – Eric33187 Mar 27 '21 at 04:46

2 Answers2

1

You could use DateFormatter to achieve this.

let formatter = DateFormatter()
formatter.dateFormat = "hh:mm a"
formatter.amSymbol = "am"
formatter.pmSymbol = "pm"

let dateString = formatter.string(from: Date())
print(dateString) // prints "12:17 pm"

If you want to only include single digits for the hour, then you only include one "h" in the dateFormat:

formatter.dateFormat = "h:mm a" // prints "1:30 pm" instead of "01:30 pm"
rohanphadte
  • 978
  • 6
  • 19
  • You should not chose how the user should display their date/time. You should use date formatter date/time style and respect the user device locale and settings – Leo Dabus Mar 25 '21 at 19:59
  • How is this answer relevant to the question? OP wants to format 2 integer values and not a Date object. – Joakim Danielson Mar 25 '21 at 21:31
  • @JoakimDanielson True..... I guess I didn't look at the answer close enough to see that it requires a `Date` and not `Int`s. Anyone know how to answer my specific question? – Eric33187 Mar 25 '21 at 22:08
0
let formatter = DateFormatter()
formatter.dateFormat = "hh:mm a"
formatter.calendar = .current

let hour: Int = 9
let minute: Int = 30

var dateComponents = DateComponents()
dateComponents.hour = hour
dateComponents.minute = minute

If let fixedDate: Date = Calendar.current.date(from: dateComponents) {
let formattedString = formatter.string(from: fixedDate)
print(formattedString) //prints 09:30 AM
}

You need to add current calendar/date to get am or pm from your time from what I know.

EDIT:

Thanks to Leo Dabus in the comments for pointing this out: the above method will result in a date that is on January 1st 0001, if the date is important for you you have to specify the date (day/month/year)

for example:

let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "hh:mm a"
formatter.calendar = .current

let hour: Int = 9
let minute: Int = 30

var dateComponents = DateComponents()
dateComponents.hour = hour
dateComponents.minute = minute
dateComponents.year = Calendar.current.component(.year, from: date)
dateComponents.month = Calendar.current.component(.month, from: date)
dateComponents.day = Calendar.current.component(.day, from: date)

If let fixedDate: Date = Calendar.current.date(from: dateComponents) {
let formattedString = formatter.string(from: fixedDate)
print(formattedString)
}

shadow of arman
  • 395
  • 1
  • 7
  • 16