Bringing in Doug Scandrett's mods for generating more detailed relative date phrases into a branch for posterity.

--HG--
branch : more-detailed-datephrase-generation
This commit is contained in:
Bill Garrison
2013-03-24 14:48:19 -04:00
parent 0e4d056132
commit f28d668817
7 changed files with 154 additions and 34 deletions

View File

@@ -42,7 +42,7 @@
F2A33B3C12AD9CA400459019 /* SORelativeDateTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SORelativeDateTransformer.m; path = ../SORelativeDateTransformer/SORelativeDateTransformer.m; sourceTree = "<group>"; };
F2A33B3D12AD9CA400459019 /* TesterViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TesterViewController.h; sourceTree = "<group>"; };
F2A33B3E12AD9CA400459019 /* TesterViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TesterViewController.m; sourceTree = "<group>"; };
F2CD10CF166401CA004A81F3 /* README.markdown */ = {isa = PBXFileReference; lastKnownFileType = text; name = README.markdown; path = ../README.markdown; sourceTree = "<group>"; };
F2CD10CF166401CA004A81F3 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = text; name = README.md; path = ../README.md; sourceTree = "<group>"; };
F2CD10D1166402F8004A81F3 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -72,7 +72,7 @@
isa = PBXGroup;
children = (
DAD012DB16C8311E00AF7B51 /* SORelativeDateTransformer.bundle */,
F2CD10CF166401CA004A81F3 /* README.markdown */,
F2CD10CF166401CA004A81F3 /* README.md */,
F2A33B3B12AD9CA400459019 /* SORelativeDateTransformer.h */,
F2A33B3C12AD9CA400459019 /* SORelativeDateTransformer.m */,
F2A33B3412AD9C7C00459019 /* Tester App Source */,

View File

@@ -18,6 +18,7 @@
}
- (IBAction) datePickerChangedValue:(id)sender;
- (IBAction) relativeDepthChangeValue:(UISegmentedControl *)sender;
@end

View File

@@ -28,4 +28,11 @@
[relativeDateLabel setText: [relativeDateTransformer transformedValue:[datePicker date]]];
}
- (IBAction) relativeDepthChangeValue:(UISegmentedControl *)sender
{
relativeDateTransformer.relativeTransformDepth = (RelativeTransformDepth)sender.selectedSegmentIndex;
// update UI
[relativeDateLabel setText: [relativeDateTransformer transformedValue:[datePicker date]]];
}
@end

View File

