Detect if time format is in 12hr or 24hr format - objective-c

Is there any way to detect if the current device of the app uses 12h our 24h format, so that I can use one NSDateFormatter for 12h and one for 24h depending on the users language/loaction setting? Just Like the UIDatePicker detects and shows the AM/PM picker if it is 12h format.

I figured it out, its pretty easy. I just added this code to viewDidLoad :
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[NSLocale currentLocale]];
[formatter setDateStyle:NSDateFormatterNoStyle];
[formatter setTimeStyle:NSDateFormatterShortStyle];
NSString *dateString = [formatter stringFromDate:[NSDate date]];
NSRange amRange = [dateString rangeOfString:[formatter AMSymbol]];
NSRange pmRange = [dateString rangeOfString:[formatter PMSymbol]];
BOOL is24h = (amRange.location == NSNotFound && pmRange.location == NSNotFound);
[formatter release];
NSLog(#"%#\n",(is24h ? #"YES" : #"NO"));
And it perfectly returns YES or NO depending on the locale.

And here is a Swift 3.0 updated version
func using12hClockFormat() -> Bool {
let formatter = DateFormatter()
formatter.locale = Locale.current
formatter.dateStyle = .none
formatter.timeStyle = .short
let dateString = formatter.string(from: Date())
let amRange = dateString.range(of: formatter.amSymbol)
let pmRange = dateString.range(of: formatter.pmSymbol)
return !(pmRange == nil && amRange == nil)
}

this is swift solution that worked for me, those two above did not.
let dateString: String = DateFormatter.dateFormat(
fromTemplate: "j", options: 0,
locale: Locale.current
)!
if(dateString.contains("a")){
// 12 h format
return true
}else{
// 24 h format
return false
}

For Swift 5.3.
Tested on Xcode 12.
func is12hClockFormat() -> Bool {
let formatString = DateFormatter.dateFormat(
fromTemplate: "j",
options: 0,
locale: Locale.current
)!
return formatString.contains("a")
}
This uses a special date template string called "j". According to the ICU Spec, "j"...
requests the preferred hour format for the locale (h, H, K, or k), as determined by whether h, H, K, or k is used in the standard short time format for the locale. In the implementation of such an API, 'j' must be replaced by h, H, K, or k before beginning a match against availableFormats data. Note that use of 'j' in a skeleton passed to an API is the only way to have a skeleton request a locale's preferred time cycle type (12-hour or 24-hour).
That last sentence is important. It "is the only way to have a skeleton request a locale's preferred time cycle type". Since NSDateFormatter and NSCalendar are built on the ICU library, the same holds true here.
Collected from https://stackoverflow.com/a/11660380/3428146

Here is the Swift version:
func using12hClockFormat() -> Bool {
let formatter = NSDateFormatter()
formatter.locale = NSLocale.currentLocale()
formatter.dateStyle = NSDateFormatterStyle.NoStyle
formatter.timeStyle = NSDateFormatterStyle.ShortStyle
let dateString = formatter.stringFromDate(NSDate())
let amRange = dateString.rangeOfString(formatter.AMSymbol)
let pmRange = dateString.rangeOfString(formatter.PMSymbol)
return !(pmRange == nil && amRange == nil)
}

Objective C category NSDate+Extensions:
#import Foundation;
#interface NSDate (Extensions)
- (NSString *)getTimeString;
#end
#import "NSDate+Extensions.h"
#implementation NSDate (Extensions)
- (NSString *)getTimeString
{
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
if ([self isTwelveHourDateFormat]) {
[formatter setDateFormat:#"hh:mm\ndd MMM"];
}
else {
[formatter setDateFormat:#"HH:mm\ndd MMM"];
}
return [formatter stringFromDate:self];
}
- (BOOL)isTwelveHourDateFormat
{
NSString *dateFormat = [NSDateFormatter dateFormatFromTemplate:#"j" options:0 locale:[NSLocale currentLocale]];
return [dateFormat containsString:#"a"];
}
#end

Related

How do I convert a parameterised enum/ enum with associated values from Swift to Objective-C? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
NOTE: As an exercise, I am converting Swift code to Objective-C.
CODE: Original Swift code from which I converted:
case .ISO8601(let isoFormat):
let dateFormat = (isoFormat != nil) ? isoFormat! : ISO8601Format(dateString: string as String)
let formatter = NSDate.formatter(format: dateFormat.rawValue)
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
formatter.timeZone = NSTimeZone.localTimeZone()
formatter.dateFormat = dateFormat.rawValue
if let date = formatter.dateFromString(string as String) {
self.init(timeInterval:0, sinceDate:date)
} else {
self.init()
}
WHAT I TRIED: The Objective-C conversion I tried:
else if([format.dateFormatType compare: ISO8601DateFormatType] == NSOrderedSame) {
NSString *isoFormat = // WHAT DO TO HERE ?;
NSString *dateFormat = (isoFormat != nil) ? isoFormat : ISO8601DateFormatType;
NSDateFormatter *formatter = [NSDate formatterWithFormat: dateFormat andTimeZone: [NSTimeZone localTimeZone ] andLocale: [NSLocale currentLocale]];
formatter.locale = [NSLocale localeWithLocaleIdentifier: (#"en_US_POSIX")];
NSLog([NSString stringWithFormat:#"%#", dateFormat]);
// dateFormat = ISO8601
NSDate *date = [formatter dateFromString:(string)];
//
// ISSUE IS HERRRREEEE ^
//
NSLog([NSString stringWithFormat:#"%#", date]);
// date = nil
if (date != nil){
return [self initWithTimeInterval: 0 sinceDate: date];
}
else {
return [self init];
}
}
The approach I took was to use a separate class to reproduce the behaviour of a swift enum with associated values (this was a originally a large swift enum with associated values):
DateFormat.m
#import "DateFormat.h"
#implementation DateFormat
NSString * const ISO8601DateFormatType = #"ISO8601";
NSString * const DotNetDateFormatType = #"DotNet";
NSString * const RSSDateFormatType = #"EEE, d MMM yyyy HH:mm:ss ZZZ";
NSString * const AltRSSDateFormatType = #"d MMM yyyy HH:mm:ss ZZZ";
NSString * const CustomDateFormatType = #"Custom";
NSString * const ISOFormatYear = #"yyyy";
NSString * const ISOFormatYearMonth = #"yyyy-MM"; // 1997-07
NSString * const ISOFormatDate = #"yyyy-MM-dd"; // 1997-07-16
NSString * const ISOFormatDateTime = #"yyyy-MM-dd'T'HH:mmZ"; // 1997-07-16T19:20+01:00
NSString * const ISOFormatDateTimeSec = #"yyyy-MM-dd'T'HH:mm:ssZ"; // 1997-07-16T19:20:30+01:00
NSString * const ISOFormatDateTimeMilliSec = #"yyyy-MM-dd'T'HH:mm:ss.SSSZ"; // 1997-07-16T19:20:30.45+01:00
- (instancetype) initWithType: (NSString *) formatType details: (NSString *) details {
if(self = [super init]) {
_dateFormatType = formatType;
_formatDetails = details;
}
return self;
}
+ (instancetype) ISODateFormat: (NSString *) isoFormat
{
return [[DateFormat alloc] initWithType: ISO8601DateFormatType details: isoFormat];
}
+ (instancetype) DotNetDateFormat
{
return [[DateFormat alloc] initWithType: DotNetDateFormatType details: nil];
}
+ (instancetype) RSSDateFormat
{
return [[DateFormat alloc] initWithType: RSSDateFormatType details: nil];
}
+ (instancetype) AltRSSDateFormat
{
return [[DateFormat alloc] initWithType: AltRSSDateFormatType details: nil];
}
+ (instancetype) CustomDateFormat: (NSString *) formatString
{
return [[DateFormat alloc] initWithType: CustomDateFormatType details: formatString];
}
#end
MY ISSUE:
In my code, dateFromString returns nil because the format passed as a parameter is incorrect. The reason that happens is because of the way I converted the enum with associated value line: case .ISO8601(let isoFormat):
I don't know how to convert case .ISO8601(let isoFormat): into Objective-C.
The issue is that enums with associated values only exist in Swift, not in Objective-C.
So how can I convert that enum with associated value line case .ISO8601(let isoFormat): in Objective-C ?
SOLUTION:
EDIT: The answer was simple, I just had to write: NSString *isoFormat = format.formatDetails;in the Objective-C conversion I showed at the beginning of the question. (Check my Class DateFormat implementation file to understand)
Let me explain your code for you:
NSString *isoFormat = ISO8601DateFormatType;
(assigns string ISO8601 to isoFormat)
NSString *dateFormat = (isoFormat != nil) ? isoFormat : ISO8601DateFormatType;
(isoFormat is never nil so the condition is always true. If it were false, we would again assign string ISO8601).
NSDateFormatter *formatter = ...
(we get some formatter, it doesn't matter how because we are overwriting all its important properties anyway)
formatter.locale = [NSLocale localeWithLocaleIdentifier: (#"en_US_POSIX")];
(let's overwrite the [NSLocale currentLocale] above with POSIX locale, that's fine)
formatter.timeZone = [NSTimeZone localTimeZone];
(let's set time zone again)
formatter.dateFormat = dateFormat;
(let's set string ISO8601 to be the date format, this is not a valid date format)
NSDate *date = [formatter dateFromString:(string)];
(let's try to parse a date using an invalid date format ISO8601. Obviously, this will return nil)
In short - the problem is exactly the same as the problem in your previous question Error in dateFormat returns nil

NSDateFormatter returns unexpected String from date [duplicate]

It seems that NSDateFormatter has a "feature" that bites you unexpectedly: If you do a simple "fixed" format operation such as:
NSDateFormatter* fmt = [[NSDateFormatter alloc] init];
[fmt setDateFormat:#"yyyyMMddHHmmss"];
NSString* dateStr = [fmt stringFromDate:someDate];
[fmt release];
Then it works fine in the US and most locales UNTIL ... someone with their phone set to a 24-hour region sets the 12/24 hour switch in settings to 12. Then the above starts tacking "AM" or "PM" onto the end of the resulting string.
(See, eg, NSDateFormatter, am I doing something wrong or is this a bug?)
(And see https://developer.apple.com/library/content/qa/qa1480/_index.html)
Apparently Apple has declared this to be "BAD" -- Broken As Designed, and they aren't going to fix it.
The circumvention is apparently to set the locale of the date formatter for a specific region, generally the US, but this is a bit messy:
NSLocale *loc = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
[df setLocale: loc];
[loc release];
Not too bad in onesies-twosies, but I'm dealing with about ten different apps, and the first one I look at has 43 instances of this scenario.
So any clever ideas for a macro/overridden class/whatever to minimize the effort to change everything, without making the code to obscure? (My first instinct is to override NSDateFormatter with a version that would set the locale in the init method. Requires changing two lines -- the alloc/init line and the added import.)
##Added
This is what I've come up with so far -- seems to work in all scenarios:
#implementation BNSDateFormatter
-(id)init {
static NSLocale* en_US_POSIX = nil;
NSDateFormatter* me = [super init];
if (en_US_POSIX == nil) {
en_US_POSIX = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
}
[me setLocale:en_US_POSIX];
return me;
}
#end
##Update
Re OMZ's proposal, here is what I'm finding --
Here is the category version -- h file:
#import <Foundation/Foundation.h>
#interface NSDateFormatter (Locale)
- (id)initWithSafeLocale;
#end
Category m file:
#import "NSDateFormatter+Locale.h"
#implementation NSDateFormatter (Locale)
- (id)initWithSafeLocale {
static NSLocale* en_US_POSIX = nil;
self = [super init];
if (en_US_POSIX == nil) {
en_US_POSIX = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
}
NSLog(#"Category's locale: %# %#", en_US_POSIX.description, [en_US_POSIX localeIdentifier]);
[self setLocale:en_US_POSIX];
return self;
}
#end
The code:
NSDateFormatter* fmt;
NSString* dateString;
NSDate* date1;
NSDate* date2;
NSDate* date3;
NSDate* date4;
fmt = [[NSDateFormatter alloc] initWithSafeLocale];
[fmt setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
dateString = [fmt stringFromDate:[NSDate date]];
NSLog(#"dateString = %#", dateString);
date1 = [fmt dateFromString:#"2001-05-05 12:34:56"];
NSLog(#"date1 = %#", date1.description);
date2 = [fmt dateFromString:#"2001-05-05 22:34:56"];
NSLog(#"date2 = %#", date2.description);
date3 = [fmt dateFromString:#"2001-05-05 12:34:56PM"];
NSLog(#"date3 = %#", date3.description);
date4 = [fmt dateFromString:#"2001-05-05 12:34:56 PM"];
NSLog(#"date4 = %#", date4.description);
[fmt release];
fmt = [[BNSDateFormatter alloc] init];
[fmt setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
dateString = [fmt stringFromDate:[NSDate date]];
NSLog(#"dateString = %#", dateString);
date1 = [fmt dateFromString:#"2001-05-05 12:34:56"];
NSLog(#"date1 = %#", date1.description);
date2 = [fmt dateFromString:#"2001-05-05 22:34:56"];
NSLog(#"date2 = %#", date2.description);
date3 = [fmt dateFromString:#"2001-05-05 12:34:56PM"];
NSLog(#"date3 = %#", date3.description);
date4 = [fmt dateFromString:#"2001-05-05 12:34:56 PM"];
NSLog(#"date4 = %#", date4.description);
[fmt release];
The result:
2011-07-11 17:44:43.243 DemoApp[160:307] Category's locale: <__NSCFLocale: 0x11a820> en_US_POSIX
2011-07-11 17:44:43.257 DemoApp[160:307] dateString = 2011-07-11 05:44:43 PM
2011-07-11 17:44:43.264 DemoApp[160:307] date1 = (null)
2011-07-11 17:44:43.272 DemoApp[160:307] date2 = (null)
2011-07-11 17:44:43.280 DemoApp[160:307] date3 = (null)
2011-07-11 17:44:43.298 DemoApp[160:307] date4 = 2001-05-05 05:34:56 PM +0000
2011-07-11 17:44:43.311 DemoApp[160:307] Extended class's locale: <__NSCFLocale: 0x11a820> en_US_POSIX
2011-07-11 17:44:43.336 DemoApp[160:307] dateString = 2011-07-11 17:44:43
2011-07-11 17:44:43.352 DemoApp[160:307] date1 = 2001-05-05 05:34:56 PM +0000
2011-07-11 17:44:43.369 DemoApp[160:307] date2 = 2001-05-06 03:34:56 AM +0000
2011-07-11 17:44:43.380 DemoApp[160:307] date3 = (null)
2011-07-11 17:44:43.392 DemoApp[160:307] date4 = (null)
The phone [make that an iPod Touch] is set to Great Britain, with the 12/24 switch set to 12. There's a clear difference in the two results, and I judge the category version to be wrong. Note that the log in the category version IS getting executed (and stops placed in the code are hit), so it's not simply a case of the code somehow not getting used.
##A curious observation
Modified the category implementation slightly:
#import "NSDateFormatter+Locale.h"
#implementation NSDateFormatter (Locale)
- (id)initWithSafeLocale {
static NSLocale* en_US_POSIX2 = nil;
self = [super init];
if (en_US_POSIX2 == nil) {
en_US_POSIX2 = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
}
NSLog(#"Category's locale: %# %#", en_US_POSIX2.description, [en_US_POSIX2 localeIdentifier]);
[self setLocale:en_US_POSIX2];
NSLog(#"Category's object: %# and object's locale: %# %#", self.description, self.locale.description, [self.locale localeIdentifier]);
return self;
}
#end
Basically just changed the name of the static locale variable (in case there was some conflict with the static declared in the subclass) and added the extra NSLog. But look what that NSLog prints:
2011-07-15 16:35:24.322 DemoApp[214:307] Category's locale: <__NSCFLocale: 0x160550> en_US_POSIX
2011-07-15 16:35:24.338 DemoApp[214:307] Category's object: <NSDateFormatter: 0x160d90> and object's locale: <__NSCFLocale: 0x12be70> en_GB
2011-07-15 16:35:24.345 DemoApp[214:307] dateString = 2011-07-15 04:35:24 PM
2011-07-15 16:35:24.370 DemoApp[214:307] date1 = (null)
2011-07-15 16:35:24.378 DemoApp[214:307] date2 = (null)
2011-07-15 16:35:24.390 DemoApp[214:307] date3 = (null)
2011-07-15 16:35:24.404 DemoApp[214:307] date4 = 2001-05-05 05:34:56 PM +0000
As you can see, the setLocale simply didn't. The locale of the formatter is still en_GB. It appears that there is something "strange" about an init method in a category.
Duh!!
Sometimes you have an "Aha!!" moment, sometimes it's more of a "Duh!!" This is the latter. In the category for initWithSafeLocale the "super" init was coded as self = [super init];. This inits the SUPERCLASS of NSDateFormatter but does not init the NSDateFormatter object itself.
Apparently when this initialization is skipped, setLocale "bounces off", presumably because of some missing data structure in the object. Changing the init to self = [self init]; causes the NSDateFormatter initialization to occur, and setLocale is happy again.
Here is the "final" source for the category's .m:
#import "NSDateFormatter+Locale.h"
#implementation NSDateFormatter (Locale)
- (id)initWithSafeLocale {
static NSLocale* en_US_POSIX = nil;
self = [self init];
if (en_US_POSIX == nil) {
en_US_POSIX = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
}
[self setLocale:en_US_POSIX];
return self;
}
#end
Instead of subclassing, you could create an NSDateFormatter category with an additional initializer that takes care of assigning the locale and possibly also a format string, so you'd have a ready-to-use formatter right after initializing it.
#interface NSDateFormatter (LocaleAdditions)
- (id)initWithPOSIXLocaleAndFormat:(NSString *)formatString;
#end
#implementation NSDateFormatter (LocaleAdditions)
- (id)initWithPOSIXLocaleAndFormat:(NSString *)formatString {
self = [super init];
if (self) {
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
[self setLocale:locale];
[locale release];
[self setFormat:formatString];
}
return self;
}
#end
Then you could use NSDateFormatter anywhere in your code with just:
NSDateFormatter* fmt = [[NSDateFormatter alloc] initWithPOSIXLocaleAndFormat:#"yyyyMMddHHmmss"];
You might want to prefix your category method somehow to avoid name conflicts, just in case Apple decides to add such a method in a future version of the OS.
In case you're always using the same date format(s), you could also add category methods that return singleton instances with certain configurations (something like +sharedRFC3339DateFormatter). Be aware however that NSDateFormatter is not thread-safe and you have to use locks or #synchronized blocks when you're using the same instance from multiple threads.
May I suggest something totally different because to be honest all of this is somewhat running down a rabbit hole.
You should be using one NSDateFormatter with dateFormat set and locale forced to en_US_POSIX for receiving dates (from servers/APIs).
Then you should be using a different NSDateFormatter for the UI which you will set the timeStyle/dateStyle properties - this way you're not having an explicit dateFormat set by yourself, thus falsely assuming that format will be used.
This means UI is driven by user preferences (am/pm vs 24 hour, and date strings formatted correctly to user choice - from iOS settings), whereas dates that are "coming into" your app are always being "parsed" correctly to an NSDate for you to use.
Here is the solution for that problem in the swift version. In swift we can use extension instead of category.
So, Here I have created the extension for the DateFormatter and inside that initWithSafeLocale
returns the DateFormatter with the relevant Locale, Here in our case that is en_US_POSIX, Apart from that also provided couple of date formation methods.
Swift 4
extension DateFormatter {
private static var dateFormatter = DateFormatter()
class func initWithSafeLocale(withDateFormat dateFormat: String? = nil) -> DateFormatter {
dateFormatter = DateFormatter()
var en_US_POSIX: Locale? = nil;
if (en_US_POSIX == nil) {
en_US_POSIX = Locale.init(identifier: "en_US_POSIX")
}
dateFormatter.locale = en_US_POSIX
if dateFormat != nil, let format = dateFormat {
dateFormatter.dateFormat = format
}else{
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
}
return dateFormatter
}
// ------------------------------------------------------------------------------------------
class func getDateFromString(string: String, fromFormat dateFormat: String? = nil) -> Date? {
if dateFormat != nil, let format = dateFormat {
dateFormatter = DateFormatter.initWithSafeLocale(withDateFormat: format)
}else{
dateFormatter = DateFormatter.initWithSafeLocale()
}
guard let date = dateFormatter.date(from: string) else {
return nil
}
return date
}
// ------------------------------------------------------------------------------------------
class func getStringFromDate(date: Date, fromDateFormat dateFormat: String? = nil)-> String {
if dateFormat != nil, let format = dateFormat {
dateFormatter = DateFormatter.initWithSafeLocale(withDateFormat: format)
}else{
dateFormatter = DateFormatter.initWithSafeLocale()
}
let string = dateFormatter.string(from: date)
return string
} }
usage description:
let date = DateFormatter.getDateFromString(string: "11-07-2001”, fromFormat: "dd-MM-yyyy")
print("custom date : \(date)")
let dateFormatter = DateFormatter.initWithSafeLocale(withDateFormat: "yyyy-MM-dd HH:mm:ss")
let dt = DateFormatter.getDateFromString(string: "2001-05-05 12:34:56")
print("base date = \(dt)")
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let dateString = dateFormatter.string(from: Date())
print("dateString = " + dateString)
let date1 = dateFormatter.date(from: "2001-05-05 12:34:56")
print("date1 = \(String(describing: date1))")
let date2 = dateFormatter.date(from: "2001-05-05 22:34:56")
print("date2 = \(String(describing: date2))")
let date3 = dateFormatter.date(from: "2001-05-05 12:34:56PM")
print("date3 = \(String(describing: date3))")
let date4 = dateFormatter.date(from: "2001-05-05 12:34:56 PM")
print("date4 = \(String(describing: date4))")

Unable to convert original date using dateFromString

I am trying to convert a time string using the following code
NSString *origDate = #"2012-12-06T09:27:18+08:00";
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setFormatterBehavior:NSDateFormatterBehavior10_4];
[df setDateFormat:#"yyyy-mm-dd HH:mm:ss VVVV"];
NSDate *convertedDate = [df dateFromString:origDate];
However when I print the convertedDate, it returns me null. My guess is that the Date format U am using does not match. How can I modify the code to make it work? What format can I use to match my string?
EDIT (After referring to apple's documentation)
I checked the date format documentation on apple's page and found the following code
NSDateFormatter *rfc3339DateFormatter = [[NSDateFormatter alloc] init];
NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
[rfc3339DateFormatter setLocale:enUSPOSIXLocale];
[rfc3339DateFormatter setDateFormat:#"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
[rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
// Convert the RFC 3339 date time string to an NSDate.
NSDate *date = [rfc3339DateFormatter dateFromString:rfc3339DateTimeString];
The format above seems to match what I have in the original date string "2012-12-06T09:27:18+08:00". However I am still getting a null value back. Am I getting closer? How else can I update this?
Based on your original input, this format string provided to your date formatter should get the job done:
#"yyyy'-'MM'-'dd'T'HH':'mm':'ssZZZZZ"
Note: I had tested this under Mac OS X 10.8.2.
The format String will parse on iOS6 (not iOS5 -> nil) but it is useless for output, since the parsed date will loose it's timezone information.
Output will be something like "2012-12-06T17:27:18Z" in iOS6 maybe this is depending on wether the timezone is set to GMT.
my Code:
static NSDateFormatter *gXmlDateFormatter = nil;
// lazy init
+ (NSDateFormatter *)xmlDateFormatter
{
// e.g. updateDateTime="2012-09-18T11:06:19+00:00"
if (gXmlDateFormatter == nil) {
// prepare for parsinf Arinc-ISO-XML-dates input
gXmlDateFormatter = [[NSDateFormatter alloc] init];
gXmlDateFormatter = [NSTimeZone timeZoneForSecondsFromGMT:0];
gXmlDateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
gXmlDateFormatter.dateFormat = #"yyyy'-'MM'-'dd'T'HH':'mm':'ssZZZZZ"; // only parsing! in iOS 6 (iOS5 will parse nil)
// gXmlDateFormatter.dateFormat = #"yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"; // all iOS but with NO colons in timeZone (add/remove)
}
NSLog(#"gXmlDateFormatter:%#",gXmlDateFormatter);
return gXmlDateFormatter;
}
// there's a problem with the above dateformater and iOS5 creating nil-results
+ (NSDate *)dateFromXMLString:(NSString *)arincDateString
{
NSString *dateString = arincDateString;
// xmlDateStrings may contain a ':' in the timezone part. iOS and Unicode DO NOT
// so always remove the xml-standard colon ':' from the timezone to make it iOS/Unicode compatible
// xml: http://www.w3schools.com/schema/schema_dtypes_date.asp
// iOS: http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns)
NSRange zRange = NSMakeRange(arincDateString.length-3, 1);
dateString = [arincDateString stringByReplacingOccurrencesOfString:#":" withString:#"" options:0 range:zRange];
NSDate *date = [self.arincDateFormatter dateFromString:dateString];
if(!date)NSLog(#"PARSING arincDateString:'%#' -> (NSDate*)%# ",arincDateString,date);
return date;
}
+ (NSString *)xmlStringFromDate:(NSDate *)date
{
if( !date ) return nil; // exit on nil date
#autoreleasepool {
NSString *dateString = [self.arincDateFormatter stringFromDate:date];
// iOS5 does not use a ':' in the timeZone part but xml needs it
// so allways add the xml-standard colon ':' into the timezone
NSMutableString *string = [NSMutableString stringWithString:dateString];
if( 22 < string.length ) { // prevent crashing
[string insertString:#":" atIndex:22];
} else {
NSLog(#"date output string too short:%d<22",string.length);
}
dateString = string;
if(!dateString)
NSLog(#"OUTPUT '%#' -> (NSString*)%#",date,dateString);
return dateString;
}
}

How to display date as "15th November 2010" in iPhone SDK?

HI,
I need to display date as "15th November 2010" in iPhone SDK.
How do I do that?
Thanks!
You can use a Date Formatter as explained in this post:
// Given some NSDate* date
NSDateFormatter* formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setDateFormat:#"dd MMM yyyy"];
NSString* formattedDate = [formatter stringFromDate:date];
I believe you can simply just put "th" at the end of the dd in the format string. like this:
#"ddth MMM yyy
but I don't have my Mac in front of me to test it out. If that doesn't work you can try something like this:
[formatter setDateFormat:#"dd"];
NSString* day = [formatter stringFromDate:date];
[formatter setDateFormat:#"MMM yyyy"];
NSString* monthAndYear = [formatter stringFromDate:date];
NSString* date = [NSString stringWithFormat:#"%#th %#", day, monthAndYear];
I know I'm answering something old; but I did the following.
#implementation myClass
+ (NSString *) dayOfTheMonthToday
{
NSDateFormatter *DayFormatter=[[NSDateFormatter alloc] init];
[DayFormatter setDateFormat:#"dd"];
NSString *dayString = [DayFormatter stringFromDate:[NSDate date]];
//yes, I know I could combined these two lines - I just don't like all that nesting
NSString *dayStringwithsuffix = [myClass buildRankString:[NSNumber numberWithInt:[dayString integerValue]]];
NSLog (#"Today is the %# day of the month", dayStringwithsuffix);
}
+ (NSString *)buildRankString:(NSNumber *)rank
{
NSString *suffix = nil;
int rankInt = [rank intValue];
int ones = rankInt % 10;
int tens = floor(rankInt / 10);
tens = tens % 10;
if (tens == 1) {
suffix = #"th";
} else {
switch (ones) {
case 1 : suffix = #"st"; break;
case 2 : suffix = #"nd"; break;
case 3 : suffix = #"rd"; break;
default : suffix = #"th";
}
}
NSString *rankString = [NSString stringWithFormat:#"%#%#", rank, suffix];
return rankString;
}
#end
I grabbed the previous class method from this answer: NSNumberFormatter and 'th' 'st' 'nd' 'rd' (ordinal) number endings

Convert NSDate to NSString

How do I convert, NSDate to NSString so that only the year in #"yyyy" format is output to the string?
How about...
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy"];
//Optionally for time zone conversions
[formatter setTimeZone:[NSTimeZone timeZoneWithName:#"..."]];
NSString *stringFromDate = [formatter stringFromDate:myNSDateInstance];
//unless ARC is active
[formatter release];
Swift 4.2 :
func stringFromDate(_ date: Date) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "dd MMM yyyy HH:mm" //yyyy
return formatter.string(from: date)
}
I don't know how we all missed this: localizedStringFromDate:dateStyle:timeStyle:
NSString *dateString = [NSDateFormatter localizedStringFromDate:[NSDate date]
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterFullStyle];
NSLog(#"%#",dateString);
outputs '13/06/12 00:22:39 GMT+03:00'
Hope to add more value by providing the normal formatter including the year, month and day with the time.
You can use this formatter for more than just a year
[dateFormat setDateFormat: #"yyyy-MM-dd HH:mm:ss zzz"];
there are a number of NSDate helpers on the web, I tend to use:
https://github.com/billymeltdown/nsdate-helper/
Readme extract below:
NSString *displayString = [NSDate stringForDisplayFromDate:date];
This produces the following kinds of output:
‘3:42 AM’ – if the date is after midnight today
‘Tuesday’ – if the date is within the last seven days
‘Mar 1’ – if the date is within the current calendar year
‘Mar 1, 2008’ – else ;-)
In Swift:
var formatter = NSDateFormatter()
formatter.dateFormat = "yyyy"
var dateString = formatter.stringFromDate(YourNSDateInstanceHERE)
NSDateFormatter *dateformate=[[NSDateFormatter alloc]init];
[dateformate setDateFormat:#"yyyy"]; // Date formater
NSString *date = [dateformate stringFromDate:[NSDate date]]; // Convert date to string
NSLog(#"date :%#",date);
If you don't have NSDate -descriptionWithCalendarFormat:timeZone:locale: available (I don't believe iPhone/Cocoa Touch includes this) you may need to use strftime and monkey around with some C-style strings. You can get the UNIX timestamp from an NSDate using NSDate -timeIntervalSince1970.
+(NSString*)date2str:(NSDate*)myNSDateInstance onlyDate:(BOOL)onlyDate{
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
if (onlyDate) {
[formatter setDateFormat:#"yyyy-MM-dd"];
}else{
[formatter setDateFormat: #"yyyy-MM-dd HH:mm:ss"];
}
//Optionally for time zone conversions
// [formatter setTimeZone:[NSTimeZone timeZoneWithName:#"..."]];
NSString *stringFromDate = [formatter stringFromDate:myNSDateInstance];
return stringFromDate;
}
+(NSDate*)str2date:(NSString*)dateStr{
if ([dateStr isKindOfClass:[NSDate class]]) {
return (NSDate*)dateStr;
}
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd"];
NSDate *date = [dateFormat dateFromString:dateStr];
return date;
}
Just add this extension:
extension NSDate {
var stringValue: String {
let formatter = NSDateFormatter()
formatter.dateFormat = "yourDateFormat"
return formatter.stringFromDate(self)
}
}
If you are on Mac OS X you can write:
NSString* s = [[NSDate date] descriptionWithCalendarFormat:#"%Y_%m_%d_%H_%M_%S" timeZone:nil locale:nil];
However this is not available on iOS.
It's swift format :
func dateFormatterWithCalendar(calndarIdentifier: Calendar.Identifier, dateFormat: String) -> DateFormatter {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: calndarIdentifier)
formatter.dateFormat = dateFormat
return formatter
}
//Usage
let date = Date()
let fotmatter = dateFormatterWithCalendar(calndarIdentifier: .gregorian, dateFormat: "yyyy")
let dateString = fotmatter.string(from: date)
print(dateString) //2018
swift 4 answer
static let dateformat: String = "yyyy-MM-dd'T'HH:mm:ss"
public static func stringTodate(strDate : String) -> Date
{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = dateformat
let date = dateFormatter.date(from: strDate)
return date!
}
public static func dateToString(inputdate : Date) -> String
{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = dateformat
return formatter.string(from: inputdate)
}
Use extension to have clear code
You can write an extension to convert any Date object to any desired calendar and any format
extension Date{
func asString(format: String = "yy/MM/dd HH:mm",
for identifier: Calendar.Identifier = .persian) -> String {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: identifier)
formatter.dateFormat = format
return formatter.string(from: self)
}
}
Then use it like this:
let now = Date()
print(now.asString()) // prints -> 00/04/18 20:25
print(now.asString(format: "yyyy/MM/dd")) // prints -> 1400/04/18
print(now.asString(format: "MM/dd", for: .gregorian)) // prints -> 07/09
To learn how to specify your desired format string take a look at this link.
For a complete reference on how to format dates see Apple's official Date Formatting Guide here.
Simple way to use C# styled way to convert Date to String.
usage:
let a = time.asString()
// 1990-03-25
let b = time.asString("MM ∕ dd ∕ yyyy, hh꞉mm a")
// 03 / 25 / 1990, 10:33 PM
extensions:
extension Date {
func asString(_ template: String? = nil) -> String {
if let template = template {
let df = DateFormatter.with(template: template)
return df.string(from: self)
}
else {
return globalDateFormatter.string(from: self)
}
}
}
// Here you can set default template for DateFormatter
public let globalDateFormatter: DateFormatter = DateFormatter.with(template: "y-M-d")
public extension DateFormatter {
static func with(template: String ) -> DateFormatter {
let df = DateFormatter()
df.dateFormat = template
return df
}
}
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:myNSDateInstance];
NSInteger year = [components year];
// NSInteger month = [components month];
NSString *yearStr = [NSString stringWithFormat:#"%ld", year];
Define your own utility for format your date required date format
for eg.
NSString * stringFromDate(NSDate *date)
{ NSDateFormatter *formatter
[[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MM ∕ dd ∕ yyyy, hh꞉mm a"];
return [formatter stringFromDate:date];
}
#ios #swift #convertDateinString
Simply just do like this to "convert date into string" as per format you passed:
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-YYYY" // pass formate here
let myString = formatter.string(from: date) // this will convert Date in String
Note: You can specify different formats such like "yyyy-MM-dd", "yyyy", "MM" etc...
Update for iOS 15
iOS 15 now supports calling .formatted on Date objects directly without an explicit DateFormatter.
Example for common formats
Documentation
date.formatted() // 6/8/2021, 7:30 PM
date.formatted(date: .omitted, time: .complete) // 19:30
date.formatted(date: .omitted, time: .standard) // 07:30 PM
date.formatted(date: .omitted, time: .shortened) // 7:30 PM
date.formatted(date: .omitted, time: .omitted)
Alternative syntax
Documentation
// We can also specify each DateComponent separately by chaining modifiers.
date.formatted(.dateTime.weekday(.wide).day().month().hour().minute())
// Tuesday, Jun 8, 7:30 pm
// Answer to specific question
date.formatted(.dateTime.year())
for Objective-C:
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:0];
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.dateFormat = #"yyyy";
NSString *dateString = [formatter stringFromDate:date];
for Swift:
let now = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy"
let dateString = formatter.string(from: now)
That's a good website for nsdateformatter.You can preview date strings with different DateFormatter in different local.