Different day formatting while using NSDateFormatter on OS X Mavericks and Yosemite - osx-mavericks

Given following program:
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
#autoreleasepool {
NSDateFormatter *f = [[NSDateFormatter alloc] init];
f.timeStyle = NSDateFormatterNoStyle;
f.dateStyle = NSDateFormatterMediumStyle;
f.calendar = [NSCalendar autoupdatingCurrentCalendar];
f.timeZone = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
f.locale = [NSLocale localeWithLocaleIdentifier:#"ru"];
NSDate *feb_01_2012 = [NSDate dateWithTimeIntervalSince1970:1328054400];
NSLog(#"%#", [f stringFromDate:feb_01_2012]);
}
}
When compiled on OS X Yosemite produces following output:
2014-12-08 22:31:49.109 Untitled[14149:1398844] 1 февр. 2012 г.
However when compiled on OS X Mavericks it yields slightly different output (notice zero padding in day):
2014-12-08 22:27:01.890 formatter[54709:507] 01 февр. 2012 г.
Any ideas why this might happen? Bug only shows up for ru locale :-\
UPDATE: Found a way to get same amount of zeroes on both machines. Using +dateFormatFromTemplate:options:locale: gives expected result:
NSLocale *l = [NSLocale localeWithLocaleIdentifier:#"ru"];
f.locale = l;
f.dateFormat = [NSDateFormatter dateFormatFromTemplate:#"yMMMd"
options:0
locale:l];

Maybe different medium date format settings?

Maybe try to specify Russian locale more strictly. For example as [NSLocale localeWithLocaleIdentifier:#"ru_RU"];. I can suppose locale with identifier ru defaults to different locales on different OS X version. Though it's still weird...
And just to remind you can see the list of all locales available using [NSLocale availableLocaleIdentifiers].

Related

NSDateComponenets giving wrong year value

I use some code to translate a list of dates into different sections by month:
- (NSString *) calculateSectionPeriod{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth) fromDate: [self userDate]];
NSString *tmp = [NSString stringWithFormat:#"%ld", ([components year] * 1000) + [components month]];
NSLog(#"Section period = %#", tmp);
return tmp;
}
The userDate is set from a NSDatePicker.
In most cases, this prints strings like "2015006" (June 2015) or "2017011" (November 2017). However, in some cases, for some users of my app, it ends up printing "15003" (and ends up causing some problems in Core Data). This is coming from their own reports, and I haven't been able to reproduce the issue on my own device, but it's clear to me that the problem is with this code.
Any reason where the 'year' component would print '15' instead of of '2015'? Is it by region or some language settings? I've tried setting different regions and languages, but haven't been able to reproduce the problem myself.

Xcode preprocessor macro for dates

I want to "hardcode" an expiration date into my beta code. Right now I manually calculate a unix date and compare that to the current date time:
if([[NSDate date] timeIntervalSince1970]>1422748800) mustHalt = TRUE;
I'd like a way of replacing the 1422748800 with a macro that generates the equivalent number for a date 90 days in the future at compile time.
Any suggestions?
The predefined macro __DATE__ is what you need. Here is a SO question related to this. But maybe you want to use a code like this:
const int daysToExpire = 14;
NSString *compileDate = [NSString stringWithUTF8String:__DATE__];
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat:#"MMM d yyyy"];
NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
[df setLocale:usLocale];
NSDate *expireDate = [df dateFromString:compileDate];
bool isExpired = ([[NSDate date] compare:expireDate] == NSOrderedDescending); // decide for it

runModalForTypes works but setAllowedFileTypes+runModal doesn't?

When I use
NSArray* fileTypes = [[NSArray alloc] initWithObjects:#"pdf", #"PDF", nil];
NSInteger result = [openPanel runModalForTypes:fileTypes];
it works as expected (only pdf files can be selected), but when I use
NSArray* fileTypes = [[NSArray alloc] initWithObjects:#"pdf", #"PDF", nil];
[openPanel setAllowedFileTypes:fileTypes];
NSInteger result = [openPanel runModal];
I can select all types of files, not just pdfs. The documentation says that runModalForTypes is deprecated and we should use the second way. Am I doing something wrong?
Are you building for Mac OS X 10.6 and later? According to the header file: "On versions less than 10.6, this property is ignored."
Otherwise, your code looks correct (assuming you release fileTypes at some point) and works for me (tested on Mac OS X 10.7.2).
One minor suggestion is to use a Uniform Type Identifier to identify PDFs instead of hard-coding file extensions, like so:
NSArray *fileTypes = [NSArray arrayWithObjects:(id)kUTTypePDF, nil];

Problem with negative date on iPad and not on simulator

I'm working on an history application so I need to cope with date before and after JC.
I'm trying to parse a string with the form "01/01/-200" but it returns a null date while it's working with "01/01/200".
Here is my code :
NSDateFormatter* dateFormatter = [[[NSDateFormatter alloc]init] autorelease];
[dateFormatter setDateFormat:#"dd/MM/y"]; // #TODO Get negative date
[dateFormatter setLenient:NO];
NSDate* date = [dateFormatter dateFromString:dateString];
return date;
I also try using with the form "01/01/200 BC" setDateFormat:#"dd/MM/y G" but I can't make it work neither.
As mvds suggests in his answer, I tried the format "01/01/200 BC" on the simulator, and it's working... the problem only occurs on my iPad (version 3.2.1)
Do you have an idea how to do this properly ?
I just tried this:
NSDateFormatter* dateFormatter = [[[NSDateFormatter alloc]init] autorelease];
[dateFormatter setDateFormat:#"dd/MM/y G"];
NSDate *date = [dateFormatter dateFromString:#"01/01/200 BC"];
NSLog(#"refdate %#",[dateFormatter stringFromDate:date]);
date = [date addTimeInterval:24*3600*365*2];
NSLog(#"2 years later %#",[dateFormatter stringFromDate:date]);
which outputs:
refdate 01/01/200 BC
2 years later 01/01/198 BC
This is on 3.2, iPad simulator, so not the most recent SDK, but iPad nonetheless. Do you get different results, running this?
I finally find the trick.
The problem is that my iPad is in French so the Era has a different format :
BC is "av. J.-C."
AD is "ap. J.-C."
So I just had to change my XML file to get the correct format when parsing.
In order to display my date in the AD-BC format, I just convert it afterward :
+ (NSString*) convertIntoBCADString:(NSString*) originalString
{
NSString* newString = [originalString stringByReplacingOccurrencesOfString:#"av. J.-C." withString:#"BC"];
return [newString stringByReplacingOccurrencesOfString:#"ap. J.-C." withString:#"AD"];
}

Easiest way to format a number with thousand separators to an NSString according to the Locale

I can't seem to find an easy way to do it. The exact thing I need is:
[NSString stringWithFormat:#"%d doodads", n];
Where n is an int. So for 1234 I'd want this string (under my locale):
#"1,234 doodads"
Thanks.
For 10.6 this works:
NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior: NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
NSString *numberString = [numberFormatter stringFromNumber: [NSNumber numberWithInteger: i]];
And it properly handles localization.
I have recently discovered this one-liner:
[#1234567 descriptionWithLocale:[NSLocale currentLocale]]; // 1,234,567
Or in Swift 2:
1234567.descriptionWithLocale(NSLocale.currentLocale()) // 1,234,567
Swift 3/4:
(1234567 as NSNumber).description(withLocale: Locale.current)
Formatted per the question:
[#(n) descriptionWithLocale:[NSLocale currentLocale]];
Formatted without Objective-C literals:
[[NSNumber numberWithInt:n] descriptionWithLocale:[NSLocale currentLocale]];
This is the solution I was looking for when I asked the question. Available since iOS 2.0 and OS X 10.0, documented to return a string version of the number formatted as per the locale provided. stringValue is even documented to use this method but passing nil.
Seeing as it is my question and this fits my answer best, I am tempted to change the tick, but it seems cruel. Update I changed the tick, this answer is the answer.
The below doesn't address the locale, but it is a better way (in my opinion) of setting the thousand separator on the number formatter.
NSNumberFormatter *numberFormat = [[[NSNumberFormatter alloc] init] autorelease];
numberFormat.usesGroupingSeparator = YES;
numberFormat.groupingSeparator = #",";
numberFormat.groupingSize = 3;
Todd Ransom answered this perfectly.
I would just like to add (in a separate comment, so I can show some nicely formatted code), that if you plan to do this regularly, it's worth creating an NSString helper class.
So, create yourself an NSStringHelper.h containing this:
#import <Foundation/Foundation.h>
#interface NSString (NSStringHelper)
+(NSString*)formatWithThousandSeparator:(NSInteger)number;
#end
..and an NSStringHelper.m file containing this:
#import "NSStringHelper.h"
#implementation NSString (NSStringHelper)
+(NSString*)formatWithThousandSeparator:(NSInteger)number
{
// Format a number with thousand seperators, eg: "12,345"
NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior: NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
NSString *result = [numberFormatter stringFromNumber:[NSNumber numberWithInteger:number]];
return result;
}
#end
This gives you the perfect way to reuse this code in future projects.
#import "NSStringHelper.h"
NSInteger numOfUsers = 12345;
NSString* strNumberOfUsers = [NSString formatWithThousandSeparator:numOfUsers];
Cool, hey ?
Again, apologies for reposting Todd's answer (which was exactly what I was looking for !), but this is a great way to solve the problem, and have it ready to be used in your future XCode projects.
Use an NSNumberFormatter.

Resources