[ACCEPTED]-Ordinal Month-day Suffix Option for NSDateFormatter setDateFormat-nsdateformatter
None of these answers were as aesthetically 3 pleasing as what I'm using, so I thought 2 I would share:
Swift 3:
func daySuffix(from date: Date) -> String {
let calendar = Calendar.current
let dayOfMonth = calendar.component(.day, from: date)
switch dayOfMonth {
case 1, 21, 31: return "st"
case 2, 22: return "nd"
case 3, 23: return "rd"
default: return "th"
}
}
Objective-C:
- (NSString *)daySuffixForDate:(NSDate *)date {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSInteger dayOfMonth = [calendar component:NSCalendarUnitDay fromDate:date];
switch (dayOfMonth) {
case 1:
case 21:
case 31: return @"st";
case 2:
case 22: return @"nd";
case 3:
case 23: return @"rd";
default: return @"th";
}
}
Obviously, this 1 only works for English.
NSDate *date = [NSDate date];
NSDateFormatter *prefixDateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[prefixDateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
[prefixDateFormatter setDateFormat:@"h:mm a EEEE MMMM d"];
NSString *prefixDateString = [prefixDateFormatter stringFromDate:date];
NSDateFormatter *monthDayFormatter = [[[NSDateFormatter alloc] init] autorelease];
[monthDayFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
[monthDayFormatter setDateFormat:@"d"];
int date_day = [[monthDayFormatter stringFromDate:date] intValue];
NSString *suffix_string = @"|st|nd|rd|th|th|th|th|th|th|th|th|th|th|th|th|th|th|th|th|th|st|nd|rd|th|th|th|th|th|th|th|st";
NSArray *suffixes = [suffix_string componentsSeparatedByString: @"|"];
NSString *suffix = [suffixes objectAtIndex:date_day];
NSString *dateString = [prefixDateString stringByAppendingString:suffix];
NSLog(@"%@", dateString);
0
This is easily done as of iOS9
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterOrdinalStyle;
NSArray<NSNumber *> *numbers = @[@1, @2, @3, @4, @5];
for (NSNumber *number in numbers) {
NSLog(@"%@", [formatter stringFromNumber:number]);
}
// "1st", "2nd", "3rd", "4th", "5th"
Taken from NSHipster
Swift 2.2:
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = .OrdinalStyle
let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for number in numbers {
print(numberFormatter.stringFromNumber(number)!)
}
0
Here's another implementation for a method 3 to generate the suffix. The suffixes it 2 produces are only valid in English and may 1 not be correct in other languages:
- (NSString *)suffixForDayInDate:(NSDate *)date
{
NSInteger day = [[[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] components:NSDayCalendarUnit fromDate:date] day];
if (day >= 11 && day <= 13) {
return @"th";
} else if (day % 10 == 1) {
return @"st";
} else if (day % 10 == 2) {
return @"nd";
} else if (day % 10 == 3) {
return @"rd";
} else {
return @"th";
}
}
Date formatters on Mac OS 10.5 and the iPhone 4 use TR35 as their format specifier standard. This 3 spec doesn't allow for such a suffix on 2 any date; if you want one, you'll have to 1 generate it yourself.
This is already implemented in the Foundation.
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .ordinal
numberFormatter.locale = Locale.current
numberFormatter.string(for: 1) //Should produce 1st
numberFormatter.string(for: 2) //Should produce 2nd
numberFormatter.string(for: 3) //Should produce 3rd
numberFormatter.string(for: 4) //Should produce 4th
0
This will do the formatting in two steps: first, create 11 a sub-string that is the day with an appropriate 10 suffix, then create a format string for 9 the remaining parts, plugging in the already-formatted 8 day.
func ordinalDate(date: Date) -> String {
let ordinalFormatter = NumberFormatter()
ordinalFormatter.numberStyle = .ordinal
let day = Calendar.current.component(.day, from: date)
let dayOrdinal = ordinalFormatter.string(from: NSNumber(value: day))!
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "h:mm a EEEE MMMM '\(dayOrdinal)'"
return dateFormatter.string(from: Date())
}
Since the ordinal day is built by NumberFormatter
, it 7 should work in all languages, not just English.
You 6 could get a format string ordered for the 5 current locale by replacing the assignment 4 to dateFormat
with this:
dateFormatter.dateFormat = DateFormatter.dateFormat(fromTemplate: "h:mm a EEEE MMMM d", options: 0, locale: dateFormatter.locale)?.replacingOccurrences(of: "d", with: "'\(dayOrdinal)'")
Note the advice from several 3 others that creating formatters is expensive, so 2 you should cache and reuse them in code 1 that is called frequently.
Matt Andersen's answer is quite elaborate, and 11 so is SDJMcHattie. But NSDateFormatter is 10 quite heavy on the cpu and if you call this 9 100x you really see the impact, so here 8 is a combined solution derived from the 7 answers above. (Please note that the above 6 are still correct)
NSDateFormatter is crazily 5 expensive to create. Create once and reuse, but beware: it's 4 not thread safe, so one per thread.
Assuming 3 self.date = [NSDate date];
- (NSString *)formattedDate{
static NSDateFormatter *_dateFormatter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_dateFormatter = [[NSDateFormatter alloc] init];
_dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
_dateFormatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
});
_dateFormatter.dateFormat = [NSString stringWithFormat:@"h:mm a EEEE MMMM d'%@'", [self suffixForDayInDate:self.date]];
NSString *date = [_dateFormatter stringFromDate:self.date];
return date;
}
/* SDJMcHattie's code, this is more convenient 2 than using an array */
- (NSString *)suffixForDayInDate:(NSDate *)date{
NSInteger day = [[[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] components:NSDayCalendarUnit fromDate:date] day];
if (day >= 11 && day <= 13) {
return @"th";
} else if (day % 10 == 1) {
return @"st";
} else if (day % 10 == 2) {
return @"nd";
} else if (day % 10 == 3) {
return @"rd";
} else {
return @"th";
}
}
Output: 3:11 PM Saturday 1 August 15th
None of the answers uses the ordinal number 2 style already present in Number Formatter 1 in swift.
var dateString: String {
let calendar = Calendar.current
let dateComponents = calendar.component(.day, from: date)
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .ordinal
let day = numberFormatter.string(from: dateComponents as NSNumber)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM"
return day! + dateFormatter.string(from: date)
}
This will give string in format "10:10 1 PM Saturday, 2nd August"
-(NSString*) getTimeInString:(NSDate*)date
{
NSString* string=@"";
NSDateComponents *components = [[NSCalendar currentCalendar] components: NSCalendarUnitDay fromDate:date];
if(components.day == 1 || components.day == 21 || components.day == 31){
string = @"st";
}else if (components.day == 2 || components.day == 22){
string = @"nd";
}else if (components.day == 3 || components.day == 23){
string = @"rd";
}else{
string = @"th";
}
NSDateFormatter *prefixDateFormatter = [[NSDateFormatter alloc] init]; [prefixDateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
[prefixDateFormatter setDateFormat:[NSString stringWithFormat:@"h:mm a EEEE, d'%@' MMMM",string]];
NSString *dateString = [prefixDateFormatter stringFromDate:date];
return dateString;
}
Or if you want the suffix for any number:
extension Int {
public func suffix() -> String {
let absSelf = abs(self)
switch (absSelf % 100) {
case 11...13:
return "th"
default:
switch (absSelf % 10) {
case 1:
return "st"
case 2:
return "nd"
case 3:
return "rd"
default:
return "th"
}
}
}
}
The 15 thinking being that there are 5 possibilities 14 for positive numbers. It's first place digit 13 is 1 being "st". It's second place digit 12 is 2 being "2nd". It's third place digit 11 is 3 being "rd". Any other case is "th", or 10 if it's second place digit is 1, then the 9 above rules do not apply and it is "th".
Modulo 8 100 gives us the digit's last two numbers, so 7 we can check for 11 to 13. Modulo 10 gives 6 us the digit's last number, so we can check 5 for 1, 2, 3 if not caught by the first condition.
Try 4 that extension in playgrounds:
let a = -1
a.suffix() // "st"
let b = 1112
b.suffix() // "th"
let c = 32
c.suffix() // "nd"
Would love 3 to see if there is an even shorter way to 2 write this using binary operations and/or 1 an array!
- (void)viewDidLoad
{
NSDate *date = [NSDate date];
NSDateFormatter *prefixDateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[prefixDateFormatter setDateFormat:@"yyy-dd-MM"];
date = [prefixDateFormatter dateFromString:@"2014-6-03"]; //enter yourdate
[prefixDateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
[prefixDateFormatter setDateFormat:@"EEEE MMMM d"];
NSString *prefixDateString = [prefixDateFormatter stringFromDate:date];
NSDateFormatter *monthDayFormatter = [[[NSDateFormatter alloc] init] autorelease];
[monthDayFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
[monthDayFormatter setDateFormat:@"d"];
int date_day = [[monthDayFormatter stringFromDate:date] intValue];
NSString *suffix_string = @"|st|nd|rd|th|th|th|th|th|th|th|th|th|th|th|th|th|th|th|th|th|st|nd|rd|th|th|th|th|th|th|th|st";
NSArray *suffixes = [suffix_string componentsSeparatedByString: @"|"];
NSString *suffix = [suffixes objectAtIndex:date_day];
NSString *dateString = [prefixDateString stringByAppendingString:suffix];
NSLog(@"%@", dateString);
}
0
I added these two methods to NSDate with 7 a category NSDate+Additions.
\- (NSString *)monthDayYear
{
NSDateFormatter * dateFormatter = NSDateFormatter.new;
[dateFormatter setDateFormat:@"MMMM d*, YYYY"];
NSString *dateString = [dateFormatter stringFromDate:self];
return [dateString stringByReplacingOccurrencesOfString:@"*" withString:[self ordinalSuffixForDay]];
}
\- (NSString *)ordinalSuffixForDay {
NSDateFormatter * dateFormatter = NSDateFormatter.new;
[dateFormatter setDateFormat:@"d"];
NSString *dateString = [dateFormatter stringFromDate:self];
NSString *suffix = @"th";
if ([dateString length] == 2 && [dateString characterAtIndex:0] == '1') {
return suffix;
}
switch ([dateString characterAtIndex:[dateString length]-1]) {
case '1':
suffix = @"st";
break;
case '2':
suffix = @"nd";
break;
case '3':
suffix = @"rd";
break;
}
return suffix;
}
You could make 6 them more efficient by combining them and 5 indexing the one's place digit of the day 4 within your format string as the switch 3 point. I opted to separate the functionality 2 so the ordinal suffixes can be called separately 1 for different date formats.
- (NSString *)dayWithSuffixForDate:(NSDate *)date {
NSInteger day = [[[NSCalendar currentCalendar] components:NSDayCalendarUnit fromDate:date] day];
NSString *dayOfMonthWithSuffix, *suffix = nil ;
if(day>0 && day <=31)
{
switch (day)
{
case 1:
case 21:
case 31: suffix = @"st";
break;
case 2:
case 22: suffix = @"nd";
break;
case 3:
case 23: suffix = @"rd";
break;
default: suffix = @"th";
break;
}
dayOfMonthWithSuffix = [NSString stringWithFormat:@"%ld%@", (long)day , suffix];
}
return dayOfMonthWithSuffix;
}
0
I just used some of the answers on here 13 to solve this problem myself, but I think 12 my solution is just a little bit different 11 from some of the solutions here.
I like using 10 NumberFormatter to properly handle ordinal 9 formatting (with localization), and DateFormatter 8 for the rest, but I don't like that some 7 of these solutions require re-building the 6 date formatter per-use (which is expensive).
Here's 5 what I'm using, which should give decent 4 localization by way way of leaning on Apple's 3 APIs, and shouldn't be too heavy on processing 2 because of the static creation of the formatters 1 (requires iOS 9.0+):
// ...in a class that needs ordinal date formatting
static let stringFormatDateFormatter: DateFormatter = {
let formatter = DateFormatter()
// A date format, replacing `d` with `'%@'` string format placeholder
formatter.dateFormat = "MMM '%@', YYYY"
return formatter
}()
static let ordinalFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .ordinal
return formatter
}()
func ordinalDateString(from date: Date) -> String {
// Pull the day from the date
let day = NSCalendar.current.component(.day, from: date)
// Create the ordinal, fall back to non-ordinal number due to optionality
let dayOrdinal = Self.ordinalFormatter.string(for: day) ?? "\(day)"
// Create the formatter with placeholder for day (e.g. "Jan %@, 2011")
let dateStringFormat = Self.stringFormatDateFormatter.string(from: date)
// Inject ordinal ("Jan 10th, 2011")
return String(format: dateStringFormat, dayOrdinal)
}
Swift 5 - Number Formatter + Date Formatter
You can use the ordinal number style already 5 present in NumberFormatter in swift.
func formatted(date: Date, in calendar: Calendar = Calendar.current) -> String {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .ordinal
let day = calendar.component(.day, from: date)
let dateFormat: String
if let dayOrdinal = numberFormatter.string(from: NSNumber(integerLiteral: day)) {
dateFormat = "E, '\(dayOrdinal)' MMM yyyy 'at' h:mma"
} else {
dateFormat = "E, d MMM yyyy 'at' h:mma"
}
let formatter = DateFormatter()
formatter.dateFormat = dateFormat
formatter.amSymbol = "am"
formatter.pmSymbol = "pm"
return formatter.string(from: date)
}
Obs: This way you will 4 avoid the force unwrap, you can use a custom 3 Calendar
and, if needed, you can define date Format: String
outside 2 the function (just pass it through the method 1 declaration).
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.