Merge pull request #3130 from daboe01/fixed-NSDateFormatter-unable-to-parse-dates-in-Javascript-Date.toISOString-format

fixed: NSDateFormatter was unable to parse dates in Javascript Date.toISOString format
This commit is contained in:
daboe01
2025-07-01 16:21:30 +02:00
committed by GitHub
2 changed files with 47 additions and 0 deletions
+12
View File
@@ -1291,6 +1291,18 @@ var _separatorsCharacterSet = nil;
if (!aString)
return [[CPDate alloc] initWithTimeIntervalSinceReferenceDate:-31622400];
// Special case for ISO8601 format, which is poorly handled by the generic parser.
if (aFormat === @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") {
var d = new Date(aString);
if (d && !isNaN(d.getTime())) {
// The native JS Date object handles ISO 8601 strings correctly (as UTC).
// We can create a CPDate directly from the resulting timestamp.
var kCFAbsoluteTimeIntervalSince1970 = 978307200.0;
var interval = (d.getTime() / 1000.0) - kCFAbsoluteTimeIntervalSince1970;
return [CPDate dateWithTimeIntervalSinceReferenceDate:interval];
}
}
if (aFormat == nil)
return nil;
+35
View File
@@ -1245,6 +1245,41 @@
[self assert:result equals:nil];
}
- (void)testDateFromStringISO8601
{
// This test verifies the fix for issue #2537.
// The parser should correctly handle the ISO 8601 format produced by Date.toISOString().
var dateFormatter = [[CPDateFormatter alloc] init];
[dateFormatter setLocale:[[CPLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
// Test with a fixed date string.
var fixedISOString = @"2017-03-31T16:40:15.768Z";
var parsedFixedDate = [dateFormatter dateFromString:fixedISOString];
// Create the expected date using a known-good method (JS Date parsing) to avoid relying on
// the code under test. We create it from a time interval, which supports millisecond precision.
var expectedTimestamp = new Date(fixedISOString).getTime() / 1000.0;
var expectedFixedDate = [CPDate dateWithTimeIntervalSince1970:expectedTimestamp];
// Now assert that the formatter produced the same date.
// isEqualToDate: compares the underlying time interval, which is what we want.
[self assertTrue:[parsedFixedDate isEqualToDate:expectedFixedDate] message:@"DateFormatter should correctly parse a fixed ISO 8601 string."];
// Test with a dynamically generated date string.
var liveJSDate = new Date();
var liveISOString = liveJSDate.toISOString();
var parsedLiveDate = [dateFormatter dateFromString:liveISOString];
// The fix converts from a JS Date, so we can compare time intervals for accuracy.
var liveJSDateTime = liveJSDate.getTime();
var parsedLiveDateTime = [parsedLiveDate timeIntervalSince1970] * 1000;
// We allow a small tolerance for floating point rounding.
[self assertTrue:Math.abs(liveJSDateTime - parsedLiveDateTime) < 1.0 message:@"DateFormatter should correctly parse a dynamically generated ISO 8601 string."];
}
- (void)testGetObjectValueAcceptsNilErrorDescription
{
var date = nil,