diff --git a/Sources/ISO8601Serialization.m b/Sources/ISO8601Serialization.m index fb94b9c..aa3b00c 100644 --- a/Sources/ISO8601Serialization.m +++ b/Sources/ISO8601Serialization.m @@ -8,6 +8,8 @@ #import "ISO8601Serialization.h" +static const NSInteger kNanosecondsInMicrosecond = 1000; + @implementation ISO8601Serialization + (NSDateComponents * __nullable)dateComponentsForString:(NSString * __nonnull)string { @@ -80,6 +82,20 @@ + (NSDateComponents * __nullable)dateComponentsForString:(NSString * __nonnull)s scanner.scanLocation = scannerLocation; } + // Microsecond + scannerLocation = scanner.scanLocation; + if ([scanner scanString:@"." intoString:nil]) { + NSString *decimalString = nil; + if (![scanner scanCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet] intoString:&decimalString]) { + return dateComponents; + } + NSString *paddedString = [decimalString stringByPaddingToLength:6 withString:@"0" startingAtIndex:0]; + NSInteger microseconds = paddedString.integerValue; + dateComponents.nanosecond = microseconds * kNanosecondsInMicrosecond; + } else { + scanner.scanLocation = scannerLocation; + } + // Zulu scannerLocation = scanner.scanLocation; [scanner scanUpToString:@"Z" intoString:nil]; @@ -130,19 +146,24 @@ + (NSString * __nullable)stringForDateComponents:(NSDateComponents * __nonnull)c NSString *string = @""; BOOL hasDate = components.year != NSDateComponentUndefined || components.month != NSDateComponentUndefined || components.day != NSDateComponentUndefined; BOOL hasTime = components.hour != NSDateComponentUndefined || components.minute != NSDateComponentUndefined || components.second != NSDateComponentUndefined || components.timeZone; + BOOL hasNanoseconds = components.nanosecond != NSDateComponentUndefined; if (!hasDate && !hasTime) { return nil; } if (hasDate) { - string = [string stringByAppendingFormat:@"%04li-%02i-%02i", (long)components.year, - (int)components.month, - (int)components.day]; + string = [string stringByAppendingFormat:@"%04li-%02li-%02li", (long)components.year, + (long)components.month, + (long)components.day]; } if (hasTime) { - string = [string stringByAppendingFormat:@"%@%02i:%02i:%02i", hasDate ? @"T" : @"", - (int)components.hour, - (int)components.minute, - (int)components.second]; + string = [string stringByAppendingFormat:@"%@%02li:%02li:%02li", hasDate ? @"T" : @"", + (long)components.hour, + (long)components.minute, + (long)components.second]; + } + if (hasNanoseconds) { + NSInteger microseconds = lround((double)components.nanosecond / kNanosecondsInMicrosecond); + string = [string stringByAppendingFormat:@".%06li", (long)microseconds]; } NSTimeZone *timeZone = components.timeZone; diff --git a/Sources/NSDate+ISO8601.h b/Sources/NSDate+ISO8601.h index 766e95a..4385404 100644 --- a/Sources/NSDate+ISO8601.h +++ b/Sources/NSDate+ISO8601.h @@ -61,5 +61,6 @@ @return A string representation of the receiver in ISO8601 format in `timeZone`. */ - (NSString * __nullable)ISO8601StringWithTimeZone:(NSTimeZone * __nullable)timeZone usingCalendar:(NSCalendar * __nullable)calendar; +- (NSString * __nullable)ISO8601StringWithTimeZone:(NSTimeZone * __nullable)timeZone usingCalendar:(NSCalendar * __nullable)calendar includeMicroseconds:(BOOL)includeMicroseconds; @end diff --git a/Sources/NSDate+ISO8601.m b/Sources/NSDate+ISO8601.m index 46744ef..180b391 100644 --- a/Sources/NSDate+ISO8601.m +++ b/Sources/NSDate+ISO8601.m @@ -47,8 +47,7 @@ - (NSString * __nullable)ISO8601String { return [self ISO8601StringWithTimeZone:[NSTimeZone localTimeZone] usingCalendar:nil]; } - -- (NSString * __nullable)ISO8601StringWithTimeZone:(NSTimeZone * __nullable)timeZone usingCalendar:(NSCalendar * __nullable)calendar { +- (NSString * __nullable)ISO8601StringWithTimeZone:(NSTimeZone * __nullable)timeZone usingCalendar:(NSCalendar * __nullable)calendar includeMicroseconds:(BOOL)includeMicroseconds { if (!calendar) { calendar = [NSCalendar currentCalendar]; } @@ -61,9 +60,16 @@ - (NSString * __nullable)ISO8601StringWithTimeZone:(NSTimeZone * __nullable)time NSCalendarUnit units = (NSCalendarUnit)(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond | NSCalendarUnitTimeZone); + if (includeMicroseconds) { + units |= NSCalendarUnitNanosecond; + } NSDateComponents *dateComponents = [calendar components:units fromDate:self]; return [ISO8601Serialization stringForDateComponents:dateComponents]; } +- (NSString * __nullable)ISO8601StringWithTimeZone:(NSTimeZone * __nullable)timeZone usingCalendar:(NSCalendar * __nullable)calendar { + return [self ISO8601StringWithTimeZone:timeZone usingCalendar:calendar includeMicroseconds:NO]; +} + @end diff --git a/Tests/DateTests.swift b/Tests/DateTests.swift index 2ed7db8..b26d803 100644 --- a/Tests/DateTests.swift +++ b/Tests/DateTests.swift @@ -25,5 +25,10 @@ class DateTests: XCTestCase { func testWriting() { XCTAssertEqual(NSDate(timeIntervalSince1970: 1406759723).ISO8601StringWithTimeZone(nil, usingCalendar: nil), "2014-07-30T22:35:23Z") + XCTAssertEqual(NSDate(timeIntervalSince1970: 1406759723).ISO8601StringWithTimeZone(nil, usingCalendar: nil, includeMicroseconds: true), "2014-07-30T22:35:23.000000Z") + XCTAssertEqual(NSDate(timeIntervalSince1970: 1406759723.123).ISO8601StringWithTimeZone(nil, usingCalendar: nil, includeMicroseconds: true), "2014-07-30T22:35:23.123000Z") + XCTAssertEqual(NSDate(timeIntervalSince1970: 1406759723.123456).ISO8601StringWithTimeZone(nil, usingCalendar: nil, includeMicroseconds: true), "2014-07-30T22:35:23.123456Z") + XCTAssertEqual(NSDate(timeIntervalSince1970: 1406759723.1234567).ISO8601StringWithTimeZone(nil, usingCalendar: nil, includeMicroseconds: true), "2014-07-30T22:35:23.123457Z") + XCTAssertEqual(NSDate(timeIntervalSince1970: 1406759723.1234564).ISO8601StringWithTimeZone(nil, usingCalendar: nil, includeMicroseconds: true), "2014-07-30T22:35:23.123456Z") } } diff --git a/Tests/SerializationTests.swift b/Tests/SerializationTests.swift index 10992de..9adf26f 100644 --- a/Tests/SerializationTests.swift +++ b/Tests/SerializationTests.swift @@ -18,44 +18,50 @@ class SerializationTests: XCTestCase { } func testReadingUTC() { - XCTAssertEqual(parse("1971-02-03T04:05:06.789Z"), components(year: 1971, month: 2, day: 3, hour: 4, minute: 5, second: 6, timeZoneOffset: 0)) - XCTAssertEqual(parse("1971-02-03T04:05:06.78Z"), components(year: 1971, month: 2, day: 3, hour: 4, minute: 5, second: 6, timeZoneOffset: 0)) - XCTAssertEqual(parse("1971-02-03T04:05:06.7Z"), components(year: 1971, month: 2, day: 3, hour: 4, minute: 5, second: 6, timeZoneOffset: 0)) + XCTAssertEqual(parse("1971-02-03T04:05:06.789Z"), components(year: 1971, month: 2, day: 3, hour: 4, minute: 5, second: 6, millisecond: 789, timeZoneOffset: 0)) + XCTAssertEqual(parse("1971-02-03T04:05:06.78Z"), components(year: 1971, month: 2, day: 3, hour: 4, minute: 5, second: 6, millisecond: 780, timeZoneOffset: 0)) + XCTAssertEqual(parse("1971-02-03T04:05:06.7Z"), components(year: 1971, month: 2, day: 3, hour: 4, minute: 5, second: 6, millisecond: 700, timeZoneOffset: 0)) XCTAssertEqual(parse("1971-02-03T04:05:06Z"), components(year: 1971, month: 2, day: 3, hour: 4, minute: 5, second: 6, timeZoneOffset: 0)) XCTAssertEqual(parse("1971-02-03T04:05Z"), components(year: 1971, month: 2, day: 3, hour: 4, minute: 5, timeZoneOffset: 0)) - XCTAssertEqual(parse("1970-01-01T00:00:00.000Z"), components(year: 1970, month: 1, day: 1, hour: 0, minute: 0, second: 0, timeZoneOffset: 0)) - XCTAssertEqual(parse("1970-06-30T01:06:40.981Z"), components(year: 1970, month: 6, day: 30, hour: 1, minute: 6, second: 40, timeZoneOffset: 0)) - XCTAssertEqual(parse("2058-02-20T18:29:11.100Z"), components(year: 2058, month: 2, day: 20, hour: 18, minute: 29, second: 11, timeZoneOffset: 0)) - XCTAssertEqual(parse("3001-01-01T08:00:00.000Z"), components(year: 3001, month: 1, day: 1, hour: 8, minute: 0, second: 0, timeZoneOffset: 0)) - XCTAssertEqual(parse("2013-02-20T18:29:11.100Z"), components(year: 2013, month: 2, day: 20, hour: 18, minute: 29, second: 11, timeZoneOffset: 0)) + XCTAssertEqual(parse("1970-01-01T00:00:00.000Z"), components(year: 1970, month: 1, day: 1, hour: 0, minute: 0, second: 0, millisecond: 0, timeZoneOffset: 0)) + XCTAssertEqual(parse("1970-06-30T01:06:40.981Z"), components(year: 1970, month: 6, day: 30, hour: 1, minute: 6, second: 40, millisecond: 981, timeZoneOffset: 0)) + XCTAssertEqual(parse("2058-02-20T18:29:11.100Z"), components(year: 2058, month: 2, day: 20, hour: 18, minute: 29, second: 11, millisecond: 100, timeZoneOffset: 0)) + XCTAssertEqual(parse("3001-01-01T08:00:00.000Z"), components(year: 3001, month: 1, day: 1, hour: 8, minute: 0, second: 0, millisecond: 0, timeZoneOffset: 0)) + XCTAssertEqual(parse("2013-02-20T18:29:11.100Z"), components(year: 2013, month: 2, day: 20, hour: 18, minute: 29, second: 11, millisecond: 100, timeZoneOffset: 0)) + XCTAssertEqual(parse("2013-02-20T18:29:11.1000Z"), components(year: 2013, month: 2, day: 20, hour: 18, minute: 29, second: 11, millisecond: 100, timeZoneOffset: 0)) + XCTAssertEqual(parse("2013-02-20T18:29:11.100000Z"), components(year: 2013, month: 2, day: 20, hour: 18, minute: 29, second: 11, millisecond: 100, timeZoneOffset: 0)) + XCTAssertEqual(parse("2013-02-20T18:29:11.000123Z"), components(year: 2013, month: 2, day: 20, hour: 18, minute: 29, second: 11, microsecond: 123, timeZoneOffset: 0)) + XCTAssertEqual(parse("2013-02-20T18:29:11.001234Z"), components(year: 2013, month: 2, day: 20, hour: 18, minute: 29, second: 11, microsecond: 1234, timeZoneOffset: 0)) + XCTAssertEqual(parse("2013-02-20T18:29:11.012345Z"), components(year: 2013, month: 2, day: 20, hour: 18, minute: 29, second: 11, microsecond: 12345, timeZoneOffset: 0)) + XCTAssertEqual(parse("2013-02-20T18:29:11.123456Z"), components(year: 2013, month: 2, day: 20, hour: 18, minute: 29, second: 11, microsecond: 123456, timeZoneOffset: 0)) } func testReadingTimeZones() { - XCTAssertEqual(parse("1971-02-03T09:16:06.789+0511"), components(year: 1971, month: 2, day: 3, hour: 9, minute: 16, second: 6, timeZoneOffset: 18660)) - XCTAssertEqual(parse("1971-02-03T09:16:06.78+0511"), components(year: 1971, month: 2, day: 3, hour: 9, minute: 16, second: 6, timeZoneOffset: 18660)) - XCTAssertEqual(parse("1971-02-03T09:16:06.7+0511"), components(year: 1971, month: 2, day: 3, hour: 9, minute: 16, second: 6, timeZoneOffset: 18660)) + XCTAssertEqual(parse("1971-02-03T09:16:06.789+0511"), components(year: 1971, month: 2, day: 3, hour: 9, minute: 16, second: 6, millisecond: 789, timeZoneOffset: 18660)) + XCTAssertEqual(parse("1971-02-03T09:16:06.78+0511"), components(year: 1971, month: 2, day: 3, hour: 9, minute: 16, second: 6, millisecond: 780, timeZoneOffset: 18660)) + XCTAssertEqual(parse("1971-02-03T09:16:06.7+0511"), components(year: 1971, month: 2, day: 3, hour: 9, minute: 16, second: 6, millisecond: 700, timeZoneOffset: 18660)) XCTAssertEqual(parse("1971-02-03T09:16:06+0511"), components(year: 1971, month: 2, day: 3, hour: 9, minute: 16, second: 6, timeZoneOffset: 18660)) XCTAssertEqual(parse("1971-02-03T09:16+0511"), components(year: 1971, month: 2, day: 3, hour: 9, minute: 16, timeZoneOffset: 18660)) - XCTAssertEqual(parse("1970-06-29T21:06:40.981-0400"), components(year: 1970, month: 6, day: 29, hour: 21, minute: 6, second: 40, timeZoneOffset: -14400)) - XCTAssertEqual(parse("1969-12-31T16:00:00.000-0800"), components(year: 1969, month: 12, day: 31, hour: 16, minute: 0, second: 0, timeZoneOffset: -28800)) - XCTAssertEqual(parse("2058-02-20T13:29:11.100-0500"), components(year: 2058, month: 2, day: 20, hour: 13, minute: 29, second: 11, timeZoneOffset: -18000)) - XCTAssertEqual(parse("2013-02-20T13:29:11.100-0500"), components(year: 2013, month: 2, day: 20, hour: 13, minute: 29, second: 11, timeZoneOffset: -18000)) - XCTAssertEqual(parse("2013-02-20T13:29:11.100-0501"), components(year: 2013, month: 2, day: 20, hour: 13, minute: 29, second: 11, timeZoneOffset: -18060)) + XCTAssertEqual(parse("1970-06-29T21:06:40.981-0400"), components(year: 1970, month: 6, day: 29, hour: 21, minute: 6, second: 40, millisecond: 981, timeZoneOffset: -14400)) + XCTAssertEqual(parse("1969-12-31T16:00:00.000-0800"), components(year: 1969, month: 12, day: 31, hour: 16, minute: 0, second: 0, millisecond: 0, timeZoneOffset: -28800)) + XCTAssertEqual(parse("2058-02-20T13:29:11.100-0500"), components(year: 2058, month: 2, day: 20, hour: 13, minute: 29, second: 11, millisecond: 100, timeZoneOffset: -18000)) + XCTAssertEqual(parse("2013-02-20T13:29:11.100-0500"), components(year: 2013, month: 2, day: 20, hour: 13, minute: 29, second: 11, millisecond: 100, timeZoneOffset: -18000)) + XCTAssertEqual(parse("2013-02-20T13:29:11.100-0501"), components(year: 2013, month: 2, day: 20, hour: 13, minute: 29, second: 11, millisecond: 100, timeZoneOffset: -18060)) } func testLeapYear() { - XCTAssertEqual(parse("1972-02-29T00:00:00.000Z"), components(year: 1972, month: 2, day: 29, hour: 0, minute: 0, second: 0, timeZoneOffset: 0)) - XCTAssertEqual(parse("1976-02-29T00:00:00.000Z"), components(year: 1976, month: 2, day: 29, hour: 0, minute: 0, second: 0, timeZoneOffset: 0)) - XCTAssertEqual(parse("1980-02-29T00:00:00.000Z"), components(year: 1980, month: 2, day: 29, hour: 0, minute: 0, second: 0, timeZoneOffset: 0)) - XCTAssertEqual(parse("1984-02-29T00:00:00.000Z"), components(year: 1984, month: 2, day: 29, hour: 0, minute: 0, second: 0, timeZoneOffset: 0)) - XCTAssertEqual(parse("1988-02-29T00:00:00.000Z"), components(year: 1988, month: 2, day: 29, hour: 0, minute: 0, second: 0, timeZoneOffset: 0)) - XCTAssertEqual(parse("1992-02-29T00:00:00.000Z"), components(year: 1992, month: 2, day: 29, hour: 0, minute: 0, second: 0, timeZoneOffset: 0)) - XCTAssertEqual(parse("1996-02-29T00:00:00.000Z"), components(year: 1996, month: 2, day: 29, hour: 0, minute: 0, second: 0, timeZoneOffset: 0)) - XCTAssertEqual(parse("2000-02-29T00:00:00.000Z"), components(year: 2000, month: 2, day: 29, hour: 0, minute: 0, second: 0, timeZoneOffset: 0)) - XCTAssertEqual(parse("2004-02-29T00:00:00.000Z"), components(year: 2004, month: 2, day: 29, hour: 0, minute: 0, second: 0, timeZoneOffset: 0)) - XCTAssertEqual(parse("2008-02-29T00:00:00.000Z"), components(year: 2008, month: 2, day: 29, hour: 0, minute: 0, second: 0, timeZoneOffset: 0)) - XCTAssertEqual(parse("2012-02-29T00:00:00.000Z"), components(year: 2012, month: 2, day: 29, hour: 0, minute: 0, second: 0, timeZoneOffset: 0)) - XCTAssertEqual(parse("2016-02-29T00:00:00.000Z"), components(year: 2016, month: 2, day: 29, hour: 0, minute: 0, second: 0, timeZoneOffset: 0)) + XCTAssertEqual(parse("1972-02-29T00:00:00.000Z"), components(year: 1972, month: 2, day: 29, hour: 0, minute: 0, second: 0, millisecond: 0, timeZoneOffset: 0)) + XCTAssertEqual(parse("1976-02-29T00:00:00.000Z"), components(year: 1976, month: 2, day: 29, hour: 0, minute: 0, second: 0, millisecond: 0, timeZoneOffset: 0)) + XCTAssertEqual(parse("1980-02-29T00:00:00.000Z"), components(year: 1980, month: 2, day: 29, hour: 0, minute: 0, second: 0, millisecond: 0, timeZoneOffset: 0)) + XCTAssertEqual(parse("1984-02-29T00:00:00.000Z"), components(year: 1984, month: 2, day: 29, hour: 0, minute: 0, second: 0, millisecond: 0, timeZoneOffset: 0)) + XCTAssertEqual(parse("1988-02-29T00:00:00.000Z"), components(year: 1988, month: 2, day: 29, hour: 0, minute: 0, second: 0, millisecond: 0, timeZoneOffset: 0)) + XCTAssertEqual(parse("1992-02-29T00:00:00.000Z"), components(year: 1992, month: 2, day: 29, hour: 0, minute: 0, second: 0, millisecond: 0, timeZoneOffset: 0)) + XCTAssertEqual(parse("1996-02-29T00:00:00.000Z"), components(year: 1996, month: 2, day: 29, hour: 0, minute: 0, second: 0, millisecond: 0, timeZoneOffset: 0)) + XCTAssertEqual(parse("2000-02-29T00:00:00.000Z"), components(year: 2000, month: 2, day: 29, hour: 0, minute: 0, second: 0, millisecond: 0, timeZoneOffset: 0)) + XCTAssertEqual(parse("2004-02-29T00:00:00.000Z"), components(year: 2004, month: 2, day: 29, hour: 0, minute: 0, second: 0, millisecond: 0, timeZoneOffset: 0)) + XCTAssertEqual(parse("2008-02-29T00:00:00.000Z"), components(year: 2008, month: 2, day: 29, hour: 0, minute: 0, second: 0, millisecond: 0, timeZoneOffset: 0)) + XCTAssertEqual(parse("2012-02-29T00:00:00.000Z"), components(year: 2012, month: 2, day: 29, hour: 0, minute: 0, second: 0, millisecond: 0, timeZoneOffset: 0)) + XCTAssertEqual(parse("2016-02-29T00:00:00.000Z"), components(year: 2016, month: 2, day: 29, hour: 0, minute: 0, second: 0, millisecond: 0, timeZoneOffset: 0)) } func testReadingExtensively() { @@ -76,8 +82,8 @@ class SerializationTests: XCTestCase { XCTAssertEqual(parse("-011985-04-12T10:15:30"), components(year: -11985, month: 4, day: 12, hour: 10, minute: 15, second: 30)) XCTAssertEqual(parse("02-04-12"), components(year: 2, month: 4, day: 12)) XCTAssertEqual(parse("0002-04-12"), components(year: 2, month: 4, day: 12)) - XCTAssertEqual(parse("2013-06-27T15:39:32.508Z"), components(year: 2013, month: 6, day: 27, hour: 15, minute: 39, second: 32, timeZoneOffset: 0)) - XCTAssertEqual(parse("2014-03-18T20:00:00.000-07:00"), components(year: 2014, month: 3, day: 18, hour: 20, minute: 0, second: 0, timeZoneOffset: -7 * 60 * 60)) + XCTAssertEqual(parse("2013-06-27T15:39:32.508Z"), components(year: 2013, month: 6, day: 27, hour: 15, minute: 39, second: 32, millisecond: 508, timeZoneOffset: 0)) + XCTAssertEqual(parse("2014-03-18T20:00:00.000-07:00"), components(year: 2014, month: 3, day: 18, hour: 20, minute: 0, second: 0, millisecond: 0, timeZoneOffset: -7 * 60 * 60)) XCTAssertEqual(parse("1999-05-23 23:55:21+0900"), components(year: 1999, month: 5, day: 23, hour: 23, minute: 55, second: 21, timeZoneOffset: 9 * 60 * 60)) XCTAssertEqual(parse("1999-05-23 23:55:21-0900"), components(year: 1999, month: 5, day: 23, hour: 23, minute: 55, second: 21, timeZoneOffset: -9 * 60 * 60)) XCTAssertEqual(parse("1999-05-23 23:55:21-1100"), components(year: 1999, month: 5, day: 23, hour: 23, minute: 55, second: 21, timeZoneOffset: -11 * 60 * 60)) @@ -90,11 +96,11 @@ class SerializationTests: XCTestCase { XCTAssertEqual(parse("2014-03-13"), components(year: 2014, month: 3, day: 13)) XCTAssertEqual(parse("2014-03-13T10:42:12"), components(year: 2014, month: 3, day: 13, hour: 10, minute: 42, second: 12)) XCTAssertEqual(parse("2014-03-13T10:42:12"), components(year: 2014, month: 3, day: 13, hour: 10, minute: 42, second: 12)) - XCTAssertEqual(parse("2014-03-13T10:42:12.123"), components(year: 2014, month: 3, day: 13, hour: 10, minute: 42, second: 12)) - XCTAssertEqual(parse("2014-03-13T10:42:12.123Z"), components(year: 2014, month: 3, day: 13, hour: 10, minute: 42, second: 12, timeZoneOffset: 0)) + XCTAssertEqual(parse("2014-03-13T10:42:12.123"), components(year: 2014, month: 3, day: 13, hour: 10, minute: 42, second: 12, millisecond: 123)) + XCTAssertEqual(parse("2014-03-13T10:42:12.123Z"), components(year: 2014, month: 3, day: 13, hour: 10, minute: 42, second: 12, millisecond: 123, timeZoneOffset: 0)) XCTAssertEqual(parse("2014-03-13T10:42:12Z"), components(year: 2014, month: 3, day: 13, hour: 10, minute: 42, second: 12, timeZoneOffset: 0)) - XCTAssertEqual(parse("2014-03-13T10:42:12.123+07:00"), components(year: 2014, month: 3, day: 13, hour: 10, minute: 42, second: 12, timeZoneOffset: 7 * 60 * 60)) - XCTAssertEqual(parse("2014-03-13T10:42:12.123-09:30"), components(year: 2014, month: 3, day: 13, hour: 10, minute: 42, second: 12, timeZoneOffset: -9.5 * 60 * 60)) + XCTAssertEqual(parse("2014-03-13T10:42:12.123+07:00"), components(year: 2014, month: 3, day: 13, hour: 10, minute: 42, second: 12, millisecond: 123, timeZoneOffset: 7 * 60 * 60)) + XCTAssertEqual(parse("2014-03-13T10:42:12.123-09:30"), components(year: 2014, month: 3, day: 13, hour: 10, minute: 42, second: 12, millisecond: 123, timeZoneOffset: -9.5 * 60 * 60)) XCTAssertEqual(parse("2014-03-13T10:42:12+07:00"), components(year: 2014, month: 3, day: 13, hour: 10, minute: 42, second: 12, timeZoneOffset: 7 * 60 * 60)) XCTAssertEqual(parse("2014-03-13T10:42:12-09:30"), components(year: 2014, month: 3, day: 13, hour: 10, minute: 42, second: 12, timeZoneOffset: -9.5 * 60 * 60)) } @@ -112,6 +118,8 @@ class SerializationTests: XCTestCase { XCTAssertEqual(parse("2015-03-13T10:42"), components(year: 2015, month: 3, day: 13, hour: 10, minute: 42)) XCTAssertEqual(parse("2015-03-13T10:42:💩"), components(year: 2015, month: 3, day: 13, hour: 10, minute: 42)) XCTAssertEqual(parse("2015-03-13T10:42:10"), components(year: 2015, month: 3, day: 13, hour: 10, minute: 42, second: 10)) + XCTAssertEqual(parse("2015-03-13T10:42:10.💩"), components(year: 2015, month: 3, day: 13, hour: 10, minute: 42, second: 10)) + XCTAssertEqual(parse("2015-03-13T10:42:10.100"), components(year: 2015, month: 3, day: 13, hour: 10, minute: 42, second: 10, millisecond: 100)) XCTAssertEqual(parse("2015-03-13T10:42:10+💩"), components(year: 2015, month: 3, day: 13, hour: 10, minute: 42, second: 10)) } diff --git a/Tests/TestHelper.swift b/Tests/TestHelper.swift index 8b02220..271cf84 100644 --- a/Tests/TestHelper.swift +++ b/Tests/TestHelper.swift @@ -9,6 +9,9 @@ import ISO8601 import Foundation +let microsecondsInMillisecond = 1000 +let nanosecondsInMicrosecond = 1000 + func parse(string: String) -> NSDateComponents? { return ISO8601Serialization.dateComponentsForString(string) } @@ -17,7 +20,11 @@ func serialize(components: NSDateComponents) -> String? { return ISO8601Serialization.stringForDateComponents(components) } -func components(year year: Int? = nil, month: Int? = nil, day: Int? = nil, hour: Int? = nil, minute: Int? = nil, second: Int? = nil, timeZoneOffset: NSTimeInterval? = nil) -> NSDateComponents { +func components(year year: Int? = nil, month: Int? = nil, day: Int? = nil, hour: Int? = nil, minute: Int? = nil, second: Int? = nil, millisecond: Int, timeZoneOffset: NSTimeInterval? = nil) -> NSDateComponents { + return components(year: year, month: month, day: day, hour: hour, minute: minute, second: second, microsecond: millisecond * microsecondsInMillisecond, timeZoneOffset: timeZoneOffset) +} + +func components(year year: Int? = nil, month: Int? = nil, day: Int? = nil, hour: Int? = nil, minute: Int? = nil, second: Int? = nil, microsecond: Int? = nil, timeZoneOffset: NSTimeInterval? = nil) -> NSDateComponents { let comps = NSDateComponents() if let year = year { comps.year = year @@ -43,6 +50,10 @@ func components(year year: Int? = nil, month: Int? = nil, day: Int? = nil, hour: comps.second = second } + if let microsecond = microsecond { + comps.nanosecond = microsecond * nanosecondsInMicrosecond + } + if let timeZoneOffset = timeZoneOffset { comps.timeZone = NSTimeZone(forSecondsFromGMT: Int(timeZoneOffset)) }