44

I have a countdown timer which countsdown from the current date/time to a specific future date/time. It is working great except for one problem. I input the future date using NSDateFormatter and dateFromString. It doesn't seem to be able to accept any time (hour) over 12 though indicating it is not support 24 hour clock. Is there a way to enable 24 hour clock support or a workaround? Here is some of my code:

NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat:@"yyyy-MM-dd hh:mm:ss"];
NSDate *myDate = [df dateFromString:@"2010-03-14 15:00:00"];
Flexicoder
  • 8,251
  • 4
  • 42
  • 56
John
  • 441
  • 1
  • 4
  • 3
  • what about the localisation...? should not you have localised the `NSDateFormatter` properly first? – holex Dec 27 '13 at 00:29

6 Answers6

69

NSDateFormatter follows the Unicode standard for date and time patterns. Use 'H' for the hour on a 24-hour clock:

NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSDate *myDate = [df dateFromString:@"2010-03-14 15:00:00"];
VoidPointer
  • 17,651
  • 15
  • 54
  • 58
  • 12
    Astonishing isn't it? I just found one of my own apps was failing because despite using HH in a format string, the user setting overrides this. Crazy! – Roger Mar 21 '11 at 23:22
  • 2
    This answer is misleading. I agree with Mavrick3 and Roger. The user setting overrides the HH of the standard time pattern. The Date Formatters Documentation, annoyingly hints at this behaviour: In all cases, you should consider that formatters default to using the user’s locale (currentLocale) superimposed with the user’s preference settings. – user363349 Feb 02 '12 at 15:23
  • 4
    what should we do if date format is 24 hours as mentioned in original question and user's locale is set to 12 hours? In that case [[NSDate date] timeIntervalSinceDate:@"my 24 hour datetime"] returns "nan"...very annoying! Could anyone please tell me how to get this working please. Basically I will need to use 24 hours clock always to calculate timeIntervalSinceDate irrespective of user's locale settings. I am not sure how to do this. – Paresh Masani Feb 03 '12 at 14:40
  • 2
    `NSDateFormatter` behaviour is explained in [Technical Q&A QA1480](https://developer.apple.com/library/ios/qa/qa1480/_index.html#//apple_ref/doc/uid/DTS40009878). – albertamg Nov 20 '15 at 08:59
  • 3
    To override the user's settings, set the date formatter's locale directly: `df.locale = ...` – ChrisJF Jan 09 '17 at 17:49
58

I had the same problem and using HH worked only on some devices, like Roger also verified. In the end this was the solution that worked for me, I hope it works for others. Finding this answer was difficult, there are no forums with it, it was literally trial and error following the apple documentation.

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];

    [dateFormatter setLocale:enUSPOSIXLocale];

    NSString *dateFormat = @"dd/MM/yyyy HH:mm"; //MM for month, mm for minutes

    [dateFormatter setDateFormat:dateFormat];
    [dateFormatter setTimeZone:[NSTimeZone localTimeZone]];
    date = [dateFormatter dateFromString:string];
amadour
  • 1,600
  • 11
  • 20
crojassoto
  • 601
  • 5
  • 5
  • That should be marked as the answer. This totally works. – aramusss Oct 06 '15 at 15:42
  • 1
    This is explained in Technical Q&A QA1480: [... if you're working with fixed-format dates, you should first set the locale of the date formatter to something appropriate for your fixed format. In most cases the best locale to choose is "en_US_POSIX"...](https://developer.apple.com/library/ios/qa/qa1480/_index.html#//apple_ref/doc/uid/DTS40009878) – albertamg Nov 20 '15 at 08:56
  • My god this works but it's an awkward issue nevertheless – j3141592653589793238 Nov 06 '20 at 20:55
13

My solution on Swift:

let formatter = NSDateFormatter()
var defIdentifer = formatter.locale.localeIdentifier
if !defIdentifer.hasSuffix("_POSIX") {
    defIdentifer = defIdentifer+"_POSIX"
    let locale = NSLocale(localeIdentifier: defIdentifer)
    formatter.locale = locale
}
formatter.dateFormat = "HH:mm"
shim
  • 9,289
  • 12
  • 69
  • 108
EvGeniy Ilyin
  • 1,817
  • 1
  • 21
  • 38
4

I had a similar problem recently, instead of HH, NSDateFormatter ignored hh, a(AM/PM Symbol) and G (cyclic era name) in my app.

And I was surprised to find that if I go to localization setting of my device and make some random choice, all the freaks are gone and the error cannot be produced again. Very weird.

Then I tested on simulator to do some study on it. There is my solution:

After you created the NSDateFormatter, explicitly set the locale property even you are using current locale, more importantly, DON'T use [NSLocale currentLocale], this one is bugged and can be somehow "overriden" by user setting, use systemLocale or explicitly create an NSLocale instance using a locale identifer.

Eddie Deng
  • 1,399
  • 1
  • 18
  • 30
4

Taken from the Apple Technical Q&A on NSDateFormatters

Q: I'm using NSDateFormatter to parse an Internet-style date, but this fails for some users in some regions. I've set a specific date format string; shouldn't that force NSDateFormatter to work independently of the user's region settings?

A: No. While setting a date format string will appear to work for most users, it's not the right solution to this problem. There are many places where format strings behave in unexpected ways.

This is how I have done mine in Swift:

private let dateFormatter: NSDateFormatter = {
    let dateFormatter = NSDateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
    dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
    dateFormatter.timeZone = NSTimeZone.init(forSecondsFromGMT: 0)
    return dateFormatter
}()
Community
  • 1
  • 1
Harry Bloom
  • 2,359
  • 25
  • 17
2

Objective C version of getting NSDate from 24-hour string when user has set 12 hour format on their iPhone without changing locale and setting timezone:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
NSString *localeId = dateFormatter.locale.localeIdentifier;
if (! [localeId hasSuffix:@"_POSIX"]) {
    localeId = [localeId stringByAppendingString:@"_POSIX"];
    dateFormatter.locale = [NSLocale localeWithLocaleIdentifier:localeId];
}
dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH.mm.ss";

NSDate *date  = [dateFormatter dateFromString:dateText];
Denis Kutlubaev
  • 15,320
  • 6
  • 84
  • 70