diff --git a/Foundation/CPDate.j b/Foundation/CPDate.j index 3d4d12915..2bf756f9c 100644 --- a/Foundation/CPDate.j +++ b/Foundation/CPDate.j @@ -231,4 +231,41 @@ var CPDateTimeKey = @"CPDateTimeKey"; @end +// Based on 'Universal JavaScript Date.parse for ISO 8601' available at https://github.com/csnover/js-iso8601. +var numericKeys = [1, 4, 5, 6, 7, 10, 11]; +Date.parseISO8601 = function (date) +{ + var timestamp, struct, minutesOffset = 0; + + // First, check for native parsing. + timestamp = Date.parse(date); + + if (isNaN(timestamp) && (struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) + { + // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC + for (var i = 0, k; (k = numericKeys[i]); ++i) + { + struct[k] = +struct[k] || 0; + } + + // allow undefined days and months + struct[2] = (+struct[2] || 1) - 1; + struct[3] = +struct[3] || 1; + + if (struct[8] !== 'Z' && struct[9] !== undefined) + { + minutesOffset = struct[10] * 60 + struct[11]; + + if (struct[9] === '+') + { + minutesOffset = 0 - minutesOffset; + } + } + + return Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]); + } + + return timestamp; +}; + Date.prototype.isa = CPDate; diff --git a/Objective-J/CFPropertyList.js b/Objective-J/CFPropertyList.js index cd1bbc3d2..00174387c 100644 --- a/Objective-J/CFPropertyList.js +++ b/Objective-J/CFPropertyList.js @@ -297,6 +297,7 @@ var XML_XML = "xml", PLIST_DICTIONARY = "dict", PLIST_ARRAY = "array", PLIST_STRING = "string", + PLIST_DATE = "date", PLIST_BOOLEAN_TRUE = "true", PLIST_BOOLEAN_FALSE = "false", PLIST_NUMBER_REAL = "real", @@ -596,6 +597,10 @@ CFPropertyList.propertyListFromXML = function(/*String | XMLNode*/ aStringOrXMLN object = decodeHTMLComponent(FIRST_CHILD(XMLNode) ? TEXT_CONTENT(XMLNode) : ""); break; + + case PLIST_DATE: var timestamp = Date.parseISO8601(TEXT_CONTENT(XMLNode)); + object = isNaN(timestamp) ? new Date() : new Date(timestamp); + break; case PLIST_BOOLEAN_TRUE: object = YES; break; diff --git a/Tests/Objective-J/CFPropertyListTest.j b/Tests/Objective-J/CFPropertyListTest.j index 25882ecad..9534a81f2 100644 --- a/Tests/Objective-J/CFPropertyListTest.j +++ b/Tests/Objective-J/CFPropertyListTest.j @@ -14,4 +14,14 @@ var FILE = require("file"), }); } +- (void)testDateDeserialization +{ + var path = FILE.join(FILE.dirname(module.path), "PropertyLists/XMLDate.plist"), + object = CFPropertyList.readPropertyListFromFile(path), + date = [object objectForKey:@"date"]; + + [self assert:[CPDate class] equals:[date class]]; + [self assert:[[CPDate alloc] initWithString:"2012-01-01 10:00:00 +0100"] equals:date]; +} + @end diff --git a/Tests/Objective-J/PropertyLists/XMLDate.plist b/Tests/Objective-J/PropertyLists/XMLDate.plist new file mode 100644 index 000000000..64cb0a85c --- /dev/null +++ b/Tests/Objective-J/PropertyLists/XMLDate.plist @@ -0,0 +1,8 @@ + + + + + date + 2012-01-01T09:00:00Z + +