Can You Override Nsdateformatter 12 VS 24 Hour Time Format Without Using a Custom Dateformat

How can you properly support the iOS 12 hour / 24 hour time override in Date & Time settings for DateFormatters?

From the documentation for dateFormat on DateFormatter:

You should only set this property when working with fixed format
representations, as discussed in Working With Fixed Format Date
Representations. For user-visible representations, you should use the
dateStyle and timeStyle properties, or the
setLocalizedDateFormatFromTemplate(_:) method if your desired format
cannot be achieved using the predefined styles; both of these
properties and this method provide a localized date representation
appropriate for display to the user.

I still don't really understand why Foundation bothers to modify the date format if you're not using a fixed locale on your date formatter, but it seems the documentation does not recommend using the custom date format for non-fixed format representations.

setLocalizedDateFormatFromTemplate appears to be what you need if you can't just rely on dateStyle and timeStyle, although it doesn't appear to automatically change with a user's 24 hour time setting, so one could check to see if 24 hour time is on before specifying a template…

This method is supposed to allow you to specify a format without losing out on the formatting that is specific to each locale.

EDIT response from Apple Developer Relations (same idea, but more detail):

The correct way to handle this is to avoid specifying "hh" or "HH"
based on locale and instead using the "jj" skeleton symbol in
conjunction with -[NSDateFormatter setLocalizedDateFormatFromTemplate:].

From the "hour" section of the Date Field Symbol Table
(https://www.unicode.org/reports/tr35/tr35-dates.html#dfst-hour):

Input skeleton symbol In such a context, it requests the
preferred hour format for the locale (h, H, K, or K), as determined by
the preferred attribute of the hours element in the supplemental data.

By using "jj" you can get a format which best matches the user's
current settings:

NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setLocalizedDateFormatFromTemplate:@"jj:mm"];
NSLog(@"%@: 'jj:mm' => '%@' ('%@')", formatter.locale.localeIdentifier, formatter.dateFormat, [formatter stringFromDate:[NSDate date]]);

results in

  • en_US (12-hour): "en_US: 'jj:mm' => 'h:mm a' ('1:44 PM')
  • en_US (24-hour): "en_US: 'jj:mm' => 'HH:mm' ('13:44')
  • en_GB (12-hour): "en_GB: 'jj:mm' => 'h:mm a' ('1:44 pm')
  • en_GB (24-hour): "en_GB: 'jj:mm' => 'HH:mm' ('13:44')

This allows you to match your preferred format while keeping with the
user's preferred time settings.

Is there a better way to format Date with DateFormatter according to user's Date & Time preference?

You can use DateFormatter static method dateFormat(fromTemplate tmplate: String, options opts: Int, locale: Locale?) -> String? passing j format and .current locale and check if it contains "a":

extension DateFormatter {
static var is24Hour: Bool {
dateFormat(fromTemplate: "j", options: 0, locale: .current)?.contains("a") == false
}
}


extension Formatter {
static let customHour: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.setLocalizedDateFormatFromTemplate("jj")
return dateFormatter
}()
}


extension Date {
var customHour: String { Formatter.customHour.string(from: self) }
}


DateFormatter.is24Hour  // false
Date().customHour // "11 AM"



Note that there is no need to check if 24hour setting is on or not unless the user changes it after the formatter has been initialized. If want to make sure it reflects this as well:



extension Formatter {
static let date = DateFormatter()
}


extension Date {
var customHour: String {
Formatter.date.setLocalizedDateFormatFromTemplate("jj")
return Formatter.date.string(from: self)
}
}


Date().customHour       // "11 AM"

How to convert date in 12 or 24 hour time format to internet date?

Your question is a little bit confusing. I am assuming that you want to take an input date that either looks like "Jan 15, 2021 22:05" (24-hour time) or "Jan 15, 2021 10:05 PM" (12 hour time) and convert it to standard "internet date" ISO8601DateFormatter date format.

You say you want to determine the input selection based on "device selection". I'm assuming you mean that you expect to see input dates in either 12 or 24 hour format based on the settings on the user's device.

This function will let you figure out the 12/24 hour setting of the user's device:

func dateFormatIs24Hour() -> Bool {
guard let dateFormat = DateFormatter.dateFormat (fromTemplate: "j",
options:0,
locale: Locale.current) else {
return false
}
return !dateFormat.contains("a")
}

You should then be able to generate a date formatter formatString based on the device's 12/24 hour setting:

func MMMddyyyyDateFormat(twentyFourHour: Bool) -> String {
let hourFormat = is24Hour ? "HH" : "hh"
let amOrPmSuffix = is24Hour ? "" : "a"
let dateFormat = "MMM dd, yyyy \(hourFormat):mm \(amOrPmSuffix)"
return dateFormat
}

And you can set up an ISO8601DateFormatter to output dates in "internet date format:

let internetDateFormatter = ISO8601DateFormatter()
internetDateFormatter.formatOptions = .withInternetDateTime

Pulling it all together in a sample viewDidLoad() function for a shell iOS app:

override func viewDidLoad() {
super.viewDidLoad()
is24Hour = dateFormatIs24Hour()


let inputDateFormatter = DateFormatter()
inputDateFormatter.dateFormat = MMMddyyyyDateFormat(twentyFourHour: is24Hour)

let internetDateFormatter = ISO8601DateFormatter()
internetDateFormatter.formatOptions = .withInternetDateTime

//-----------------------------------------
//This code to generate a value `randomDate` using `thirtyDays` is
//only for testing. It generates a random date that is in a range
//from roughly 30 days in the past to 30 days in the future.
let thirtyDays = 30.0 * 24 * 60 * 60

let randomInterval = Double.random(in: -thirtyDays...thirtyDays)
let randomDate = Date().addingTimeInterval(randomInterval)
//-----------------------------------------


let inputDateString = inputDateFormatter.string(from: randomDate)

var outputDateString = ""

if let convertedDate = inputDateFormatter.date(from: inputDateString) {
outputDateString = internetDateFormatter.string(from: convertedDate)
}
print("Date string \(inputDateString) in Internet format = \(outputDateString)")
}

How to convert 24 hr String time into 12 hr time String format

Use NSDateFormatter to turn the string in a NSDate. Then use the dateformatter again to change the NSDate to a string.

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = @"HH:mm";
NSDate *date = [dateFormatter dateFromString:@"15:15"];

dateFormatter.dateFormat = @"hh:mm a";
NSString *pmamDateString = [dateFormatter stringFromDate:date];

Code might contain some errors, written without compiling or testing.



Related Topics



Leave a reply



Submit