From bb6b738fc7658399e842204d6051eef3c19bc3a6 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 29 Jun 2025 19:34:56 +0200 Subject: [PATCH 1/5] fixed: CPTimeZone initWithName: does not return the timeZone with the good daylight saving --- Foundation/CPTimeZone.j | 43 ++++++-- Tests/Foundation/CPTimeZoneTest.j | 160 ++++++++---------------------- 2 files changed, 79 insertions(+), 124 deletions(-) diff --git a/Foundation/CPTimeZone.j b/Foundation/CPTimeZone.j index ed31dc6ad..2ae4310c2 100644 --- a/Foundation/CPTimeZone.j +++ b/Foundation/CPTimeZone.j @@ -523,19 +523,48 @@ function abbreviationForDate(date) { _name = tzName; - var keys = [abbreviationDictionary keyEnumerator], - key; + var now = [CPDate date], + abbreviation = nil; - while (key = [keys nextObject]) + // Try to get the abbreviation for the current date. This is the only reliable way to + // distinguish between standard and daylight saving time for a given time zone name. + // For example, for "America/Los_Angeles", this will return "PST" or "PDT" depending on the date. + try { - var value = [abbreviationDictionary valueForKey:key]; + var dateString = now.toLocaleString('en-US', { timeZone: tzName, timeZoneName: 'short' }), + lastPart = dateString.substring(dateString.lastIndexOf(' ') + 1); - if ([value isEqualToString:_name]) + // Verify that the abbreviation returned by the browser is one we know about + // and that it maps to the correct time zone name. + if ([[abbreviationDictionary valueForKey:lastPart] isEqualToString:tzName]) + abbreviation = lastPart; + } + catch (e) + { + // The browser may not support the time zone name, or toLocaleString is not fully implemented. + // In this case, we'll fall back to the old logic below. + } + + // Fallback: If we couldn't determine the abbreviation dynamically, + // resort to the old (and potentially incorrect) method of picking the first match. + if (!abbreviation) + { + var keys = [abbreviationDictionary keyEnumerator], + key; + + while (key = [keys nextObject]) { - _abbreviation = key; - break; + var value = [abbreviationDictionary valueForKey:key]; + + if ([value isEqualToString:_name]) + { + abbreviation = key; + break; + } } } + + _abbreviation = abbreviation; } return self; diff --git a/Tests/Foundation/CPTimeZoneTest.j b/Tests/Foundation/CPTimeZoneTest.j index eeae89c42..4d678dd2c 100644 --- a/Tests/Foundation/CPTimeZoneTest.j +++ b/Tests/Foundation/CPTimeZoneTest.j @@ -62,28 +62,50 @@ [self assert:timeZone equals:nil]; } -- (void)testTimeZoneWithName +- (void)testInitWithNameSelectsCorrectAbbreviationForDate { - var timeZone = [CPTimeZone timeZoneWithName:@"America/Los_Angeles"]; + var originalDateClassMethod = CPDate.date; - // The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary - if ([timeZone abbreviation] === @"PDT") { - [self assert:[timeZone name] equals:@"America/Los_Angeles"]; - [self assert:[timeZone abbreviation] equals:@"PDT"]; - [self assert:[timeZone secondsFromGMT] equals:(-420 * 60)]; - [self assert:[timeZone description] equals:@"America/Los_Angeles (PDT) offset -25200"]; - } else { - [self assert:[timeZone name] equals:@"America/Los_Angeles"]; - [self assert:[timeZone abbreviation] equals:@"PST"]; - [self assert:[timeZone secondsFromGMT] equals:(-480 * 60)]; - [self assert:[timeZone description] equals:@"America/Los_Angeles (PST) offset -28800"]; + try + { + // Test 1: Date is during Daylight Saving Time (e.g., July in Northern Hemisphere for "America/Los_Angeles") + var summerDate = [[CPDate alloc] initWithString:@"2022-07-01 12:00:00 +0000"]; + CPDate.date = function() { return summerDate; }; + + // Test instance method init + var timeZonePDT = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles" data:_data]; + [self assertNotNil:timeZonePDT message:"Time zone should not be nil for a valid name."]; + [self assert:[timeZonePDT abbreviation] equals:@"PDT" message:"Abbreviation should be PDT during summer"]; + [self assert:[timeZonePDT secondsFromGMT] equals:(-420 * 60) message:"Seconds from GMT should be correct for PDT"]; + [self assert:[timeZonePDT description] equals:@"America/Los_Angeles (PDT) offset -25200" message:"Description should be correct for PDT"]; + [self assert:[timeZonePDT data] equals:_data message:@"Data should be correctly associated with time zone"]; + + // Test class method factory + var timeZonePDTClass = [CPTimeZone timeZoneWithName:@"America/Los_Angeles"]; + [self assert:[timeZonePDTClass abbreviation] equals:@"PDT" message:"Class method should return zone with PDT abbreviation during summer"]; + + + // Test 2: Date is during Standard Time (e.g., January) + var winterDate = [[CPDate alloc] initWithString:@"2022-01-01 12:00:00 +0000"]; + CPDate.date = function() { return winterDate; }; + + // Test instance method init + var timeZonePST = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles" data:_data]; + [self assertNotNil:timeZonePST message:"Time zone should not be nil for a valid name."]; + [self assert:[timeZonePST abbreviation] equals:@"PST" message:"Abbreviation should be PST during winter"]; + [self assert:[timeZonePST secondsFromGMT] equals:(-480 * 60) message:"Seconds from GMT should be correct for PST"]; + [self assert:[timeZonePST description] equals:@"America/Los_Angeles (PST) offset -28800" message:"Description should be correct for PST"]; + [self assert:[timeZonePST data] equals:_data message:"Data should be correctly associated with time zone"]; + + // Test class method factory + var timeZonePSTClass = [CPTimeZone timeZoneWithName:@"America/Los_Angeles"]; + [self assert:[timeZonePSTClass abbreviation] equals:@"PST" message:"Class method should return zone with PST abbreviation during winter"]; + } + finally + { + // Restore the original class method to avoid side-effects in other tests + CPDate.date = originalDateClassMethod; } - [self assert:[timeZone localizedName:CPTimeZoneNameStyleStandard locale:_locale] equals:@"Pacific Standard Time"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortStandard locale:_locale] equals:@"PST"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleDaylightSaving locale:_locale] equals:@"Pacific Daylight Time"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale] equals:@"PDT"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleGeneric locale:_locale] equals:@"Pacific Time"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortGeneric locale:_locale] equals:@"PT"]; } - (void)testTimeZoneWithWrongName @@ -105,31 +127,6 @@ } } -- (void)testTimeZoneWithNameWithData -{ - var timeZone = [CPTimeZone timeZoneWithName:@"America/Los_Angeles" data:_data]; - - // The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary - if ([timeZone abbreviation] === @"PDT") { - [self assert:[timeZone name] equals:@"America/Los_Angeles"]; - [self assert:[timeZone abbreviation] equals:@"PDT"]; - [self assert:[timeZone secondsFromGMT] equals:(-420 * 60)]; - [self assert:[timeZone description] equals:@"America/Los_Angeles (PDT) offset -25200"]; - } else { - [self assert:[timeZone name] equals:@"America/Los_Angeles"]; - [self assert:[timeZone abbreviation] equals:@"PST"]; - [self assert:[timeZone secondsFromGMT] equals:(-480 * 60)]; - [self assert:[timeZone description] equals:@"America/Los_Angeles (PST) offset -28800"]; - } - [self assert:[timeZone data] equals:_data]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleStandard locale:_locale] equals:@"Pacific Standard Time"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortStandard locale:_locale] equals:@"PST"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleDaylightSaving locale:_locale] equals:@"Pacific Daylight Time"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale] equals:@"PDT"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleGeneric locale:_locale] equals:@"Pacific Time"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortGeneric locale:_locale] equals:@"PT"]; -} - - (void)testTimeZoneWithWrongNameWithData { var timeZone = [CPTimeZone timeZoneWithName:@"America/Los_Angelesezdez" data:_data]; @@ -170,30 +167,6 @@ [self assert:timeZone equals:nil]; } -- (void)testInitTimeZoneWithName -{ - var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"]; - - // The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary - if ([timeZone abbreviation] === @"PDT") { - [self assert:[timeZone name] equals:@"America/Los_Angeles"]; - [self assert:[timeZone abbreviation] equals:@"PDT"]; - [self assert:[timeZone secondsFromGMT] equals:(-420 * 60)]; - [self assert:[timeZone description] equals:@"America/Los_Angeles (PDT) offset -25200"]; - } else { - [self assert:[timeZone name] equals:@"America/Los_Angeles"]; - [self assert:[timeZone abbreviation] equals:@"PST"]; - [self assert:[timeZone secondsFromGMT] equals:(-480 * 60)]; - [self assert:[timeZone description] equals:@"America/Los_Angeles (PST) offset -28800"]; - } - [self assert:[timeZone localizedName:CPTimeZoneNameStyleStandard locale:_locale] equals:@"Pacific Standard Time"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortStandard locale:_locale] equals:@"PST"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleDaylightSaving locale:_locale] equals:@"Pacific Daylight Time"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale] equals:@"PDT"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleGeneric locale:_locale] equals:@"Pacific Time"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortGeneric locale:_locale] equals:@"PT"]; -} - - (void)testInitTimeZoneWithWrongName { var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angelesezdez"]; @@ -213,31 +186,6 @@ } } -- (void)testInitTimeZoneWithNameWithData -{ - var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles" data:_data]; - - // The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary - if ([timeZone abbreviation] === @"PDT") { - [self assert:[timeZone name] equals:@"America/Los_Angeles"]; - [self assert:[timeZone abbreviation] equals:@"PDT"]; - [self assert:[timeZone secondsFromGMT] equals:(-420 * 60)]; - [self assert:[timeZone description] equals:@"America/Los_Angeles (PDT) offset -25200"]; - } else { - [self assert:[timeZone name] equals:@"America/Los_Angeles"]; - [self assert:[timeZone abbreviation] equals:@"PST"]; - [self assert:[timeZone secondsFromGMT] equals:(-480 * 60)]; - [self assert:[timeZone description] equals:@"America/Los_Angeles (PST) offset -28800"]; - } - [self assert:[timeZone data] equals:_data]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleStandard locale:_locale] equals:@"Pacific Standard Time"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortStandard locale:_locale] equals:@"PST"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleDaylightSaving locale:_locale] equals:@"Pacific Daylight Time"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale] equals:@"PDT"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleGeneric locale:_locale] equals:@"Pacific Time"]; - [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortGeneric locale:_locale] equals:@"PT"]; -} - - (void)testInitTimeZoneWithWrongNameWithData { var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angelesezdez" data:_data]; @@ -291,32 +239,10 @@ [self assert:seconds equals:nil]; } -- (void)testSecondsFromGMT -{ - var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"]; - - // The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary - if ([timeZone abbreviation] === @"PDT") { - [self assert:[timeZone secondsFromGMT] equals:(-420 * 60)]; - } else { - [self assert:[timeZone secondsFromGMT] equals:(-480 * 60)]; - } -} - -- (void)testDescription -{ - var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"]; - - // The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary - if ([timeZone abbreviation] === @"PDT") { - [self assert:[timeZone description] equals:@"America/Los_Angeles (PDT) offset -25200"]; - } else { - [self assert:[timeZone description] equals:@"America/Los_Angeles (PST) offset -28800"]; - } -} - - (void)testLocalizedName { + // This test works because the localized strings for PDT and PST are identical. + // The abbreviation chosen will depend on when the test is run. var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"]; [self assert:[timeZone localizedName:CPTimeZoneNameStyleStandard locale:_locale] equals:@"Pacific Standard Time"]; [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortStandard locale:_locale] equals:@"PST"]; From 263ec36d80c83445a2c812078c1c47bf87502d8a Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 29 Jun 2025 19:42:12 +0200 Subject: [PATCH 2/5] fixed: test --- Tests/Foundation/CPTimeZoneTest.j | 55 ++++++++++--------------------- 1 file changed, 18 insertions(+), 37 deletions(-) diff --git a/Tests/Foundation/CPTimeZoneTest.j b/Tests/Foundation/CPTimeZoneTest.j index 4d678dd2c..b1acff5c5 100644 --- a/Tests/Foundation/CPTimeZoneTest.j +++ b/Tests/Foundation/CPTimeZoneTest.j @@ -64,48 +64,29 @@ - (void)testInitWithNameSelectsCorrectAbbreviationForDate { - var originalDateClassMethod = CPDate.date; + // This test verifies the fix for issue #2382. + // We need to test that initializing a time zone with a name (e.g., 'America/Los_Angeles') + // and a specific date correctly selects the abbreviation for that date (PST vs. PDT). - try - { - // Test 1: Date is during Daylight Saving Time (e.g., July in Northern Hemisphere for "America/Los_Angeles") - var summerDate = [[CPDate alloc] initWithString:@"2022-07-01 12:00:00 +0000"]; - CPDate.date = function() { return summerDate; }; + // Let's find the time zone for 'America/Los_Angeles'. + var pacificTimeZone = [CPTimeZone timeZoneWithName:@"America/Los_Angeles"]; + [self assertTrue:pacificTimeZone !== nil message:@"Time zone for America/Los_Angeles should be found"]; - // Test instance method init - var timeZonePDT = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles" data:_data]; - [self assertNotNil:timeZonePDT message:"Time zone should not be nil for a valid name."]; - [self assert:[timeZonePDT abbreviation] equals:@"PDT" message:"Abbreviation should be PDT during summer"]; - [self assert:[timeZonePDT secondsFromGMT] equals:(-420 * 60) message:"Seconds from GMT should be correct for PDT"]; - [self assert:[timeZonePDT description] equals:@"America/Los_Angeles (PDT) offset -25200" message:"Description should be correct for PDT"]; - [self assert:[timeZonePDT data] equals:_data message:@"Data should be correctly associated with time zone"]; + // A date in winter, when standard time (PST) is active. (e.g., January 15) + var standardDate = [CPDate dateWithTimeIntervalSinceReferenceDate:474681600]; // Jan 15, 2016 + [self assertTrue:standardDate !== nil message:@"Should be able to create a date in standard time."]; - // Test class method factory - var timeZonePDTClass = [CPTimeZone timeZoneWithName:@"America/Los_Angeles"]; - [self assert:[timeZonePDTClass abbreviation] equals:@"PDT" message:"Class method should return zone with PDT abbreviation during summer"]; + // A date in summer, when daylight saving time (PDT) is active. (e.g., July 15) + var daylightDate = [CPDate dateWithTimeIntervalSinceReferenceDate:490219200]; // Jul 15, 2016 + [self assertTrue:daylightDate !== nil message:@"Should be able to create a date in daylight saving time."]; + // Get the abbreviation for the winter date. It should be "PST". + var standardAbbr = [pacificTimeZone abbreviationForDate:standardDate]; + [self assert:standardAbbr equals:@"PST" message:@"Abbreviation for a date in winter should be PST."]; - // Test 2: Date is during Standard Time (e.g., January) - var winterDate = [[CPDate alloc] initWithString:@"2022-01-01 12:00:00 +0000"]; - CPDate.date = function() { return winterDate; }; - - // Test instance method init - var timeZonePST = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles" data:_data]; - [self assertNotNil:timeZonePST message:"Time zone should not be nil for a valid name."]; - [self assert:[timeZonePST abbreviation] equals:@"PST" message:"Abbreviation should be PST during winter"]; - [self assert:[timeZonePST secondsFromGMT] equals:(-480 * 60) message:"Seconds from GMT should be correct for PST"]; - [self assert:[timeZonePST description] equals:@"America/Los_Angeles (PST) offset -28800" message:"Description should be correct for PST"]; - [self assert:[timeZonePST data] equals:_data message:"Data should be correctly associated with time zone"]; - - // Test class method factory - var timeZonePSTClass = [CPTimeZone timeZoneWithName:@"America/Los_Angeles"]; - [self assert:[timeZonePSTClass abbreviation] equals:@"PST" message:"Class method should return zone with PST abbreviation during winter"]; - } - finally - { - // Restore the original class method to avoid side-effects in other tests - CPDate.date = originalDateClassMethod; - } + // Get the abbreviation for the summer date. It should be "PDT". + var daylightAbbr = [pacificTimeZone abbreviationForDate:daylightDate]; + [self assert:daylightAbbr equals:@"PDT" message:@"Abbreviation for a date in summer should be PDT."]; } - (void)testTimeZoneWithWrongName From 74f204e6fae4f9592fa80cfdfefc01de7c71e568 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 29 Jun 2025 20:20:58 +0200 Subject: [PATCH 3/5] fixed: tests and implementation --- Foundation/CPTimeZone.j | 67 +++++++---- Tests/Foundation/CPTimeZoneTest.j | 184 ++++++++++++++++++++++++++---- 2 files changed, 202 insertions(+), 49 deletions(-) diff --git a/Foundation/CPTimeZone.j b/Foundation/CPTimeZone.j index 2ae4310c2..65b230123 100644 --- a/Foundation/CPTimeZone.j +++ b/Foundation/CPTimeZone.j @@ -54,6 +54,35 @@ function abbreviationForDate(date) return abbreviation; } + +function _abbreviationForNameAndDate(tzName, date) +{ + // This is a helper function based on the existing `abbreviationForDate`. + // It determines the abbreviation for a given IANA name based on the provided date, + // which allows it to respect daylight saving time. + try { + var options = { + timeZone: tzName, + timeZoneName: 'long' + }; + // The 'en-US' locale provides a predictable format for parsing. + var dateString = date.toLocaleString('en-US', options); + + // This regex is copied from the global 'abbreviationForDate' function. + // It strips the date and time, leaving the long time zone name. + var longTZName = dateString.replace(/^([0]?\d|[1][0-2])\/((?:[0]?|[1-2])\d|[3][0-1])\/([2][01]|[1][6-9])\d{2}(,?\s*([0]?\d|[1][0-2])(\:[0-5]\d){1,2})*\s*([aApP][mM]{0,2})?\s*/, ""); + + // Create the abbreviation from the long name (e.g., "Pacific Daylight Time" -> "PDT") + var abbreviation = longTZName.split(" ").map(function(l) { return l[0]}).join(""); + + return abbreviation; + } catch (e) { + // The tzName might be invalid for toLocaleString, which throws a RangeError. + // In this case, we can't determine the abbreviation. + return nil; + } +} + /*! @class CPTimeZone @ingroup foundation @@ -523,32 +552,20 @@ function abbreviationForDate(date) { _name = tzName; - var now = [CPDate date], - abbreviation = nil; + // Determine the abbreviation based on the current date to handle DST. + var currentAbbreviation = _abbreviationForNameAndDate(tzName, [CPDate date]); - // Try to get the abbreviation for the current date. This is the only reliable way to - // distinguish between standard and daylight saving time for a given time zone name. - // For example, for "America/Los_Angeles", this will return "PST" or "PDT" depending on the date. - try + // If we got a valid abbreviation from the date, and it's one we know about, use it. + // Otherwise, fall back to the old logic. + if (currentAbbreviation && [abbreviationDictionary containsKey:currentAbbreviation]) { - var dateString = now.toLocaleString('en-US', { timeZone: tzName, timeZoneName: 'short' }), - lastPart = dateString.substring(dateString.lastIndexOf(' ') + 1); - - // Verify that the abbreviation returned by the browser is one we know about - // and that it maps to the correct time zone name. - if ([[abbreviationDictionary valueForKey:lastPart] isEqualToString:tzName]) - abbreviation = lastPart; + _abbreviation = currentAbbreviation; } - catch (e) - { - // The browser may not support the time zone name, or toLocaleString is not fully implemented. - // In this case, we'll fall back to the old logic below. - } - - // Fallback: If we couldn't determine the abbreviation dynamically, - // resort to the old (and potentially incorrect) method of picking the first match. - if (!abbreviation) + else { + // FALLBACK: Find the first matching abbreviation in the dictionary. + // Note: This is not DST-aware and may not be correct, but it preserves + // the original behavior for cases where the dynamic lookup fails. var keys = [abbreviationDictionary keyEnumerator], key; @@ -558,13 +575,15 @@ function abbreviationForDate(date) if ([value isEqualToString:_name]) { - abbreviation = key; + _abbreviation = key; break; } } } - _abbreviation = abbreviation; + // If no abbreviation could be found by any means, initialization fails. + if (!_abbreviation) + return nil; } return self; diff --git a/Tests/Foundation/CPTimeZoneTest.j b/Tests/Foundation/CPTimeZoneTest.j index b1acff5c5..52d4e7041 100644 --- a/Tests/Foundation/CPTimeZoneTest.j +++ b/Tests/Foundation/CPTimeZoneTest.j @@ -62,31 +62,28 @@ [self assert:timeZone equals:nil]; } -- (void)testInitWithNameSelectsCorrectAbbreviationForDate +- (void)testTimeZoneWithName { - // This test verifies the fix for issue #2382. - // We need to test that initializing a time zone with a name (e.g., 'America/Los_Angeles') - // and a specific date correctly selects the abbreviation for that date (PST vs. PDT). + var timeZone = [CPTimeZone timeZoneWithName:@"America/Los_Angeles"]; - // Let's find the time zone for 'America/Los_Angeles'. - var pacificTimeZone = [CPTimeZone timeZoneWithName:@"America/Los_Angeles"]; - [self assertTrue:pacificTimeZone !== nil message:@"Time zone for America/Los_Angeles should be found"]; - - // A date in winter, when standard time (PST) is active. (e.g., January 15) - var standardDate = [CPDate dateWithTimeIntervalSinceReferenceDate:474681600]; // Jan 15, 2016 - [self assertTrue:standardDate !== nil message:@"Should be able to create a date in standard time."]; - - // A date in summer, when daylight saving time (PDT) is active. (e.g., July 15) - var daylightDate = [CPDate dateWithTimeIntervalSinceReferenceDate:490219200]; // Jul 15, 2016 - [self assertTrue:daylightDate !== nil message:@"Should be able to create a date in daylight saving time."]; - - // Get the abbreviation for the winter date. It should be "PST". - var standardAbbr = [pacificTimeZone abbreviationForDate:standardDate]; - [self assert:standardAbbr equals:@"PST" message:@"Abbreviation for a date in winter should be PST."]; - - // Get the abbreviation for the summer date. It should be "PDT". - var daylightAbbr = [pacificTimeZone abbreviationForDate:daylightDate]; - [self assert:daylightAbbr equals:@"PDT" message:@"Abbreviation for a date in summer should be PDT."]; + // The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary + if ([timeZone abbreviation] === @"PDT") { + [self assert:[timeZone name] equals:@"America/Los_Angeles"]; + [self assert:[timeZone abbreviation] equals:@"PDT"]; + [self assert:[timeZone secondsFromGMT] equals:(-420 * 60)]; + [self assert:[timeZone description] equals:@"America/Los_Angeles (PDT) offset -25200"]; + } else { + [self assert:[timeZone name] equals:@"America/Los_Angeles"]; + [self assert:[timeZone abbreviation] equals:@"PST"]; + [self assert:[timeZone secondsFromGMT] equals:(-480 * 60)]; + [self assert:[timeZone description] equals:@"America/Los_Angeles (PST) offset -28800"]; + } + [self assert:[timeZone localizedName:CPTimeZoneNameStyleStandard locale:_locale] equals:@"Pacific Standard Time"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortStandard locale:_locale] equals:@"PST"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleDaylightSaving locale:_locale] equals:@"Pacific Daylight Time"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale] equals:@"PDT"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleGeneric locale:_locale] equals:@"Pacific Time"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortGeneric locale:_locale] equals:@"PT"]; } - (void)testTimeZoneWithWrongName @@ -108,6 +105,31 @@ } } +- (void)testTimeZoneWithNameWithData +{ + var timeZone = [CPTimeZone timeZoneWithName:@"America/Los_Angeles" data:_data]; + + // The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary + if ([timeZone abbreviation] === @"PDT") { + [self assert:[timeZone name] equals:@"America/Los_Angeles"]; + [self assert:[timeZone abbreviation] equals:@"PDT"]; + [self assert:[timeZone secondsFromGMT] equals:(-420 * 60)]; + [self assert:[timeZone description] equals:@"America/Los_Angeles (PDT) offset -25200"]; + } else { + [self assert:[timeZone name] equals:@"America/Los_Angeles"]; + [self assert:[timeZone abbreviation] equals:@"PST"]; + [self assert:[timeZone secondsFromGMT] equals:(-480 * 60)]; + [self assert:[timeZone description] equals:@"America/Los_Angeles (PST) offset -28800"]; + } + [self assert:[timeZone data] equals:_data]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleStandard locale:_locale] equals:@"Pacific Standard Time"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortStandard locale:_locale] equals:@"PST"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleDaylightSaving locale:_locale] equals:@"Pacific Daylight Time"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale] equals:@"PDT"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleGeneric locale:_locale] equals:@"Pacific Time"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortGeneric locale:_locale] equals:@"PT"]; +} + - (void)testTimeZoneWithWrongNameWithData { var timeZone = [CPTimeZone timeZoneWithName:@"America/Los_Angelesezdez" data:_data]; @@ -148,6 +170,30 @@ [self assert:timeZone equals:nil]; } +- (void)testInitTimeZoneWithName +{ + var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"]; + + // The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary + if ([timeZone abbreviation] === @"PDT") { + [self assert:[timeZone name] equals:@"America/Los_Angeles"]; + [self assert:[timeZone abbreviation] equals:@"PDT"]; + [self assert:[timeZone secondsFromGMT] equals:(-420 * 60)]; + [self assert:[timeZone description] equals:@"America/Los_Angeles (PDT) offset -25200"]; + } else { + [self assert:[timeZone name] equals:@"America/Los_Angeles"]; + [self assert:[timeZone abbreviation] equals:@"PST"]; + [self assert:[timeZone secondsFromGMT] equals:(-480 * 60)]; + [self assert:[timeZone description] equals:@"America/Los_Angeles (PST) offset -28800"]; + } + [self assert:[timeZone localizedName:CPTimeZoneNameStyleStandard locale:_locale] equals:@"Pacific Standard Time"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortStandard locale:_locale] equals:@"PST"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleDaylightSaving locale:_locale] equals:@"Pacific Daylight Time"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale] equals:@"PDT"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleGeneric locale:_locale] equals:@"Pacific Time"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortGeneric locale:_locale] equals:@"PT"]; +} + - (void)testInitTimeZoneWithWrongName { var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angelesezdez"]; @@ -167,6 +213,31 @@ } } +- (void)testInitTimeZoneWithNameWithData +{ + var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles" data:_data]; + + // The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary + if ([timeZone abbreviation] === @"PDT") { + [self assert:[timeZone name] equals:@"America/Los_Angeles"]; + [self assert:[timeZone abbreviation] equals:@"PDT"]; + [self assert:[timeZone secondsFromGMT] equals:(-420 * 60)]; + [self assert:[timeZone description] equals:@"America/Los_Angeles (PDT) offset -25200"]; + } else { + [self assert:[timeZone name] equals:@"America/Los_Angeles"]; + [self assert:[timeZone abbreviation] equals:@"PST"]; + [self assert:[timeZone secondsFromGMT] equals:(-480 * 60)]; + [self assert:[timeZone description] equals:@"America/Los_Angeles (PST) offset -28800"]; + } + [self assert:[timeZone data] equals:_data]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleStandard locale:_locale] equals:@"Pacific Standard Time"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortStandard locale:_locale] equals:@"PST"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleDaylightSaving locale:_locale] equals:@"Pacific Daylight Time"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortDaylightSaving locale:_locale] equals:@"PDT"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleGeneric locale:_locale] equals:@"Pacific Time"]; + [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortGeneric locale:_locale] equals:@"PT"]; +} + - (void)testInitTimeZoneWithWrongNameWithData { var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angelesezdez" data:_data]; @@ -212,6 +283,7 @@ [self assert:seconds equals:(_date.getTimezoneOffset() * -60)]; } +-al - (void)testSecondsFromGMTForDateWithNilDate { var timeZone = [CPTimeZone localTimeZone], @@ -220,10 +292,32 @@ [self assert:seconds equals:nil]; } +- (void)testSecondsFromGMT +{ + var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"]; + + // The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary + if ([timeZone abbreviation] === @"PDT") { + [self assert:[timeZone secondsFromGMT] equals:(-420 * 60)]; + } else { + [self assert:[timeZone secondsFromGMT] equals:(-480 * 60)]; + } +} + +- (void)testDescription +{ + var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"]; + + // The current implementation can return daylight saving time or standard time depending on the current implmentation on CPDictionary + if ([timeZone abbreviation] === @"PDT") { + [self assert:[timeZone description] equals:@"America/Los_Angeles (PDT) offset -25200"]; + } else { + [self assert:[timeZone description] equals:@"America/Los_Angeles (PST) offset -28800"]; + } +} + - (void)testLocalizedName { - // This test works because the localized strings for PDT and PST are identical. - // The abbreviation chosen will depend on when the test is run. var timeZone = [[CPTimeZone alloc] initWithName:@"America/Los_Angeles"]; [self assert:[timeZone localizedName:CPTimeZoneNameStyleStandard locale:_locale] equals:@"Pacific Standard Time"]; [self assert:[timeZone localizedName:CPTimeZoneNameStyleShortStandard locale:_locale] equals:@"PST"]; @@ -249,4 +343,44 @@ [self assert:[timeZone1 isEqualToTimeZone:timeZone2] equals:NO]; } +- (void)testInitWithNameRespectsDaylightSaving +{ + // This test verifies that initWithName: correctly selects the abbreviation + // (e.g., PST vs. PDT) based on the current date's daylight saving status. + + // 1. Test a time zone that observes DST, like America/Los_Angeles. + var laTimeZoneName = @"America/Los_Angeles"; + + // To create a reliable test, we determine the "ground truth" abbreviation + // for the current time using the same browser API the implementation uses. + var expectedAbbreviationLA; + try { + // This logic mimics the _abbreviationForNameAndDate helper function in CPTimeZone.j + var options = { timeZone: laTimeZoneName, timeZoneName: 'long' }; + var dateString = (new Date()).toLocaleString('en-US', options); + var longTZName = dateString.replace(/^([0]?\d|[1][0-2])\/((?:[0]?|[1-2])\d|[3][0-1])\/([2][01]|[1][6-9])\d{2}(,?\s*([0]?\d|[1][0-2])(\:[0-5]\d){1,2})*\s*([aApP][mM]{0,2})?\s*/, ""); + expectedAbbreviationLA = longTZName.split(" ").map(function(l) { return l[0]}).join(""); + + } catch (e) { + [self fail:[CPString stringWithFormat:@"Could not determine expected abbreviation for %@. Error: %@", laTimeZoneName, e.message]]; + return; + } + + var timeZoneLA = [[CPTimeZone alloc] initWithName:laTimeZoneName]; + [self assertNotNil:timeZoneLA message:@"Time zone for America/Los_Angeles should be created successfully."]; + [self assert:[timeZoneLA abbreviation] + equals:expectedAbbreviationLA + message:[CPString stringWithFormat:@"The abbreviation should be %@ based on the current date.", expectedAbbreviationLA]]; + + // 2. Test a time zone that does not observe DST, like Pacific/Honolulu. + var hnlTimeZoneName = @"Pacific/Honolulu"; + var timeZoneHNL = [[CPTimeZone alloc] initWithName:hnlTimeZoneName]; + + [self assertNotNil:timeZoneHNL message:@"Time zone for Pacific/Honolulu should be created successfully."]; + // For a non-DST zone, the abbreviation is constant. + [self assert:[timeZoneHNL abbreviation] + equals:@"HST" + message:@"The abbreviation for a non-DST zone like Honolulu should always be HST."]; +} + @end From 25708554fd52ce84ade6d8c4b3be611f0294a77d Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 29 Jun 2025 20:22:38 +0200 Subject: [PATCH 4/5] fixed: typo --- Tests/Foundation/CPTimeZoneTest.j | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/Foundation/CPTimeZoneTest.j b/Tests/Foundation/CPTimeZoneTest.j index 52d4e7041..9a17a557a 100644 --- a/Tests/Foundation/CPTimeZoneTest.j +++ b/Tests/Foundation/CPTimeZoneTest.j @@ -283,7 +283,6 @@ [self assert:seconds equals:(_date.getTimezoneOffset() * -60)]; } --al - (void)testSecondsFromGMTForDateWithNilDate { var timeZone = [CPTimeZone localTimeZone], From b6461ba4fb0ee40e6eb1b2511d715e2fc33f1b31 Mon Sep 17 00:00:00 2001 From: daboe01 Date: Sun, 29 Jun 2025 20:28:43 +0200 Subject: [PATCH 5/5] fixed: tests --- Tests/Foundation/CPTimeZoneTest.j | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/Tests/Foundation/CPTimeZoneTest.j b/Tests/Foundation/CPTimeZoneTest.j index 9a17a557a..ef05ecb32 100644 --- a/Tests/Foundation/CPTimeZoneTest.j +++ b/Tests/Foundation/CPTimeZoneTest.j @@ -349,37 +349,35 @@ // 1. Test a time zone that observes DST, like America/Los_Angeles. var laTimeZoneName = @"America/Los_Angeles"; - - // To create a reliable test, we determine the "ground truth" abbreviation - // for the current time using the same browser API the implementation uses. var expectedAbbreviationLA; + try { // This logic mimics the _abbreviationForNameAndDate helper function in CPTimeZone.j var options = { timeZone: laTimeZoneName, timeZoneName: 'long' }; var dateString = (new Date()).toLocaleString('en-US', options); var longTZName = dateString.replace(/^([0]?\d|[1][0-2])\/((?:[0]?|[1-2])\d|[3][0-1])\/([2][01]|[1][6-9])\d{2}(,?\s*([0]?\d|[1][0-2])(\:[0-5]\d){1,2})*\s*([aApP][mM]{0,2})?\s*/, ""); expectedAbbreviationLA = longTZName.split(" ").map(function(l) { return l[0]}).join(""); - } catch (e) { - [self fail:[CPString stringWithFormat:@"Could not determine expected abbreviation for %@. Error: %@", laTimeZoneName, e.message]]; + [self fail:"Could not determine expected abbreviation for America/Los_Angeles"]; return; } var timeZoneLA = [[CPTimeZone alloc] initWithName:laTimeZoneName]; - [self assertNotNil:timeZoneLA message:@"Time zone for America/Los_Angeles should be created successfully."]; - [self assert:[timeZoneLA abbreviation] - equals:expectedAbbreviationLA - message:[CPString stringWithFormat:@"The abbreviation should be %@ based on the current date.", expectedAbbreviationLA]]; + + if (timeZoneLA == nil) + [self fail:"Time zone for America/Los_Angeles should be created successfully."]; + + [self assert:[timeZoneLA abbreviation] equals:expectedAbbreviationLA]; // 2. Test a time zone that does not observe DST, like Pacific/Honolulu. var hnlTimeZoneName = @"Pacific/Honolulu"; var timeZoneHNL = [[CPTimeZone alloc] initWithName:hnlTimeZoneName]; - [self assertNotNil:timeZoneHNL message:@"Time zone for Pacific/Honolulu should be created successfully."]; + if (timeZoneHNL == nil) + [self fail:"Time zone for Pacific/Honolulu should be created successfully."]; + // For a non-DST zone, the abbreviation is constant. - [self assert:[timeZoneHNL abbreviation] - equals:@"HST" - message:@"The abbreviation for a non-DST zone like Honolulu should always be HST."]; + [self assert:[timeZoneHNL abbreviation] equals:@"HST"]; } @end