Various fixes.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@14613 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
CaS 2002-10-01 10:25:40 +00:00
parent 804ad341cd
commit e06f28da0d
3 changed files with 385 additions and 128 deletions

View file

@ -57,6 +57,7 @@
+ (NSTimeZone*) timeZoneWithName: (NSString*)name data: (NSData*)data; + (NSTimeZone*) timeZoneWithName: (NSString*)name data: (NSData*)data;
- (NSString*) abbreviation; - (NSString*) abbreviation;
- (NSString*) abbreviationForDate: (NSDate*)when; - (NSString*) abbreviationForDate: (NSDate*)when;
- (NSData*) data;
- (id) initWithName: (NSString*)name; - (id) initWithName: (NSString*)name;
- (id) initWithName: (NSString*)name data: (NSData*)data; - (id) initWithName: (NSString*)name data: (NSData*)data;
- (BOOL) isDaylightSavingTime; - (BOOL) isDaylightSavingTime;

View file

@ -59,7 +59,9 @@
#define GREGORIAN_REFERENCE 730486 #define GREGORIAN_REFERENCE 730486
@class GSTimeZone; @class GSTimeZone;
@class NSConcreteAbsoluteTimeZone; @class GSAbsTimeZone;
static NSString *cformat = @"%Y-%m-%d %H:%M:%S %z";
static NSTimeZone *localTZ = nil; static NSTimeZone *localTZ = nil;
@ -85,7 +87,11 @@ static NSString* (*dstAbrIMP)(id, SEL, id);
static inline int static inline int
offset(NSTimeZone *tz, NSDate *d) offset(NSTimeZone *tz, NSDate *d)
{ {
if (tz == localTZ) if (tz == nil)
{
return 0;
}
if (tz == localTZ && offIMP != 0)
{ {
return (*offIMP)(tz, offSEL, d); return (*offIMP)(tz, offSEL, d);
} }
@ -93,11 +99,11 @@ offset(NSTimeZone *tz, NSDate *d)
{ {
Class c = GSObjCClass(tz); Class c = GSObjCClass(tz);
if (c == dstClass) if (c == dstClass && dstOffIMP != 0)
{ {
return (*dstOffIMP)(tz, offSEL, d); return (*dstOffIMP)(tz, offSEL, d);
} }
if (c == absClass) if (c == absClass && absOffIMP != 0)
{ {
return (*absOffIMP)(tz, offSEL, d); return (*absOffIMP)(tz, offSEL, d);
} }
@ -113,7 +119,11 @@ offset(NSTimeZone *tz, NSDate *d)
static inline NSString* static inline NSString*
abbrev(NSTimeZone *tz, NSDate *d) abbrev(NSTimeZone *tz, NSDate *d)
{ {
if (tz == localTZ) if (tz == nil)
{
return @"GMT";
}
if (tz == localTZ && abrIMP != 0)
{ {
return (*abrIMP)(tz, abrSEL, d); return (*abrIMP)(tz, abrSEL, d);
} }
@ -121,11 +131,11 @@ abbrev(NSTimeZone *tz, NSDate *d)
{ {
Class c = GSObjCClass(tz); Class c = GSObjCClass(tz);
if (c == dstClass) if (c == dstClass && dstAbrIMP != 0)
{ {
return (*dstAbrIMP)(tz, abrSEL, d); return (*dstAbrIMP)(tz, abrSEL, d);
} }
if (c == absClass) if (c == absClass && absAbrIMP != 0)
{ {
return (*absAbrIMP)(tz, abrSEL, d); return (*absAbrIMP)(tz, abrSEL, d);
} }
@ -274,7 +284,7 @@ GSBreakTime(NSTimeInterval when, int *year, int *month, int *day,
localTZ = RETAIN([NSTimeZone localTimeZone]); localTZ = RETAIN([NSTimeZone localTimeZone]);
dstClass = [GSTimeZone class]; dstClass = [GSTimeZone class];
absClass = [NSConcreteAbsoluteTimeZone class]; absClass = [GSAbsTimeZone class];
offSEL = @selector(secondsFromGMTForDate:); offSEL = @selector(secondsFromGMTForDate:);
offIMP = (int (*)(id,SEL,id)) offIMP = (int (*)(id,SEL,id))
@ -393,7 +403,7 @@ GSBreakTime(NSTimeInterval when, int *year, int *month, int *day,
{ {
// +++ What is the locale? // +++ What is the locale?
return [self initWithString: description return [self initWithString: description
calendarFormat: @"%Y-%m-%d %H:%M:%S %z" calendarFormat: cformat
locale: nil]; locale: nil];
} }
@ -1162,13 +1172,13 @@ static inline int getDigits(const char *from, char *to, int limit)
// Assign time zone detail // Assign time zone detail
if (aTimeZone == nil) if (aTimeZone == nil)
{ {
_time_zone = RETAIN(localTZ); _time_zone = localTZ; // retain is a no-op for the local timezone.
} }
else else
{ {
_time_zone = RETAIN(aTimeZone); _time_zone = RETAIN(aTimeZone);
} }
_calendar_format = @"%Y-%m-%d %H:%M:%S %z"; _calendar_format = cformat;
_seconds_since_ref = s; _seconds_since_ref = s;
/* /*
@ -1210,11 +1220,11 @@ static inline int getDigits(const char *from, char *to, int limit)
_seconds_since_ref = seconds; _seconds_since_ref = seconds;
if (_calendar_format == nil) if (_calendar_format == nil)
{ {
_calendar_format = @"%Y-%m-%d %H:%M:%S %z"; _calendar_format = cformat;
} }
if (_time_zone == nil) if (_time_zone == nil)
{ {
_time_zone = RETAIN(localTZ); _time_zone = localTZ; // retain is a no-op for the local timezone.
} }
return self; return self;
} }
@ -1387,8 +1397,8 @@ static inline int getDigits(const char *from, char *to, int limit)
} }
#define UNIX_REFERENCE_INTERVAL -978307200.0 #define UNIX_REFERENCE_INTERVAL -978307200.0
- (NSString *)descriptionWithCalendarFormat: (NSString *)format - (NSString*) descriptionWithCalendarFormat: (NSString*)format
locale: (NSDictionary *)locale locale: (NSDictionary*)locale
{ {
char buf[1024]; char buf[1024];
const char *f; const char *f;
@ -1666,10 +1676,16 @@ static inline int getDigits(const char *from, char *to, int limit)
{ {
newDate = (NSCalendarDate*)NSCopyObject(self, 0, zone); newDate = (NSCalendarDate*)NSCopyObject(self, 0, zone);
if (newDate) if (newDate != nil)
{ {
newDate->_calendar_format = [_calendar_format copyWithZone: zone]; if (_calendar_format != cformat)
newDate->_time_zone = RETAIN(_time_zone); {
newDate->_calendar_format = [_calendar_format copyWithZone: zone];
}
if (_time_zone != localTZ)
{
newDate->_time_zone = RETAIN(_time_zone);
}
} }
} }
return newDate; return newDate;
@ -1866,7 +1882,7 @@ static inline int getDigits(const char *from, char *to, int limit)
*/ */
s = GSTime(day, month, year, hour, minute, second, mil); s = GSTime(day, month, year, hour, minute, second, mil);
c = [NSCalendarDate alloc]; c = [NSCalendarDate alloc];
c->_calendar_format = @"%Y-%m-%d %H:%M:%S %z"; c->_calendar_format = cformat;
c->_time_zone = RETAIN([self timeZone]); c->_time_zone = RETAIN([self timeZone]);
c->_seconds_since_ref = s; c->_seconds_since_ref = s;

View file

@ -106,8 +106,9 @@
#define ZONES_DIR @"zones/" #define ZONES_DIR @"zones/"
@class NSConcreteAbsoluteTimeZone; @class GSAbsTimeZone;
@class NSConcreteTimeZoneDetail; @class GSTimeZoneDetail;
@class GSAbsTimeZoneDetail;
@class GSPlaceholderTimeZone; @class GSPlaceholderTimeZone;
@ -117,25 +118,38 @@
static GSPlaceholderTimeZone *defaultPlaceholderTimeZone; static GSPlaceholderTimeZone *defaultPlaceholderTimeZone;
static NSMapTable *placeholderMap; static NSMapTable *placeholderMap;
/* Temporary structure for holding time zone details. */ /*
* Temporary structure for holding time zone details.
* This is the format in the data object.
*/
struct ttinfo struct ttinfo
{ {
char offset[4]; // Seconds east of UTC char offset[4]; // Seconds east of UTC
BOOL isdst; // Daylight savings time? unsigned char isdst; // Daylight savings time?
char abbr_idx; // Index into time zone abbreviations string unsigned char abbr_idx; // Index into time zone abbreviations string
}; };
/*
* 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;
} TypeInfo;
@interface GSTimeZone : NSTimeZone @interface GSTimeZone : NSTimeZone
{ {
@public @public
NSString *timeZoneName; NSString *timeZoneName;
NSMutableData *md; NSData *timeZoneData;
unsigned int n_trans; unsigned int n_trans;
unsigned int n_types; unsigned int n_types;
gss32 *trans; gss32 *trans;
TypeInfo *types;
unsigned char *idxs; unsigned char *idxs;
NSString **abbrevs;
struct ttinfo *types;
} }
@end @end
@ -201,11 +215,12 @@ decode (const void *ptr)
@interface GSPlaceholderTimeZone : NSTimeZone @interface GSPlaceholderTimeZone : NSTimeZone
@end @end
@interface NSConcreteAbsoluteTimeZone : NSTimeZone @interface GSAbsTimeZone : NSTimeZone
{ {
NSString *name; @public
id detail; NSString *name;
int offset; // Offset from UTC in seconds. id detail;
int offset; // Offset from UTC in seconds.
} }
- (id) initWithOffset: (int)anOffset; - (id) initWithOffset: (int)anOffset;
@ -214,8 +229,7 @@ decode (const void *ptr)
@interface NSLocalTimeZone : NSTimeZone @interface NSLocalTimeZone : NSTimeZone
@end @end
@interface GSTimeZoneDetail : NSTimeZoneDetail
@interface NSConcreteTimeZoneDetail : NSTimeZoneDetail
{ {
NSTimeZone *timeZone; // Time zone which created this object. NSTimeZone *timeZone; // Time zone which created this object.
NSString *abbrev; // Abbreviation for time zone detail. NSString *abbrev; // Abbreviation for time zone detail.
@ -229,6 +243,14 @@ decode (const void *ptr)
withDST: (BOOL)isDST; withDST: (BOOL)isDST;
@end @end
@interface GSAbsTimeZoneDetail : NSTimeZoneDetail
{
GSAbsTimeZone *zone; // Time zone which created this object.
}
- (id) initWithTimeZone: (GSAbsTimeZone*)aZone;
@end
/* Private methods for obtaining resource file names. */ /* Private methods for obtaining resource file names. */
@interface NSTimeZone (Private) @interface NSTimeZone (Private)
+ (void) _becomeThreaded: (NSNotification*)notification; + (void) _becomeThreaded: (NSNotification*)notification;
@ -317,8 +339,14 @@ decode (const void *ptr)
- (id) initWithName: (NSString*)name data: (NSData*)data - (id) initWithName: (NSString*)name data: (NSData*)data
{ {
NSTimeZone *zone; NSTimeZone *zone;
unsigned length = [name length];
if ([name isEqual: @"NSLocalTimeZone"]) if (length == 0)
{
NSLog(@"Disallowed null time zone name");
return nil;
}
if (length == 15 && [name isEqual: @"NSLocalTimeZone"])
{ {
zone = RETAIN(localTimeZone); zone = RETAIN(localTimeZone);
DESTROY(self); DESTROY(self);
@ -327,50 +355,76 @@ decode (const void *ptr)
/* /*
* Return a cached time zone if possible. * Return a cached time zone if possible.
* NB. if data of cached zone does not match new data ... don't use cache
*/ */
if (zone_mutex != nil) if (zone_mutex != nil)
{ {
[zone_mutex lock]; [zone_mutex lock];
} }
zone = [zoneDictionary objectForKey: name]; zone = [zoneDictionary objectForKey: name];
if (data != [zone data] && [data isEqual: [zone data]] == NO)
{
zone = nil;
}
IF_NO_GC(RETAIN(zone)); IF_NO_GC(RETAIN(zone));
if (zone_mutex != nil) if (zone_mutex != nil)
{ {
[zone_mutex unlock]; [zone_mutex unlock];
} }
if (zone == nil) if (zone == nil)
{ {
if ([name hasPrefix: @"NSAbsoluteTimeZone:"] == YES) unichar c;
{ unsigned i;
int i = [[name substringFromIndex: 19] intValue];
zone = [[NSConcreteAbsoluteTimeZone alloc] initWithOffset: 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];
}
}
}
}
} }
else
if (zone == nil && length > 19
&& [name hasPrefix: @"NSAbsoluteTimeZone:"] == YES)
{
i = [[name substringFromIndex: 19] intValue];
zone = [[GSAbsTimeZone alloc] initWithOffset: i];
}
if (zone == nil)
{ {
if (data == nil) if (data == nil)
{ {
NSString *fileName; NSString *fileName;
unsigned length; const char *str = [name UTF8String];
length = [name length]; /* Make sure that only time zone files are accessed.
if (length == 0) FIXME: Make this more robust. */
if ((str)[0] == '/' || strchr(str, '.') != NULL)
{ {
NSLog(@"Disallowed null time zone name"); NSLog(@"Disallowed time zone name `%@'.", name);
return nil; return nil;
} }
else
{
const char *str = [name lossyCString];
/* 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]; fileName = [NSTimeZoneClass getTimeZoneFile: name];
if (fileName == nil) if (fileName == nil)
@ -417,6 +471,11 @@ decode (const void *ptr)
return self; return self;
} }
- (NSData*) data
{
return [[NSTimeZoneClass defaultTimeZone] data];
}
- (void) encodeWithCoder: (NSCoder*)aCoder - (void) encodeWithCoder: (NSCoder*)aCoder
{ {
[aCoder encodeObject: @"NSLocalTimeZone"]; [aCoder encodeObject: @"NSLocalTimeZone"];
@ -478,13 +537,13 @@ decode (const void *ptr)
@end @end
@implementation NSConcreteAbsoluteTimeZone @implementation GSAbsTimeZone
static NSMapTable *absolutes = 0; static NSMapTable *absolutes = 0;
+ (void) initialize + (void) initialize
{ {
if (self == [NSConcreteAbsoluteTimeZone class]) if (self == [GSAbsTimeZone class])
{ {
absolutes = NSCreateMapTable(NSIntMapKeyCallBacks, absolutes = NSCreateMapTable(NSIntMapKeyCallBacks,
NSNonOwnedPointerMapValueCallBacks, 0); NSNonOwnedPointerMapValueCallBacks, 0);
@ -515,13 +574,36 @@ static NSMapTable *absolutes = 0;
- (id) initWithOffset: (int)anOffset - (id) initWithOffset: (int)anOffset
{ {
NSConcreteAbsoluteTimeZone *z; GSAbsTimeZone *z;
int extra;
int sign = anOffset >= 0 ? 1 : -1;
/*
* Round the offset to the nearest minute, (for MacOS-X compatibility)
* and ensure it is no more than 18 hours.
*/
anOffset *= sign;
extra = anOffset % 60;
if (extra < 30)
{
anOffset -= extra;
}
else
{
anOffset += 60 - extra;
}
if (anOffset > 64800)
{
RELEASE(self);
return nil;
}
anOffset *= sign;
if (zone_mutex != nil) if (zone_mutex != nil)
{ {
[zone_mutex lock]; [zone_mutex lock];
} }
z = (NSConcreteAbsoluteTimeZone*)NSMapGet(absolutes, (void*)(gsaddr)anOffset); z = (GSAbsTimeZone*)NSMapGet(absolutes, (void*)(gsaddr)anOffset);
if (z != nil) if (z != nil)
{ {
IF_NO_GC(RETAIN(z)); IF_NO_GC(RETAIN(z));
@ -529,11 +611,25 @@ static NSMapTable *absolutes = 0;
} }
else else
{ {
name = [[NSString alloc] initWithFormat: @"NSAbsoluteTimeZone:%d", if (anOffset % 60 == 0)
anOffset]; {
detail = [[NSConcreteTimeZoneDetail alloc] char s = (anOffset >= 0) ? '+' : '-';
initWithTimeZone: self withAbbrev: name int i = (anOffset >= 0) ? anOffset / 60 : -anOffset / 60;
withOffset: anOffset withDST: NO]; int h = i / 60;
int m = i % 60;
name = [[NSString alloc] initWithFormat: @"GMT%c%02d%02d", s, h, m];
}
else
{
/*
* 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; offset = anOffset;
z = self; z = self;
NSMapInsert(absolutes, (void*)(gsaddr)anOffset, (void*)z); NSMapInsert(absolutes, (void*)(gsaddr)anOffset, (void*)z);
@ -571,10 +667,14 @@ static NSMapTable *absolutes = 0;
return detail; return detail;
} }
- (NSString*) timeZoneName
{
return name;
}
@end @end
@implementation NSConcreteTimeZoneDetail @implementation GSTimeZoneDetail
- (void) dealloc - (void) dealloc
{ {
@ -639,6 +739,76 @@ static NSMapTable *absolutes = 0;
@end @end
@implementation GSAbsTimeZoneDetail
- (NSString*) abbreviation
{
return zone->name;
}
- (NSString*) abbreviationForDate: (NSDate*)aDate
{
return zone->name;
}
- (void) dealloc
{
RELEASE(zone);
[super dealloc];
}
- (id) initWithTimeZone: (GSAbsTimeZone*)aZone
{
zone = RETAIN(aZone);
return self;
}
- (BOOL) isDaylightSavingTimeZone
{
return NO;
}
- (BOOL) isDaylightSavingTimeZoneForDate: (NSDate*)aDate
{
return NO;
}
- (NSString*) name
{
return zone->name;
}
- (NSString*) timeZoneAbbreviation
{
return zone->name;
}
- (NSArray*) timeZoneDetailArray
{
return [zone timeZoneDetailArray];
}
- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)date
{
return self;
}
- (int) timeZoneSecondsFromGMT
{
return zone->offset;
}
- (int) timeZoneSecondsFromGMTForDate: (NSDate*)aDate
{
return zone->offset;
}
@end
/** /**
* <p> * <p>
* If the GNUstep time zone datafiles become too out of date, one * If the GNUstep time zone datafiles become too out of date, one
@ -646,17 +816,17 @@ static NSMapTable *absolutes = 0;
* url="ftp://elsie.nci.nih.gov/pub/">ftp://elsie.nci.nih.gov/pub/</uref> * url="ftp://elsie.nci.nih.gov/pub/">ftp://elsie.nci.nih.gov/pub/</uref>
* and compile it as specified in the README file in the * and compile it as specified in the README file in the
* NSTimeZones directory. * NSTimeZones directory.
* * </p>
* Time zone names in NSDates should be GMT, MET etc. not * <p>Time zone names in NSDates should be GMT, MET etc. not
* Europe/Berlin, America/Washington etc. * Europe/Berlin, America/Washington etc.
* * </p>
* The problem with this is that various time zones may use the * The problem with this is that various time zones may use the
* same abbreviation (e.g. Australia/Brisbane and * same abbreviation (e.g. Australia/Brisbane and
* America/New_York both use EST), and some time zones * America/New_York both use EST), and some time zones
* may have different rules for daylight saving time even if the * may have different rules for daylight saving time even if the
* abbreviation and offsets from UTC are the same. * abbreviation and offsets from UTC are the same.
* * </p>
* The problems with depending on the OS for providing time zone * <p>The problems with depending on the OS for providing time zone
* info are that some methods for the NSTimeZone classes might be * info are that some methods for the NSTimeZone classes might be
* difficult to implement, and also that time zone names may vary * difficult to implement, and also that time zone names may vary
* wildly between OSes (this could be a big problem when * wildly between OSes (this could be a big problem when
@ -1010,14 +1180,19 @@ static NSMapTable *absolutes = 0;
return regionsArray; return regionsArray;
} }
/**
* Return a timezone for the specified offset from GMT.<br />
* The timezone returned does <em>not</em> use daylight savings time.
* The actual timezone returned has an offset rounded to the nearest
* minute.<br />
* Time zones with an offset of more than +/- 18 hours are disallowed,
* and nil is returned.
*/
+ (NSTimeZone*) timeZoneForSecondsFromGMT: (int)seconds + (NSTimeZone*) timeZoneForSecondsFromGMT: (int)seconds
{ {
NSTimeZone *zone; NSTimeZone *zone;
/* We simply return the following because an existing time zone with zone = [[GSAbsTimeZone alloc] initWithOffset: seconds];
the given offset might not always have the same offset (daylight
savings time, change in standard time, etc.). */
zone = [[NSConcreteAbsoluteTimeZone alloc] initWithOffset: seconds];
return AUTORELEASE(zone); return AUTORELEASE(zone);
} }
@ -1089,6 +1264,14 @@ static NSMapTable *absolutes = 0;
return RETAIN(self); return RETAIN(self);
} }
/**
* Returns the data with which the receiver was initialised.
*/
- (NSData*) data
{
return nil;
}
- (NSString*) description - (NSString*) description
{ {
return [self name]; return [self name];
@ -1113,11 +1296,21 @@ static NSMapTable *absolutes = 0;
return self; return self;
} }
/**
* Initialise a timezone with the supplied name. May return a cached
* timezone object rather than the newly created one.
*/
- (id) initWithName: (NSString*)name - (id) initWithName: (NSString*)name
{ {
return [self initWithName: name data: nil]; return [self initWithName: name data: nil];
} }
/**
* Initialises a time zone object using the supplied data object.<br />
* This method is intended for internal use by the NSTimeZone
* class cluster.
* Don't use it ... use -initWithName: instead.
*/
- (id) initWithName: (NSString*)name data: (NSData*)data - (id) initWithName: (NSString*)name data: (NSData*)data
{ {
[self notImplemented: _cmd]; [self notImplemented: _cmd];
@ -1161,7 +1354,10 @@ static NSMapTable *absolutes = 0;
{ {
if (aTimeZone == self) if (aTimeZone == self)
return YES; return YES;
if ([[self name] isEqual: [aTimeZone name]] == YES) if ([[self name] isEqual: [aTimeZone name]] == NO)
return NO;
if (([self data] == nil && [aTimeZone data] == nil)
|| [[self name] isEqual: [aTimeZone name]] == YES)
return YES; return YES;
return NO; return NO;
} }
@ -1174,7 +1370,9 @@ static NSMapTable *absolutes = 0;
- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder - (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder
{ {
if ([aCoder isByref] == NO) if ([aCoder isByref] == NO)
return self; {
return self;
}
return [super replacementObjectForPortCoder: aCoder]; return [super replacementObjectForPortCoder: aCoder];
} }
@ -1222,7 +1420,11 @@ static NSMapTable *absolutes = 0;
@end @end
/**
* This class serves no useful purpose in GNUstep, and is provided
* solely for backward compatibility with the OpenStep spec. It is
* missing entirely from MacOS-X.
*/
@implementation NSTimeZoneDetail @implementation NSTimeZoneDetail
- (NSString*) description - (NSString*) description
@ -1306,7 +1508,7 @@ static NSMapTable *absolutes = 0;
* We locate the index of the highest transition before the date, or zero * We locate the index of the highest transition before the date, or zero
* if there is no transition before it. * if there is no transition before it.
*/ */
static struct ttinfo* static TypeInfo*
chop(NSTimeInterval since, GSTimeZone *zone) chop(NSTimeInterval since, GSTimeZone *zone)
{ {
gss32 when = (gss32)since; gss32 when = (gss32)since;
@ -1320,7 +1522,7 @@ chop(NSTimeInterval since, GSTimeZone *zone)
unsigned n_types = zone->n_types; unsigned n_types = zone->n_types;
/* /*
* If and the first transition is greater than our date, * If the first transition is greater than our date,
* we locate the first non-DST transition and use that offset, * we locate the first non-DST transition and use that offset,
* or just use the first transition. * or just use the first transition.
*/ */
@ -1364,39 +1566,44 @@ chop(NSTimeInterval since, GSTimeZone *zone)
} }
static NSTimeZoneDetail* static NSTimeZoneDetail*
newDetailInZoneForType(GSTimeZone *zone, struct ttinfo *type) newDetailInZoneForType(GSTimeZone *zone, TypeInfo *type)
{ {
NSConcreteTimeZoneDetail *detail; GSTimeZoneDetail *detail;
detail = [NSConcreteTimeZoneDetail alloc]; detail = [GSTimeZoneDetail alloc];
detail = [detail initWithTimeZone: zone detail = [detail initWithTimeZone: zone
withAbbrev: zone->abbrevs[(int)type->abbr_idx] withAbbrev: type->abbreviation
withOffset: decode(type->offset) withOffset: type->offset
withDST: type->isdst > 0 ? YES : NO]; withDST: type->isdst];
return detail; return detail;
} }
- (NSString*) abbreviationForDate: (NSDate*)aDate - (NSString*) abbreviationForDate: (NSDate*)aDate
{ {
struct ttinfo *type = chop([aDate timeIntervalSince1970], self); TypeInfo *type = chop([aDate timeIntervalSince1970], self);
return abbrevs[(int)type->abbr_idx]; return type->abbreviation;
}
- (NSData*) data
{
return timeZoneData;
} }
- (void) dealloc - (void) dealloc
{ {
RELEASE(timeZoneName); RELEASE(timeZoneName);
if (abbrevs != 0) RELEASE(timeZoneData);
if (types != 0)
{ {
unsigned i; unsigned i;
for (i = 0; i < n_types; i++) for (i = 0; i < n_types; i++)
{ {
TEST_RELEASE(abbrevs[i]); RELEASE(types[i].abbreviation);
} }
NSZoneFree(NSDefaultMallocZone(), abbrevs); NSZoneFree(NSDefaultMallocZone(), types);
} }
RELEASE(md);
[super dealloc]; [super dealloc];
} }
@ -1405,11 +1612,12 @@ newDetailInZoneForType(GSTimeZone *zone, struct ttinfo *type)
static NSString *fileException = @"GSTimeZoneFileException"; static NSString *fileException = @"GSTimeZoneFileException";
timeZoneName = [name copy]; timeZoneName = [name copy];
md = [data mutableCopy]; timeZoneData = [data copy];
NS_DURING NS_DURING
{ {
void *bytes = [md mutableBytes]; const void *bytes = [timeZoneData bytes];
unsigned length = [md length]; unsigned length = [timeZoneData length];
void *buf;
unsigned pos = 0; unsigned pos = 0;
unsigned i, charcnt; unsigned i, charcnt;
unsigned char *abbr; unsigned char *abbr;
@ -1428,55 +1636,87 @@ newDetailInZoneForType(GSTimeZone *zone, struct ttinfo *type)
format: @"TZ_MAGIC is incorrect"]; format: @"TZ_MAGIC is incorrect"];
} }
n_trans = GSSwapBigI32ToHost(*(gss32*)header->tzh_timecnt); n_trans = GSSwapBigI32ToHost(*(gss32*)header->tzh_timecnt);
if (pos + 5*n_trans > length) n_types = GSSwapBigI32ToHost(*(gss32*)header->tzh_typecnt);
charcnt = GSSwapBigI32ToHost(*(gss32*)header->tzh_charcnt);
i = pos;
i += sizeof(gss32)*n_trans;
if (i > length)
{ {
[NSException raise: fileException [NSException raise: fileException
format: @"Transitions list is truncated"]; format: @"Transitions list is truncated"];
} }
i += n_trans;
/* Read in transitions. */ if (i > length)
trans = (gss32*)(bytes + pos);
for (i = 0; i < n_trans; i++)
{ {
trans[i] = GSSwapBigI32ToHost(*(gss32*)(bytes + pos)); [NSException raise: fileException
pos += 4; format: @"Transition indexes are truncated"];
} }
idxs = (unsigned char*)(bytes + pos); i += sizeof(struct ttinfo)*n_types;
pos += n_trans; if (i > length)
n_types = GSSwapBigI32ToHost(*(gss32*)header->tzh_typecnt);
if (pos + n_types*sizeof(struct ttinfo) > length)
{ {
[NSException raise: fileException [NSException raise: fileException
format: @"Types list is truncated"]; format: @"Types list is truncated"];
} }
types = (struct ttinfo*)(bytes + pos); if (i + charcnt > length)
pos += n_types*sizeof(struct ttinfo);
charcnt = decode(header->tzh_charcnt);
charcnt = GSSwapBigI32ToHost(*(gss32*)header->tzh_charcnt);
if (pos + charcnt > length)
{ {
[NSException raise: fileException [NSException raise: fileException
format: @"Abbreviations list is truncated"]; format: @"Abbreviations list is truncated"];
} }
abbr = (char*)(bytes + pos);
/* /*
* Create an NSString object to hold each abbreviation. * Now calculate size we need to store the information
* for efficient access ... not the same saze as the data
* we received.
*/ */
abbrevs = NSZoneMalloc(NSDefaultMallocZone(), sizeof(id)*charcnt); i = n_trans * (sizeof(gss32)+1) + n_types * sizeof(TypeInfo);
memset(abbrevs, '\0', sizeof(id)*charcnt); buf = NSZoneMalloc(NSDefaultMallocZone(), i);
types = (TypeInfo*)buf;
buf += (n_types * sizeof(TypeInfo));
trans = (gss32*)buf;
buf += (n_trans * sizeof(gss32));
idxs = (unsigned char*)buf;
/* Read in transitions. */
for (i = 0; i < n_trans; i++)
{
trans[i] = GSSwapBigI32ToHost(*(gss32*)(bytes + pos));
pos += sizeof(gss32);
}
for (i = 0; i < n_trans; i++)
{
idxs[i] = *(unsigned char*)(bytes + pos);
pos++;
}
for (i = 0; i < n_types; i++) for (i = 0; i < n_types; i++)
{ {
struct ttinfo *inf = types + i; struct ttinfo *ptr = (struct ttinfo*)(bytes + pos);
int loc = inf->abbr_idx;
if (abbrevs[loc] == nil) types[i].isdst = (ptr->isdst != 0 ? YES : NO);
{ types[i].abbr_idx = ptr->abbr_idx;
abbrevs[loc] = [[NSString alloc] initWithCString: abbr + loc]; types[i].offset = decode(ptr->offset);
} pos += sizeof(struct ttinfo);
} }
abbr = (char*)(bytes + pos);
{
id abbrevs[charcnt];
memset(abbrevs, '\0', sizeof(id)*charcnt);
for (i = 0; i < n_types; i++)
{
int loc = types[i].abbr_idx;
if (abbrevs[loc] == nil)
{
abbrevs[loc] = [[NSString alloc] initWithCString: abbr + loc];
}
else
{
RETAIN(abbrevs[loc]);
}
types[i].abbreviation = abbrevs[loc];
}
}
if (zone_mutex != nil) if (zone_mutex != nil)
{ {
@ -1503,9 +1743,9 @@ newDetailInZoneForType(GSTimeZone *zone, struct ttinfo *type)
- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate - (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate
{ {
struct ttinfo *type = chop([aDate timeIntervalSince1970], self); TypeInfo *type = chop([aDate timeIntervalSince1970], self);
return type->isdst > 0 ? YES : NO; return type->isdst;
} }
- (NSString*) name - (NSString*) name
@ -1515,9 +1755,9 @@ newDetailInZoneForType(GSTimeZone *zone, struct ttinfo *type)
- (int) secondsFromGMTForDate: (NSDate*)aDate - (int) secondsFromGMTForDate: (NSDate*)aDate
{ {
struct ttinfo *type = chop([aDate timeIntervalSince1970], self); TypeInfo *type = chop([aDate timeIntervalSince1970], self);
return decode(type->offset); return type->offset;
} }
- (NSArray*) timeZoneDetailArray - (NSArray*) timeZoneDetailArray
@ -1540,7 +1780,7 @@ newDetailInZoneForType(GSTimeZone *zone, struct ttinfo *type)
- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)aDate - (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)aDate
{ {
struct ttinfo *type; TypeInfo *type;
NSTimeZoneDetail *detail; NSTimeZoneDetail *detail;
type = chop([aDate timeIntervalSince1970], self); type = chop([aDate timeIntervalSince1970], self);