--- ../core/base/Source/NSTimeZone.m.cvs 2003-11-19 17:39:06.000000000 +0800 +++ ../core/base/Source/NSTimeZone.m 2003-12-14 12:30:54.000000000 +0800 @@ -39,13 +39,45 @@ eventually have to change the implementation to prevent the year 2038 problem.) - The local time zone can be specified with the user defaults - database, the GNUSTEP_TZ environment variable, the file LOCAL_TIME_FILE, - the TZ environment variable, or the fallback time zone (which is UTC), + The local time zone can be specified with: + 1) the user defaults database + 2) the GNUSTEP_TZ environment variable + 3) the file LOCAL_TIME_FILE in _time_zone_path() + 4) the TZ environment variable + 5) or the fallback time zone (which is UTC) with the ones listed first having precedence. Any time zone must be a file name in ZONES_DIR. + Files & File System Heirarchy info: + =================================== + + Default place for the NSTimeZone directory is _time_zone_path(): + {$(GNUSTEP_SYSTEM_ROOT)Libary/Libraries/Resources/TIME_ZONE_DIR} + + LOCAL_TIME_FILE is a text file with the name of the time zone file. + + ZONES_DIR is a sub-directory under TIME_ZONE_DIR + + (dir) ../System/Library/Libraries/Resources/.. + (dir) NSTimeZone + (file) localtime {text; time zone eg Australia/Perth} + (dir) zones + + Since NSTimeZone gets the name from LOCAL_TIME_FILE it's sufficient + to symlink this to the time zone name used elsewhere. For example: + Debian uses "/etc/timezone" + + A number of POSIX systems have the zone files already installed. + For these systems it is sufficient to symlink ZONES_DIR to the + platform specific location. + For (g)libc6 this is /usr/share/zoneinfo + For Solaris this is /usr/share/lib/zoneinfo + + Note that full zone info is required, especially the various "GMT" + files which are created especially for OPENSTEP compatibility. + Zone info comes from the Olson time database. + FIXME?: use leap seconds? */ #include "config.h" @@ -87,7 +119,8 @@ /* Key for local time zone in user defaults. */ #define LOCALDBKEY @"Local Time Zone" -/* Directory that contains the time zone data. */ +/* Directory that contains the time zone data. + Expected in Resources directory for library bundle. */ #define TIME_ZONE_DIR @"NSTimeZones" /* Location of time zone abbreviation dictionary. It is a text file @@ -113,13 +146,13 @@ @class GSTimeZoneDetail; @class GSAbsTimeZoneDetail; -@class GSPlaceholderTimeZone; +@class GSPlaceholderTimeZone; /* * Information for abstract placeholder class. */ -static GSPlaceholderTimeZone *defaultPlaceholderTimeZone; -static NSMapTable *placeholderMap; +static GSPlaceholderTimeZone *defaultPlaceholderTimeZone; +static NSMapTable *placeholderMap; /* * Temporary structure for holding time zone details. @@ -127,8 +160,8 @@ */ struct ttinfo { - char offset[4]; // Seconds east of UTC - unsigned char isdst; // Daylight savings time? + char offset[4]; // Seconds east of UTC + unsigned char isdst; // Daylight savings time? unsigned char abbr_idx; // Index into time zone abbreviations string }; @@ -136,30 +169,30 @@ * And this is the structure used in the time zone instances. */ typedef struct { - gss32 offset; - BOOL isdst; - unsigned char abbr_idx; - char pad[2]; - NSString *abbreviation; + gss32 offset; + BOOL isdst; + unsigned char abbr_idx; + char pad[2]; + NSString *abbreviation; } TypeInfo; -@interface GSTimeZone : NSTimeZone +@interface GSTimeZone : NSTimeZone { @public - NSString *timeZoneName; - NSData *timeZoneData; - unsigned int n_trans; - unsigned int n_types; - gss32 *trans; - TypeInfo *types; - unsigned char *idxs; + NSString *timeZoneName; + NSData *timeZoneData; + unsigned int n_trans; + unsigned int n_types; + gss32 *trans; + TypeInfo *types; + unsigned char *idxs; } @end -static NSTimeZone *defaultTimeZone = nil; -static NSTimeZone *localTimeZone = nil; -static NSTimeZone *systemTimeZone = nil; +static NSTimeZone *defaultTimeZone = nil; +static NSTimeZone *localTimeZone = nil; +static NSTimeZone *systemTimeZone = nil; /* Dictionary for time zones. Each time zone must have a unique name. */ @@ -171,8 +204,8 @@ /* Lock for creating time zones. */ static NSRecursiveLock *zone_mutex = nil; -static Class NSTimeZoneClass; -static Class GSPlaceholderTimeZoneClass; +static Class NSTimeZoneClass; +static Class GSPlaceholderTimeZoneClass; /* Decode the four bytes at PTR as a signed integer in network byte order. Based on code included in the GNU C Library 2.0.3. */ @@ -205,8 +238,8 @@ NSBundle *gbundle; gbundle = [NSBundle bundleForLibrary: @"gnustep-base"]; return [gbundle pathForResource: subpath - ofType: @"" - inDirectory: TIME_ZONE_DIR]; + ofType: @"" + inDirectory: TIME_ZONE_DIR]; } /* Object enumerator for NSInternalAbbrevDict. */ @@ -230,9 +263,9 @@ @interface GSAbsTimeZone : NSTimeZone { @public - NSString *name; - id detail; - int offset; // Offset from UTC in seconds. + NSString *name; + id detail; + int offset; // Offset from UTC in seconds. } - (id) initWithOffset: (int)anOffset; @@ -243,21 +276,21 @@ @interface GSTimeZoneDetail : NSTimeZoneDetail { - NSTimeZone *timeZone; // Time zone which created this object. - NSString *abbrev; // Abbreviation for time zone detail. - int offset; // Offset from UTC in seconds. - BOOL is_dst; // Is it daylight savings time? + NSTimeZone *timeZone; // Time zone which created this object. + NSString *abbrev; // Abbreviation for time zone detail. + int offset; // Offset from UTC in seconds. + BOOL is_dst; // Is it daylight savings time? } - (id) initWithTimeZone: (NSTimeZone*)aZone - withAbbrev: (NSString*)anAbbrev - withOffset: (int)anOffset - withDST: (BOOL)isDST; + withAbbrev: (NSString*)anAbbrev + withOffset: (int)anOffset + withDST: (BOOL)isDST; @end @interface GSAbsTimeZoneDetail : NSTimeZoneDetail { - GSAbsTimeZone *zone; // Time zone which created this object. + GSAbsTimeZone *zone; // Time zone which created this object. } - (id) initWithTimeZone: (GSAbsTimeZone*)aZone; @@ -324,7 +357,7 @@ - (NSEnumerator*) objectEnumerator { return AUTORELEASE([[NSInternalAbbrevDictObjectEnumerator alloc] - initWithDict: [NSTimeZone abbreviationMap]]); + initWithDict: [NSTimeZone abbreviationMap]]); } - (id) objectForKey: (NSString*)key @@ -340,18 +373,18 @@ - (id) autorelease { NSWarnLog(@"-autorelease sent to uninitialised time zone"); - return self; // placeholders never get released. + return self; // placeholders never get released. } - (void) dealloc { - return; // placeholders never get deallocated. + return; // placeholders never get deallocated. } - (id) initWithName: (NSString*)name data: (NSData*)data { - NSTimeZone *zone; - unsigned length = [name length]; + NSTimeZone *zone; + unsigned length = [name length]; if (length == 0) { @@ -386,68 +419,68 @@ if (zone == nil) { - unichar c; - unsigned i; + unichar c; + unsigned i; if (length == 8 && [name hasPrefix: @"GMT"] == YES - && ((c = [name characterAtIndex: 3]) == '+' || c == '-')) - { - c = [name characterAtIndex: 4]; - if (c >= '0' && c <= '9') - { - i = c - '0'; - c = [name characterAtIndex: 5]; - if (c >= '0' && c <= '9') - { - i = i * 10 + (c - '0'); - c = [name characterAtIndex: 6]; - if (c >= '0' && c <= '9') - { - i = i * 6 + (c - '0'); - c = [name characterAtIndex: 7]; - if (c >= '0' && c <= '9') - { - i = i * 10 + (c - '0'); - zone = [[GSAbsTimeZone alloc] initWithOffset: i*60]; - } - } - } - } - } + && ((c = [name characterAtIndex: 3]) == '+' || c == '-')) + { + c = [name characterAtIndex: 4]; + if (c >= '0' && c <= '9') + { + i = c - '0'; + c = [name characterAtIndex: 5]; + if (c >= '0' && c <= '9') + { + i = i * 10 + (c - '0'); + c = [name characterAtIndex: 6]; + if (c >= '0' && c <= '9') + { + i = i * 6 + (c - '0'); + c = [name characterAtIndex: 7]; + if (c >= '0' && c <= '9') + { + i = i * 10 + (c - '0'); + zone = [[GSAbsTimeZone alloc] initWithOffset: i*60]; + } + } + } + } + } if (zone == nil && length > 19 - && [name hasPrefix: @"NSAbsoluteTimeZone:"] == YES) - { - i = [[name substringFromIndex: 19] intValue]; + && [name hasPrefix: @"NSAbsoluteTimeZone:"] == YES) + { + i = [[name substringFromIndex: 19] intValue]; - zone = [[GSAbsTimeZone alloc] initWithOffset: i]; - } + zone = [[GSAbsTimeZone alloc] initWithOffset: i]; + } if (zone == nil) - { - if (data == nil) - { - NSString *fileName; - const char *str = [name UTF8String]; - - /* Make sure that only time zone files are accessed. - FIXME: Make this more robust. */ - if ((str)[0] == '/' || strchr(str, '.') != NULL) - { - NSLog(@"Disallowed time zone name `%@'.", name); - return nil; - } - - fileName = [NSTimeZoneClass getTimeZoneFile: name]; - if (fileName == nil) - { - NSLog(@"Unknown time zone name `%@'.", name); - return nil; - } - data = [NSData dataWithContentsOfFile: fileName]; - } - zone = [[GSTimeZone alloc] initWithName: name data: data]; - } + { + if (data == nil) + { + NSString *fileName; + const char *str = [name UTF8String]; + + /* Make sure that only time zone files are accessed. + FIXME: Make this more robust. */ + if ((str)[0] == '/' || strchr(str, '.') != NULL) + { + NSLog(@"Disallowed time zone name `%@'.", name); + return nil; + } + + fileName = [NSTimeZoneClass getTimeZoneFile: name]; + if (fileName == nil) + { + NSLog(@"Unknown time zone name `%@'.", name); + return nil; + } + data = [NSData dataWithContentsOfFile: fileName]; + } + zone = [[GSTimeZone alloc] initWithName: name data: data]; + } } RELEASE(self); return zone; @@ -455,18 +488,18 @@ - (void) release { - return; // placeholders never get released. + return; // placeholders never get released. } - (id) retain { - return self; // placeholders never get retained. + return self; // placeholders never get retained. } @end -@implementation NSLocalTimeZone +@implementation NSLocalTimeZone - (NSString*) abbreviation { @@ -551,15 +584,15 @@ @implementation GSAbsTimeZone -static int uninitialisedOffset = 100000; -static NSMapTable *absolutes = 0; +static int uninitialisedOffset = 100000; +static NSMapTable *absolutes = 0; + (void) initialize { if (self == [GSAbsTimeZone class]) { absolutes = NSCreateMapTable(NSIntMapKeyCallBacks, - NSNonOwnedPointerMapValueCallBacks, 0); + NSNonOwnedPointerMapValueCallBacks, 0); } } @@ -573,10 +606,10 @@ if (offset != uninitialisedOffset) { if (zone_mutex != nil) - [zone_mutex lock]; + [zone_mutex lock]; NSMapRemove(absolutes, (void*)(gsaddr)offset); if (zone_mutex != nil) - [zone_mutex unlock]; + [zone_mutex unlock]; } RELEASE(name); RELEASE(detail); @@ -590,9 +623,9 @@ - (id) initWithOffset: (int)anOffset { - GSAbsTimeZone *z; - int extra; - int sign = anOffset >= 0 ? 1 : -1; + GSAbsTimeZone *z; + int extra; + int sign = anOffset >= 0 ? 1 : -1; /* * Set the uninitialised offset so that dealloc before full @@ -634,25 +667,25 @@ else { if (anOffset % 60 == 0) - { - char s = (anOffset >= 0) ? '+' : '-'; - int i = (anOffset >= 0) ? anOffset / 60 : -anOffset / 60; - int h = i / 60; - int m = i % 60; - char buf[9]; - - sprintf(buf, "GMT%c%02d%02d", s, h, m); - name = [[NSString alloc] initWithCString: buf]; - } + { + char s = (anOffset >= 0) ? '+' : '-'; + int i = (anOffset >= 0) ? anOffset / 60 : -anOffset / 60; + int h = i / 60; + int m = i % 60; + char buf[9]; + + sprintf(buf, "GMT%c%02d%02d", s, h, m); + name = [[NSString alloc] initWithCString: buf]; + } else - { - /* - * Should never happen now we round to the minute - * for MacOS-X compatibnility. - */ - name = [[NSString alloc] initWithFormat: @"NSAbsoluteTimeZone:%d", - anOffset]; - } + { + /* + * Should never happen now we round to the minute + * for MacOS-X compatibnility. + */ + name = [[NSString alloc] initWithFormat: @"NSAbsoluteTimeZone:%d", + anOffset]; + } detail = [[GSAbsTimeZoneDetail alloc] initWithTimeZone: self]; offset = anOffset; z = self; @@ -715,12 +748,12 @@ } - (id) initWithTimeZone: (NSTimeZone*)aZone - withAbbrev: (NSString*)anAbbrev - withOffset: (int)anOffset - withDST: (BOOL)isDST + withAbbrev: (NSString*)anAbbrev + withOffset: (int)anOffset + withDST: (BOOL)isDST { timeZone = RETAIN(aZone); - abbrev = anAbbrev; // NB. Depend on this being retained in aZone + abbrev = anAbbrev; // NB. Depend on this being retained in aZone offset = anOffset; is_dst = isDST; return self; @@ -856,14 +889,23 @@ * wildly between OSes (this could be a big problem when * archiving is used between different systems). *
+ * On platforms where the zone info files are already installed + * elsewhere it is sufficient to use a symlink provided the GMT+/- + * zones are added. */ @implementation NSTimeZone +/* + * DEPRICATED. + */ + (NSDictionary*) abbreviationDictionary { return fake_abbrev_dict; } +/* + * Returns an abbreviation to time zone map which is quite large. + */ + (NSDictionary*) abbreviationMap { /* Instead of creating the abbreviation dictionary when the class is @@ -882,7 +924,7 @@ /* Read dictionary from file. */ abbreviationDictionary = [[NSMutableDictionary alloc] init]; fileName = [NSTimeZone getAbbreviationFile]; -#if defined(__WIN32__) +#if defined(__WIN32__) file = fopen([fileName fileSystemRepresentation], "rb"); #else file = fopen([fileName fileSystemRepresentation], "r"); @@ -899,10 +941,10 @@ the_abbrev = [NSString stringWithCString: abbrev]; a = [abbreviationDictionary objectForKey: the_abbrev]; if (a == nil) - { - a = [[NSMutableArray alloc] init]; - [abbreviationDictionary setObject: a forKey: the_abbrev]; - } + { + a = [[NSMutableArray alloc] init]; + [abbreviationDictionary setObject: a forKey: the_abbrev]; + } [a addObject: the_name]; } fclose(file); @@ -920,42 +962,42 @@ * is called. */ if (z == NSDefaultMallocZone() || z == 0) - { - /* - * As a special case, we can return a placeholder for a time zone - * in the default malloc zone extremely efficiently. - */ - return defaultPlaceholderTimeZone; - } + { + /* + * As a special case, we can return a placeholder for a time zone + * in the default malloc zone extremely efficiently. + */ + return defaultPlaceholderTimeZone; + } else - { - id obj; + { + id obj; - /* - * For anything other than the default zone, we need to - * locate the correct placeholder in the (lock protected) - * table of placeholders. - */ - if (zone_mutex != nil) - { - [zone_mutex lock]; - } - obj = (id)NSMapGet(placeholderMap, (void*)z); - if (obj == nil) - { - /* - * There is no placeholder object for this zone, so we - * create a new one and use that. - */ - obj = (id)NSAllocateObject(GSPlaceholderTimeZoneClass, 0, z); - NSMapInsert(placeholderMap, (void*)z, (void*)obj); - } - if (zone_mutex != nil) - { - [zone_mutex unlock]; - } - return obj; - } + /* + * For anything other than the default zone, we need to + * locate the correct placeholder in the (lock protected) + * table of placeholders. + */ + if (zone_mutex != nil) + { + [zone_mutex lock]; + } + obj = (id)NSMapGet(placeholderMap, (void*)z); + if (obj == nil) + { + /* + * There is no placeholder object for this zone, so we + * create a new one and use that. + */ + obj = (id)NSAllocateObject(GSPlaceholderTimeZoneClass, 0, z); + NSMapInsert(placeholderMap, (void*)z, (void*)obj); + } + if (zone_mutex != nil) + { + [zone_mutex unlock]; + } + return obj; + } } else { @@ -968,7 +1010,7 @@ */ + (NSTimeZone*) defaultTimeZone { - NSTimeZone *zone; + NSTimeZone *zone; if (zone_mutex != nil) { @@ -981,13 +1023,13 @@ else { if (zone_mutex != nil) - { - zone = AUTORELEASE(RETAIN(defaultTimeZone)); - } + { + zone = AUTORELEASE(RETAIN(defaultTimeZone)); + } else - { - zone = defaultTimeZone; - } + { + zone = defaultTimeZone; + } } if (zone_mutex != nil) { @@ -1008,25 +1050,25 @@ * Set up infrastructure for placeholder timezones. */ defaultPlaceholderTimeZone = (GSPlaceholderTimeZone*) - NSAllocateObject(GSPlaceholderTimeZoneClass, 0, NSDefaultMallocZone()); - placeholderMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, - NSNonRetainedObjectMapValueCallBacks, 0); + NSAllocateObject(GSPlaceholderTimeZoneClass, 0, NSDefaultMallocZone()); + placeholderMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, + NSNonRetainedObjectMapValueCallBacks, 0); localTimeZone = [[NSLocalTimeZone alloc] init]; fake_abbrev_dict = [[NSInternalAbbrevDict alloc] init]; if ([NSThread isMultiThreaded]) - { - [self _becomeThreaded: nil]; - } + { + [self _becomeThreaded: nil]; + } else - { - [[NSNotificationCenter defaultCenter] - addObserver: self - selector: @selector(_becomeThreaded:) - name: NSWillBecomeMultiThreadedNotification - object: nil]; - } + { + [[NSNotificationCenter defaultCenter] + addObserver: self + selector: @selector(_becomeThreaded:) + name: NSWillBecomeMultiThreadedNotification + object: nil]; + } } } @@ -1067,18 +1109,18 @@ * cause recursion ... */ if (aTimeZone == localTimeZone) - { - aTimeZone = [self systemTimeZone]; - } + { + aTimeZone = [self systemTimeZone]; + } if (zone_mutex != nil) - { - [zone_mutex lock]; - } + { + [zone_mutex lock]; + } ASSIGN(defaultTimeZone, aTimeZone); if (zone_mutex != nil) - { - [zone_mutex unlock]; - } + { + [zone_mutex unlock]; + } } } @@ -1087,7 +1129,7 @@ */ + (NSTimeZone*) systemTimeZone { - NSTimeZone *zone = nil; + NSTimeZone *zone = nil; if (zone_mutex != nil) { @@ -1095,63 +1137,66 @@ } if (systemTimeZone == nil) { - NSString *localZoneString = nil; + NSString *localZoneString = nil; /* * setup default value in case something goes wrong. */ systemTimeZone = RETAIN([NSTimeZoneClass timeZoneForSecondsFromGMT: 0]); + /* + * Try to get timezone from user defaults database + */ localZoneString = [[NSUserDefaults standardUserDefaults] - stringForKey: LOCALDBKEY]; + stringForKey: LOCALDBKEY]; + /* + * Try to get timezone from GNUSTEP_TZ environment variable. + */ if (localZoneString == nil) - { - /* - * Try to get timezone from GNUSTEP_TZ environment variable. - */ - localZoneString = [[[NSProcessInfo processInfo] - environment] objectForKey: @"GNUSTEP_TZ"]; - } + { + localZoneString = [[[NSProcessInfo processInfo] + environment] objectForKey: @"GNUSTEP_TZ"]; + } + /* + * Try to get timezone from LOCAL_TIME_FILE + */ if (localZoneString == nil) - { - /* - * Try to get timezone from LOCAL_TIME_FILE. - */ - NSString *f = _time_zone_path(LOCAL_TIME_FILE); - if (f != nil) - { - localZoneString = [NSString stringWithContentsOfFile: f]; - localZoneString = [localZoneString stringByTrimmingSpaces]; - } - } + { + NSString *f = _time_zone_path(LOCAL_TIME_FILE); + if (f != nil) + { + localZoneString = [NSString stringWithContentsOfFile: f]; + localZoneString = [localZoneString stringByTrimmingSpaces]; + } + } #if HAVE_TZSET + /* + * Try to get timezone from tzset and tzname + */ if (localZoneString == nil) - { - /* - * Try to get timezone from tzset and tzname - */ - tzset(); - if (tzname[0] != NULL && *tzname[0] != '\0') - localZoneString = [NSString stringWithCString: tzname[0]]; - } + { + tzset(); + if (tzname[0] != NULL && *tzname[0] != '\0') + localZoneString = [NSString stringWithCString: tzname[0]]; + } #else + /* + * Try to get timezone from standard unix environment variable. + */ if (localZoneString == nil) - { - /* - * Try to get timezone from standard unix environment variable. - */ - localZoneString = [[[NSProcessInfo processInfo] - environment] objectForKey: @"TZ"]; - } + { + localZoneString = [[[NSProcessInfo processInfo] + environment] objectForKey: @"TZ"]; + } #endif if (localZoneString != nil) - { - zone = [defaultPlaceholderTimeZone initWithName: localZoneString]; - } + { + zone = [defaultPlaceholderTimeZone initWithName: localZoneString]; + } else - { - NSLog(@"No local time zone specified."); - } + { + NSLog(@"No local time zone specified."); + } /* * If local time zone fails to allocate, then allocate something @@ -1177,10 +1222,14 @@ return zone; } +/** + * Returns an array of all the known regions.