From aad6800d14648da39c80d3e55504cfc72d900692 Mon Sep 17 00:00:00 2001 From: Todd White Date: Wed, 22 Jul 2026 11:07:06 -0400 Subject: [PATCH 1/2] CFTimeZone: name and abbreviate GMT offsets like Apple CFTimeZoneCreateWithTimeIntervalFromGMT gave every zone the same GMT+HH:MM string for both its name and its abbreviation. Apple names a zero offset GMT and others GMT with a four-digit offset (GMT+0500), but abbreviates them compactly (GMT, GMT+5, GMT+5:30). Build the padded name and the compact abbreviation separately. The existing basic test is updated. --- Source/CFTimeZone.c | 28 ++++++++++++++++++---- Tests/CFTimeZone/basic.m | 13 ++++------ Tests/CFTimeZone/gmt_name.m | 47 +++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 Tests/CFTimeZone/gmt_name.m diff --git a/Source/CFTimeZone.c b/Source/CFTimeZone.c index da8a104..460bb8c 100644 --- a/Source/CFTimeZone.c +++ b/Source/CFTimeZone.c @@ -306,11 +306,29 @@ CFTimeZoneCreateWithTimeIntervalFromGMT (CFAllocatorRef alloc, tzfile.header.tzh_timecnt[3] = 1; tzfile.header.tzh_typecnt[3] = 1; tzfile.ttinfo.offset = CFSwapInt32HostToBig((SInt32)ti); - numChars = snprintf (tzfile.abbrev, 10, "GMT%c%02d:%02d", sign, hour, min); - tzfile.header.tzh_charcnt[3] = numChars; - - name = CFStringCreateWithCString (alloc, tzfile.abbrev, - kCFStringEncodingASCII); + /* The abbreviation is compact (GMT+5, GMT+5:30) while the name is padded + to four digits (GMT+0500). A zero offset is just GMT. */ + { + char nameBuf[16]; + + if ((SInt32) ti == 0) + { + numChars = snprintf (tzfile.abbrev, 10, "GMT"); + snprintf (nameBuf, sizeof (nameBuf), "GMT"); + } + else + { + if (min == 0) + numChars = snprintf (tzfile.abbrev, 10, "GMT%c%d", sign, hour); + else + numChars = snprintf (tzfile.abbrev, 10, "GMT%c%d:%02d", + sign, hour, min); + snprintf (nameBuf, sizeof (nameBuf), "GMT%c%02d%02d", sign, hour, min); + } + tzfile.header.tzh_charcnt[3] = numChars; + + name = CFStringCreateWithCString (alloc, nameBuf, kCFStringEncodingASCII); + } data = CFDataCreateWithBytesNoCopy (alloc, (UInt8*)&tzfile, sizeof(struct TZFile) - (10 - numChars), kCFAllocatorNull); new = CFTimeZoneCreate (alloc, name, data); diff --git a/Tests/CFTimeZone/basic.m b/Tests/CFTimeZone/basic.m index 2e2c16f..9be9547 100644 --- a/Tests/CFTimeZone/basic.m +++ b/Tests/CFTimeZone/basic.m @@ -9,21 +9,16 @@ int main (void) { CFTimeZoneRef tz; CFStringRef str; - CFStringRef expected; CFTimeInterval ti; CFAbsoluteTime at; tz = CFTimeZoneCreateWithTimeIntervalFromGMT (NULL, 0.0); PASS_CF(tz != NULL, "CFTimeZone create successfully."); -#ifdef __APPLE__ - expected = CFSTR("GMT"); -#else - expected = CFSTR("GMT+00:00"); -#endif - PASS_CFEQ(CFTimeZoneGetName(tz), expected, "CFTimeZone has correct name."); - + PASS_CFEQ(CFTimeZoneGetName(tz), CFSTR("GMT"), + "CFTimeZone has correct name."); + str = CFTimeZoneCopyAbbreviation (tz, 0.0); - PASS_CFEQ(str, expected, "Time zone abbreviations are equal."); + PASS_CFEQ(str, CFSTR("GMT"), "Time zone abbreviations are equal."); ti = CFTimeZoneGetSecondsFromGMT (tz, 0.0); PASS_CF(ti == 0.0, "GMT+00:00 offset from GMT is %g", ti); diff --git a/Tests/CFTimeZone/gmt_name.m b/Tests/CFTimeZone/gmt_name.m new file mode 100644 index 0000000..b230a45 --- /dev/null +++ b/Tests/CFTimeZone/gmt_name.m @@ -0,0 +1,47 @@ +#include "CoreFoundation/CFTimeZone.h" +#include "../CFTesting.h" + +/* The name of a GMT-offset zone is GMT for a zero offset and GMT followed by + the signed four-digit offset otherwise; the abbreviation is compact. */ + +static CFStringRef +tzname_of (CFTimeInterval ti) +{ + return CFTimeZoneGetName ( + CFTimeZoneCreateWithTimeIntervalFromGMT (NULL, ti)); +} + +static CFStringRef +tzabbr_of (CFTimeInterval ti) +{ + return CFTimeZoneCopyAbbreviation ( + CFTimeZoneCreateWithTimeIntervalFromGMT (NULL, ti), 0.0); +} + +int main (void) +{ + PASS_CFEQ (tzname_of (0.0), CFSTR ("GMT"), "A zero offset is named GMT."); + PASS_CFEQ (tzname_of (5 * 3600), CFSTR ("GMT+0500"), + "A five-hour offset is named GMT+0500."); + PASS_CFEQ (tzname_of (-5 * 3600), CFSTR ("GMT-0500"), + "A negative five-hour offset is named GMT-0500."); + PASS_CFEQ (tzname_of (5 * 3600 + 30 * 60), CFSTR ("GMT+0530"), + "A five-and-a-half-hour offset is named GMT+0530."); + PASS_CFEQ (tzname_of (30 * 60), CFSTR ("GMT+0030"), + "A thirty-minute offset is named GMT+0030."); + + PASS_CFEQ (tzabbr_of (0.0), CFSTR ("GMT"), + "A zero offset abbreviates to GMT."); + PASS_CFEQ (tzabbr_of (5 * 3600), CFSTR ("GMT+5"), + "A five-hour offset abbreviates to GMT+5."); + PASS_CFEQ (tzabbr_of (-5 * 3600), CFSTR ("GMT-5"), + "A negative five-hour offset abbreviates to GMT-5."); + PASS_CFEQ (tzabbr_of (5 * 3600 + 30 * 60), CFSTR ("GMT+5:30"), + "A five-and-a-half-hour offset abbreviates to GMT+5:30."); + PASS_CFEQ (tzabbr_of (30 * 60), CFSTR ("GMT+0:30"), + "A thirty-minute offset abbreviates to GMT+0:30."); + PASS_CFEQ (tzabbr_of (10 * 3600), CFSTR ("GMT+10"), + "A ten-hour offset abbreviates to GMT+10."); + + return 0; +} From 9b3efe69e9680f6cf5dabd757cd1beb27e5f8e9d Mon Sep 17 00:00:00 2001 From: Todd White Date: Thu, 23 Jul 2026 08:49:28 -0400 Subject: [PATCH 2/2] Tests: release the time zone behind the GMT name and abbreviation helpers --- Tests/CFTimeZone/gmt_name.m | 63 +++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/Tests/CFTimeZone/gmt_name.m b/Tests/CFTimeZone/gmt_name.m index b230a45..f910b9e 100644 --- a/Tests/CFTimeZone/gmt_name.m +++ b/Tests/CFTimeZone/gmt_name.m @@ -7,41 +7,64 @@ static CFStringRef tzname_of (CFTimeInterval ti) { - return CFTimeZoneGetName ( - CFTimeZoneCreateWithTimeIntervalFromGMT (NULL, ti)); + CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT (NULL, ti); + CFStringRef name = (CFStringRef) CFRetain (CFTimeZoneGetName (tz)); + CFRelease (tz); + return name; } static CFStringRef tzabbr_of (CFTimeInterval ti) { - return CFTimeZoneCopyAbbreviation ( - CFTimeZoneCreateWithTimeIntervalFromGMT (NULL, ti), 0.0); + CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT (NULL, ti); + CFStringRef abbr = CFTimeZoneCopyAbbreviation (tz, 0.0); + CFRelease (tz); + return abbr; } int main (void) { - PASS_CFEQ (tzname_of (0.0), CFSTR ("GMT"), "A zero offset is named GMT."); - PASS_CFEQ (tzname_of (5 * 3600), CFSTR ("GMT+0500"), - "A five-hour offset is named GMT+0500."); - PASS_CFEQ (tzname_of (-5 * 3600), CFSTR ("GMT-0500"), + CFStringRef s; + + s = tzname_of (0.0); + PASS_CFEQ (s, CFSTR ("GMT"), "A zero offset is named GMT."); + CFRelease (s); + s = tzname_of (5 * 3600); + PASS_CFEQ (s, CFSTR ("GMT+0500"), "A five-hour offset is named GMT+0500."); + CFRelease (s); + s = tzname_of (-5 * 3600); + PASS_CFEQ (s, CFSTR ("GMT-0500"), "A negative five-hour offset is named GMT-0500."); - PASS_CFEQ (tzname_of (5 * 3600 + 30 * 60), CFSTR ("GMT+0530"), + CFRelease (s); + s = tzname_of (5 * 3600 + 30 * 60); + PASS_CFEQ (s, CFSTR ("GMT+0530"), "A five-and-a-half-hour offset is named GMT+0530."); - PASS_CFEQ (tzname_of (30 * 60), CFSTR ("GMT+0030"), - "A thirty-minute offset is named GMT+0030."); + CFRelease (s); + s = tzname_of (30 * 60); + PASS_CFEQ (s, CFSTR ("GMT+0030"), "A thirty-minute offset is named GMT+0030."); + CFRelease (s); - PASS_CFEQ (tzabbr_of (0.0), CFSTR ("GMT"), - "A zero offset abbreviates to GMT."); - PASS_CFEQ (tzabbr_of (5 * 3600), CFSTR ("GMT+5"), - "A five-hour offset abbreviates to GMT+5."); - PASS_CFEQ (tzabbr_of (-5 * 3600), CFSTR ("GMT-5"), + s = tzabbr_of (0.0); + PASS_CFEQ (s, CFSTR ("GMT"), "A zero offset abbreviates to GMT."); + CFRelease (s); + s = tzabbr_of (5 * 3600); + PASS_CFEQ (s, CFSTR ("GMT+5"), "A five-hour offset abbreviates to GMT+5."); + CFRelease (s); + s = tzabbr_of (-5 * 3600); + PASS_CFEQ (s, CFSTR ("GMT-5"), "A negative five-hour offset abbreviates to GMT-5."); - PASS_CFEQ (tzabbr_of (5 * 3600 + 30 * 60), CFSTR ("GMT+5:30"), + CFRelease (s); + s = tzabbr_of (5 * 3600 + 30 * 60); + PASS_CFEQ (s, CFSTR ("GMT+5:30"), "A five-and-a-half-hour offset abbreviates to GMT+5:30."); - PASS_CFEQ (tzabbr_of (30 * 60), CFSTR ("GMT+0:30"), + CFRelease (s); + s = tzabbr_of (30 * 60); + PASS_CFEQ (s, CFSTR ("GMT+0:30"), "A thirty-minute offset abbreviates to GMT+0:30."); - PASS_CFEQ (tzabbr_of (10 * 3600), CFSTR ("GMT+10"), - "A ten-hour offset abbreviates to GMT+10."); + CFRelease (s); + s = tzabbr_of (10 * 3600); + PASS_CFEQ (s, CFSTR ("GMT+10"), "A ten-hour offset abbreviates to GMT+10."); + CFRelease (s); return 0; }