I've been playing around with date formatting and came up with a simple test to see if it's working as intended. It creates a number of dates starting from current and going back in time 15 minutes with each iteration. Then test outputs formatted date and the actual date data to compare:
- (void)testDateFormatting {
NSDate *date;
NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setLocale:[NSLocale localeWithLocaleIdentifier:@"ru_RU" ]];
[formatter setDateFormat:@"HH:mm EE dd.MM"];
for (NSUInteger i = 0; i < 500; i++) {
NSTimeInterval time = - (60.0 * i * 15);
date = [NSDate dateWithTimeIntervalSinceNow:time];
NSString *formattedDateString = [SCRBDateFormatter stringFormattedToXAgoFromDate:date];
NSLog(@"%@ - %@", formattedDateString, [formatter stringFromDate:date]);
XCTAssert(formattedDateString, @"TEST_ERROR: stringFormattedToXAgoFromDate: returned nil.");
}
}
When I increased the number of iterations to 500 I've noticed that like 1% of the output is messed up.
UPDATE: looking closely to output I've noticed that even the sequence is not right, how is this possible? (Changed time gap to 1 minute from 15 to see a little better the sequence)
2015-09-16 16:28:42.292 ScoreBook[5081:666920] 35 МИН. НАЗАД - 15:53 ср 16.09
2015-09-16 16:28:42.293 ScoreBook[5081:666920] 36 МИН. НАЗАД - 15:52 ср 16.09
2015-09-16 16:28:42.293 ScoreBook[5081:666920] 37 МИН. НАЗАД - 15:51 ср 16.09
2015-09-16 16:28:42.294 ScoreBook[5081:666920] 38 МИН. НАЗАср 16.09
2015-09-16 16:28:42.293 ScoreBook[5081:666920] 36 МИН. НАЗАД - 15:52 ср 16.09
2015-09-16 16:28:42.293 ScoreBook[5081:666920] 37 МИН. НАЗАД - 15:51 ср 16.09
2015-09-16 16:28:42.294 ScoreBook[5081:666920] 38 МИН. НАЗА\320Д - 15:50 ср 16.09
2015-09-16 16:28:42.294 ScoreBook[5081:666920] 39 МИН. НАЗАД - 15:49 ср 16.09
2015-09-16 16:28:42.294 ScoreBook[5081:666920] 40 МИН. НАЗАД - 15:48 ср 16.09
2015-09-16 16:28:42.295 ScoreBook[5081:666920] 41 МИН. НАЗАД - 15:47 ср 16.09
Of course the output should be the same. I'm not sure what's causing this. Seems like the printed string is being changed in the middle of printing. I thought all this methods are synchronous. Any idea where to look?
Actual app doesn't seem to have this problems, they occur only in test.
Here's formatting code. I know it's very poorly optimised right now with lots of return statements, this is not the point.
+ (NSString *)stringFormattedToXAgoFromDate:(NSDate *)date {
NSString *result = nil;
static NSCalendar *calendar = nil;
if (!calendar) {
calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
}
NSTimeInterval timeGap = [[NSDate date] timeIntervalSince1970] - [date timeIntervalSince1970];
if (timeGap < 0) {
NSLog(@"SCRB_ERROR: scrb_formatDateToXTimeAgoString: cannot format date. Date argument is later than current date.");
return nil;
}
NSDateComponents *components = [calendar components:NSCalendarUnitMinute | NSCalendarUnitHour | NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitSecond | NSCalendarUnitYear fromDate:date toDate:[NSDate date] options:0];
static NSDateFormatter *formatter = nil;
if (!formatter) {
formatter = [NSDateFormatter new];
NSLocale *_ruLocale = [NSLocale localeWithLocaleIdentifier:kRuLocaleIdentifier];
[formatter setLocale:_ruLocale];
}
if (timeGap < 60) {
result = @"ТОЛЬКО ЧТО";
return result;
}
if (timeGap < 60*60) {
result = [NSString stringWithFormat:@"%td МИН. НАЗАД", components.minute];
return result;
}
if (timeGap < 60*60 * 5) {
result = [NSString stringWithFormat:@"%td Ч. НАЗАД", components.hour];
return result;
}
if (timeGap < 60*60 * 24 * 2) {
if ([calendar isDateInToday:date]) {
[formatter setDateFormat:@"HH:mm"];
result = [formatter stringFromDate:date];
return result;
} else if ([calendar isDateInYesterday:date]) {
return @"ВЧЕРА";
}
}
if (timeGap < 60*60*24 * 7) {
[formatter setDateFormat:@"EEEE"];
result = [[formatter stringFromDate:date] uppercaseString];
return result;
}
if (components.year < 1) {
[formatter setDateFormat:@"dd MMM"];
result = [[formatter stringFromDate:date] uppercaseString];
return result;
}
if (components.year >= 0) {
[formatter setDateFormat:@"dd.MM.YYYY"];
result = [formatter stringFromDate:date];
return result;
}
return result;
}
UPDATE 2: adding [NSThread sleepForTimeInterval:0.001];
at the end of each iteration seemed to get rid of the problem. I still have no idea why.
I also noticed that \320
appearing from time to time, it lead me to idea that it might something to do with characters encoding. Changing every string to english characters only solves the problem too. I also came across this post, but didn't quite understand if it's actually related to my problem. The sequence skip is still the biggest mystery to me.
I kept investigating and added two lines. This one before the for-in loop
NSMutableArray *array = [NSMutableArray new];
and this one inside the loop
[array addObject:formattedDateString];
and a breakpoint at the end of the test. Strings inside the array doesn't have any mistakes that appear in the log.