@@ -2,18 +2,19 @@
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1280</int>
<string key="IBDocument.SystemVersion">12C54</string>
<string key="IBDocument.InterfaceBuilderVersion">2843</string>
<string key="IBDocument.SystemVersion">12C3006</string>
<string key="IBDocument.InterfaceBuilderVersion">3084</string>
<string key="IBDocument.AppKitVersion">1187.34</string>
<string key="IBDocument.HIToolboxVersion">625.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">1929</string>
<string key="NS.object.0">2083</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBProxyObject</string>
<string>IBUIDatePicker</string>
<string>IBUILabel</string>
<string>IBUISegmentedControl</string>
<string>IBUIView</string>
</array>
<array key="IBDocument.PluginDependencies">
@@ -82,6 +83,7 @@ AQABAAEAAQAB//+dkAEA//+PgAAE//+dkAEI//+dkAEMUERUAFBTVABQV1QAUFBUAAAAAAEAAAABA</b
<string key="NSFrame">{{20, 334}, {280, 61}}</string>
<reference key="NSSuperview" ref="774585933"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="1035436030"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC40MDAwMDAwMDYgMC40MDAwMDAwMDYgMC40MDAwMDAwMDYAA</bytes>
@@ -160,6 +162,44 @@ AQABAAEAAQAB//+dkAEA//+PgAAE//+dkAEI//+dkAEMUERUAFBTVABQV1QAUFBUAAAAAAEAAAABA</b
<bool key="IBUIAdjustsFontSizeToFit">NO</bool>
<double key="preferredMaxLayoutWidth">275</double>
</object>
<object class="IBUISegmentedControl" id="1035436030">
<reference key="NSNextResponder" ref="774585933"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{16, 410}, {290, 30}}</string>
<reference key="NSSuperview" ref="774585933"/>
<reference key="NSWindow"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="IBUIOpaque">NO</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<int key="IBSegmentControlStyle">2</int>
<int key="IBNumberOfSegments">3</int>
<int key="IBSelectedSegmentIndex">0</int>
<array key="IBSegmentTitles">
<string>First Match</string>
<string>Second Match</string>
<string>Third Match</string>
</array>
<array class="NSMutableArray" key="IBSegmentWidths">
<real value="0.0"/>
<real value="0.0"/>
<real value="0.0"/>
</array>
<array class="NSMutableArray" key="IBSegmentEnabledStates">
<boolean value="YES"/>
<boolean value="YES"/>
<boolean value="YES"/>
</array>
<array class="NSMutableArray" key="IBSegmentContentOffsets">
<string>{0, 0}</string>
<string>{0, 0}</string>
<string>{0, 0}</string>
</array>
<array class="NSMutableArray" key="IBSegmentImages">
<object class="NSNull" id="4"/>
<reference ref="4"/>
<reference ref="4"/>
</array>
</object>
</array>
<string key="NSFrame">{{0, 20}, {320, 460}}</string>
<reference key="NSSuperview"/>
@@ -209,6 +249,15 @@ AQABAAEAAQAB//+dkAEA//+PgAAE//+dkAEI//+dkAEMUERUAFBTVABQV1QAUFBUAAAAAAEAAAABA</b
</object>
<int key="connectionID">14</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">relativeDepthChangeValue:</string>
<reference key="source" ref="1035436030"/>
<reference key="destination" ref="372490531"/>
<int key="IBEventType">13</int>
</object>
<int key="connectionID">16</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
@@ -236,6 +285,7 @@ AQABAAEAAQAB//+dkAEA//+PgAAE//+dkAEI//+dkAEMUERUAFBTVABQV1QAUFBUAAAAAAEAAAABA</b
<reference ref="7301982"/>
<reference ref="210052879"/>
<reference ref="1007823394"/>
<reference ref="1035436030"/>
</array>
<reference key="parent" ref="0"/>
</object>
@@ -254,6 +304,11 @@ AQABAAEAAQAB//+dkAEA//+PgAAE//+dkAEI//+dkAEMUERUAFBTVABQV1QAUFBUAAAAAAEAAAABA</b
<reference key="object" ref="7301982"/>
<reference key="parent" ref="774585933"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">15</int>
<reference key="object" ref="1035436030"/>
<reference key="parent" ref="774585933"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
@@ -263,6 +318,7 @@ AQABAAEAAQAB//+dkAEA//+PgAAE//+dkAEI//+dkAEMUERUAFBTVABQV1QAUFBUAAAAAAEAAAABA</b
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="10.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="11.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="15.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="6.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="9.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</dictionary>
@@ -270,24 +326,27 @@ AQABAAEAAQAB//+dkAEA//+PgAAE//+dkAEI//+dkAEMUERUAFBTVABQV1QAUFBUAAAAAAEAAAABA</b
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">14</int>
<int key="maxID">16</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
<object class="IBPartialClassDescription">
<string key="className">TesterViewController</string>
<string key="superclassName">UIViewController</string>
<object class="NSMutableDictionary" key="actions">
<string key="NS.key.0">datePickerChangedValue:</string>
<string key="NS.object.0">id</string>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
<string key="NS.key.0">datePickerChangedValue:</string>
<object class="IBActionInfo" key="NS.object.0">
<dictionary class="NSMutableDictionary" key="actions">
<string key="datePickerChangedValue:">id</string>
<string key="relativeDepthChangeValue:">UISegmentedControl</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="actionInfosByName">
<object class="IBActionInfo" key="datePickerChangedValue:">
<string key="name">datePickerChangedValue:</string>
<string key="candidateClassName">id</string>
</object>
</object>
<object class="IBActionInfo" key="relativeDepthChangeValue:">
<string key="name">relativeDepthChangeValue:</string>
<string key="candidateClassName">UISegmentedControl</string>
</object>
</dictionary>
<dictionary class="NSMutableDictionary" key="outlets">
<string key="datePicker">UIDatePicker</string>
<string key="relativeDateLabel">UILabel</string>
@@ -317,10 +376,10 @@ AQABAAEAAQAB//+dkAEA//+PgAAE//+dkAEI//+dkAEMUERUAFBTVABQV1QAUFBUAAAAAAEAAAABA</b
</object>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
<real value="1536" key="NS.object.0"/>
<real value="1552" key="NS.object.0"/>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">1929</string>
<string key="IBCocoaTouchPluginVersion">2083</string>
</data>
</archive>

View File

