From 4f7e59ee213c6bca82ee049fda1db8ffaec6ea83 Mon Sep 17 00:00:00 2001 From: Todd White Date: Wed, 22 Jul 2026 10:16:18 -0400 Subject: [PATCH 1/2] CFTimeZone: resolve GMT offset names CFTimeZoneCreateWithName looked for a zone file for every name, so a GMT offset such as GMT+05:00 (which has no file) returned NULL. In particular a calendar whose time zone was created from a GMT offset could not be copied back, since CFCalendarCopyTimeZone recreates the zone from its name. Parse a GMT offset name and build the zone from the offset directly. --- Source/CFTimeZone.c | 24 +++++++++++++++++++++++- Tests/CFTimeZone/gmt.m | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 Tests/CFTimeZone/gmt.m diff --git a/Source/CFTimeZone.c b/Source/CFTimeZone.c index da8a104..6d52eb8 100644 --- a/Source/CFTimeZone.c +++ b/Source/CFTimeZone.c @@ -328,7 +328,29 @@ CFTimeZoneCreateWithName (CFAllocatorRef alloc, CFStringRef name, CFURLRef path; CFDataRef data; CFTimeZoneRef new; - + char cname[16]; + + /* A GMT offset such as GMT+05:00 has no zone file; build it directly. */ + if (CFStringGetCString (name, cname, sizeof (cname), kCFStringEncodingASCII) + && strncmp (cname, "GMT", 3) == 0 + && (cname[3] == '+' || cname[3] == '-')) + { + int sign = (cname[3] == '-') ? -1 : 1; + const char *p = cname + 4; + int hh = 0; + int mm = 0; + + while (*p >= '0' && *p <= '9') + hh = hh * 10 + (*p++ - '0'); + if (*p == ':') + ++p; + while (*p >= '0' && *p <= '9') + mm = mm * 10 + (*p++ - '0'); + + return CFTimeZoneCreateWithTimeIntervalFromGMT (alloc, + sign * (hh * 3600 + mm * 60)); + } + if (tryAbbrev) { CFDictionaryRef abbrevs; diff --git a/Tests/CFTimeZone/gmt.m b/Tests/CFTimeZone/gmt.m new file mode 100644 index 0000000..bfbe29c --- /dev/null +++ b/Tests/CFTimeZone/gmt.m @@ -0,0 +1,35 @@ +#include "CoreFoundation/CFTimeZone.h" +#include "CoreFoundation/CFCalendar.h" +#include "../CFTesting.h" + +/* CFTimeZoneCreateWithName resolves GMT and UTC offset names, so a calendar + whose time zone is a GMT offset can be copied back. */ + +int main (void) +{ + CFTimeZoneRef z0 = CFTimeZoneCreateWithTimeIntervalFromGMT (NULL, 0.0); + CFTimeZoneRef tz; + CFCalendarRef cal; + + tz = CFTimeZoneCreateWithName (NULL, CFSTR ("GMT"), true); + PASS_CF (tz != NULL && CFTimeZoneGetSecondsFromGMT (tz, 0.0) == 0, + "GMT resolves to a zero offset."); + + tz = CFTimeZoneCreateWithName (NULL, CFSTR ("UTC"), true); + PASS_CF (tz != NULL && CFTimeZoneGetSecondsFromGMT (tz, 0.0) == 0, + "UTC resolves to a zero offset."); + + tz = CFTimeZoneCreateWithName (NULL, CFSTR ("GMT-05:00"), true); + PASS_CF (tz != NULL && CFTimeZoneGetSecondsFromGMT (tz, 0.0) == -18000, + "GMT-05:00 resolves to minus 18000 seconds."); + + cal = CFCalendarCreateWithIdentifier (NULL, kCFGregorianCalendar); + CFCalendarSetTimeZone (cal, z0); + tz = CFCalendarCopyTimeZone (cal); + PASS_CF (tz != NULL && CFTimeZoneGetSecondsFromGMT (tz, 0.0) == 0, + "A calendar's GMT time zone can be copied back."); + + CFRelease (cal); + CFRelease (z0); + return 0; +} From df61052312f6b5633dbb9a38bde993151633f17a Mon Sep 17 00:00:00 2001 From: Todd White Date: Thu, 23 Jul 2026 08:48:21 -0400 Subject: [PATCH 2/2] Tests: release each resolved time zone in the GMT-name test --- Tests/CFTimeZone/gmt.m | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Tests/CFTimeZone/gmt.m b/Tests/CFTimeZone/gmt.m index bfbe29c..988497e 100644 --- a/Tests/CFTimeZone/gmt.m +++ b/Tests/CFTimeZone/gmt.m @@ -14,20 +14,28 @@ int main (void) tz = CFTimeZoneCreateWithName (NULL, CFSTR ("GMT"), true); PASS_CF (tz != NULL && CFTimeZoneGetSecondsFromGMT (tz, 0.0) == 0, "GMT resolves to a zero offset."); + if (tz != NULL) + CFRelease (tz); tz = CFTimeZoneCreateWithName (NULL, CFSTR ("UTC"), true); PASS_CF (tz != NULL && CFTimeZoneGetSecondsFromGMT (tz, 0.0) == 0, "UTC resolves to a zero offset."); + if (tz != NULL) + CFRelease (tz); tz = CFTimeZoneCreateWithName (NULL, CFSTR ("GMT-05:00"), true); PASS_CF (tz != NULL && CFTimeZoneGetSecondsFromGMT (tz, 0.0) == -18000, "GMT-05:00 resolves to minus 18000 seconds."); + if (tz != NULL) + CFRelease (tz); cal = CFCalendarCreateWithIdentifier (NULL, kCFGregorianCalendar); CFCalendarSetTimeZone (cal, z0); tz = CFCalendarCopyTimeZone (cal); PASS_CF (tz != NULL && CFTimeZoneGetSecondsFromGMT (tz, 0.0) == 0, "A calendar's GMT time zone can be copied back."); + if (tz != NULL) + CFRelease (tz); CFRelease (cal); CFRelease (z0);