├── .gitattributes ├── ISO8601.xcodeproj ├── project.pbxproj └── xcuserdata │ └── miroslavperovic.xcuserdatad │ └── xcschemes │ └── ISO8601.xcscheme ├── ISO8601 ├── ISO8601.swift └── ISO8601Playground.playground │ ├── Contents.swift │ ├── contents.xcplayground │ └── timeline.xctimeline ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ISO8601.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DCD0F6A71B397F5B00B648B3 /* ISO8601.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD0F6A61B397F5B00B648B3 /* ISO8601.swift */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | DCD0F6A11B397F5B00B648B3 /* CopyFiles */ = { 15 | isa = PBXCopyFilesBuildPhase; 16 | buildActionMask = 2147483647; 17 | dstPath = /usr/share/man/man1/; 18 | dstSubfolderSpec = 0; 19 | files = ( 20 | ); 21 | runOnlyForDeploymentPostprocessing = 1; 22 | }; 23 | /* End PBXCopyFilesBuildPhase section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | DCD0F6A31B397F5B00B648B3 /* ISO8601 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ISO8601; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | DCD0F6A61B397F5B00B648B3 /* ISO8601.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ISO8601.swift; sourceTree = ""; }; 28 | DCD0F6AD1B39870F00B648B3 /* ISO8601Playground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = ISO8601Playground.playground; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | DCD0F6A01B397F5B00B648B3 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | DCD0F69A1B397F5B00B648B3 = { 43 | isa = PBXGroup; 44 | children = ( 45 | DCD0F6A51B397F5B00B648B3 /* ISO8601 */, 46 | DCD0F6A41B397F5B00B648B3 /* Products */, 47 | ); 48 | sourceTree = ""; 49 | }; 50 | DCD0F6A41B397F5B00B648B3 /* Products */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | DCD0F6A31B397F5B00B648B3 /* ISO8601 */, 54 | ); 55 | name = Products; 56 | sourceTree = ""; 57 | }; 58 | DCD0F6A51B397F5B00B648B3 /* ISO8601 */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | DCD0F6A61B397F5B00B648B3 /* ISO8601.swift */, 62 | DCD0F6AD1B39870F00B648B3 /* ISO8601Playground.playground */, 63 | ); 64 | path = ISO8601; 65 | sourceTree = ""; 66 | }; 67 | /* End PBXGroup section */ 68 | 69 | /* Begin PBXNativeTarget section */ 70 | DCD0F6A21B397F5B00B648B3 /* ISO8601 */ = { 71 | isa = PBXNativeTarget; 72 | buildConfigurationList = DCD0F6AA1B397F5B00B648B3 /* Build configuration list for PBXNativeTarget "ISO8601" */; 73 | buildPhases = ( 74 | DCD0F69F1B397F5B00B648B3 /* Sources */, 75 | DCD0F6A01B397F5B00B648B3 /* Frameworks */, 76 | DCD0F6A11B397F5B00B648B3 /* CopyFiles */, 77 | ); 78 | buildRules = ( 79 | ); 80 | dependencies = ( 81 | ); 82 | name = ISO8601; 83 | productName = ISO8601; 84 | productReference = DCD0F6A31B397F5B00B648B3 /* ISO8601 */; 85 | productType = "com.apple.product-type.tool"; 86 | }; 87 | /* End PBXNativeTarget section */ 88 | 89 | /* Begin PBXProject section */ 90 | DCD0F69B1B397F5B00B648B3 /* Project object */ = { 91 | isa = PBXProject; 92 | attributes = { 93 | LastUpgradeCheck = 0700; 94 | ORGANIZATIONNAME = "Miroslav Perovic"; 95 | TargetAttributes = { 96 | DCD0F6A21B397F5B00B648B3 = { 97 | CreatedOnToolsVersion = 7.0; 98 | }; 99 | }; 100 | }; 101 | buildConfigurationList = DCD0F69E1B397F5B00B648B3 /* Build configuration list for PBXProject "ISO8601" */; 102 | compatibilityVersion = "Xcode 3.2"; 103 | developmentRegion = English; 104 | hasScannedForEncodings = 0; 105 | knownRegions = ( 106 | en, 107 | ); 108 | mainGroup = DCD0F69A1B397F5B00B648B3; 109 | productRefGroup = DCD0F6A41B397F5B00B648B3 /* Products */; 110 | projectDirPath = ""; 111 | projectRoot = ""; 112 | targets = ( 113 | DCD0F6A21B397F5B00B648B3 /* ISO8601 */, 114 | ); 115 | }; 116 | /* End PBXProject section */ 117 | 118 | /* Begin PBXSourcesBuildPhase section */ 119 | DCD0F69F1B397F5B00B648B3 /* Sources */ = { 120 | isa = PBXSourcesBuildPhase; 121 | buildActionMask = 2147483647; 122 | files = ( 123 | DCD0F6A71B397F5B00B648B3 /* ISO8601.swift in Sources */, 124 | ); 125 | runOnlyForDeploymentPostprocessing = 0; 126 | }; 127 | /* End PBXSourcesBuildPhase section */ 128 | 129 | /* Begin XCBuildConfiguration section */ 130 | DCD0F6A81B397F5B00B648B3 /* Debug */ = { 131 | isa = XCBuildConfiguration; 132 | buildSettings = { 133 | ALWAYS_SEARCH_USER_PATHS = NO; 134 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 135 | CLANG_CXX_LIBRARY = "libc++"; 136 | CLANG_ENABLE_MODULES = YES; 137 | CLANG_ENABLE_OBJC_ARC = YES; 138 | CLANG_WARN_BOOL_CONVERSION = YES; 139 | CLANG_WARN_CONSTANT_CONVERSION = YES; 140 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 141 | CLANG_WARN_EMPTY_BODY = YES; 142 | CLANG_WARN_ENUM_CONVERSION = YES; 143 | CLANG_WARN_INT_CONVERSION = YES; 144 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 145 | CLANG_WARN_UNREACHABLE_CODE = YES; 146 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 147 | COPY_PHASE_STRIP = NO; 148 | DEBUG_INFORMATION_FORMAT = dwarf; 149 | ENABLE_STRICT_OBJC_MSGSEND = YES; 150 | ENABLE_TESTABILITY = YES; 151 | GCC_C_LANGUAGE_STANDARD = gnu99; 152 | GCC_DYNAMIC_NO_PIC = NO; 153 | GCC_NO_COMMON_BLOCKS = YES; 154 | GCC_OPTIMIZATION_LEVEL = 0; 155 | GCC_PREPROCESSOR_DEFINITIONS = ( 156 | "DEBUG=1", 157 | "$(inherited)", 158 | ); 159 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 160 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 161 | GCC_WARN_UNDECLARED_SELECTOR = YES; 162 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 163 | GCC_WARN_UNUSED_FUNCTION = YES; 164 | GCC_WARN_UNUSED_VARIABLE = YES; 165 | MACOSX_DEPLOYMENT_TARGET = 10.11; 166 | MTL_ENABLE_DEBUG_INFO = YES; 167 | ONLY_ACTIVE_ARCH = YES; 168 | SDKROOT = macosx; 169 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 170 | }; 171 | name = Debug; 172 | }; 173 | DCD0F6A91B397F5B00B648B3 /* Release */ = { 174 | isa = XCBuildConfiguration; 175 | buildSettings = { 176 | ALWAYS_SEARCH_USER_PATHS = NO; 177 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 178 | CLANG_CXX_LIBRARY = "libc++"; 179 | CLANG_ENABLE_MODULES = YES; 180 | CLANG_ENABLE_OBJC_ARC = YES; 181 | CLANG_WARN_BOOL_CONVERSION = YES; 182 | CLANG_WARN_CONSTANT_CONVERSION = YES; 183 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 184 | CLANG_WARN_EMPTY_BODY = YES; 185 | CLANG_WARN_ENUM_CONVERSION = YES; 186 | CLANG_WARN_INT_CONVERSION = YES; 187 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 188 | CLANG_WARN_UNREACHABLE_CODE = YES; 189 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 190 | COPY_PHASE_STRIP = NO; 191 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 192 | ENABLE_NS_ASSERTIONS = NO; 193 | ENABLE_STRICT_OBJC_MSGSEND = YES; 194 | GCC_C_LANGUAGE_STANDARD = gnu99; 195 | GCC_NO_COMMON_BLOCKS = YES; 196 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 197 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 198 | GCC_WARN_UNDECLARED_SELECTOR = YES; 199 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 200 | GCC_WARN_UNUSED_FUNCTION = YES; 201 | GCC_WARN_UNUSED_VARIABLE = YES; 202 | MACOSX_DEPLOYMENT_TARGET = 10.11; 203 | MTL_ENABLE_DEBUG_INFO = NO; 204 | SDKROOT = macosx; 205 | }; 206 | name = Release; 207 | }; 208 | DCD0F6AB1B397F5B00B648B3 /* Debug */ = { 209 | isa = XCBuildConfiguration; 210 | buildSettings = { 211 | PRODUCT_NAME = "$(TARGET_NAME)"; 212 | }; 213 | name = Debug; 214 | }; 215 | DCD0F6AC1B397F5B00B648B3 /* Release */ = { 216 | isa = XCBuildConfiguration; 217 | buildSettings = { 218 | PRODUCT_NAME = "$(TARGET_NAME)"; 219 | }; 220 | name = Release; 221 | }; 222 | /* End XCBuildConfiguration section */ 223 | 224 | /* Begin XCConfigurationList section */ 225 | DCD0F69E1B397F5B00B648B3 /* Build configuration list for PBXProject "ISO8601" */ = { 226 | isa = XCConfigurationList; 227 | buildConfigurations = ( 228 | DCD0F6A81B397F5B00B648B3 /* Debug */, 229 | DCD0F6A91B397F5B00B648B3 /* Release */, 230 | ); 231 | defaultConfigurationIsVisible = 0; 232 | defaultConfigurationName = Release; 233 | }; 234 | DCD0F6AA1B397F5B00B648B3 /* Build configuration list for PBXNativeTarget "ISO8601" */ = { 235 | isa = XCConfigurationList; 236 | buildConfigurations = ( 237 | DCD0F6AB1B397F5B00B648B3 /* Debug */, 238 | DCD0F6AC1B397F5B00B648B3 /* Release */, 239 | ); 240 | defaultConfigurationIsVisible = 0; 241 | }; 242 | /* End XCConfigurationList section */ 243 | }; 244 | rootObject = DCD0F69B1B397F5B00B648B3 /* Project object */; 245 | } 246 | -------------------------------------------------------------------------------- /ISO8601.xcodeproj/xcuserdata/miroslavperovic.xcuserdatad/xcschemes/ISO8601.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ISO8601/ISO8601.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // ISO8601 4 | // 5 | // Created by Miroslav Perovic on 6/23/15. 6 | // Copyright © 2015 Miroslav Perovic. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class ISO8601Formatter: NSFormatter { 12 | enum ISO8601DateStyle: Int { 13 | case CalendarLongStyle // Default (YYYY-MM-DD) 14 | case CalendarShortStyle // (YYYYMMDD) 15 | case OrdinalLongStyle // (YYYY-DDD) 16 | case OrdinalShortStyle // (YYYYDDD) 17 | case WeekLongStyle // (YYYY-Www-D) 18 | case WeekShortStyle // (YYYYWwwD) 19 | } 20 | 21 | enum ISO8601TimeStyle: Int { 22 | case None 23 | case LongStyle // Default (hh:mm:ss) 24 | case ShortStyle // (hhmmss) 25 | } 26 | 27 | enum ISO8601TimeZoneStyle: Int { 28 | case None 29 | case UTC // Default (Z) 30 | case LongStyle // (±hh:mm) 31 | case ShortStyle // (±hhmm) 32 | } 33 | 34 | enum ISO8601FractionSeparator: Int { 35 | case Comma // Default (,) 36 | case Dot // (.) 37 | } 38 | 39 | var dateStyle: ISO8601DateStyle 40 | var timeStyle: ISO8601TimeStyle 41 | var fractionSeparator: ISO8601FractionSeparator 42 | var timeZoneStyle: ISO8601TimeZoneStyle 43 | var fractionDigits: Int 44 | 45 | let days365 = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334] 46 | let days366 = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335] 47 | 48 | convenience override init() { 49 | self.init( 50 | dateStyle: .CalendarLongStyle, 51 | timeStyle: .LongStyle, 52 | fractionSeparator: .Comma, 53 | fractionDigits: 6, 54 | timeZoneStyle: .UTC 55 | ) 56 | } 57 | 58 | init(dateStyle: ISO8601DateStyle, timeStyle: ISO8601TimeStyle, fractionSeparator: ISO8601FractionSeparator, fractionDigits: Int, timeZoneStyle: ISO8601TimeZoneStyle) { 59 | self.dateStyle = dateStyle 60 | self.timeStyle = timeStyle 61 | self.fractionSeparator = fractionSeparator 62 | self.fractionDigits = fractionDigits 63 | self.timeZoneStyle = timeZoneStyle 64 | 65 | super.init() 66 | } 67 | 68 | required convenience init?(coder aDecoder: NSCoder) { 69 | self.init( 70 | dateStyle: .CalendarLongStyle, 71 | timeStyle: .LongStyle, 72 | fractionSeparator: .Comma, 73 | fractionDigits: 6, 74 | timeZoneStyle: .UTC 75 | ) 76 | } 77 | 78 | func stringFromDate(date: NSDate) -> String? { 79 | let calendar = NSCalendar.currentCalendar() 80 | if timeZoneStyle == .UTC { 81 | calendar.timeZone = NSTimeZone(forSecondsFromGMT: 0) 82 | } 83 | 84 | let dateComponents = calendar.components( 85 | [.Year, .Month, .Day, .WeekOfYear, .Hour, .Minute, .Second, .Weekday, .WeekdayOrdinal, .WeekOfYear, .YearForWeekOfYear, .TimeZone], 86 | fromDate: date 87 | ) 88 | var string: String 89 | 90 | if dateComponents.year < 0 || dateComponents.year > 9999 { 91 | return nil 92 | } 93 | 94 | string = String(format: "%04li", dateComponents.year) 95 | if dateStyle == .WeekLongStyle || dateStyle == .WeekShortStyle { 96 | // For weekOfYear calculation see more at: https://en.wikipedia.org/wiki/ISO_8601#Week_dates 97 | if date.weekOfYear() == 53 { 98 | string = String(format: "%04li", dateComponents.year - 1) 99 | } 100 | } 101 | 102 | switch dateStyle { 103 | case .CalendarLongStyle: 104 | string = string + String(format: "-%02i-%02i", dateComponents.month, dateComponents.day) 105 | case .CalendarShortStyle: 106 | string = string + String(format: "%02i%02i", dateComponents.month, dateComponents.day) 107 | case .OrdinalLongStyle: 108 | string = string + String(format: "-%03i", date.dayOfYear()) 109 | case .OrdinalShortStyle: 110 | string = string + String(format: "%03i", date.dayOfYear()) 111 | case .WeekLongStyle: 112 | if dateComponents.weekday > 1 { 113 | string = string + String(format: "-W%02i-%01i", date.weekOfYear(), dateComponents.weekday - 1) 114 | } else { 115 | string = string + String(format: "-W%02i-%01i", date.weekOfYear(), 7) 116 | } 117 | case .WeekShortStyle: 118 | if dateComponents.weekday > 1 { 119 | string = string + String(format: "W%02i%01i", dateComponents.weekOfYear, dateComponents.weekday - 1) 120 | } else { 121 | string = string + String(format: "W%02i%01i", dateComponents.weekOfYear, 7) 122 | } 123 | } 124 | 125 | let timeString: String 126 | switch timeStyle { 127 | case .LongStyle: 128 | timeString = String(format: "T%02i:%02i:%02i", dateComponents.hour, dateComponents.minute, dateComponents.second) 129 | case .ShortStyle: 130 | timeString = String(format: "T%02i:%02i:%02i", dateComponents.hour, dateComponents.minute, dateComponents.second) 131 | case .None: 132 | return string 133 | } 134 | string = string + timeString 135 | 136 | if let timeZone = dateComponents.timeZone { 137 | let timeZoneString: String 138 | switch timeZoneStyle { 139 | case .UTC: 140 | timeZoneString = "Z" 141 | 142 | case .LongStyle, .ShortStyle: 143 | let hoursOffset = timeZone.secondsFromGMT / 3600 144 | let secondsOffset = 0 145 | let sign = hoursOffset >= 0 ? "+" : "-" 146 | if timeZoneStyle == .LongStyle { 147 | timeZoneString = String(format: "%@%02i:%02i", sign, hoursOffset, secondsOffset) 148 | } else { 149 | timeZoneString = String(format: "%@%02i%02i", sign, hoursOffset, secondsOffset) 150 | } 151 | 152 | case .None: 153 | return string 154 | } 155 | string = string + timeZoneString 156 | } 157 | 158 | return string 159 | } 160 | 161 | func dateFromString(string: String) -> NSDate? { 162 | let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)! 163 | gregorian.firstWeekday = 2 // Monday 164 | let str = self.convertBasicToExtended(string) 165 | 166 | let scanner = NSScanner(string: str) 167 | scanner.charactersToBeSkipped = nil 168 | 169 | let dateComponents = NSDateComponents() 170 | 171 | // Year 172 | var year = 0 173 | guard scanner.scanInteger(&year) else { 174 | return nil 175 | } 176 | 177 | guard year >= 0 && year <= 9999 else { 178 | return nil 179 | } 180 | dateComponents.year = year 181 | 182 | // Month or Week 183 | guard scanner.scanString("-", intoString: nil) else { 184 | return gregorian.dateFromComponents(dateComponents) 185 | } 186 | 187 | var month = 0 188 | var ordinalDay = 0 189 | switch dateStyle { 190 | case .CalendarLongStyle, .CalendarShortStyle: 191 | guard scanner.scanInteger(&month) else { 192 | return gregorian.dateFromComponents(dateComponents) 193 | } 194 | dateComponents.month = month 195 | 196 | case .OrdinalLongStyle, .OrdinalShortStyle: 197 | guard scanner.scanInteger(&ordinalDay) else { 198 | return gregorian.dateFromComponents(dateComponents) 199 | } 200 | let daysArray: [Int] 201 | if ((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0 { 202 | daysArray = days366 203 | } else { 204 | daysArray = days365 205 | } 206 | var theMonth = 0 207 | for startDay in daysArray { 208 | theMonth++ 209 | if startDay > ordinalDay { 210 | month = theMonth - 1 211 | break 212 | } 213 | } 214 | dateComponents.month = month 215 | 216 | case .WeekLongStyle, .WeekShortStyle: 217 | guard scanner.scanString("W", intoString: nil) else { 218 | return gregorian.dateFromComponents(dateComponents) 219 | } 220 | 221 | var week = 0 222 | guard scanner.scanInteger(&week) else { 223 | return gregorian.dateFromComponents(dateComponents) 224 | } 225 | if week < 0 || week > 53 { 226 | return gregorian.dateFromComponents(dateComponents) 227 | } 228 | dateComponents.weekOfYear = week 229 | } 230 | 231 | // Day or DayOfWeek 232 | var day = 0 233 | switch dateStyle { 234 | case .CalendarLongStyle, .CalendarShortStyle: 235 | guard scanner.scanString("-", intoString: nil) else { 236 | return gregorian.dateFromComponents(dateComponents) 237 | } 238 | 239 | guard scanner.scanInteger(&day) else { 240 | return gregorian.dateFromComponents(dateComponents) 241 | } 242 | dateComponents.day = day 243 | 244 | case .OrdinalLongStyle, .OrdinalShortStyle: 245 | let daysArray: [Int] 246 | if ((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0 { 247 | daysArray = days366 248 | } else { 249 | daysArray = days365 250 | } 251 | var theDay = 0 252 | var previousStartDay = 0 253 | for startDay in daysArray { 254 | if startDay > ordinalDay { 255 | theDay = ordinalDay - previousStartDay 256 | break 257 | } 258 | previousStartDay = startDay 259 | } 260 | dateComponents.day = theDay 261 | 262 | case .WeekLongStyle, .WeekShortStyle: 263 | guard scanner.scanString("-", intoString: nil) else { 264 | return gregorian.dateFromComponents(dateComponents) 265 | } 266 | 267 | guard scanner.scanInteger(&day) else { 268 | return gregorian.dateFromComponents(dateComponents) 269 | } 270 | if day < 0 || day > 7 { 271 | return gregorian.dateFromComponents(dateComponents) 272 | } else { 273 | dateComponents.weekday = day 274 | } 275 | } 276 | 277 | // Time 278 | guard scanner.scanCharactersFromSet(NSCharacterSet(charactersInString: "T"), intoString: nil) else { 279 | return gregorian.dateFromComponents(dateComponents) 280 | } 281 | 282 | // Hour 283 | var hour = 0 284 | guard scanner.scanInteger(&hour) else { 285 | return gregorian.dateFromComponents(dateComponents) 286 | } 287 | if timeStyle != .None { 288 | if hour < 0 || hour > 23 { 289 | return gregorian.dateFromComponents(dateComponents) 290 | } else { 291 | dateComponents.hour = hour 292 | } 293 | } 294 | 295 | // Minute 296 | guard scanner.scanString(":", intoString: nil) else { 297 | return gregorian.dateFromComponents(dateComponents) 298 | } 299 | 300 | var minute = 0 301 | guard scanner.scanInteger(&minute) else { 302 | return gregorian.dateFromComponents(dateComponents) 303 | } 304 | if timeStyle != .None { 305 | if minute < 0 || minute > 59 { 306 | return gregorian.dateFromComponents(dateComponents) 307 | } else { 308 | dateComponents.minute = minute 309 | } 310 | } 311 | 312 | // Second 313 | var scannerLocation = scanner.scanLocation 314 | if scanner.scanString(":", intoString: nil) { 315 | var second = 0 316 | guard scanner.scanInteger(&second) else { 317 | return gregorian.dateFromComponents(dateComponents) 318 | } 319 | if timeStyle != .None { 320 | if second < 0 || second > 59 { 321 | return gregorian.dateFromComponents(dateComponents) 322 | } else { 323 | dateComponents.second = second 324 | } 325 | } 326 | } else { 327 | scanner.scanLocation = scannerLocation 328 | } 329 | 330 | // Zulu 331 | scannerLocation = scanner.scanLocation 332 | scanner.scanUpToString("Z", intoString: nil) 333 | if scanner.scanString("Z", intoString: nil) { 334 | dateComponents.timeZone = NSTimeZone(forSecondsFromGMT: 0) 335 | 336 | return gregorian.dateFromComponents(dateComponents) 337 | } 338 | 339 | // Move back to the end of time 340 | scanner.scanLocation = scannerLocation 341 | 342 | // Look for offset 343 | let signs = NSCharacterSet(charactersInString: "+-") 344 | scanner.scanUpToCharactersFromSet(signs, intoString: nil) 345 | var sign: NSString? 346 | guard scanner.scanCharactersFromSet(signs, intoString: &sign) else { 347 | return gregorian.dateFromComponents(dateComponents) 348 | } 349 | 350 | // Offset hour 351 | var timeZoneOffset = 0 352 | var timeZoneOffsetHour = 0 353 | var timeZoneOffsetMinute = 0 354 | guard scanner.scanInteger(&timeZoneOffsetHour) else { 355 | return gregorian.dateFromComponents(dateComponents) 356 | } 357 | 358 | // Check for colon 359 | let colonExists = scanner.scanString(":", intoString: nil) 360 | if !colonExists && timeZoneOffsetHour > 14 { 361 | timeZoneOffsetMinute = timeZoneOffsetHour % 100 362 | timeZoneOffsetHour = Int(floor(Double(timeZoneOffsetHour) / 100.0)) 363 | } else { 364 | scanner.scanInteger(&timeZoneOffsetMinute) 365 | } 366 | 367 | timeZoneOffset = (timeZoneOffsetHour * 60 * 60) + (timeZoneOffsetMinute * 60) 368 | dateComponents.timeZone = NSTimeZone(forSecondsFromGMT: timeZoneOffset * (sign == "-" ? -1 : 1)) 369 | 370 | return gregorian.dateFromComponents(dateComponents) 371 | } 372 | 373 | 374 | // Private methods 375 | private func checkAndUpdateTimeZone(string: NSMutableString, insertAtIndex index: Int) -> NSMutableString { 376 | if self.timeZoneStyle == .ShortStyle { 377 | string.insertString(":", atIndex: index) 378 | } 379 | 380 | return string 381 | } 382 | 383 | private func convertBasicToExtended(string: String) -> String { 384 | func checkAndUpdateTimeStyle(var string: NSMutableString, insertAtIndex index: Int) -> NSMutableString { 385 | if (self.timeStyle == .LongStyle) { 386 | string = self.checkAndUpdateTimeZone(string, insertAtIndex: index + 9) 387 | } else if (self.timeStyle == .ShortStyle) { 388 | string = self.checkAndUpdateTimeZone(string, insertAtIndex: index + 7) 389 | string.insertString(":", atIndex: index + 2) 390 | string.insertString(":", atIndex: index) 391 | } 392 | 393 | return string 394 | } 395 | 396 | var str: NSMutableString = NSMutableString(string: string) 397 | switch self.dateStyle { 398 | case .CalendarLongStyle: 399 | str = checkAndUpdateTimeStyle(str, insertAtIndex: 13) 400 | 401 | case .CalendarShortStyle: 402 | str = checkAndUpdateTimeStyle(str, insertAtIndex: 11) 403 | str.insertString("-", atIndex: 6) 404 | str.insertString("-", atIndex: 4) 405 | 406 | case .OrdinalLongStyle: 407 | str = checkAndUpdateTimeStyle(str, insertAtIndex: 11) 408 | 409 | case .OrdinalShortStyle: 410 | str = checkAndUpdateTimeStyle(str, insertAtIndex: 10) 411 | str.insertString("-", atIndex: 4) 412 | 413 | case .WeekLongStyle: 414 | str = checkAndUpdateTimeStyle(str, insertAtIndex: 13) 415 | 416 | case .WeekShortStyle: 417 | str = checkAndUpdateTimeStyle(str, insertAtIndex: 11) 418 | str.insertString("-", atIndex: 7) 419 | str.insertString("-", atIndex: 4) 420 | } 421 | 422 | return String(str) 423 | } 424 | } 425 | 426 | 427 | extension NSDate { 428 | func isLeapYear() -> Bool { 429 | let dateComponents = NSCalendar.currentCalendar().components( 430 | [.Year, .Month, .Day, .WeekOfYear, .Hour, .Minute, .Second, .Weekday, .WeekdayOrdinal, .WeekOfYear, .TimeZone], 431 | fromDate: self 432 | ) 433 | return ((dateComponents.year % 4) == 0 && (dateComponents.year % 100) != 0) || (dateComponents.year % 400) == 0 ? true : false 434 | } 435 | 436 | func dayOfYear() -> Int { 437 | let dateFormatter = NSDateFormatter() 438 | dateFormatter.dateFormat = "D" 439 | 440 | return Int(dateFormatter.stringFromDate(self))! 441 | } 442 | 443 | func weekOfYear() -> Int { 444 | let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)! 445 | gregorian.firstWeekday = 2 // Monday 446 | gregorian.minimumDaysInFirstWeek = 4 447 | let components = gregorian.components([.WeekOfYear, .YearForWeekOfYear], fromDate: self) 448 | let week = components.weekOfYear 449 | 450 | return week 451 | } 452 | } -------------------------------------------------------------------------------- /ISO8601/ISO8601Playground.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import Foundation 4 | 5 | final class ISO8601Formatter: NSFormatter { 6 | enum ISO8601DateStyle: Int { 7 | case CalendarLongStyle // Default (YYYY-MM-DD) 8 | case CalendarShortStyle // (YYYYMMDD) 9 | case OrdinalLongStyle // (YYYY-DDD) 10 | case OrdinalShortStyle // (YYYYDDD) 11 | case WeekLongStyle // (YYYY-Www-D) 12 | case WeekShortStyle // (YYYYWwwD) 13 | } 14 | 15 | enum ISO8601TimeStyle: Int { 16 | case None 17 | case LongStyle // Default (hh:mm:ss) 18 | case ShortStyle // (hhmmss) 19 | } 20 | 21 | enum ISO8601TimeZoneStyle: Int { 22 | case None 23 | case UTC // Default (Z) 24 | case LongStyle // (±hh:mm) 25 | case ShortStyle // (±hhmm) 26 | } 27 | 28 | enum ISO8601FractionSeparator: Int { 29 | case Comma // Default (,) 30 | case Dot // (.) 31 | } 32 | 33 | var dateStyle: ISO8601DateStyle 34 | var timeStyle: ISO8601TimeStyle 35 | var fractionSeparator: ISO8601FractionSeparator 36 | var timeZoneStyle: ISO8601TimeZoneStyle 37 | var fractionDigits: Int 38 | 39 | let days365 = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334] 40 | let days366 = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335] 41 | 42 | convenience override init() { 43 | self.init( 44 | dateStyle: .CalendarLongStyle, 45 | timeStyle: .LongStyle, 46 | fractionSeparator: .Comma, 47 | fractionDigits: 6, 48 | timeZoneStyle: .UTC 49 | ) 50 | } 51 | 52 | init(dateStyle: ISO8601DateStyle, timeStyle: ISO8601TimeStyle, fractionSeparator: ISO8601FractionSeparator, fractionDigits: Int, timeZoneStyle: ISO8601TimeZoneStyle) { 53 | self.dateStyle = dateStyle 54 | self.timeStyle = timeStyle 55 | self.fractionSeparator = fractionSeparator 56 | self.fractionDigits = fractionDigits 57 | self.timeZoneStyle = timeZoneStyle 58 | 59 | super.init() 60 | } 61 | 62 | required convenience init?(coder aDecoder: NSCoder) { 63 | self.init( 64 | dateStyle: .CalendarLongStyle, 65 | timeStyle: .LongStyle, 66 | fractionSeparator: .Comma, 67 | fractionDigits: 6, 68 | timeZoneStyle: .UTC 69 | ) 70 | } 71 | 72 | func stringFromDate(date: NSDate) -> String? { 73 | let calendar = NSCalendar.currentCalendar() 74 | if timeZoneStyle == .UTC { 75 | calendar.timeZone = NSTimeZone(forSecondsFromGMT: 0) 76 | } 77 | let dateComponents = calendar.components( 78 | [.Year, .Month, .Day, .WeekOfYear, .Hour, .Minute, .Second, .Weekday, .WeekdayOrdinal, .WeekOfYear, .YearForWeekOfYear, .TimeZone], 79 | fromDate: date 80 | ) 81 | print(dateComponents) 82 | var string: String 83 | 84 | if dateComponents.year < 0 || dateComponents.year > 9999 { 85 | return nil 86 | } 87 | 88 | string = String(format: "%04li", dateComponents.year) 89 | if dateStyle == .WeekLongStyle || dateStyle == .WeekShortStyle { 90 | // For weekOfYear calculation see more at: https://en.wikipedia.org/wiki/ISO_8601#Week_dates 91 | if date.weekOfYear() == 53 { 92 | string = String(format: "%04li", dateComponents.year - 1) 93 | } 94 | } 95 | 96 | switch dateStyle { 97 | case .CalendarLongStyle: 98 | string = string + String(format: "-%02i-%02i", dateComponents.month, dateComponents.day) 99 | case .CalendarShortStyle: 100 | string = string + String(format: "%02i%02i", dateComponents.month, dateComponents.day) 101 | case .OrdinalLongStyle: 102 | string = string + String(format: "-%03i", date.dayOfYear()) 103 | case .OrdinalShortStyle: 104 | string = string + String(format: "%03i", date.dayOfYear()) 105 | case .WeekLongStyle: 106 | if dateComponents.weekday > 1 { 107 | string = string + String(format: "-W%02i-%01i", date.weekOfYear(), dateComponents.weekday - 1) 108 | } else { 109 | string = string + String(format: "-W%02i-%01i", date.weekOfYear(), 7) 110 | } 111 | case .WeekShortStyle: 112 | if dateComponents.weekday > 1 { 113 | string = string + String(format: "W%02i%01i", dateComponents.weekOfYear, dateComponents.weekday - 1) 114 | } else { 115 | string = string + String(format: "W%02i%01i", dateComponents.weekOfYear, 7) 116 | } 117 | } 118 | 119 | let timeString: String 120 | switch timeStyle { 121 | case .LongStyle: 122 | timeString = String(format: "T%02i:%02i:%02i", dateComponents.hour, dateComponents.minute, dateComponents.second) 123 | case .ShortStyle: 124 | timeString = String(format: "T%02i:%02i:%02i", dateComponents.hour, dateComponents.minute, dateComponents.second) 125 | case .None: 126 | return string 127 | } 128 | string = string + timeString 129 | 130 | if let timeZone = dateComponents.timeZone { 131 | let timeZoneString: String 132 | switch timeZoneStyle { 133 | case .UTC: 134 | timeZoneString = "Z" 135 | 136 | case .LongStyle, .ShortStyle: 137 | let hoursOffset = timeZone.secondsFromGMT / 3600 138 | let secondsOffset = 0 139 | let sign = hoursOffset >= 0 ? "+" : "-" 140 | if timeZoneStyle == .LongStyle { 141 | timeZoneString = String(format: "%@%02i:%02i", sign, hoursOffset, secondsOffset) 142 | } else { 143 | timeZoneString = String(format: "%@%02i%02i", sign, hoursOffset, secondsOffset) 144 | } 145 | 146 | case .None: 147 | return string 148 | } 149 | string = string + timeZoneString 150 | } 151 | 152 | return string 153 | } 154 | 155 | func dateFromString(string: String) -> NSDate? { 156 | let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)! 157 | gregorian.firstWeekday = 2 // Monday 158 | let str = self.convertBasicToExtended(string) 159 | 160 | let scanner = NSScanner(string: str) 161 | scanner.charactersToBeSkipped = nil 162 | 163 | let dateComponents = NSDateComponents() 164 | 165 | // Year 166 | var year = 0 167 | guard scanner.scanInteger(&year) else { 168 | return nil 169 | } 170 | 171 | guard year >= 0 && year <= 9999 else { 172 | return nil 173 | } 174 | dateComponents.year = year 175 | 176 | // Month or Week 177 | guard scanner.scanString("-", intoString: nil) else { 178 | return gregorian.dateFromComponents(dateComponents) 179 | } 180 | 181 | var month = 0 182 | var ordinalDay = 0 183 | switch dateStyle { 184 | case .CalendarLongStyle, .CalendarShortStyle: 185 | guard scanner.scanInteger(&month) else { 186 | return gregorian.dateFromComponents(dateComponents) 187 | } 188 | dateComponents.month = month 189 | 190 | case .OrdinalLongStyle, .OrdinalShortStyle: 191 | guard scanner.scanInteger(&ordinalDay) else { 192 | return gregorian.dateFromComponents(dateComponents) 193 | } 194 | let daysArray: [Int] 195 | if ((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0 { 196 | daysArray = days366 197 | } else { 198 | daysArray = days365 199 | } 200 | var theMonth = 0 201 | for startDay in daysArray { 202 | theMonth++ 203 | if startDay > ordinalDay { 204 | month = theMonth - 1 205 | break 206 | } 207 | } 208 | dateComponents.month = month 209 | 210 | case .WeekLongStyle, .WeekShortStyle: 211 | guard scanner.scanString("W", intoString: nil) else { 212 | return gregorian.dateFromComponents(dateComponents) 213 | } 214 | 215 | var week = 0 216 | guard scanner.scanInteger(&week) else { 217 | return gregorian.dateFromComponents(dateComponents) 218 | } 219 | if week < 0 || week > 53 { 220 | return gregorian.dateFromComponents(dateComponents) 221 | } 222 | dateComponents.weekOfYear = week 223 | } 224 | 225 | // Day or DayOfWeek 226 | var day = 0 227 | switch dateStyle { 228 | case .CalendarLongStyle, .CalendarShortStyle: 229 | guard scanner.scanString("-", intoString: nil) else { 230 | return gregorian.dateFromComponents(dateComponents) 231 | } 232 | 233 | guard scanner.scanInteger(&day) else { 234 | return gregorian.dateFromComponents(dateComponents) 235 | } 236 | dateComponents.day = day 237 | 238 | case .OrdinalLongStyle, .OrdinalShortStyle: 239 | let daysArray: [Int] 240 | if ((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0 { 241 | daysArray = days366 242 | } else { 243 | daysArray = days365 244 | } 245 | var theDay = 0 246 | var previousStartDay = 0 247 | for startDay in daysArray { 248 | if startDay > ordinalDay { 249 | theDay = ordinalDay - previousStartDay 250 | break 251 | } 252 | previousStartDay = startDay 253 | } 254 | dateComponents.day = theDay 255 | 256 | case .WeekLongStyle, .WeekShortStyle: 257 | guard scanner.scanString("-", intoString: nil) else { 258 | return gregorian.dateFromComponents(dateComponents) 259 | } 260 | 261 | guard scanner.scanInteger(&day) else { 262 | return gregorian.dateFromComponents(dateComponents) 263 | } 264 | if day < 0 || day > 7 { 265 | return gregorian.dateFromComponents(dateComponents) 266 | } else { 267 | dateComponents.weekday = day 268 | } 269 | } 270 | 271 | // Time 272 | guard scanner.scanCharactersFromSet(NSCharacterSet(charactersInString: "T"), intoString: nil) else { 273 | return gregorian.dateFromComponents(dateComponents) 274 | } 275 | 276 | // Hour 277 | var hour = 0 278 | guard scanner.scanInteger(&hour) else { 279 | return gregorian.dateFromComponents(dateComponents) 280 | } 281 | if timeStyle != .None { 282 | if hour < 0 || hour > 23 { 283 | return gregorian.dateFromComponents(dateComponents) 284 | } else { 285 | dateComponents.hour = hour 286 | } 287 | } 288 | 289 | // Minute 290 | guard scanner.scanString(":", intoString: nil) else { 291 | return gregorian.dateFromComponents(dateComponents) 292 | } 293 | 294 | var minute = 0 295 | guard scanner.scanInteger(&minute) else { 296 | return gregorian.dateFromComponents(dateComponents) 297 | } 298 | if timeStyle != .None { 299 | if minute < 0 || minute > 59 { 300 | return gregorian.dateFromComponents(dateComponents) 301 | } else { 302 | dateComponents.minute = minute 303 | } 304 | } 305 | 306 | // Second 307 | var scannerLocation = scanner.scanLocation 308 | if scanner.scanString(":", intoString: nil) { 309 | var second = 0 310 | guard scanner.scanInteger(&second) else { 311 | return gregorian.dateFromComponents(dateComponents) 312 | } 313 | if timeStyle != .None { 314 | if second < 0 || second > 59 { 315 | return gregorian.dateFromComponents(dateComponents) 316 | } else { 317 | dateComponents.second = second 318 | } 319 | } 320 | } else { 321 | scanner.scanLocation = scannerLocation 322 | } 323 | 324 | // Zulu 325 | scannerLocation = scanner.scanLocation 326 | scanner.scanUpToString("Z", intoString: nil) 327 | if scanner.scanString("Z", intoString: nil) { 328 | dateComponents.timeZone = NSTimeZone(forSecondsFromGMT: 0) 329 | 330 | return gregorian.dateFromComponents(dateComponents) 331 | } 332 | 333 | // Move back to the end of time 334 | scanner.scanLocation = scannerLocation 335 | 336 | // Look for offset 337 | let signs = NSCharacterSet(charactersInString: "+-") 338 | scanner.scanUpToCharactersFromSet(signs, intoString: nil) 339 | var sign: NSString? 340 | guard scanner.scanCharactersFromSet(signs, intoString: &sign) else { 341 | return gregorian.dateFromComponents(dateComponents) 342 | } 343 | 344 | // Offset hour 345 | var timeZoneOffset = 0 346 | var timeZoneOffsetHour = 0 347 | var timeZoneOffsetMinute = 0 348 | guard scanner.scanInteger(&timeZoneOffsetHour) else { 349 | return gregorian.dateFromComponents(dateComponents) 350 | } 351 | 352 | // Check for colon 353 | let colonExists = scanner.scanString(":", intoString: nil) 354 | if !colonExists && timeZoneOffsetHour > 14 { 355 | timeZoneOffsetMinute = timeZoneOffsetHour % 100 356 | timeZoneOffsetHour = Int(floor(Double(timeZoneOffsetHour) / 100.0)) 357 | } else { 358 | scanner.scanInteger(&timeZoneOffsetMinute) 359 | } 360 | 361 | timeZoneOffset = (timeZoneOffsetHour * 60 * 60) + (timeZoneOffsetMinute * 60) 362 | dateComponents.timeZone = NSTimeZone(forSecondsFromGMT: timeZoneOffset * (sign == "-" ? -1 : 1)) 363 | 364 | return gregorian.dateFromComponents(dateComponents) 365 | } 366 | 367 | 368 | // Private methods 369 | private func checkAndUpdateTimeZone(string: NSMutableString, insertAtIndex index: Int) -> NSMutableString { 370 | if self.timeZoneStyle == .ShortStyle { 371 | string.insertString(":", atIndex: index) 372 | } 373 | 374 | return string 375 | } 376 | 377 | private func convertBasicToExtended(string: String) -> String { 378 | func checkAndUpdateTimeStyle(var string: NSMutableString, insertAtIndex index: Int) -> NSMutableString { 379 | if (self.timeStyle == .LongStyle) { 380 | string = self.checkAndUpdateTimeZone(string, insertAtIndex: index + 9) 381 | } else if (self.timeStyle == .ShortStyle) { 382 | string = self.checkAndUpdateTimeZone(string, insertAtIndex: index + 7) 383 | string.insertString(":", atIndex: index + 2) 384 | string.insertString(":", atIndex: index) 385 | } 386 | 387 | return string 388 | } 389 | 390 | var str: NSMutableString = NSMutableString(string: string) 391 | switch self.dateStyle { 392 | case .CalendarLongStyle: 393 | str = checkAndUpdateTimeStyle(str, insertAtIndex: 13) 394 | 395 | case .CalendarShortStyle: 396 | str = checkAndUpdateTimeStyle(str, insertAtIndex: 11) 397 | str.insertString("-", atIndex: 6) 398 | str.insertString("-", atIndex: 4) 399 | 400 | case .OrdinalLongStyle: 401 | str = checkAndUpdateTimeStyle(str, insertAtIndex: 11) 402 | 403 | case .OrdinalShortStyle: 404 | str = checkAndUpdateTimeStyle(str, insertAtIndex: 10) 405 | str.insertString("-", atIndex: 4) 406 | 407 | case .WeekLongStyle: 408 | str = checkAndUpdateTimeStyle(str, insertAtIndex: 13) 409 | 410 | case .WeekShortStyle: 411 | str = checkAndUpdateTimeStyle(str, insertAtIndex: 11) 412 | str.insertString("-", atIndex: 7) 413 | str.insertString("-", atIndex: 4) 414 | } 415 | 416 | return String(str) 417 | } 418 | } 419 | 420 | 421 | extension NSDate { 422 | func isLeapYear() -> Bool { 423 | let dateComponents = NSCalendar.currentCalendar().components( 424 | [.Year, .Month, .Day, .WeekOfYear, .Hour, .Minute, .Second, .Weekday, .WeekdayOrdinal, .WeekOfYear, .TimeZone], 425 | fromDate: self 426 | ) 427 | return ((dateComponents.year % 4) == 0 && (dateComponents.year % 100) != 0) || (dateComponents.year % 400) == 0 ? true : false 428 | } 429 | 430 | func dayOfYear() -> Int { 431 | let dateFormatter = NSDateFormatter() 432 | dateFormatter.dateFormat = "D" 433 | 434 | return Int(dateFormatter.stringFromDate(self))! 435 | } 436 | 437 | func weekOfYear() -> Int { 438 | let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)! 439 | gregorian.firstWeekday = 2 // Monday 440 | gregorian.minimumDaysInFirstWeek = 4 441 | let components = gregorian.components([.WeekOfYear, .YearForWeekOfYear], fromDate: self) 442 | let week = components.weekOfYear 443 | 444 | return week 445 | } 446 | } 447 | 448 | let indigoDate = NSDate(timeIntervalSince1970: 1436260803.069) 449 | let formatter = ISO8601Formatter() 450 | formatter.fractionSeparator = .Dot 451 | print(formatter.stringFromDate(indigoDate)) 452 | 453 | let date = formatter.dateFromString("2011-02-27T11:03:06+09:00") 454 | 455 | 456 | let date2 = ISO8601Formatter().dateFromString("2013-09-12T07:24:56+04:00") 457 | 458 | let string = ISO8601Formatter().stringFromDate(date!) 459 | 460 | //let formatter2 = ISO8601Formatter() 461 | //formatter2.dateStyle = .CalendarLongStyle 462 | //formatter2.timeStyle = .LongStyle 463 | //formatter2.timeZoneStyle = .LongStyle 464 | //let string = formatter2.stringFromDate(date) 465 | 466 | print(string) 467 | 468 | //print(date.isLeapYear()) 469 | //print(date.dayOfYear()) 470 | //print(date.weekOfYear()) 471 | -------------------------------------------------------------------------------- /ISO8601/ISO8601Playground.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ISO8601/ISO8601Playground.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Miroslav Perovic 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 | # ISO8601Formatter 2 | 3 | A small Swift NSFormatter subclass. 4 | 5 | # USAGE 6 | 7 | To convert ISO 8601 string to NSDate you can use ISO8601Formatter() without any configuration 8 | ``` swift 9 | let date = ISO8601Formatter().dateFromString("2013-09-12T07:24:56+04:00")! 10 | ``` 11 | 12 | or you can use ISO8601Formatter the same way as you do with NSDateFormatter. 13 | ``` swift 14 | let formatter = ISO8601Formatter() 15 | formatter.timeStyle = .LongStyle 16 | formatter.dateStyle = .LongStyle 17 | let date = formatter.dateFromString("2013-09-12T07:24:56+04:00")! 18 | ``` 19 | 20 | To convert NSDate to ISO 8601 formatted string. 21 | ``` swift 22 | let string = ISO8601Formatter().stringFromDate(date) 23 | ``` 24 | 25 | Also you can customize output. 26 | ``` swift 27 | let formatter = ISO8601Formatter() 28 | formatter.dateStyle = .CalendarLongStyle 29 | formatter.timeStyle = .LongStyle 30 | formatter.timeZoneStyle = .LongStyle 31 | let string = formatter.stringFromDate(date) 32 | ``` 33 | 34 | --------------------------------------------------------------------------------