Skip to content
This repository has been archived by the owner on Aug 24, 2019. It is now read-only.

Add support for sub-second precision for reading & writing dates #30

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 28 additions & 7 deletions Sources/ISO8601Serialization.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#import "ISO8601Serialization.h"

static const NSInteger kNanosecondsInMicrosecond = 1000;

@implementation ISO8601Serialization

+ (NSDateComponents * __nullable)dateComponentsForString:(NSString * __nonnull)string {
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions Sources/NSDate+ISO8601.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 8 additions & 2 deletions Sources/NSDate+ISO8601.m
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}
Expand All @@ -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
5 changes: 5 additions & 0 deletions Tests/DateTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
Loading