├── .DS_Store
├── .gitignore
├── .swift-version
├── LICENSE
├── README.md
├── RelativeFormatter.podspec
├── RelativeFormatter.xcodeproj
├── Info.plist
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcshareddata
│ └── xcschemes
│ └── RelativeFormatter.xcscheme
├── Source
├── LocalizationHelper.swift
├── RelativeFormatter.bundle
│ ├── en.lproj
│ │ └── RelativeFormatter.strings
│ ├── es.lproj
│ │ └── RelativeFormatter.strings
│ ├── fr.lproj
│ │ └── RelativeFormatter.strings
│ ├── pt.lproj
│ │ └── RelativeFormatter.strings
│ ├── zh-Hans.lproj
│ │ └── RelativeFormatter.strings
│ └── zh-Hant.lproj
│ │ └── RelativeFormatter.strings
└── RelativeFormatter.swift
└── Tests
├── Info.plist
├── RelativeFormatterIdiomaticTests.swift
└── RelativeFormatterTests.swift
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitomule/RelativeFormatter/d15b2d38e1d94d513764c9f6aa9fcb022d76d7db/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 |
20 | # CocoaPods
21 | #
22 | # We recommend against adding the Pods directory to your .gitignore. However
23 | # you should judge for yourself, the pros and cons are mentioned at:
24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
25 | #
26 | # Pods/
27 |
28 | # Carthage
29 | #
30 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
31 | # Carthage/Checkouts
32 |
33 | Carthage/Build
34 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 3.0
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 bitomule
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RelativeFormatter
2 | Date swift extension to format dates according to current date.
3 |
4 | ## Features
5 |
6 | - [x] Format Date as Time ago
7 | - [x] Format Date as Time ahead
8 | - [x] Format using idiomatic style (today,yesterday,tomorrow)
9 | - [x] Set format precision (years,months,weeks,days,hours,minutes and seconds)
10 |
11 | ## Requirements
12 |
13 | - iOS 8.0+ / Mac OS X 10.9+
14 | - Xcode 8.0
15 | - Swift 3
16 |
17 | ## Installation
18 |
19 | > **Embedded frameworks require a minimum deployment target of iOS 8 or OS X Mavericks.**
20 | >
21 |
22 |
23 | ### CocoaPods
24 |
25 | [CocoaPods][1] is a dependency manager for Cocoa projects.
26 |
27 | CocoaPods 0.36 adds supports for Swift and embedded frameworks. You can install it with the following command:
28 |
29 | ```bash
30 | $ gem install cocoapods
31 | ```
32 |
33 | To integrate RelativeFormatter into your Xcode project using CocoaPods, specify it in your `Podfile`:
34 |
35 | ```ruby
36 | source 'https://github.com/CocoaPods/Specs.git'
37 | platform :ios, '8.0'
38 | use_frameworks!
39 |
40 | pod 'RelativeFormatter'
41 | ```
42 |
43 | Then, run the following command:
44 |
45 | ```bash
46 | $ pod install
47 | ```
48 |
49 | ### How To Use
50 |
51 | RelativeFormatter is just an NSDate extension, you can use it with any NSDate object:
52 |
53 | There’s only one function to call:
54 |
55 | ```swift
56 | relativeFormatted(idiomatic:Bool=false,precision:Precision=Precision.Second)->String
57 | ```
58 |
59 | Both parameters aren’t required.
60 |
61 | - idiomatic:Bool
62 |
63 | This parameter is false by default and allows you to use idiomatic date format or just numbers.
64 |
65 | Example:
66 |
67 | ```swift
68 | //oldDate is yesterday date
69 |
70 | oldDate.relativeFormatted()
71 |
72 | //outputs
73 | // “1 day ago”
74 |
75 | oldDate.relativeFormatted(idiomatic:true)
76 |
77 | //outputs
78 | // “yesterday”
79 | ```
80 |
81 | - precision:Precision
82 |
83 | Precision parameter allows you to define the format precission. Default value is seconds.
84 |
85 | Example:
86 |
87 |
88 | ```swift
89 | todayDate.relativeFormatted(precision:Precision.Year)
90 |
91 | //outputs
92 | // “this year”
93 |
94 | todayDate.relativeFormatted(precision:Precision.Month)
95 |
96 | //outputs
97 | // “this month”
98 |
99 | todayDate.relativeFormatted(precision:Precision.Day)
100 |
101 | //outputs
102 | // “today”
103 |
104 | todayDate.relativeFormatted(precision:Precision.Hour)
105 |
106 | //outputs
107 | // “3 hours ago”
108 | ```
109 |
110 | You can always use relativeFormatted with default parameters.
111 | If you have an NSDate representing a date 2 months ago just use:
112 |
113 | ```swift
114 | oldDate.relativeFormatted()
115 | ```
116 |
117 | And you'll get:
118 |
119 | "2 months ago"
120 |
121 | It also works for ahead dates (date in 3 years):
122 |
123 | ```swift
124 | futureDate.relativeFormatted()
125 | ```
126 |
127 | will return:
128 |
129 | "In 3 years"
130 |
131 | ### Languages
132 |
133 | RelativeFormatter includes localization for:
134 |
135 | - [x] English
136 | - [x] Spanish
137 | - [x] French
138 |
139 | If you can to include a new language please create a pull request
140 |
141 | [1]: http://cocoapods.org
--------------------------------------------------------------------------------
/RelativeFormatter.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod spec lint NSDate+RelativeFormatter.podspec' to ensure this is a
3 | # valid spec and to remove all comments including this before submitting the spec.
4 | #
5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html
6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = "RelativeFormatter"
11 | s.version = "0.8.3"
12 | s.summary = "Date swift extension to format dates relative to current date."
13 |
14 | s.description = <<-DESC
15 | RelativeFormatter adds the relativeFormatted function to Date allowing you to get.
16 |
17 | * Time ago formatted dates "2 days ago"
18 | * Time in the future formatted dates "In 2 days"
19 | DESC
20 |
21 | s.homepage = "https://github.com/bitomule/RelativeFormatter"
22 | s.license = "MIT"
23 |
24 |
25 | s.author = { "David Collado Sela" => "bitomule@gmail.com", "David Santana" => "David@2coders.com" }
26 | s.social_media_url = "http://twitter.com/bitomule"
27 |
28 | s.ios.deployment_target = '8.0'
29 | s.osx.deployment_target = '10.9'
30 | s.watchos.deployment_target = '2.0'
31 | s.tvos.deployment_target = '9.0'
32 |
33 | s.source = { :git => "https://github.com/bitomule/RelativeFormatter.git", :tag => "0.8.3" }
34 |
35 | s.source_files = 'Source/*.swift'
36 | s.resources = 'Source/RelativeFormatter.bundle'
37 | s.requires_arc = true
38 |
39 | end
40 |
--------------------------------------------------------------------------------
/RelativeFormatter.xcodeproj/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/RelativeFormatter.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 414121071B03345100DF7EB3 /* RelativeFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 414121061B03345100DF7EB3 /* RelativeFormatterTests.swift */; };
11 | 41A56CE61B022F0200347FE2 /* RelativeFormatter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 41A56CDA1B022F0200347FE2 /* RelativeFormatter.framework */; };
12 | 41A56D021B02416E00347FE2 /* RelativeFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A56CFC1B02416E00347FE2 /* RelativeFormatter.swift */; };
13 | 41A56D031B02416E00347FE2 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 41A56CFE1B02416E00347FE2 /* Info.plist */; };
14 | 41DE8FF41B024F5C00ABAFD1 /* RelativeFormatter.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 41DE8FF31B024F5C00ABAFD1 /* RelativeFormatter.bundle */; };
15 | 41DE8FF61B02508A00ABAFD1 /* LocalizationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41DE8FF51B02508A00ABAFD1 /* LocalizationHelper.swift */; };
16 | 41DE8FF71B0250BC00ABAFD1 /* RelativeFormatter.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 41DE8FF31B024F5C00ABAFD1 /* RelativeFormatter.bundle */; };
17 | 41DE8FF81B02533800ABAFD1 /* RelativeFormatterIdiomaticTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A56CFF1B02416E00347FE2 /* RelativeFormatterIdiomaticTests.swift */; };
18 | 41DE8FF91B02536B00ABAFD1 /* RelativeFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A56CFC1B02416E00347FE2 /* RelativeFormatter.swift */; };
19 | 41DE8FFA1B02536F00ABAFD1 /* LocalizationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41DE8FF51B02508A00ABAFD1 /* LocalizationHelper.swift */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXContainerItemProxy section */
23 | 41A56CE71B022F0200347FE2 /* PBXContainerItemProxy */ = {
24 | isa = PBXContainerItemProxy;
25 | containerPortal = 41A56CD11B022F0200347FE2 /* Project object */;
26 | proxyType = 1;
27 | remoteGlobalIDString = 41A56CD91B022F0200347FE2;
28 | remoteInfo = "NSDate+RelativeFormatter";
29 | };
30 | /* End PBXContainerItemProxy section */
31 |
32 | /* Begin PBXFileReference section */
33 | 414121061B03345100DF7EB3 /* RelativeFormatterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelativeFormatterTests.swift; sourceTree = ""; };
34 | 41A56CDA1B022F0200347FE2 /* RelativeFormatter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RelativeFormatter.framework; sourceTree = BUILT_PRODUCTS_DIR; };
35 | 41A56CE51B022F0200347FE2 /* RelativeFormatterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RelativeFormatterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
36 | 41A56CFC1B02416E00347FE2 /* RelativeFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelativeFormatter.swift; sourceTree = ""; };
37 | 41A56CFE1B02416E00347FE2 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
38 | 41A56CFF1B02416E00347FE2 /* RelativeFormatterIdiomaticTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelativeFormatterIdiomaticTests.swift; sourceTree = ""; };
39 | 41DE8FF31B024F5C00ABAFD1 /* RelativeFormatter.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = RelativeFormatter.bundle; sourceTree = ""; };
40 | 41DE8FF51B02508A00ABAFD1 /* LocalizationHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizationHelper.swift; sourceTree = ""; };
41 | /* End PBXFileReference section */
42 |
43 | /* Begin PBXFrameworksBuildPhase section */
44 | 41A56CD61B022F0200347FE2 /* Frameworks */ = {
45 | isa = PBXFrameworksBuildPhase;
46 | buildActionMask = 2147483647;
47 | files = (
48 | );
49 | runOnlyForDeploymentPostprocessing = 0;
50 | };
51 | 41A56CE21B022F0200347FE2 /* Frameworks */ = {
52 | isa = PBXFrameworksBuildPhase;
53 | buildActionMask = 2147483647;
54 | files = (
55 | 41A56CE61B022F0200347FE2 /* RelativeFormatter.framework in Frameworks */,
56 | );
57 | runOnlyForDeploymentPostprocessing = 0;
58 | };
59 | /* End PBXFrameworksBuildPhase section */
60 |
61 | /* Begin PBXGroup section */
62 | 41A56CD01B022F0200347FE2 = {
63 | isa = PBXGroup;
64 | children = (
65 | 41A56CF91B02416E00347FE2 /* Source */,
66 | 41A56CFD1B02416E00347FE2 /* Tests */,
67 | 41A56CDB1B022F0200347FE2 /* Products */,
68 | );
69 | sourceTree = "";
70 | };
71 | 41A56CDB1B022F0200347FE2 /* Products */ = {
72 | isa = PBXGroup;
73 | children = (
74 | 41A56CDA1B022F0200347FE2 /* RelativeFormatter.framework */,
75 | 41A56CE51B022F0200347FE2 /* RelativeFormatterTests.xctest */,
76 | );
77 | name = Products;
78 | sourceTree = "";
79 | };
80 | 41A56CF91B02416E00347FE2 /* Source */ = {
81 | isa = PBXGroup;
82 | children = (
83 | 41A56CFC1B02416E00347FE2 /* RelativeFormatter.swift */,
84 | 41DE8FF31B024F5C00ABAFD1 /* RelativeFormatter.bundle */,
85 | 41DE8FF51B02508A00ABAFD1 /* LocalizationHelper.swift */,
86 | );
87 | path = Source;
88 | sourceTree = "";
89 | };
90 | 41A56CFD1B02416E00347FE2 /* Tests */ = {
91 | isa = PBXGroup;
92 | children = (
93 | 41A56CFE1B02416E00347FE2 /* Info.plist */,
94 | 41A56CFF1B02416E00347FE2 /* RelativeFormatterIdiomaticTests.swift */,
95 | 414121061B03345100DF7EB3 /* RelativeFormatterTests.swift */,
96 | );
97 | path = Tests;
98 | sourceTree = "";
99 | };
100 | /* End PBXGroup section */
101 |
102 | /* Begin PBXHeadersBuildPhase section */
103 | 41A56CD71B022F0200347FE2 /* Headers */ = {
104 | isa = PBXHeadersBuildPhase;
105 | buildActionMask = 2147483647;
106 | files = (
107 | );
108 | runOnlyForDeploymentPostprocessing = 0;
109 | };
110 | /* End PBXHeadersBuildPhase section */
111 |
112 | /* Begin PBXNativeTarget section */
113 | 41A56CD91B022F0200347FE2 /* RelativeFormatter */ = {
114 | isa = PBXNativeTarget;
115 | buildConfigurationList = 41A56CF01B022F0200347FE2 /* Build configuration list for PBXNativeTarget "RelativeFormatter" */;
116 | buildPhases = (
117 | 41A56CD51B022F0200347FE2 /* Sources */,
118 | 41A56CD61B022F0200347FE2 /* Frameworks */,
119 | 41A56CD71B022F0200347FE2 /* Headers */,
120 | 41A56CD81B022F0200347FE2 /* Resources */,
121 | );
122 | buildRules = (
123 | );
124 | dependencies = (
125 | );
126 | name = RelativeFormatter;
127 | productName = "NSDate+RelativeFormatter";
128 | productReference = 41A56CDA1B022F0200347FE2 /* RelativeFormatter.framework */;
129 | productType = "com.apple.product-type.framework";
130 | };
131 | 41A56CE41B022F0200347FE2 /* RelativeFormatterTests */ = {
132 | isa = PBXNativeTarget;
133 | buildConfigurationList = 41A56CF31B022F0200347FE2 /* Build configuration list for PBXNativeTarget "RelativeFormatterTests" */;
134 | buildPhases = (
135 | 41A56CE11B022F0200347FE2 /* Sources */,
136 | 41A56CE21B022F0200347FE2 /* Frameworks */,
137 | 41A56CE31B022F0200347FE2 /* Resources */,
138 | );
139 | buildRules = (
140 | );
141 | dependencies = (
142 | 41A56CE81B022F0200347FE2 /* PBXTargetDependency */,
143 | );
144 | name = RelativeFormatterTests;
145 | productName = "NSDate+RelativeFormatterTests";
146 | productReference = 41A56CE51B022F0200347FE2 /* RelativeFormatterTests.xctest */;
147 | productType = "com.apple.product-type.bundle.unit-test";
148 | };
149 | /* End PBXNativeTarget section */
150 |
151 | /* Begin PBXProject section */
152 | 41A56CD11B022F0200347FE2 /* Project object */ = {
153 | isa = PBXProject;
154 | attributes = {
155 | LastSwiftMigration = 0700;
156 | LastSwiftUpdateCheck = 0700;
157 | LastUpgradeCheck = 0800;
158 | ORGANIZATIONNAME = "David Collado Sela";
159 | TargetAttributes = {
160 | 41A56CD91B022F0200347FE2 = {
161 | CreatedOnToolsVersion = 6.3.1;
162 | LastSwiftMigration = 0800;
163 | };
164 | 41A56CE41B022F0200347FE2 = {
165 | CreatedOnToolsVersion = 6.3.1;
166 | LastSwiftMigration = 0800;
167 | };
168 | };
169 | };
170 | buildConfigurationList = 41A56CD41B022F0200347FE2 /* Build configuration list for PBXProject "RelativeFormatter" */;
171 | compatibilityVersion = "Xcode 3.2";
172 | developmentRegion = English;
173 | hasScannedForEncodings = 0;
174 | knownRegions = (
175 | en,
176 | );
177 | mainGroup = 41A56CD01B022F0200347FE2;
178 | productRefGroup = 41A56CDB1B022F0200347FE2 /* Products */;
179 | projectDirPath = "";
180 | projectRoot = "";
181 | targets = (
182 | 41A56CD91B022F0200347FE2 /* RelativeFormatter */,
183 | 41A56CE41B022F0200347FE2 /* RelativeFormatterTests */,
184 | );
185 | };
186 | /* End PBXProject section */
187 |
188 | /* Begin PBXResourcesBuildPhase section */
189 | 41A56CD81B022F0200347FE2 /* Resources */ = {
190 | isa = PBXResourcesBuildPhase;
191 | buildActionMask = 2147483647;
192 | files = (
193 | 41DE8FF41B024F5C00ABAFD1 /* RelativeFormatter.bundle in Resources */,
194 | 41A56D031B02416E00347FE2 /* Info.plist in Resources */,
195 | );
196 | runOnlyForDeploymentPostprocessing = 0;
197 | };
198 | 41A56CE31B022F0200347FE2 /* Resources */ = {
199 | isa = PBXResourcesBuildPhase;
200 | buildActionMask = 2147483647;
201 | files = (
202 | 41DE8FF71B0250BC00ABAFD1 /* RelativeFormatter.bundle in Resources */,
203 | );
204 | runOnlyForDeploymentPostprocessing = 0;
205 | };
206 | /* End PBXResourcesBuildPhase section */
207 |
208 | /* Begin PBXSourcesBuildPhase section */
209 | 41A56CD51B022F0200347FE2 /* Sources */ = {
210 | isa = PBXSourcesBuildPhase;
211 | buildActionMask = 2147483647;
212 | files = (
213 | 41DE8FF61B02508A00ABAFD1 /* LocalizationHelper.swift in Sources */,
214 | 41A56D021B02416E00347FE2 /* RelativeFormatter.swift in Sources */,
215 | );
216 | runOnlyForDeploymentPostprocessing = 0;
217 | };
218 | 41A56CE11B022F0200347FE2 /* Sources */ = {
219 | isa = PBXSourcesBuildPhase;
220 | buildActionMask = 2147483647;
221 | files = (
222 | 41DE8FFA1B02536F00ABAFD1 /* LocalizationHelper.swift in Sources */,
223 | 414121071B03345100DF7EB3 /* RelativeFormatterTests.swift in Sources */,
224 | 41DE8FF91B02536B00ABAFD1 /* RelativeFormatter.swift in Sources */,
225 | 41DE8FF81B02533800ABAFD1 /* RelativeFormatterIdiomaticTests.swift in Sources */,
226 | );
227 | runOnlyForDeploymentPostprocessing = 0;
228 | };
229 | /* End PBXSourcesBuildPhase section */
230 |
231 | /* Begin PBXTargetDependency section */
232 | 41A56CE81B022F0200347FE2 /* PBXTargetDependency */ = {
233 | isa = PBXTargetDependency;
234 | target = 41A56CD91B022F0200347FE2 /* RelativeFormatter */;
235 | targetProxy = 41A56CE71B022F0200347FE2 /* PBXContainerItemProxy */;
236 | };
237 | /* End PBXTargetDependency section */
238 |
239 | /* Begin XCBuildConfiguration section */
240 | 41A56CEE1B022F0200347FE2 /* Debug */ = {
241 | isa = XCBuildConfiguration;
242 | buildSettings = {
243 | ALWAYS_SEARCH_USER_PATHS = NO;
244 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
245 | CLANG_CXX_LIBRARY = "libc++";
246 | CLANG_ENABLE_MODULES = YES;
247 | CLANG_ENABLE_OBJC_ARC = YES;
248 | CLANG_WARN_BOOL_CONVERSION = YES;
249 | CLANG_WARN_CONSTANT_CONVERSION = YES;
250 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
251 | CLANG_WARN_EMPTY_BODY = YES;
252 | CLANG_WARN_ENUM_CONVERSION = YES;
253 | CLANG_WARN_INFINITE_RECURSION = YES;
254 | CLANG_WARN_INT_CONVERSION = YES;
255 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
256 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
257 | CLANG_WARN_UNREACHABLE_CODE = YES;
258 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
259 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
260 | COPY_PHASE_STRIP = NO;
261 | CURRENT_PROJECT_VERSION = 1;
262 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
263 | ENABLE_STRICT_OBJC_MSGSEND = YES;
264 | ENABLE_TESTABILITY = YES;
265 | GCC_C_LANGUAGE_STANDARD = gnu99;
266 | GCC_DYNAMIC_NO_PIC = NO;
267 | GCC_NO_COMMON_BLOCKS = YES;
268 | GCC_OPTIMIZATION_LEVEL = 0;
269 | GCC_PREPROCESSOR_DEFINITIONS = (
270 | "DEBUG=1",
271 | "$(inherited)",
272 | );
273 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
274 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
275 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
276 | GCC_WARN_UNDECLARED_SELECTOR = YES;
277 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
278 | GCC_WARN_UNUSED_FUNCTION = YES;
279 | GCC_WARN_UNUSED_VARIABLE = YES;
280 | INFOPLIST_FILE = RelativeFormatter.xcodeproj/info.plist;
281 | IPHONEOS_DEPLOYMENT_TARGET = 8.3;
282 | MTL_ENABLE_DEBUG_INFO = YES;
283 | ONLY_ACTIVE_ARCH = YES;
284 | SDKROOT = iphoneos;
285 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
286 | SWIFT_VERSION = 3.0;
287 | TARGETED_DEVICE_FAMILY = "1,2";
288 | VERSIONING_SYSTEM = "apple-generic";
289 | VERSION_INFO_PREFIX = "";
290 | };
291 | name = Debug;
292 | };
293 | 41A56CEF1B022F0200347FE2 /* Release */ = {
294 | isa = XCBuildConfiguration;
295 | buildSettings = {
296 | ALWAYS_SEARCH_USER_PATHS = NO;
297 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
298 | CLANG_CXX_LIBRARY = "libc++";
299 | CLANG_ENABLE_MODULES = YES;
300 | CLANG_ENABLE_OBJC_ARC = YES;
301 | CLANG_WARN_BOOL_CONVERSION = YES;
302 | CLANG_WARN_CONSTANT_CONVERSION = YES;
303 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
304 | CLANG_WARN_EMPTY_BODY = YES;
305 | CLANG_WARN_ENUM_CONVERSION = YES;
306 | CLANG_WARN_INFINITE_RECURSION = YES;
307 | CLANG_WARN_INT_CONVERSION = YES;
308 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
309 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
310 | CLANG_WARN_UNREACHABLE_CODE = YES;
311 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
312 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
313 | COPY_PHASE_STRIP = NO;
314 | CURRENT_PROJECT_VERSION = 1;
315 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
316 | ENABLE_NS_ASSERTIONS = NO;
317 | ENABLE_STRICT_OBJC_MSGSEND = YES;
318 | GCC_C_LANGUAGE_STANDARD = gnu99;
319 | GCC_NO_COMMON_BLOCKS = YES;
320 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
321 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
322 | GCC_WARN_UNDECLARED_SELECTOR = YES;
323 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
324 | GCC_WARN_UNUSED_FUNCTION = YES;
325 | GCC_WARN_UNUSED_VARIABLE = YES;
326 | INFOPLIST_FILE = RelativeFormatter.xcodeproj/Info.plist;
327 | IPHONEOS_DEPLOYMENT_TARGET = 8.3;
328 | MTL_ENABLE_DEBUG_INFO = NO;
329 | SDKROOT = iphoneos;
330 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
331 | SWIFT_VERSION = 3.0;
332 | TARGETED_DEVICE_FAMILY = "1,2";
333 | VALIDATE_PRODUCT = YES;
334 | VERSIONING_SYSTEM = "apple-generic";
335 | VERSION_INFO_PREFIX = "";
336 | };
337 | name = Release;
338 | };
339 | 41A56CF11B022F0200347FE2 /* Debug */ = {
340 | isa = XCBuildConfiguration;
341 | buildSettings = {
342 | CLANG_ENABLE_MODULES = YES;
343 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
344 | DEFINES_MODULE = YES;
345 | DYLIB_COMPATIBILITY_VERSION = 1;
346 | DYLIB_CURRENT_VERSION = 1;
347 | DYLIB_INSTALL_NAME_BASE = "@rpath";
348 | INFOPLIST_FILE = RelativeFormatter.xcodeproj/Info.plist;
349 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
350 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
351 | PRODUCT_BUNDLE_IDENTIFIER = "com.bitomule.$(PRODUCT_NAME:rfc1034identifier)";
352 | PRODUCT_NAME = "$(TARGET_NAME)";
353 | SKIP_INSTALL = YES;
354 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
355 | SWIFT_VERSION = 3.0;
356 | };
357 | name = Debug;
358 | };
359 | 41A56CF21B022F0200347FE2 /* Release */ = {
360 | isa = XCBuildConfiguration;
361 | buildSettings = {
362 | CLANG_ENABLE_MODULES = YES;
363 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
364 | DEFINES_MODULE = YES;
365 | DYLIB_COMPATIBILITY_VERSION = 1;
366 | DYLIB_CURRENT_VERSION = 1;
367 | DYLIB_INSTALL_NAME_BASE = "@rpath";
368 | INFOPLIST_FILE = RelativeFormatter.xcodeproj/Info.plist;
369 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
370 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
371 | PRODUCT_BUNDLE_IDENTIFIER = "com.bitomule.$(PRODUCT_NAME:rfc1034identifier)";
372 | PRODUCT_NAME = "$(TARGET_NAME)";
373 | SKIP_INSTALL = YES;
374 | SWIFT_VERSION = 3.0;
375 | };
376 | name = Release;
377 | };
378 | 41A56CF41B022F0200347FE2 /* Debug */ = {
379 | isa = XCBuildConfiguration;
380 | buildSettings = {
381 | FRAMEWORK_SEARCH_PATHS = (
382 | "$(SDKROOT)/Developer/Library/Frameworks",
383 | "$(inherited)",
384 | );
385 | GCC_PREPROCESSOR_DEFINITIONS = (
386 | "DEBUG=1",
387 | "$(inherited)",
388 | );
389 | INFOPLIST_FILE = RelativeFormatter.xcodeproj/Info.plist;
390 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
391 | PRODUCT_BUNDLE_IDENTIFIER = "com.bitomule.$(PRODUCT_NAME:rfc1034identifier)";
392 | PRODUCT_NAME = "$(TARGET_NAME)";
393 | SWIFT_VERSION = 3.0;
394 | };
395 | name = Debug;
396 | };
397 | 41A56CF51B022F0200347FE2 /* Release */ = {
398 | isa = XCBuildConfiguration;
399 | buildSettings = {
400 | FRAMEWORK_SEARCH_PATHS = (
401 | "$(SDKROOT)/Developer/Library/Frameworks",
402 | "$(inherited)",
403 | );
404 | INFOPLIST_FILE = RelativeFormatter.xcodeproj/Info.plist;
405 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
406 | PRODUCT_BUNDLE_IDENTIFIER = "com.bitomule.$(PRODUCT_NAME:rfc1034identifier)";
407 | PRODUCT_NAME = "$(TARGET_NAME)";
408 | SWIFT_VERSION = 3.0;
409 | };
410 | name = Release;
411 | };
412 | /* End XCBuildConfiguration section */
413 |
414 | /* Begin XCConfigurationList section */
415 | 41A56CD41B022F0200347FE2 /* Build configuration list for PBXProject "RelativeFormatter" */ = {
416 | isa = XCConfigurationList;
417 | buildConfigurations = (
418 | 41A56CEE1B022F0200347FE2 /* Debug */,
419 | 41A56CEF1B022F0200347FE2 /* Release */,
420 | );
421 | defaultConfigurationIsVisible = 0;
422 | defaultConfigurationName = Release;
423 | };
424 | 41A56CF01B022F0200347FE2 /* Build configuration list for PBXNativeTarget "RelativeFormatter" */ = {
425 | isa = XCConfigurationList;
426 | buildConfigurations = (
427 | 41A56CF11B022F0200347FE2 /* Debug */,
428 | 41A56CF21B022F0200347FE2 /* Release */,
429 | );
430 | defaultConfigurationIsVisible = 0;
431 | defaultConfigurationName = Release;
432 | };
433 | 41A56CF31B022F0200347FE2 /* Build configuration list for PBXNativeTarget "RelativeFormatterTests" */ = {
434 | isa = XCConfigurationList;
435 | buildConfigurations = (
436 | 41A56CF41B022F0200347FE2 /* Debug */,
437 | 41A56CF51B022F0200347FE2 /* Release */,
438 | );
439 | defaultConfigurationIsVisible = 0;
440 | defaultConfigurationName = Release;
441 | };
442 | /* End XCConfigurationList section */
443 | };
444 | rootObject = 41A56CD11B022F0200347FE2 /* Project object */;
445 | }
446 |
--------------------------------------------------------------------------------
/RelativeFormatter.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/RelativeFormatter.xcodeproj/xcshareddata/xcschemes/RelativeFormatter.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/Source/LocalizationHelper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+Local.swift
3 | // RelativeFormatter
4 | //
5 | // Created by David Collado Sela on 12/5/15.
6 | // Copyright (c) 2015 David Collado Sela. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class LocalizationHelper{
12 | class func localize(_ key:String,count:Int?=nil)->String{
13 | let bundlePath = (Bundle(for: LocalizationHelper.self).resourcePath! as NSString).appendingPathComponent("RelativeFormatter.bundle")
14 |
15 | var localizedString = NSLocalizedString(key, tableName: "RelativeFormatter", bundle: Bundle(path: bundlePath)!, value: "", comment: "")
16 |
17 | if let count = count{
18 | localizedString = String.localizedStringWithFormat(localizedString, count)
19 | }
20 | return localizedString
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Source/RelativeFormatter.bundle/en.lproj/RelativeFormatter.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitomule/RelativeFormatter/d15b2d38e1d94d513764c9f6aa9fcb022d76d7db/Source/RelativeFormatter.bundle/en.lproj/RelativeFormatter.strings
--------------------------------------------------------------------------------
/Source/RelativeFormatter.bundle/es.lproj/RelativeFormatter.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitomule/RelativeFormatter/d15b2d38e1d94d513764c9f6aa9fcb022d76d7db/Source/RelativeFormatter.bundle/es.lproj/RelativeFormatter.strings
--------------------------------------------------------------------------------
/Source/RelativeFormatter.bundle/fr.lproj/RelativeFormatter.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitomule/RelativeFormatter/d15b2d38e1d94d513764c9f6aa9fcb022d76d7db/Source/RelativeFormatter.bundle/fr.lproj/RelativeFormatter.strings
--------------------------------------------------------------------------------
/Source/RelativeFormatter.bundle/pt.lproj/RelativeFormatter.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitomule/RelativeFormatter/d15b2d38e1d94d513764c9f6aa9fcb022d76d7db/Source/RelativeFormatter.bundle/pt.lproj/RelativeFormatter.strings
--------------------------------------------------------------------------------
/Source/RelativeFormatter.bundle/zh-Hans.lproj/RelativeFormatter.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitomule/RelativeFormatter/d15b2d38e1d94d513764c9f6aa9fcb022d76d7db/Source/RelativeFormatter.bundle/zh-Hans.lproj/RelativeFormatter.strings
--------------------------------------------------------------------------------
/Source/RelativeFormatter.bundle/zh-Hant.lproj/RelativeFormatter.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bitomule/RelativeFormatter/d15b2d38e1d94d513764c9f6aa9fcb022d76d7db/Source/RelativeFormatter.bundle/zh-Hant.lproj/RelativeFormatter.strings
--------------------------------------------------------------------------------
/Source/RelativeFormatter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RelativeFormatter.swift
3 | // RelativeFormatter
4 | //
5 | // Created by David Collado Sela on 12/5/15.
6 | // Copyright (c) 2015 David Collado Sela. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public enum Precision{
12 | case year,month,week,day,hour,minute,second
13 | }
14 |
15 | extension Date{
16 |
17 | public func relativeFormatted(_ idiomatic:Bool=false,precision:Precision=Precision.second)->String{
18 | let calendar = Calendar.current
19 | let unitFlags: NSCalendar.Unit = [NSCalendar.Unit.minute, NSCalendar.Unit.hour, NSCalendar.Unit.day, NSCalendar.Unit.weekOfYear, NSCalendar.Unit.month, NSCalendar.Unit.year, NSCalendar.Unit.second]
20 | let now = Date().normalized(precision)
21 | let normalized = self.normalized(precision)
22 | let formattedDateData:(key:String,count:Int?)
23 | if(normalized.timeIntervalSince1970 < now.timeIntervalSince1970){
24 | let components:DateComponents = (calendar as NSCalendar).components(unitFlags, from: normalized, to: now, options: [])
25 | formattedDateData = RelativeFormatter.getPastKeyAndCount(components,idiomatic:idiomatic,precision:precision)
26 | }else{
27 | let components:DateComponents = (calendar as NSCalendar).components(unitFlags, from: now, to: normalized, options: [])
28 | formattedDateData = RelativeFormatter.getFutureKeyAndCount(components,idiomatic:idiomatic,precision:precision)
29 | }
30 | return LocalizationHelper.localize(formattedDateData.key,count:formattedDateData.count)
31 | }
32 |
33 | func normalized(_ precision:Precision)->Date{
34 | let unitFlags: NSCalendar.Unit = [NSCalendar.Unit.minute, NSCalendar.Unit.hour, NSCalendar.Unit.day, NSCalendar.Unit.weekOfYear, NSCalendar.Unit.month, NSCalendar.Unit.year, NSCalendar.Unit.second]
35 | var nowDateNewcomponents = DateComponents()
36 | let nowComponets = (Calendar.current as NSCalendar).components(unitFlags, from: self)
37 | switch precision{
38 | case .year:
39 | nowDateNewcomponents.year = nowComponets.year
40 | case .month:
41 | nowDateNewcomponents.year = nowComponets.year
42 | nowDateNewcomponents.month = nowComponets.month
43 | case .week:
44 | nowDateNewcomponents.year = nowComponets.year
45 | nowDateNewcomponents.month = nowComponets.month
46 | nowDateNewcomponents.weekOfYear = nowComponets.weekOfYear
47 | case .day:
48 | nowDateNewcomponents.year = nowComponets.year
49 | nowDateNewcomponents.month = nowComponets.month
50 | nowDateNewcomponents.weekOfYear = nowComponets.weekOfYear
51 | nowDateNewcomponents.day = nowComponets.day
52 | case .hour:
53 | nowDateNewcomponents.year = nowComponets.year
54 | nowDateNewcomponents.month = nowComponets.month
55 | nowDateNewcomponents.weekOfYear = nowComponets.weekOfYear
56 | nowDateNewcomponents.day = nowComponets.day
57 | nowDateNewcomponents.hour = nowComponets.hour
58 | case .minute:
59 | nowDateNewcomponents.year = nowComponets.year
60 | nowDateNewcomponents.month = nowComponets.month
61 | nowDateNewcomponents.weekOfYear = nowComponets.weekOfYear
62 | nowDateNewcomponents.day = nowComponets.day
63 | nowDateNewcomponents.hour = nowComponets.hour
64 | nowDateNewcomponents.minute = nowComponets.minute
65 | case .second:
66 | nowDateNewcomponents.year = nowComponets.year
67 | nowDateNewcomponents.month = nowComponets.month
68 | nowDateNewcomponents.weekOfYear = nowComponets.weekOfYear
69 | nowDateNewcomponents.day = nowComponets.day
70 | nowDateNewcomponents.hour = nowComponets.hour
71 | nowDateNewcomponents.minute = nowComponets.minute
72 | nowDateNewcomponents.second = nowComponets.second
73 | }
74 | return Calendar.current.date(from: nowDateNewcomponents)!
75 | }
76 | }
77 |
78 | class RelativeFormatter {
79 |
80 | class func getPastKeyAndCount(_ components:DateComponents,idiomatic:Bool,precision:Precision)->(key:String,count:Int?){
81 | var key = ""
82 | var count:Int?
83 | if(components.year! >= 2){
84 | count = components.year
85 | key = "yearsago"
86 | }
87 | else if(components.year! >= 1){
88 | key = "yearago"
89 | }
90 | else if(components.year == 0 && precision == Precision.year){
91 | return ("thisyear",nil)
92 | }
93 | else if(components.month! >= 2){
94 | count = components.month
95 | key = "monthsago"
96 | }
97 | else if(components.month! >= 1){
98 | key = "monthago"
99 |
100 | }
101 | else if(components.month == 0 && precision == Precision.month){
102 | return ("thismonth",nil)
103 | }
104 | else if(components.weekOfYear! >= 2){
105 | count = components.weekOfYear
106 | key = "weeksago"
107 | }
108 | else if(components.weekOfYear! >= 1){
109 | key = "weekago"
110 | }
111 | else if(components.weekOfYear == 0 && precision == Precision.week){
112 | return ("thisweek",nil)
113 | }
114 | else if(components.day! >= 2){
115 | count = components.day
116 | key = "daysago"
117 | }
118 | else if(components.day! >= 1){
119 | key = "dayago"
120 | if(idiomatic){
121 | key = key + "-idiomatic"
122 | }
123 | }
124 | else if(components.day == 0 && precision == Precision.day){
125 | return ("today",nil)
126 | }
127 | else if(components.hour! >= 2){
128 | count = components.hour
129 | key = "hoursago"
130 | }
131 | else if(components.hour! >= 1){
132 | key = "hourago"
133 | }
134 | else if(components.hour == 0 && precision == Precision.hour){
135 | return ("thishour",nil)
136 | }
137 | else if(components.minute! >= 2){
138 | count = components.minute
139 | key = "minutesago"
140 | }
141 | else if(components.minute! >= 1){
142 | key = "minuteago"
143 | }
144 | else if(components.minute == 0 && precision == Precision.minute){
145 | return ("thisminute",nil)
146 | }
147 | else if(components.second! >= 2){
148 | count = components.second
149 | key = "secondsago"
150 | if(idiomatic){
151 | key = key + "-idiomatic"
152 | }
153 | }
154 | else{
155 | key = "secondago"
156 | if(idiomatic){
157 | key = "now"
158 | }
159 | }
160 |
161 | return (key,count)
162 | }
163 |
164 | class func getFutureKeyAndCount(_ components:DateComponents,idiomatic:Bool,precision:Precision)->(key:String,count:Int?){
165 | var key = ""
166 | var count:Int?
167 | if(components.year! >= 2){
168 | count = components.year
169 | key = "yearsahead"
170 | }
171 | else if(components.year! >= 1){
172 | key = "yearahead"
173 | }
174 | else if(components.year == 0 && precision == Precision.year){
175 | return ("thisyear",nil)
176 | }
177 | else if(components.month! >= 2){
178 | count = components.month
179 | key = "monthsahead"
180 | }
181 | else if(components.month! >= 1){
182 | key = "monthahead"
183 | }
184 | else if(components.month == 0 && precision == Precision.month){
185 | return ("thismonth",nil)
186 | }
187 | else if(components.weekOfYear! >= 2){
188 | count = components.weekOfYear
189 | key = "weeksahead"
190 | }
191 | else if(components.weekOfYear! >= 1){
192 | key = "weekahead"
193 | }
194 | else if(components.weekOfYear == 0 && precision == Precision.week){
195 | return ("thisweek",nil)
196 | }
197 | else if(components.day! >= 2){
198 | count = components.day
199 | key = "daysahead"
200 | }
201 | else if(components.day! >= 1){
202 | key = "dayahead"
203 | if(idiomatic){
204 | key = key + "-idiomatic"
205 | }
206 | }
207 | else if(components.day == 0 && precision == Precision.day){
208 | return ("today",nil)
209 | }
210 | else if(components.hour! >= 2){
211 | count = components.hour
212 | key = "hoursahead"
213 | }
214 | else if(components.hour! >= 1){
215 | key = "hourahead"
216 | }
217 | else if(components.hour == 0 && precision == Precision.hour){
218 | return ("thishour",nil)
219 | }
220 | else if(components.minute! >= 2){
221 | count = components.minute
222 | key = "minutesahead"
223 | }
224 | else if(components.minute! >= 1){
225 | key = "minuteahead"
226 | }
227 | else if(components.minute == 0 && precision == Precision.minute){
228 | return ("thisminute",nil)
229 | }
230 | else if(components.second! >= 2){
231 | count = components.second
232 | key = "secondsahead"
233 | if(idiomatic){
234 | key = key + "-idiomatic"
235 | }
236 | }
237 | else{
238 | key = "secondahead"
239 | if(idiomatic){
240 | key = "now"
241 | }
242 | }
243 |
244 | return (key,count)
245 | }
246 | }
247 |
--------------------------------------------------------------------------------
/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.bitomule.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Tests/RelativeFormatterIdiomaticTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RelativeFormatterIdiomaticTests.swift
3 | // RelativeFormatterIdiomaticTests
4 | //
5 | // Created by David Collado Sela on 12/5/15.
6 | // Copyright (c) 2015 David Collado Sela. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import XCTest
11 |
12 | class RelativeFormatterIdiomaticTests: XCTestCase {
13 |
14 | //MARK: - Past
15 |
16 | func testDayAgo(){
17 | let oneDayAgo = dateByDaysDifference(-1)
18 | let localizedString = oneDayAgo.relativeFormatted(true)
19 | print(localizedString, terminator: "")
20 | XCTAssertEqual(localizedString, "yesterday", "yesterday")
21 | }
22 |
23 | func testSecondsAgo(){
24 | let twoSecondsAgo = dateBySecondsDifference(-2)
25 | let localizedString = twoSecondsAgo.relativeFormatted(true)
26 | XCTAssertEqual(localizedString, "a few seconds ago", "a few seconds ago")
27 | }
28 |
29 | func testSecondAgo(){
30 | let twoSecondsAgo = dateBySecondsDifference(-1)
31 | let localizedString = twoSecondsAgo.relativeFormatted(true)
32 | XCTAssertEqual(localizedString, "now", "now")
33 | }
34 |
35 |
36 | //MARK: - Future
37 |
38 | func testDayAhead(){
39 | var oneDayAhead = dateByDaysDifference(1)
40 | oneDayAhead = oneDayAhead.addingTimeInterval(1)
41 | let localizedString = oneDayAhead.relativeFormatted(true)
42 | XCTAssertEqual(localizedString, "tomorrow", "tomorrow")
43 | }
44 |
45 | func testSecondsAhead(){
46 | var twoSecondsAhead = dateBySecondsDifference(2)
47 | twoSecondsAhead = twoSecondsAhead.addingTimeInterval(1)
48 | let localizedString = twoSecondsAhead.relativeFormatted(true)
49 | XCTAssertEqual(localizedString, "in a few seconds", "in a few seconds")
50 | }
51 |
52 | func testSecondAhead(){
53 | let oneSecondAhead = dateBySecondsDifference(1)
54 | let localizedString = oneSecondAhead.relativeFormatted(true)
55 | XCTAssertEqual(localizedString, "now", "now")
56 | }
57 |
58 | //MARK: - Helpers
59 |
60 | func dateByYearsDifference(_ years:Int) -> Date{
61 | return (Calendar.current as NSCalendar).date(byAdding: NSCalendar.Unit.year, value: years, to: Date(), options: [])!
62 | }
63 |
64 | func dateByMonthsDifference(_ months:Int) -> Date{
65 | return (Calendar.current as NSCalendar).date(byAdding: NSCalendar.Unit.month, value: months, to: Date(), options: [])!
66 | }
67 |
68 | func dateByWeeksDifference(_ weeks:Int) -> Date{
69 | return (Calendar.current as NSCalendar).date(byAdding: NSCalendar.Unit.weekOfYear, value: weeks, to: Date(), options: [])!
70 | }
71 |
72 | func dateByDaysDifference(_ days:Int) -> Date{
73 | return (Calendar.current as NSCalendar).date(byAdding: NSCalendar.Unit.day, value: days, to: Date(), options: [])!
74 | }
75 |
76 | func dateByHoursDifference(_ hours:Int) -> Date{
77 | return (Calendar.current as NSCalendar).date(byAdding: NSCalendar.Unit.hour, value: hours, to: Date(), options: [])!
78 | }
79 |
80 | func dateByMinutesDifference(_ minutes:Int) -> Date{
81 | return (Calendar.current as NSCalendar).date(byAdding: NSCalendar.Unit.minute, value: minutes, to: Date(), options: [])!
82 | }
83 |
84 | func dateBySecondsDifference(_ seconds:Int) -> Date{
85 | return (Calendar.current as NSCalendar).date(byAdding: NSCalendar.Unit.second, value: seconds, to: Date(), options: [])!
86 | }
87 |
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/Tests/RelativeFormatterTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RelativeFormatterTests.swift
3 | // RelativeFormatterTests
4 | //
5 | // Created by David Collado Sela on 12/5/15.
6 | // Copyright (c) 2015 David Collado Sela. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import XCTest
11 |
12 | class RelativeFormatterTests: XCTestCase {
13 |
14 | //MARK: - Past
15 |
16 | func testYearsAgo(){
17 | let twoYearsAgo = dateByYearsDifference(-2)
18 | let localizedString = twoYearsAgo.relativeFormatted()
19 | XCTAssertEqual(localizedString, "2 years ago", "2 years ago")
20 | }
21 |
22 | func testYearAgo(){
23 | let oneYearAgo = dateByYearsDifference(-1)
24 | let localizedString = oneYearAgo.relativeFormatted()
25 | XCTAssertEqual(localizedString, "1 year ago", "1 year ago")
26 | }
27 |
28 | func testMonthsAgo(){
29 | let twoMonthsAgo = dateByMonthsDifference(-2)
30 | let localizedString = twoMonthsAgo.relativeFormatted()
31 | XCTAssertEqual(localizedString, "2 months ago", "2 months ago")
32 | }
33 |
34 | func testMonthAgo(){
35 | let oneMonthAgo = dateByMonthsDifference(-1)
36 | let localizedString = oneMonthAgo.relativeFormatted()
37 | XCTAssertEqual(localizedString, "1 month ago", "1 month ago")
38 | }
39 |
40 | func testWeeksAgo(){
41 | let twoWeeksAgo = dateByWeeksDifference(-2)
42 | let localizedString = twoWeeksAgo.relativeFormatted()
43 | XCTAssertEqual(localizedString, "2 weeks ago", "2 weeks ago")
44 | }
45 |
46 | func testWeekAgo(){
47 | let oneWeekAgo = dateByWeeksDifference(-1)
48 | let localizedString = oneWeekAgo.relativeFormatted()
49 | XCTAssertEqual(localizedString, "1 week ago", "1 week ago")
50 | }
51 |
52 | func testDaysAgo(){
53 | let twoDaysAgo = dateByDaysDifference(-2)
54 | let localizedString = twoDaysAgo.relativeFormatted()
55 | XCTAssertEqual(localizedString, "2 days ago", "2 days ago")
56 | }
57 |
58 | func testDayAgo(){
59 | let oneDayAgo = dateByDaysDifference(-1)
60 | let localizedString = oneDayAgo.relativeFormatted()
61 | XCTAssertEqual(localizedString, "1 day ago", "1 day ago")
62 | }
63 |
64 | func testHoursAgo(){
65 | let twoHoursAgo = dateByHoursDifference(-2)
66 | let localizedString = twoHoursAgo.relativeFormatted()
67 | XCTAssertEqual(localizedString, "2 hours ago", "2 hours ago")
68 | }
69 |
70 | func testHourAgo(){
71 | let oneHourAgo = dateByHoursDifference(-1)
72 | let localizedString = oneHourAgo.relativeFormatted()
73 | XCTAssertEqual(localizedString, "1 hour ago", "1 hour ago")
74 | }
75 |
76 | func testMinutesAgo(){
77 | let twoMinutesAgo = dateByMinutesDifference(-2)
78 | let localizedString = twoMinutesAgo.relativeFormatted()
79 | XCTAssertEqual(localizedString, "2 minutes ago", "2 minutes ago")
80 | }
81 |
82 | func testMinuteAgo(){
83 | let oneMinuteAgo = dateByMinutesDifference(-1)
84 | let localizedString = oneMinuteAgo.relativeFormatted()
85 | XCTAssertEqual(localizedString, "1 minute ago", "1 minute ago")
86 | }
87 |
88 | func testSecondsAgo(){
89 | let twoSecondsAgo = dateBySecondsDifference(-2)
90 | let localizedString = twoSecondsAgo.relativeFormatted()
91 | XCTAssertEqual(localizedString, "2 seconds ago", "2 seconds ago")
92 | }
93 |
94 | func testSecondAgo(){
95 | let oneSecondAgo = dateBySecondsDifference(-1)
96 | let localizedString = oneSecondAgo.relativeFormatted()
97 | XCTAssertEqual(localizedString, "1 second ago", "1 second ago")
98 | }
99 |
100 | //MARK: - Future
101 |
102 | func testYearsAhead(){
103 | var twoYearsAhead = dateByYearsDifference(2)
104 | //We need to add 1 second as 2 exactly years doesn't work
105 | twoYearsAhead = twoYearsAhead.addingTimeInterval(1)
106 | let localizedString = twoYearsAhead.relativeFormatted()
107 | XCTAssertEqual(localizedString, "in 2 years", "in 2 years")
108 | }
109 |
110 | func testYearAhead(){
111 | var oneYearAhead = dateByYearsDifference(1)
112 | oneYearAhead = oneYearAhead.addingTimeInterval(1)
113 | let localizedString = oneYearAhead.relativeFormatted()
114 | XCTAssertEqual(localizedString, "in 1 year", "in 1 year")
115 | }
116 |
117 | func testMonthsAhead(){
118 | var twoMonthsAhead = dateByMonthsDifference(2)
119 | twoMonthsAhead = twoMonthsAhead.addingTimeInterval(1)
120 | let localizedString = twoMonthsAhead.relativeFormatted()
121 | XCTAssertEqual(localizedString, "in 2 months", "in 2 months")
122 | }
123 |
124 | func testMonthAhead(){
125 | var oneMonthAhead = dateByMonthsDifference(1)
126 | oneMonthAhead = oneMonthAhead.addingTimeInterval(1)
127 | let localizedString = oneMonthAhead.relativeFormatted()
128 | XCTAssertEqual(localizedString, "in 1 month", "in 1 month")
129 | }
130 |
131 | func testWeeksAhead(){
132 | var twoWeeksAhead = dateByWeeksDifference(2)
133 | twoWeeksAhead = twoWeeksAhead.addingTimeInterval(1)
134 | let localizedString = twoWeeksAhead.relativeFormatted()
135 | XCTAssertEqual(localizedString, "in 2 weeks", "in 2 weeks")
136 | }
137 |
138 | func testWeekAhead(){
139 | var oneWeekAhead = dateByWeeksDifference(1)
140 | oneWeekAhead = oneWeekAhead.addingTimeInterval(1)
141 | let localizedString = oneWeekAhead.relativeFormatted()
142 | XCTAssertEqual(localizedString, "in 1 week", "in 1 week")
143 | }
144 |
145 | func testDaysAhead(){
146 | var twoDaysAhead = dateByDaysDifference(2)
147 | twoDaysAhead = twoDaysAhead.addingTimeInterval(1)
148 | let localizedString = twoDaysAhead.relativeFormatted()
149 | XCTAssertEqual(localizedString, "in 2 days", "in 2 days")
150 | }
151 |
152 | func testDayAhead(){
153 | var oneDayAhead = dateByDaysDifference(1)
154 | oneDayAhead = oneDayAhead.addingTimeInterval(1)
155 | let localizedString = oneDayAhead.relativeFormatted()
156 | XCTAssertEqual(localizedString, "in 1 day", "in 1 day")
157 | }
158 |
159 | func testHoursAhead(){
160 | var twoHoursAhead = dateByHoursDifference(2)
161 | twoHoursAhead = twoHoursAhead.addingTimeInterval(1)
162 | let localizedString = twoHoursAhead.relativeFormatted()
163 | XCTAssertEqual(localizedString, "in 2 hours", "in 2 hours")
164 | }
165 |
166 | func testHourAhead(){
167 | var oneHourAhead = dateByHoursDifference(1)
168 | oneHourAhead = oneHourAhead.addingTimeInterval(1)
169 | let localizedString = oneHourAhead.relativeFormatted()
170 | XCTAssertEqual(localizedString, "in 1 hour", "in 1 hour")
171 | }
172 |
173 | func testMinutesAhead(){
174 | var twoMinutesAhead = dateByMinutesDifference(2)
175 | twoMinutesAhead = twoMinutesAhead.addingTimeInterval(1)
176 | let localizedString = twoMinutesAhead.relativeFormatted()
177 | XCTAssertEqual(localizedString, "in 2 minutes", "in 2 minutes")
178 | }
179 |
180 | func testMinuteAhead(){
181 | var oneMinuteAhead = dateByMinutesDifference(1)
182 | oneMinuteAhead = oneMinuteAhead.addingTimeInterval(1)
183 | let localizedString = oneMinuteAhead.relativeFormatted()
184 | XCTAssertEqual(localizedString, "in 1 minute", "in 1 minute")
185 | }
186 |
187 | func testSecondsAhead(){
188 | let twoSecondsAhead = dateBySecondsDifference(2)
189 | let localizedString = twoSecondsAhead.relativeFormatted()
190 | XCTAssertEqual(localizedString, "in 2 seconds", "in 2 seconds")
191 | }
192 |
193 | func testSecondAhead(){
194 | let oneSecondAhead = dateBySecondsDifference(1)
195 | let localizedString = oneSecondAhead.relativeFormatted()
196 | XCTAssertEqual(localizedString, "in 1 second", "in 1 second")
197 | }
198 |
199 | //MARK: - Precision
200 |
201 | func testYearPrecision(){
202 | let now = Date()
203 | let localizedString = now.relativeFormatted(precision:Precision.year)
204 | XCTAssertEqual(localizedString, "this year", "this year")
205 | }
206 |
207 | func testMonthPrecision(){
208 | let now = Date()
209 | let localizedString = now.relativeFormatted(precision:Precision.month)
210 | XCTAssertEqual(localizedString, "this month", "this month")
211 | }
212 |
213 | func testWeekPrecision(){
214 | let now = Date()
215 | let localizedString = now.relativeFormatted(precision:Precision.week)
216 | XCTAssertEqual(localizedString, "this week", "this week")
217 | }
218 |
219 | func testDayPrecision(){
220 | let now = Date()
221 | let localizedString = now.relativeFormatted(precision:Precision.day)
222 | XCTAssertEqual(localizedString, "today", "today")
223 | }
224 |
225 | func testHourPrecision(){
226 | let now = Date()
227 | let localizedString = now.relativeFormatted(precision:Precision.hour)
228 | XCTAssertEqual(localizedString, "less than one hour", "less than one hour")
229 | }
230 |
231 | func testMinutePrecision(){
232 | let now = Date()
233 | let localizedString = now.relativeFormatted(precision:Precision.minute)
234 | XCTAssertEqual(localizedString, "less than one minute", "less than one minute")
235 | }
236 |
237 | //MARK: - Helpers
238 |
239 | func dateByYearsDifference(_ years:Int) -> Date{
240 | return (Calendar.current as NSCalendar).date(byAdding: NSCalendar.Unit.year, value: years, to: Date(), options: [])!
241 | }
242 |
243 | func dateByMonthsDifference(_ months:Int) -> Date{
244 | return (Calendar.current as NSCalendar).date(byAdding: NSCalendar.Unit.month, value: months, to: Date(), options: [])!
245 | }
246 |
247 | func dateByWeeksDifference(_ weeks:Int) -> Date{
248 | return (Calendar.current as NSCalendar).date(byAdding: NSCalendar.Unit.weekOfYear, value: weeks, to: Date(), options: [])!
249 | }
250 |
251 | func dateByDaysDifference(_ days:Int) -> Date{
252 | return (Calendar.current as NSCalendar).date(byAdding: NSCalendar.Unit.day, value: days, to: Date(), options: [])!
253 | }
254 |
255 | func dateByHoursDifference(_ hours:Int) -> Date{
256 | return (Calendar.current as NSCalendar).date(byAdding: NSCalendar.Unit.hour, value: hours, to: Date(), options: [])!
257 | }
258 |
259 | func dateByMinutesDifference(_ minutes:Int) -> Date{
260 | return (Calendar.current as NSCalendar).date(byAdding: NSCalendar.Unit.minute, value: minutes, to: Date(), options: [])!
261 | }
262 |
263 | func dateBySecondsDifference(_ seconds:Int) -> Date{
264 | return (Calendar.current as NSCalendar).date(byAdding: NSCalendar.Unit.second, value: seconds, to: Date(), options: [])!
265 | }
266 |
267 |
268 | }
269 |
--------------------------------------------------------------------------------