├── .gitignore ├── LICENSE.txt ├── README.md ├── YPFastDateParser.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── YPFastDateParser.xccheckout └── xcuserdata │ └── jwinky.xcuserdatad │ └── xcschemes │ ├── YPFastDateParser.xcscheme │ └── xcschememanagement.plist ├── YPFastDateParser ├── YPFastDateParser-Prefix.pch ├── YPFastDateParser.h └── YPFastDateParser.m └── YPFastDateParserTests ├── YPFastDateParserTests-Info.plist ├── YPFastDateParserTests.m └── en.lproj └── InfoPlist.strings /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore common tool auto-generated files. 2 | *~ 3 | *.swp 4 | *.DS_Store? 5 | .DS_Store 6 | Icon? 7 | 8 | # Files that might appear on external disk 9 | .Spotlight-V100 10 | .Trashes 11 | 12 | # Xcode 13 | xcuserdata 14 | 15 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2013 Yelp Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | YPFastDateParser 2 | ================ 3 | 4 | An efficient API for parsing string dates, times, and timestamps into NSDate objects. 5 | 6 | This API leverages the speed of SQLite's date parsing, resulting in a speed improvement of ~2 orders of magnitude over NSDateFormatter. In addition, it accomodates some common date formats that SQLite does not directly support. 7 | 8 | Based on ideas from (http://vombat.tumblr.com/post/60530544401/date-parsing-performance-on-ios-nsdateformatter-vs) 9 | 10 | The API 11 | ------- 12 | 13 | NSDate *date = [YPFastDateParser dateFromString:@""]; 14 | 15 | Returns nil if the string cannot be parsed into a date. 16 | 17 | Date Formats 18 | ------------ 19 | 20 | yyyy-mm-dd 21 | yyyy-mm-dd hh:mm 22 | yyyy-mm-dd hh:mm:ss 23 | yyyy-mm-dd hh:mm:ss.SSS 24 | 25 | yyyy-mm-ddThh:mm // Note the literal 'T' 26 | yyyy-mm-ddThh:mm:ss 27 | yyyy-mm-ddThh:mm:ss.SSS 28 | 29 | hh:mm 30 | hh:mm:ss 31 | hh:mm:ss.SSS 32 | 33 | 'now' // Literal string 34 | DDDDDDDDDD // Julian day number as a float 35 | 36 | **Note:** Fractional seconds (.SSS) may contain an arbitrary number of digits. However, only the first three digits are significant to the result; additional digits are ignored. 37 | 38 | Time Zone Specifiers 39 | -------------------- 40 | 41 | A time zone specifier may be appended to any of the supported date formats. 42 | 43 | The following formats are supported natively by SQLite and provide optimal efficiency: 44 | 45 | Z // GMT 46 | +00:00 // Hours and minutes offset 47 | 48 | The following formats are supported by the API but incur a performance penalty: 49 | 50 | +00 // ~1.3x base speed 51 | +01 // Non-zero offset, ~3x base speed 52 | +0000 // ~4x base speed 53 | 54 | Thread Safety 55 | ------------ 56 | 57 | This API is **not** thread-safe (yet). 58 | 59 | If you are using this API across multiple threads, you are strongly advised to create a separate instance of the parser for each thread: 60 | 61 | YPFastDateParser *fastParser = [YPFastDateParser new]; 62 | NSDate *date = [fastParser dateFromString:aString]; 63 | 64 | -------------------------------------------------------------------------------- /YPFastDateParser.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | FD86E7F81846D3D200643D33 /* YPFastDateParser.m in Sources */ = {isa = PBXBuildFile; fileRef = FD86E7F71846D3D200643D33 /* YPFastDateParser.m */; }; 11 | FD86E7FF1846D3D200643D33 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD86E7FE1846D3D200643D33 /* XCTest.framework */; }; 12 | FD86E8031846D3D200643D33 /* libYPFastDateParser.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FD86E7EA1846D3D200643D33 /* libYPFastDateParser.a */; }; 13 | FD86E8091846D3D200643D33 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = FD86E8071846D3D200643D33 /* InfoPlist.strings */; }; 14 | FD86E80B1846D3D200643D33 /* YPFastDateParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FD86E80A1846D3D200643D33 /* YPFastDateParserTests.m */; }; 15 | FD86E8151846D4CF00643D33 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FD86E8141846D4CF00643D33 /* libsqlite3.dylib */; }; 16 | FD86E8171846D4D300643D33 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FD86E8161846D4D300643D33 /* CoreFoundation.framework */; }; 17 | FD86E8181846D56F00643D33 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FD86E8141846D4CF00643D33 /* libsqlite3.dylib */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | FD86E8011846D3D200643D33 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = FD86E7E21846D3D200643D33 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = FD86E7E91846D3D200643D33; 26 | remoteInfo = YPFastDateParser; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | FD86E7EA1846D3D200643D33 /* libYPFastDateParser.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libYPFastDateParser.a; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | FD86E7F01846D3D200643D33 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 33 | FD86E7F51846D3D200643D33 /* YPFastDateParser-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "YPFastDateParser-Prefix.pch"; sourceTree = ""; }; 34 | FD86E7F61846D3D200643D33 /* YPFastDateParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YPFastDateParser.h; sourceTree = ""; }; 35 | FD86E7F71846D3D200643D33 /* YPFastDateParser.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YPFastDateParser.m; sourceTree = ""; }; 36 | FD86E7FD1846D3D200643D33 /* YPFastDateParserTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YPFastDateParserTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | FD86E7FE1846D3D200643D33 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 38 | FD86E8061846D3D200643D33 /* YPFastDateParserTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "YPFastDateParserTests-Info.plist"; sourceTree = ""; }; 39 | FD86E8081846D3D200643D33 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 40 | FD86E80A1846D3D200643D33 /* YPFastDateParserTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YPFastDateParserTests.m; sourceTree = ""; }; 41 | FD86E8141846D4CF00643D33 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; 42 | FD86E8161846D4D300643D33 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | FD86E7E71846D3D200643D33 /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | FD86E8171846D4D300643D33 /* CoreFoundation.framework in Frameworks */, 51 | FD86E8151846D4CF00643D33 /* libsqlite3.dylib in Frameworks */, 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | FD86E7FA1846D3D200643D33 /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | FD86E8181846D56F00643D33 /* libsqlite3.dylib in Frameworks */, 60 | FD86E8031846D3D200643D33 /* libYPFastDateParser.a in Frameworks */, 61 | FD86E7FF1846D3D200643D33 /* XCTest.framework in Frameworks */, 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXFrameworksBuildPhase section */ 66 | 67 | /* Begin PBXGroup section */ 68 | FD86E7E11846D3D200643D33 = { 69 | isa = PBXGroup; 70 | children = ( 71 | FD86E7F31846D3D200643D33 /* YPFastDateParser */, 72 | FD86E8041846D3D200643D33 /* YPFastDateParserTests */, 73 | FD86E7EC1846D3D200643D33 /* Frameworks */, 74 | FD86E7EB1846D3D200643D33 /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | FD86E7EB1846D3D200643D33 /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | FD86E7EA1846D3D200643D33 /* libYPFastDateParser.a */, 82 | FD86E7FD1846D3D200643D33 /* YPFastDateParserTests.xctest */, 83 | ); 84 | name = Products; 85 | sourceTree = ""; 86 | }; 87 | FD86E7EC1846D3D200643D33 /* Frameworks */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | FD86E7F01846D3D200643D33 /* Foundation.framework */, 91 | FD86E8161846D4D300643D33 /* CoreFoundation.framework */, 92 | FD86E8141846D4CF00643D33 /* libsqlite3.dylib */, 93 | FD86E7FE1846D3D200643D33 /* XCTest.framework */, 94 | ); 95 | name = Frameworks; 96 | sourceTree = ""; 97 | }; 98 | FD86E7F31846D3D200643D33 /* YPFastDateParser */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | FD86E7F61846D3D200643D33 /* YPFastDateParser.h */, 102 | FD86E7F71846D3D200643D33 /* YPFastDateParser.m */, 103 | FD86E7F41846D3D200643D33 /* Supporting Files */, 104 | ); 105 | path = YPFastDateParser; 106 | sourceTree = ""; 107 | }; 108 | FD86E7F41846D3D200643D33 /* Supporting Files */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | FD86E7F51846D3D200643D33 /* YPFastDateParser-Prefix.pch */, 112 | ); 113 | name = "Supporting Files"; 114 | sourceTree = ""; 115 | }; 116 | FD86E8041846D3D200643D33 /* YPFastDateParserTests */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | FD86E80A1846D3D200643D33 /* YPFastDateParserTests.m */, 120 | FD86E8051846D3D200643D33 /* Supporting Files */, 121 | ); 122 | path = YPFastDateParserTests; 123 | sourceTree = ""; 124 | }; 125 | FD86E8051846D3D200643D33 /* Supporting Files */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | FD86E8061846D3D200643D33 /* YPFastDateParserTests-Info.plist */, 129 | FD86E8071846D3D200643D33 /* InfoPlist.strings */, 130 | ); 131 | name = "Supporting Files"; 132 | sourceTree = ""; 133 | }; 134 | /* End PBXGroup section */ 135 | 136 | /* Begin PBXHeadersBuildPhase section */ 137 | FD86E7E81846D3D200643D33 /* Headers */ = { 138 | isa = PBXHeadersBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | ); 142 | runOnlyForDeploymentPostprocessing = 0; 143 | }; 144 | /* End PBXHeadersBuildPhase section */ 145 | 146 | /* Begin PBXNativeTarget section */ 147 | FD86E7E91846D3D200643D33 /* YPFastDateParser */ = { 148 | isa = PBXNativeTarget; 149 | buildConfigurationList = FD86E80E1846D3D200643D33 /* Build configuration list for PBXNativeTarget "YPFastDateParser" */; 150 | buildPhases = ( 151 | FD86E7E61846D3D200643D33 /* Sources */, 152 | FD86E7E71846D3D200643D33 /* Frameworks */, 153 | FD86E7E81846D3D200643D33 /* Headers */, 154 | ); 155 | buildRules = ( 156 | ); 157 | dependencies = ( 158 | ); 159 | name = YPFastDateParser; 160 | productName = YPFastDateParser; 161 | productReference = FD86E7EA1846D3D200643D33 /* libYPFastDateParser.a */; 162 | productType = "com.apple.product-type.library.static"; 163 | }; 164 | FD86E7FC1846D3D200643D33 /* YPFastDateParserTests */ = { 165 | isa = PBXNativeTarget; 166 | buildConfigurationList = FD86E8111846D3D200643D33 /* Build configuration list for PBXNativeTarget "YPFastDateParserTests" */; 167 | buildPhases = ( 168 | FD86E7F91846D3D200643D33 /* Sources */, 169 | FD86E7FA1846D3D200643D33 /* Frameworks */, 170 | FD86E7FB1846D3D200643D33 /* Resources */, 171 | ); 172 | buildRules = ( 173 | ); 174 | dependencies = ( 175 | FD86E8021846D3D200643D33 /* PBXTargetDependency */, 176 | ); 177 | name = YPFastDateParserTests; 178 | productName = YPFastDateParserTests; 179 | productReference = FD86E7FD1846D3D200643D33 /* YPFastDateParserTests.xctest */; 180 | productType = "com.apple.product-type.bundle.unit-test"; 181 | }; 182 | /* End PBXNativeTarget section */ 183 | 184 | /* Begin PBXProject section */ 185 | FD86E7E21846D3D200643D33 /* Project object */ = { 186 | isa = PBXProject; 187 | attributes = { 188 | LastUpgradeCheck = 0500; 189 | ORGANIZATIONNAME = "Justin Wienckowski"; 190 | }; 191 | buildConfigurationList = FD86E7E51846D3D200643D33 /* Build configuration list for PBXProject "YPFastDateParser" */; 192 | compatibilityVersion = "Xcode 3.2"; 193 | developmentRegion = English; 194 | hasScannedForEncodings = 0; 195 | knownRegions = ( 196 | en, 197 | ); 198 | mainGroup = FD86E7E11846D3D200643D33; 199 | productRefGroup = FD86E7EB1846D3D200643D33 /* Products */; 200 | projectDirPath = ""; 201 | projectRoot = ""; 202 | targets = ( 203 | FD86E7E91846D3D200643D33 /* YPFastDateParser */, 204 | FD86E7FC1846D3D200643D33 /* YPFastDateParserTests */, 205 | ); 206 | }; 207 | /* End PBXProject section */ 208 | 209 | /* Begin PBXResourcesBuildPhase section */ 210 | FD86E7FB1846D3D200643D33 /* Resources */ = { 211 | isa = PBXResourcesBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | FD86E8091846D3D200643D33 /* InfoPlist.strings in Resources */, 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | }; 218 | /* End PBXResourcesBuildPhase section */ 219 | 220 | /* Begin PBXSourcesBuildPhase section */ 221 | FD86E7E61846D3D200643D33 /* Sources */ = { 222 | isa = PBXSourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | FD86E7F81846D3D200643D33 /* YPFastDateParser.m in Sources */, 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | }; 229 | FD86E7F91846D3D200643D33 /* Sources */ = { 230 | isa = PBXSourcesBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | FD86E80B1846D3D200643D33 /* YPFastDateParserTests.m in Sources */, 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | }; 237 | /* End PBXSourcesBuildPhase section */ 238 | 239 | /* Begin PBXTargetDependency section */ 240 | FD86E8021846D3D200643D33 /* PBXTargetDependency */ = { 241 | isa = PBXTargetDependency; 242 | target = FD86E7E91846D3D200643D33 /* YPFastDateParser */; 243 | targetProxy = FD86E8011846D3D200643D33 /* PBXContainerItemProxy */; 244 | }; 245 | /* End PBXTargetDependency section */ 246 | 247 | /* Begin PBXVariantGroup section */ 248 | FD86E8071846D3D200643D33 /* InfoPlist.strings */ = { 249 | isa = PBXVariantGroup; 250 | children = ( 251 | FD86E8081846D3D200643D33 /* en */, 252 | ); 253 | name = InfoPlist.strings; 254 | sourceTree = ""; 255 | }; 256 | /* End PBXVariantGroup section */ 257 | 258 | /* Begin XCBuildConfiguration section */ 259 | FD86E80C1846D3D200643D33 /* Debug */ = { 260 | isa = XCBuildConfiguration; 261 | buildSettings = { 262 | ALWAYS_SEARCH_USER_PATHS = NO; 263 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 264 | CLANG_CXX_LIBRARY = "libc++"; 265 | CLANG_ENABLE_OBJC_ARC = YES; 266 | CLANG_WARN_BOOL_CONVERSION = YES; 267 | CLANG_WARN_CONSTANT_CONVERSION = YES; 268 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 269 | CLANG_WARN_EMPTY_BODY = YES; 270 | CLANG_WARN_ENUM_CONVERSION = YES; 271 | CLANG_WARN_INT_CONVERSION = YES; 272 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 273 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 274 | COPY_PHASE_STRIP = NO; 275 | GCC_C_LANGUAGE_STANDARD = gnu99; 276 | GCC_DYNAMIC_NO_PIC = NO; 277 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 278 | GCC_OPTIMIZATION_LEVEL = 0; 279 | GCC_PREPROCESSOR_DEFINITIONS = ( 280 | "DEBUG=1", 281 | "$(inherited)", 282 | ); 283 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 284 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 285 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 286 | GCC_WARN_UNDECLARED_SELECTOR = YES; 287 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 288 | GCC_WARN_UNUSED_FUNCTION = YES; 289 | GCC_WARN_UNUSED_VARIABLE = YES; 290 | MACOSX_DEPLOYMENT_TARGET = 10.8; 291 | ONLY_ACTIVE_ARCH = YES; 292 | SDKROOT = macosx; 293 | }; 294 | name = Debug; 295 | }; 296 | FD86E80D1846D3D200643D33 /* Release */ = { 297 | isa = XCBuildConfiguration; 298 | buildSettings = { 299 | ALWAYS_SEARCH_USER_PATHS = NO; 300 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 301 | CLANG_CXX_LIBRARY = "libc++"; 302 | CLANG_ENABLE_OBJC_ARC = YES; 303 | CLANG_WARN_BOOL_CONVERSION = YES; 304 | CLANG_WARN_CONSTANT_CONVERSION = YES; 305 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 306 | CLANG_WARN_EMPTY_BODY = YES; 307 | CLANG_WARN_ENUM_CONVERSION = YES; 308 | CLANG_WARN_INT_CONVERSION = YES; 309 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 310 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 311 | COPY_PHASE_STRIP = YES; 312 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 313 | ENABLE_NS_ASSERTIONS = NO; 314 | GCC_C_LANGUAGE_STANDARD = gnu99; 315 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 316 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 317 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 318 | GCC_WARN_UNDECLARED_SELECTOR = YES; 319 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 320 | GCC_WARN_UNUSED_FUNCTION = YES; 321 | GCC_WARN_UNUSED_VARIABLE = YES; 322 | MACOSX_DEPLOYMENT_TARGET = 10.8; 323 | SDKROOT = macosx; 324 | }; 325 | name = Release; 326 | }; 327 | FD86E80F1846D3D200643D33 /* Debug */ = { 328 | isa = XCBuildConfiguration; 329 | buildSettings = { 330 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 331 | GCC_PREFIX_HEADER = "YPFastDateParser/YPFastDateParser-Prefix.pch"; 332 | PRODUCT_NAME = "$(TARGET_NAME)"; 333 | }; 334 | name = Debug; 335 | }; 336 | FD86E8101846D3D200643D33 /* Release */ = { 337 | isa = XCBuildConfiguration; 338 | buildSettings = { 339 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 340 | GCC_PREFIX_HEADER = "YPFastDateParser/YPFastDateParser-Prefix.pch"; 341 | PRODUCT_NAME = "$(TARGET_NAME)"; 342 | }; 343 | name = Release; 344 | }; 345 | FD86E8121846D3D200643D33 /* Debug */ = { 346 | isa = XCBuildConfiguration; 347 | buildSettings = { 348 | COMBINE_HIDPI_IMAGES = YES; 349 | FRAMEWORK_SEARCH_PATHS = ( 350 | "$(DEVELOPER_FRAMEWORKS_DIR)", 351 | "$(inherited)", 352 | ); 353 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 354 | GCC_PREFIX_HEADER = "YPFastDateParser/YPFastDateParser-Prefix.pch"; 355 | GCC_PREPROCESSOR_DEFINITIONS = ( 356 | "DEBUG=1", 357 | "$(inherited)", 358 | ); 359 | INFOPLIST_FILE = "YPFastDateParserTests/YPFastDateParserTests-Info.plist"; 360 | PRODUCT_NAME = "$(TARGET_NAME)"; 361 | WRAPPER_EXTENSION = xctest; 362 | }; 363 | name = Debug; 364 | }; 365 | FD86E8131846D3D200643D33 /* Release */ = { 366 | isa = XCBuildConfiguration; 367 | buildSettings = { 368 | COMBINE_HIDPI_IMAGES = YES; 369 | FRAMEWORK_SEARCH_PATHS = ( 370 | "$(DEVELOPER_FRAMEWORKS_DIR)", 371 | "$(inherited)", 372 | ); 373 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 374 | GCC_PREFIX_HEADER = "YPFastDateParser/YPFastDateParser-Prefix.pch"; 375 | INFOPLIST_FILE = "YPFastDateParserTests/YPFastDateParserTests-Info.plist"; 376 | PRODUCT_NAME = "$(TARGET_NAME)"; 377 | WRAPPER_EXTENSION = xctest; 378 | }; 379 | name = Release; 380 | }; 381 | /* End XCBuildConfiguration section */ 382 | 383 | /* Begin XCConfigurationList section */ 384 | FD86E7E51846D3D200643D33 /* Build configuration list for PBXProject "YPFastDateParser" */ = { 385 | isa = XCConfigurationList; 386 | buildConfigurations = ( 387 | FD86E80C1846D3D200643D33 /* Debug */, 388 | FD86E80D1846D3D200643D33 /* Release */, 389 | ); 390 | defaultConfigurationIsVisible = 0; 391 | defaultConfigurationName = Release; 392 | }; 393 | FD86E80E1846D3D200643D33 /* Build configuration list for PBXNativeTarget "YPFastDateParser" */ = { 394 | isa = XCConfigurationList; 395 | buildConfigurations = ( 396 | FD86E80F1846D3D200643D33 /* Debug */, 397 | FD86E8101846D3D200643D33 /* Release */, 398 | ); 399 | defaultConfigurationIsVisible = 0; 400 | }; 401 | FD86E8111846D3D200643D33 /* Build configuration list for PBXNativeTarget "YPFastDateParserTests" */ = { 402 | isa = XCConfigurationList; 403 | buildConfigurations = ( 404 | FD86E8121846D3D200643D33 /* Debug */, 405 | FD86E8131846D3D200643D33 /* Release */, 406 | ); 407 | defaultConfigurationIsVisible = 0; 408 | }; 409 | /* End XCConfigurationList section */ 410 | }; 411 | rootObject = FD86E7E21846D3D200643D33 /* Project object */; 412 | } 413 | -------------------------------------------------------------------------------- /YPFastDateParser.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /YPFastDateParser.xcodeproj/project.xcworkspace/xcshareddata/YPFastDateParser.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 54D6517B-CEAE-480B-A9FD-28965861B8D7 9 | IDESourceControlProjectName 10 | YPFastDateParser 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 08938463-5682-4012-BF33-24E7871B34F3 14 | ssh://github.com/Yelp/YPFastDateParser.git 15 | 16 | IDESourceControlProjectPath 17 | YPFastDateParser.xcodeproj/project.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 08938463-5682-4012-BF33-24E7871B34F3 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | ssh://github.com/Yelp/YPFastDateParser.git 25 | IDESourceControlProjectVersion 26 | 110 27 | IDESourceControlProjectWCCIdentifier 28 | 08938463-5682-4012-BF33-24E7871B34F3 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 08938463-5682-4012-BF33-24E7871B34F3 36 | IDESourceControlWCCName 37 | YPFastDateParser 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /YPFastDateParser.xcodeproj/xcuserdata/jwinky.xcuserdatad/xcschemes/YPFastDateParser.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 52 | 53 | 54 | 55 | 61 | 62 | 64 | 65 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /YPFastDateParser.xcodeproj/xcuserdata/jwinky.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | YPFastDateParser.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | FD86E7E91846D3D200643D33 16 | 17 | primary 18 | 19 | 20 | FD86E7FC1846D3D200643D33 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /YPFastDateParser/YPFastDateParser-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /YPFastDateParser/YPFastDateParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // YPFastDateParser.h 3 | // YPFastDateParser 4 | // 5 | // Created by Justin Wienckowski on 11/21/13. 6 | // Copyright (c) 2013 Yelp, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | Parses strings into NSDate objects much faster than NSDateFormatter, but only 13 | supports certain string formats. Uses SQLite for performance. 14 | 15 | Supports strings in the following formats: 16 | 17 | YYYY-MM-DD 18 | YYYY-MM-DD HH:MM 19 | YYYY-MM-DD HH:MM:SS 20 | YYYY-MM-DD HH:MM:SS.SSS 21 | YYYY-MM-DDTHH:MM 22 | YYYY-MM-DDTHH:MM:SS 23 | YYYY-MM-DDTHH:MM:SS.SSS 24 | HH:MM 25 | HH:MM:SS 26 | HH:MM:SS.SSS 27 | 'now' (literal) 28 | DDDDDDDDDD (Julian day number as a floating point value) 29 | 30 | Fractional seconds are optional and may include more than 3 digits, but digits in excess 31 | of 3 are ignored by sqlite. 32 | 33 | A time zone specifier may be appended to any of the first 10 formats. Supported time zone 34 | specifiers are (in descending order of efficiency): 35 | Z 36 | +00:00 (hours and minutes offset) 37 | +00 38 | +0000 (same as previous without colon; this is slower to parse) 39 | **/ 40 | @interface YPFastDateParser : NSObject 41 | 42 | // Parses a string into an NSDate object. Returns nil if the string could not be parsed. 43 | + (NSDate *)dateFromString:(NSString *)string; 44 | 45 | // Singleton instance 46 | + (instancetype)sharedInstance; 47 | 48 | // Instance methods 49 | - (instancetype)init; 50 | - (NSDate *)dateFromString:(NSString *)string; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /YPFastDateParser/YPFastDateParser.m: -------------------------------------------------------------------------------- 1 | // 2 | // YPFastDateParser.m 3 | // YPFastDateParser 4 | // 5 | // Created by Justin Wienckowski on 11/21/13. 6 | // Copyright (c) 2013 Yelp, Inc. All rights reserved. 7 | // 8 | 9 | #import "YPFastDateParser.h" 10 | #import 11 | 12 | static YPFastDateParser *SharedInstance; 13 | 14 | @implementation YPFastDateParser { 15 | sqlite3 *_sqliteDb; 16 | sqlite3_stmt *_statement; 17 | } 18 | 19 | + (NSDate *)dateFromString:(NSString *)string 20 | { 21 | return [[self sharedInstance] dateFromString:string]; 22 | } 23 | 24 | + (instancetype)sharedInstance 25 | { 26 | if (!SharedInstance) { 27 | SharedInstance = [[self alloc] init]; 28 | } 29 | 30 | return SharedInstance; 31 | } 32 | 33 | - (instancetype)init 34 | { 35 | if (self = [super init]) { 36 | _sqliteDb = NULL; 37 | _statement = NULL; 38 | 39 | int result = sqlite3_open(":memory:", &_sqliteDb); 40 | if (result != SQLITE_OK) [self throwSqliteException]; 41 | 42 | result = sqlite3_prepare_v2(_sqliteDb, "SELECT strftime('%s', ?), strftime('%f', ?);", -1, &_statement, NULL); 43 | if (result != SQLITE_OK) [self throwSqliteException]; 44 | } 45 | return self; 46 | } 47 | 48 | - (void)throwSqliteException 49 | { 50 | #ifdef DEBUG 51 | abort(); 52 | #endif 53 | const char *errMsg = sqlite3_errmsg(_sqliteDb); 54 | @throw [NSException exceptionWithName:@"SMFastDateTimeParserSQLiteFailure" 55 | reason:[NSString stringWithUTF8String:errMsg] 56 | userInfo:nil]; 57 | } 58 | 59 | - (void)dealloc 60 | { 61 | sqlite3_finalize(_statement); 62 | _statement = NULL; 63 | 64 | sqlite3_close(_sqliteDb); 65 | _sqliteDb = NULL; 66 | } 67 | 68 | - (NSDate *)dateFromString:(NSString *)string 69 | { 70 | return [self dateFromString:string timeZoneAdjusted:NO]; 71 | } 72 | 73 | - (NSDate *)dateFromString:(NSString *)string timeZoneAdjusted:(BOOL)adjusted 74 | { 75 | NSAssert(_sqliteDb != NULL, @"SQLite db connection is not initialized!"); 76 | if (!string.length) return nil; 77 | 78 | // Time zone format common case 79 | if ([string hasSuffix:@"+00"]) 80 | string = [string stringByReplacingCharactersInRange:NSMakeRange(string.length - 3, 3) withString:@"Z"]; 81 | 82 | int result; 83 | 84 | result = sqlite3_reset(_statement); 85 | if (result != SQLITE_OK) [self throwSqliteException]; 86 | 87 | result = sqlite3_clear_bindings(_statement); 88 | if (result != SQLITE_OK) [self throwSqliteException]; 89 | 90 | result = sqlite3_bind_text(_statement, 1, [string UTF8String], -1, SQLITE_STATIC); 91 | if (result != SQLITE_OK) [self throwSqliteException]; 92 | 93 | result = sqlite3_bind_text(_statement, 2, [string UTF8String], -1, SQLITE_STATIC); 94 | if (result != SQLITE_OK) [self throwSqliteException]; 95 | 96 | result = sqlite3_step(_statement); 97 | if (result != SQLITE_ROW) [self throwSqliteException]; 98 | 99 | if (sqlite3_column_type(_statement, 0) == SQLITE_NULL) { 100 | if (adjusted) { 101 | return nil; 102 | } else { 103 | NSString *fixedString = [[self class] fixTimeZoneSpecifier:string]; 104 | return [self dateFromString:fixedString timeZoneAdjusted:YES]; 105 | } 106 | } 107 | 108 | sqlite3_int64 interval = sqlite3_column_int64(_statement, 0); 109 | double seconds = sqlite3_column_double(_statement, 1); 110 | 111 | // Extract the fraction component of the seconds 112 | double sintegral; 113 | double sfraction = modf(seconds, &sintegral); 114 | 115 | NSDate *date = [NSDate dateWithTimeIntervalSince1970:(double)interval + sfraction]; 116 | 117 | return date; 118 | } 119 | 120 | // Attempts to convert other time zone specifier formats to SQLite-compatible format 121 | + (NSString *)fixTimeZoneSpecifier:(NSString *)string 122 | { 123 | static NSRegularExpression *TwoDigitOffset; 124 | static NSRegularExpression *FourDigitOffset; 125 | 126 | static dispatch_once_t onceToken; 127 | dispatch_once(&onceToken, ^{ 128 | NSError *error; 129 | 130 | TwoDigitOffset = [NSRegularExpression regularExpressionWithPattern:@"[\\+\\-][0-9]{2}" 131 | options:0 error:&error]; 132 | if (!TwoDigitOffset) { 133 | @throw [NSException exceptionWithName:NSInvalidArgumentException 134 | reason:error.localizedDescription 135 | userInfo:@{@"error": error}]; 136 | } 137 | 138 | FourDigitOffset = [NSRegularExpression regularExpressionWithPattern:@"([\\+\\-][0-9]{2})([0-9]{2})" 139 | options:0 error:&error]; 140 | if (!FourDigitOffset) { 141 | @throw [NSException exceptionWithName:NSInvalidArgumentException 142 | reason:error.localizedDescription 143 | userInfo:@{@"error": error}]; 144 | } 145 | }); 146 | 147 | NSTextCheckingResult *match; 148 | NSString *testStr = [string substringWithRange:NSMakeRange(string.length - 3, 3)]; 149 | 150 | if ((match = [TwoDigitOffset firstMatchInString:testStr options:0 range:(NSRange){0,3}])) { 151 | return [string stringByAppendingString:@":00"]; 152 | } 153 | 154 | testStr = [string substringWithRange:NSMakeRange(string.length - 5, 5)]; 155 | 156 | if ((match = [FourDigitOffset firstMatchInString:testStr options:0 range:(NSRange){0, 5}])) { 157 | NSString *hrPart = [testStr substringWithRange:[match rangeAtIndex:1]]; 158 | NSString *minPart = [testStr substringWithRange:[match rangeAtIndex:2]]; 159 | NSString *newTZSpecifier = [NSString stringWithFormat:@"%@:%@", hrPart, minPart]; 160 | 161 | return [string stringByReplacingCharactersInRange:NSMakeRange(string.length - 5, 5) withString:newTZSpecifier]; 162 | } 163 | 164 | return string; 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /YPFastDateParserTests/YPFastDateParserTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.yelp.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /YPFastDateParserTests/YPFastDateParserTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // YPFastDateParserTests.m 3 | // YPFastDateParser 4 | // 5 | // Created by Justin Wienckowski on 11/21/13. 6 | // Copyright (c) 2013 Yelp, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "YPFastDateParser.h" 11 | 12 | 13 | @interface YPFastDateParserTests : XCTestCase 14 | @end 15 | 16 | @implementation YPFastDateParserTests 17 | 18 | - (void)testNilString 19 | { 20 | XCTAssertNil([YPFastDateParser dateFromString:nil], @"Nil string should return nil"); 21 | } 22 | 23 | - (void)testBlankString 24 | { 25 | XCTAssertNil([YPFastDateParser dateFromString:@""], @"Blank string should return nil"); 26 | } 27 | 28 | - (void)testInvalidStrings 29 | { 30 | XCTAssertNil([YPFastDateParser dateFromString:@"abc123"], @"Invalid string should return nil"); 31 | XCTAssertNil([YPFastDateParser dateFromString:@"123abc"], @"Invalid string should return nil"); 32 | XCTAssertNil([YPFastDateParser dateFromString:@"01-01-1980"], @"Invalid string should return nil"); 33 | XCTAssertNil([YPFastDateParser dateFromString:@"12h30"], @"Invalid string should return nil"); 34 | } 35 | 36 | - (void)testFractionalSeconds 37 | { 38 | // 1351321200 == 2012-10-27 07:00:00 UTC 39 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 40 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00"], @"Fractional seconds are optional"); 41 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200.123], 42 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00.123"], @"Fractional seconds are honored"); 43 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200.123], 44 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00.123456"], @"Fractional seconds in excess of 3 are ignored"); 45 | } 46 | 47 | - (void)testValidStrings 48 | { 49 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 50 | [YPFastDateParser dateFromString:@"2012-10-27 07:00"], @"Valid string should return date"); 51 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 52 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00"], @"Valid string should return date"); 53 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200.123], 54 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00.123"], @"Valid string should return date"); 55 | 56 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 57 | [YPFastDateParser dateFromString:@"2012-10-27 07:00Z"], @"Valid string should return date"); 58 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 59 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00Z"], @"Valid string should return date"); 60 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200.123], 61 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00.123Z"], @"Valid string should return date"); 62 | 63 | 64 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 65 | [YPFastDateParser dateFromString:@"2012-10-27T07:00"], @"Valid string should return date"); 66 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 67 | [YPFastDateParser dateFromString:@"2012-10-27T07:00:00"], @"Valid string should return date"); 68 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200.123], 69 | [YPFastDateParser dateFromString:@"2012-10-27T07:00:00.123"], @"Valid string should return date"); 70 | 71 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 72 | [YPFastDateParser dateFromString:@"2012-10-27T07:00Z"], @"Valid string should return date"); 73 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 74 | [YPFastDateParser dateFromString:@"2012-10-27T07:00:00Z"], @"Valid string should return date"); 75 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200.123], 76 | [YPFastDateParser dateFromString:@"2012-10-27T07:00:00.123Z"], @"Valid string should return date"); 77 | 78 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351296000], 79 | [YPFastDateParser dateFromString:@"2012-10-27"], @"2012-10-27 converts to 2012-10-27 00:00:00.000"); 80 | 81 | XCTAssertEqual((double)946722600.0, 82 | (double)[[YPFastDateParser dateFromString:@"10:30"] timeIntervalSince1970], 83 | @"10:30 converts to 2000-01-01 10:30:00.000"); 84 | XCTAssertEqual((double)946722623.0, 85 | (double)[[YPFastDateParser dateFromString:@"10:30:23"] timeIntervalSince1970], 86 | @"10:30:23 converts to 2000-01-01 10:30:23.000"); 87 | XCTAssertEqual((double)946722623.543, 88 | (double)[[YPFastDateParser dateFromString:@"10:30:23.543"] timeIntervalSince1970], 89 | @"10:30:23.543 converts to 2000-01-01 10:30:23.543"); 90 | 91 | XCTAssertEqualWithAccuracy((double)[[NSDate date] timeIntervalSince1970], 92 | (double)[[YPFastDateParser dateFromString:@"now"] timeIntervalSince1970], 93 | 0.000999f, @"'now' converts to now with 3 decimal precision"); 94 | } 95 | 96 | - (void)testTimeZones 97 | { 98 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 99 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00Z"], @"Zulu time zone suffix honored"); 100 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 101 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00+00:00"], @"+00:00 time zone specifier honored"); 102 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 103 | [YPFastDateParser dateFromString:@"2012-10-27 08:30:00+01:30"], @"+01:30 time zone specifier honored"); 104 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 105 | [YPFastDateParser dateFromString:@"2012-10-27 04:30:00-02:30"], @"-02:30 time zone specifier honored"); 106 | } 107 | 108 | - (void)testFixesMalformedTimeZones 109 | { 110 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 111 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00+00"], @"+00 time zone suffix honored"); 112 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 113 | [YPFastDateParser dateFromString:@"2012-10-27 11:00:00+04"], @"+04 time zone suffix honored"); 114 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 115 | [YPFastDateParser dateFromString:@"2012-10-27 00:00:00.000-07"], @"-07 time zone suffix honored"); 116 | 117 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 118 | [YPFastDateParser dateFromString:@"2012-10-27 09:00:00+0200"], @"+0200 time zone suffix honored"); 119 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 120 | [YPFastDateParser dateFromString:@"2012-10-27 09:34:00+0234"], @"+0234 time zone suffix honored"); 121 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 122 | [YPFastDateParser dateFromString:@"2012-10-27 05:00:00-0200"], @"-0200 time zone suffix honored"); 123 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 124 | [YPFastDateParser dateFromString:@"2012-10-27 04:26:00-0234"], @"-0234 time zone suffix honored"); 125 | 126 | 127 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 128 | [YPFastDateParser dateFromString:@"2012-10-27 05:00:00.000 -02"], @"-02 time zone suffix honored with space"); 129 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 130 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00.000 +00"], @"+00 time zone suffix honored with space"); 131 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200], 132 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00.000 +0000"], @"+0000 time zone suffix honored with space"); 133 | XCTAssertEqualObjects([NSDate dateWithTimeIntervalSince1970:1351321200.876], 134 | [YPFastDateParser dateFromString:@"2012-10-27 00:00:00.876 -0700"], @"-0700 time zone suffix honored with space"); 135 | } 136 | 137 | - (void)testPerformance 138 | { 139 | // Base (fastest) case 140 | NSDate *start = [NSDate date]; 141 | for (int n = 0; n < 50000; n++) { 142 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00Z"]; 143 | } 144 | NSDate *end = [NSDate date]; 145 | NSTimeInterval duration = [end timeIntervalSinceDate:start]; 146 | NSLog(@"Base case duration: %f", duration); 147 | XCTAssertTrue(duration < 0.25f, @"Base case duration %f is too slow!", duration); 148 | 149 | // Fast case 150 | start = [NSDate date]; 151 | for (int n = 0; n < 50000; n++) { 152 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00+00"]; 153 | } 154 | end = [NSDate date]; 155 | duration = [end timeIntervalSinceDate:start]; 156 | NSLog(@"Fast case duration: %f", duration); 157 | XCTAssertTrue(duration < 0.4f, @"Fast case duration %f is too slow!", duration); 158 | 159 | // Medium case 160 | start = [NSDate date]; 161 | for (int n = 0; n < 50000; n++) { 162 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00+01"]; 163 | } 164 | end = [NSDate date]; 165 | duration = [end timeIntervalSinceDate:start]; 166 | NSLog(@"Medium case duration: %f", duration); 167 | XCTAssertTrue(duration < 0.8f, @"Medium case duration %f too slow!", duration); 168 | 169 | // Slow case 170 | start = [NSDate date]; 171 | for (int n = 0; n < 50000; n++) { 172 | [YPFastDateParser dateFromString:@"2012-10-27 07:00:00+0130"]; 173 | } 174 | end = [NSDate date]; 175 | duration = [end timeIntervalSinceDate:start]; 176 | NSLog(@"Slow case duration: %f", duration); 177 | XCTAssertTrue(duration < 1.2f, @"Slow case duration %f too slow!", duration); 178 | } 179 | 180 | @end 181 | -------------------------------------------------------------------------------- /YPFastDateParserTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | --------------------------------------------------------------------------------