@@ -28,12 +28,20 @@ SORelativeDateTransformer is a value transformer that generates a human-readable
#import <Foundation/Foundation.h>
typedef enum {
FirstMatchOnly = 0,
IncludeSecondMatchIgnoreZero,
IncludeThirdMatchIgnoreZero,
}RelativeTransformDepth;
@interface SORelativeDateTransformer : NSValueTransformer
{
NSCalendar *__calendar;
NSUInteger __unitFlags;
NSArray *__dateComponentSelectorNames;
NSMutableArray *__partialDateStrings;
}
@property (nonatomic) RelativeTransformDepth relativeTransformDepth;
/**
\brief Transform an NSDate into a phrase expressing the relative difference between that date and now.

View File

@@ -43,6 +43,9 @@ static inline NSString *SORelativeDateLocalizedString(NSString *key, NSString *c
__unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSWeekCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
__dateComponentSelectorNames = [[NSArray alloc] initWithObjects:@"year", @"month", @"week", @"day", @"hour", @"minute", @"second", nil];
__partialDateStrings = [[NSMutableArray alloc] init];
_relativeTransformDepth = FirstMatchOnly;
return self;
}
@@ -51,6 +54,7 @@ static inline NSString *SORelativeDateLocalizedString(NSString *key, NSString *c
{
[__calendar release];
[__dateComponentSelectorNames release];
[__partialDateStrings release];
[super dealloc];
}
#endif
@@ -76,6 +80,9 @@ static inline NSString *SORelativeDateLocalizedString(NSString *key, NSString *c
return SORelativeDateLocalizedString(@"now", @"label for current date-time");
}
// Clear any stored values
[__partialDateStrings removeAllObjects];
// Default return value is "now".
id transformedValue = SORelativeDateLocalizedString(@"now", @"label for current date-time");
@@ -87,7 +94,10 @@ static inline NSString *SORelativeDateLocalizedString(NSString *key, NSString *c
// Iterate the array of NSDateComponent selectors, which are sorted in decreasing order of time span: year, month, day, etc.
// For the first NSDateComponent time span method that returns a reasonable non-zero value, use that value to compute the relative-to-now date phrase string.
// Keep track of relative transform depth, compare against desired.
RelativeTransformDepth currentDepth = FirstMatchOnly;
BOOL isRelativePastDate;
for (NSString *selectorName in __dateComponentSelectorNames)
{
// Invoke the NSDateComponent selector matching the current iteration, and obtain the component's value.
@@ -123,28 +133,63 @@ static inline NSString *SORelativeDateLocalizedString(NSString *key, NSString *c
// Generate the langugage-friendly phrase representing the relative difference between the input date and now.
BOOL isRelativePastDate = (relativeDifference > 0); // positive values indicate a relative past date; negative values indicate a future date.
// Use the appropriate string formatting template depending on whether the given date is a relative past or a relative future date.
isRelativePastDate = (relativeDifference > 0); // positive values indicate a relative past date; negative values indicate a future date.
if (isRelativePastDate) {
// Fetch the string format template for relative past dates from the localization file and crunch out a formatted string.
NSString *pastDatePhraseTemplate = SORelativeDateLocalizedString(@"formatTemplateForRelativePastDatePhrase", nil);
transformedValue = [NSString stringWithFormat:pastDatePhraseTemplate, relativeDifference, localizedDateComponentName];
} else {
// Fetch the string format template for relative future dates from the localization file and crunch out a formatted string.
NSString *futureDatePhraseTemplate = SORelativeDateLocalizedString(@"formatTemplateForRelativeFutureDatePhrase", nil);
transformedValue = [NSString stringWithFormat:futureDatePhraseTemplate, labs (relativeDifference), localizedDateComponentName];
}
// Break from the date components iteration loop after finding the first one with a non-zero relative difference value.
break;
// if current depth is not further than expected, collect value pair
if (self.relativeTransformDepth >= currentDepth) {
// collect the target phrase for later use
NSString *template = SORelativeDateLocalizedString(@"formatTemplateSingleValuePair", nil);
NSString *existing = [NSString stringWithFormat:template, labs (relativeDifference), localizedDateComponentName];
[__partialDateStrings addObject:existing];
currentDepth++;
}
} // for loop
NSUInteger available = [__partialDateStrings count];
if (available > 0) {
transformedValue = [self transformForDepth:available-1
isPast:isRelativePastDate];
}
return transformedValue;
}
- (NSString *)transformForDepth:(RelativeTransformDepth)currentDepth
isPast:(BOOL)isRelativePastDate {
// Use the appropriate string formatting template depending on whether the given date is a relative past or a relative future date.
NSMutableString *transformedValue = [NSMutableString string];
NSString *templateForDepth;
if (isRelativePastDate) {
// Fetch the string format template for relative past dates from the localization file and crunch out a formatted string.
templateForDepth = [NSString stringWithFormat:@"formatTemplateForRelativePastDatePhrase_Depth%i",currentDepth];
} else {
// Fetch the string format template for relative future dates from the localization file and crunch out a formatted string.
templateForDepth = [NSString stringWithFormat:@"formatTemplateForRelativeFutureDatePhrase_Depth%i",currentDepth];
}
NSString *datePhraseTemplate = SORelativeDateLocalizedString(templateForDepth, nil);
NSScanner *scanner = [NSScanner scannerWithString:datePhraseTemplate];
scanner.charactersToBeSkipped = nil;
NSString *target = nil;
// Scan up to the %@, append the partial, move the scanner, repeat
for (NSString *partialString in __partialDateStrings) {
[scanner scanUpToString:@"%@" intoString:&target];
if (!target) { // check to protect against %@ at the beginning of template
target = @"";
}
target = [target stringByAppendingString:partialString];
[scanner setScanLocation:scanner.scanLocation+2];
[transformedValue appendString:target];
}
if (!scanner.isAtEnd) {
[scanner scanUpToString:@"________fin__" intoString:&target];
if (target) {
[transformedValue appendString:target];
}
}
return transformedValue;
}
@end