This repository has been archived by the owner on Jan 4, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 34
/
SORelativeDateTransformer.m
136 lines (103 loc) · 5.53 KB
/
SORelativeDateTransformer.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
Created by William Garrison on 12/6/10.
Copyright 2010 Standard Orbit Software, LLC. All rights reserved.
Derived in part from digdog's MIT-licensed NSDate-RelativeDate category method <https://github.com/digdog/NSDate-RelativeDate>
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License <http://creativecommons.org/licenses/by-sa/3.0/">
Use it, hack it, but give me some love.
*/
#import "SORelativeDateTransformer.h"
@implementation SORelativeDateTransformer
+ (NSBundle *)bundle {
static NSBundle *bundle = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURL *url = [[[NSBundle mainBundle] resourceURL] URLByAppendingPathComponent:@"SORelativeDateTransformer.bundle" isDirectory:YES];
bundle = [[NSBundle bundleWithURL:url] retain];
});
return bundle;
}
#define SORelativeDateLocalizedString(x, y) NSLocalizedStringFromTableInBundle((x), @"SORelativeDateTransformer", [SORelativeDateTransformer bundle], (y))
- (id) init
{
self = [super init];
if (self) {
__calendar = [[NSCalendar currentCalendar] retain];
__unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSWeekCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
__dateComponentSelectorNames = [[NSArray alloc] initWithObjects:@"year", @"month", @"week", @"day", @"hour", @"minute", @"second", nil];
}
return self;
}
- (void) dealloc
{
[__calendar release];
[__dateComponentSelectorNames release];
[super dealloc];
}
#pragma mark -
#pragma mark NSValueTransformer Overrides
+ (Class) transformedValueClass
{
return [NSString class];
}
+ (BOOL) allowsReverseTransformation
{
// This is one-way transformer from NSDate -> NSString
return NO;
}
- (id) transformedValue:(id)value
{
// Return early if input is whacked
if ([value isKindOfClass:[NSDate class]] == NO) {
return SORelativeDateLocalizedString(@"now", @"label for current date-time");
}
// Default return value is "now".
id transformedValue = SORelativeDateLocalizedString(@"now", @"label for current date-time");
// Obtain the date components for the relative difference between the input date and now.
NSDateComponents *relativeDifferenceComponents = [__calendar components:__unitFlags fromDate:value toDate:[NSDate date] options:0];
// 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.
for (NSString *selectorName in __dateComponentSelectorNames)
{
// Invoke the NSDateComponent selector matching the current iteration, and obtain the component's value.
NSInteger relativeDifference = 0;
{
SEL currentSelector = NSSelectorFromString(selectorName);
NSInvocation *invoker = [NSInvocation invocationWithMethodSignature:[NSDateComponents instanceMethodSignatureForSelector:currentSelector]];
[invoker setTarget:relativeDifferenceComponents];
[invoker setSelector:currentSelector];
[invoker invoke];
// Get the value from the NSDateComponent invocation for the given calendar unit.
[invoker getReturnValue:&relativeDifference];
}
// If the relative difference between the input date and now is 0 for the date component named in this iteration, press on.
// e.g. no difference between the month component of input date and now, continue iterating with the hour component next to be evaluated.
if (relativeDifference == 0) continue;
// Lookup the localized name to use for the data component in our class' strings file.
NSString *localizedDateComponentName = nil;
{
// Map the NSDateComponent method into a localization lookup key corresponding to a suitable calendar unit name, pluralizing the key as appropriate.
// E.g. @selector(year) ==> "year", @selector(month) ==> "month"
NSString *localizedDateComponentKey = selectorName;
if (labs (relativeDifference) > 1) {
localizedDateComponentKey = [NSString stringWithFormat:@"%@s", selectorName];
}
localizedDateComponentName = SORelativeDateLocalizedString(localizedDateComponentKey, nil);
}
// 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.
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;
} // for loop
return transformedValue;
}
@end