STRFTimeFormatter
是一个简单的类,它封装了strftime_l(3)
和strptime_l(3)
函数的接口,该接口复制了NSDateFormatter
的接口。这个类用于高效地将机器生成的日期转换为字符串,详情见objc.io
第9期中的“Strings and Localization”部分(见“解析机器输入”部分)。
最好通过cocoapods进行安装。
pod 'STRFTimeFormatter'
STRFTimeFormatter
复制了NSDateFormatter
类的便利函数
- (NSDate *)dateFromString:(NSString *)string;
- (NSString *)stringFromDate:(NSDate *)date;
唯一的可选设置是更改strftime_l(3)
和strptime_l(3)
函数所使用的formatString
。有关可能的标准指定符,请参阅strftime(3)
手册页面。默认为%Y-%m-%dT%H:%M:%S%z
,对应于如2014-02-18T12:42:07+0000
这样的字符串格式。
STRFTimeFormatter
不是NSFormatter
的子类,原因有两个
strptime_l(3)
比-[NSDateFormatter dateFromString:]
要快得多,但使用起来很麻烦。STRFTimeFormatter
在处理固定格式的日期字符串时提供了巨大的性能提升,但无需始终入坑C库(或者必须到处加上#import <xlocale.h>
- 多么丑陋)。
这里有证据
#import <Foundation/Foundation.h>
#import <xlocale.h>
#define LOG_SAMPLES
void startTimer(NSDate **timer);
void finishTimer(NSDate *timer, NSString *message);
int main(int argc, char *argv[])
{
@autoreleasepool {
NSUInteger numberOfDatesToCreate = 604800;
NSLog(@"Creating %llu date strings.", (unsigned long long)numberOfDatesToCreate);
NSDate *timer;
// string format to be used by strftime_l and strptime_l
const char *formatString = "%Y-%m-%dT%H:%M:%S%z";
// date formatter to be used
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
[dateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
NSMutableArray *strfStrings = [[NSMutableArray alloc] init];
NSMutableArray *formatterStrings = [[NSMutableArray alloc] init];
startTimer(&timer);
for (NSUInteger i = 0; i < numberOfDatesToCreate; i++) {
@autoreleasepool {
// create the date anyway, lets keep the comparison fair :)
NSDate *date = [NSDate dateWithTimeIntervalSince1970:(1392595200 + i)];
time_t timeInterval = [date timeIntervalSince1970];
struct tm time = *localtime(&timeInterval);
char buffer[80];
strftime_l(buffer, sizeof(buffer), formatString, &time, NULL);
NSString *dateString = [NSString stringWithCString:buffer encoding:NSASCIIStringEncoding];
#ifdef LOG_SAMPLES
if (i % 86400 == 0) {
NSLog(@"Sample: %@", dateString);
}
#endif
[strfStrings addObject:dateString];
}
}
finishTimer(timer, @"Created date strings using strftime_l(3)");
startTimer(&timer);
for (NSUInteger i = 0; i < numberOfDatesToCreate; i++) {
@autoreleasepool {
NSDate *date = [NSDate dateWithTimeIntervalSince1970:(1392595200 + i)];
NSString *dateString = [dateFormatter stringFromDate:date];
#ifdef LOG_SAMPLES
if (i % 86400 == 0) {
NSLog(@"Sample: %@", dateString);
}
#endif
[formatterStrings addObject:dateString];
}
}
finishTimer(timer, @"Created date strings using NSDateFormatter");
NSLog(@"Parsing %llu date strings.", (unsigned long long)numberOfDatesToCreate);
startTimer(&timer);
for (NSUInteger i = 0; i < numberOfDatesToCreate; i++) {
NSString *dateString = strfStrings[i];
struct tm time;
strptime_l([dateString cStringUsingEncoding:NSASCIIStringEncoding], formatString, &time, NULL);
time_t timeInterval = mktime(&time);
// create the date anyway, lets keep the comparison fair :)
__unused NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
#ifdef LOG_SAMPLES
if (i % 86400 == 0) {
NSLog(@"Sample: %@", date);
}
#endif
}
finishTimer(timer, @"Parsed date strings using strptime_l(3)");
startTimer(&timer);
for (NSUInteger i = 0; i < numberOfDatesToCreate; i++) {
NSString *dateString = formatterStrings[i];
__unused NSDate *date = [dateFormatter dateFromString:dateString];
#ifdef LOG_SAMPLES
if (i % 86400 == 0) {
NSLog(@"Sample: %@", date);
}
#endif
}
finishTimer(timer, @"Parsed date strings using NSDateFormatter");
}
}
void startTimer(NSDate **timer)
{
*timer = [NSDate date];
}
void finishTimer(NSDate *timer, NSString *message)
{
NSTimeInterval interval = -[timer timeIntervalSinceNow];
NSLog(@"%@ -- %g seconds", message, interval);
}