STRFTimeFormatter 1.1

STRFTimeFormatter 1.1

测试已测试
语言语言 Obj-CObjective C
许可证 MIT
发布最后发布2016年7月

Ell Neal维护。



  • Elliot Neal

概述

STRFTimeFormatter是一个简单的类,它封装了strftime_l(3)strptime_l(3)函数的接口,该接口复制了NSDateFormatter的接口。这个类用于高效地将机器生成的日期转换为字符串,详情见objc.io第9期中的“Strings and Localization”部分(见“解析机器输入”部分)。

安装

最好通过cocoapods进行安装。

Podfile

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);
}