Latest 1.1
Homepage https://github.com/ellneal/STRFTimeFormatter
License MIT
Platforms ios 6.0, requires ARC
Authors

Overview

STRFTimeFormatter is a simple class that encapsulates the strftime_l(3) and strptime_l(3) functions in an interface that replicates that of NSDateFormatter. This class is to be used to convert machine generated dates in an efficient manner as mentioned in issue 9 of objc.io (see the section on Parsing Machine Input).

Installation

Installation is best done via cocoapods.

Podfile

pod 'STRFTimeFormatter'

Usage

STRFTimeFormatter replicates the convenience functions of the NSDateFormatter class:

- (NSDate *)dateFromString:(NSString *)string;
- (NSString *)stringFromDate:(NSDate *)date;

The only optional setup is to change the formatString used by the strftime_l(3) and strptime_l(3) functions. See the strftime(3) man page for possible format specifiers. The default is %Y-%m-%dT%H:%M:%S%z, which corresponds to a string format such as 2014-02-18T12:42:07+0000.

STRFTimeFormatter is not a subclass of NSFormatter, for two reasons:

  • It should be cheap to allocate an instance.
  • Laziness.

The Point

strptime_l(3) is a lot faster than -[NSDateFormatter dateFromString:], but it’s a pain to use. STRFTimeFormatter gives you the huge performance bump when dealing fixed format date strings, but without the hassle of dropping into C libraries all the time (or having to put #import <xlocale.h> everywhere – how ugly).

Here’s the proof:

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

Latest podspec

{
    "name": "STRFTimeFormatter",
    "version": "1.1",
    "summary": "NSDateFormatter replacement for machine encoded dates.",
    "homepage": "https://github.com/ellneal/STRFTimeFormatter",
    "license": {
        "type": "MIT",
        "file": "LICENSE"
    },
    "authors": {
        "Elliot Neal": "[email protected]"
    },
    "source": {
        "git": "https://github.com/ellneal/STRFTimeFormatter.git",
        "tag": "v1.1"
    },
    "source_files": "STRFTimeFormatter/**/*.{h,m}",
    "requires_arc": true,
    "platforms": {
        "ios": "6.0"
    }
}

Pin It on Pinterest

Share This