46
49
FIXME?: use leap seconds? */
49
#include <base/preface.h>
52
#include "GNUstepBase/preface.h"
53
#include "GNUstepBase/GSLock.h"
50
54
#include <limits.h>
52
56
#include <stdlib.h>
53
57
#include <string.h>
54
#include <Foundation/NSArray.h>
55
#include <Foundation/NSCoder.h>
56
#include <Foundation/NSData.h>
57
#include <Foundation/NSDate.h>
58
#include <Foundation/NSDictionary.h>
59
#include <Foundation/NSException.h>
60
#include <Foundation/NSLock.h>
61
#include <Foundation/NSObject.h>
62
#include <Foundation/NSProcessInfo.h>
63
#include <Foundation/NSString.h>
64
#include <Foundation/NSUserDefaults.h>
65
#include <Foundation/NSUtilities.h>
66
#include <Foundation/NSZone.h>
67
#include <Foundation/NSBundle.h>
68
#include <Foundation/NSMapTable.h>
69
#include <Foundation/NSThread.h>
70
#include <Foundation/NSNotification.h>
71
#include <Foundation/NSPortCoder.h>
72
#include <Foundation/NSTimeZone.h>
73
#include <Foundation/NSDebug.h>
59
#include "Foundation/NSArray.h"
60
#include "Foundation/NSCoder.h"
61
#include "Foundation/NSData.h"
62
#include "Foundation/NSDate.h"
63
#include "Foundation/NSDictionary.h"
64
#include "Foundation/NSException.h"
65
#include "Foundation/NSLock.h"
66
#include "Foundation/NSObject.h"
67
#include "Foundation/NSProcessInfo.h"
68
#include "Foundation/NSString.h"
69
#include "Foundation/NSUserDefaults.h"
70
#include "Foundation/NSUtilities.h"
71
#include "Foundation/NSZone.h"
72
#include "Foundation/NSBundle.h"
73
#include "Foundation/NSMapTable.h"
74
#include "Foundation/NSThread.h"
75
#include "Foundation/NSNotification.h"
76
#include "Foundation/NSPortCoder.h"
77
#include "Foundation/NSTimeZone.h"
78
#include "Foundation/NSByteOrder.h"
79
#include "Foundation/NSDebug.h"
80
#include "GNUstepBase/GSCategories.h"
76
85
#include "tzfile.h"
345
343
return self; // placeholders never get released.
348
- (id) objectAtIndex: (unsigned)index
350
[NSException raise: NSInternalInconsistencyException
351
format: @"attempt to use uninitialised time zone"];
357
348
return; // placeholders never get deallocated.
360
- (id) initWithName: (NSString*)name
351
- (id) initWithName: (NSString*)name data: (NSData*)data
362
353
NSTimeZone *zone;
354
unsigned length = [name length];
365
* Quick return if it's the local timezone.
367
if ([name isEqual: @"NSLocalTimeZone"])
369
return [self initWithName: name data: nil];
358
NSLog(@"Disallowed null time zone name");
361
if (length == 15 && [name isEqual: @"NSLocalTimeZone"])
363
zone = RETAIN(localTimeZone);
373
369
* Return a cached time zone if possible.
370
* NB. if data of cached zone does not match new data ... don't use cache
375
372
if (zone_mutex != nil)
377
374
[zone_mutex lock];
379
376
zone = [zoneDictionary objectForKey: name];
377
if (data != nil && [data isEqual: [zone data]] == NO)
380
381
IF_NO_GC(RETAIN(zone));
381
382
if (zone_mutex != nil)
383
384
[zone_mutex unlock];
391
* Now return an absolute time zone or load one from file.
393
if ([name hasPrefix: @"NSAbsoluteTimeZone:"] == YES)
395
int i = [[name substringFromIndex: 19] intValue];
397
zone = [[NSConcreteAbsoluteTimeZone alloc] initWithOffset: i];
405
length = [name length];
408
NSLog(@"Disallowed null time zone name");
413
const char *str = [name lossyCString];
415
/* Make sure that only time zone files are accessed.
416
FIXME: Make this more robust. */
417
if ((str)[0] == '/' || strchr(str, '.') != NULL)
419
NSLog(@"Disallowed time zone name `%@'.", name);
424
fileName = [NSTimeZoneClass getTimeZoneFile: name];
427
NSLog(@"Unknown time zone name `%@'.", name);
430
data = [NSData dataWithContentsOfFile: fileName];
431
zone = [self initWithName: name data: data];
436
- (id) initWithName: (NSString*)name data: (NSData*)data
440
if ([name isEqual: @"NSLocalTimeZone"])
442
zone = RETAIN(localTimeZone);
444
else if ([name hasPrefix: @"NSAbsoluteTimeZone:"] == YES)
446
int i = [[name substringFromIndex: 19] intValue];
448
zone = [[NSConcreteAbsoluteTimeZone alloc] initWithOffset: i];
452
static NSString *fileException = @"fileException";
453
static NSString *errMess = @"File read error in NSTimeZone.";
458
const void *bytes = [data bytes];
459
unsigned length = [data length];
463
int i, n_trans, n_types, names_size;
468
struct tzhead *header;
469
struct ttinfo *types; // Temporary array for details
471
if (length < sizeof(struct tzhead))
473
[NSException raise: fileException format: errMess];
475
header = (struct tzhead *)(bytes + pos);
476
pos += sizeof(struct tzhead);
478
n_trans = decode(header->tzh_timecnt);
479
n_types = decode(header->tzh_typecnt);
480
names_size = decode(header->tzh_charcnt);
482
/* Read in transitions. */
483
trans = (char*)(bytes + pos);
485
type_idxs = (char*)(bytes + pos);
489
[NSException raise: fileException format: errMess];
491
transArray = [NSMutableArray arrayWithCapacity: n_trans];
492
for (i = 0; i < n_trans; i++)
495
addObject: [[NSInternalTimeTransition alloc]
496
initWithTime: decode(trans+(i*4))
497
withIndex: type_idxs[i]]];
500
/* Read in time zone details. */
501
types = (struct ttinfo*)(bytes + pos);
502
pos += n_types*sizeof(struct ttinfo);
505
[NSException raise: fileException format: errMess];
508
/* Read in time zone abbreviation strings. */
509
zone_abbrevs = (char*)(bytes + pos);
513
[NSException raise: fileException format: errMess];
515
abbrevsArray = NSZoneMalloc(NSDefaultMallocZone(),
516
sizeof(id)*names_size);
517
memset(abbrevsArray, '\0', sizeof(id)*names_size);
518
for (i = 0; i < n_types; i++)
520
struct ttinfo *inf = types + i;
521
int loc = inf->abbr_idx;
523
if (abbrevsArray[loc] == nil)
526
= [NSString stringWithCString: zone_abbrevs+loc];
530
zone = [NSConcreteTimeZone alloc];
532
/* Create time zone details. */
533
detailsArray = [NSMutableArray arrayWithCapacity: n_types];
534
for (i = 0; i < n_types; i++)
536
NSConcreteTimeZoneDetail *detail;
537
struct ttinfo *inf = types + i;
538
int off = decode(inf->offset);
539
BOOL dst = (inf->isdst > 0) ? YES : NO;
540
int idx = inf->abbr_idx;
541
id abr = abbrevsArray[idx];
543
detail = [[NSConcreteTimeZoneDetail alloc]
544
initWithTimeZone: zone
548
[detailsArray addObject: detail];
551
NSZoneFree(NSDefaultMallocZone(), abbrevsArray);
553
zone = [(id)zone initWithName: name
554
withTransitions: transArray
555
withDetails: detailsArray];
556
if (zone_mutex != nil)
560
[zoneDictionary setObject: zone forKey: (NSString*)[zone name]];
561
if (zone_mutex != nil)
569
if ([localException name] != fileException)
570
[localException raise];
571
NSLog(@"Unable to obtain time zone `%@'.", name);
392
if (length == 8 && [name hasPrefix: @"GMT"] == YES
393
&& ((c = [name characterAtIndex: 3]) == '+' || c == '-'))
395
c = [name characterAtIndex: 4];
396
if (c >= '0' && c <= '9')
399
c = [name characterAtIndex: 5];
400
if (c >= '0' && c <= '9')
402
i = i * 10 + (c - '0');
403
c = [name characterAtIndex: 6];
404
if (c >= '0' && c <= '9')
406
i = i * 6 + (c - '0');
407
c = [name characterAtIndex: 7];
408
if (c >= '0' && c <= '9')
410
i = i * 10 + (c - '0');
411
zone = [[GSAbsTimeZone alloc] initWithOffset: i*60];
418
if (zone == nil && length > 19
419
&& [name hasPrefix: @"NSAbsoluteTimeZone:"] == YES)
421
i = [[name substringFromIndex: 19] intValue];
423
zone = [[GSAbsTimeZone alloc] initWithOffset: i];
431
const char *str = [name UTF8String];
433
/* Make sure that only time zone files are accessed.
434
FIXME: Make this more robust. */
435
if ((str)[0] == '/' || strchr(str, '.') != NULL)
437
NSLog(@"Disallowed time zone name `%@'.", name);
441
fileName = [NSTimeZoneClass getTimeZoneFile: name];
444
NSLog(@"Unknown time zone name `%@'.", name);
447
data = [NSData dataWithContentsOfFile: fileName];
449
zone = [[GSTimeZone alloc] initWithName: name data: data];
592
@implementation NSConcreteTimeZone
597
RELEASE(transitions);
602
- (void) encodeWithCoder: (NSCoder*)aCoder
604
[aCoder encodeObject: name];
607
- (id) initWithName: (NSString*)aName
608
withTransitions: (NSArray*)trans
609
withDetails: (NSArray*)zoneDetails
614
name = [aName copyWithZone: [self zone]];
615
transitions = RETAIN(trans);
616
details = RETAIN(zoneDetails);
626
- (NSArray*) timeZoneDetailArray
631
- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)date
637
the_time = (int)[date timeIntervalSince1970];
638
count = [transitions count];
640
|| the_time < [[transitions objectAtIndex: 0] transTime])
641
/* Either DATE is before any transitions or there is no transition.
642
Return the first non-DST type, or the first one if they are all DST. */
644
unsigned detail_count;
646
detail_count = [details count];
648
while (index < detail_count
649
&& [[details objectAtIndex: index] isDaylightSavingTimeZone])
651
if (index == detail_count)
655
/* Find the first transition after DATE, and then pick the type of
656
the transition before it. */
658
for (index = 1; index < count; index++)
659
if (the_time < [[transitions objectAtIndex: index] transTime])
661
index = [[transitions objectAtIndex: index-1] detailIndex];
663
return [details objectAtIndex: index];
668
469
@implementation NSLocalTimeZone
471
- (NSString*) abbreviation
473
return [[NSTimeZoneClass defaultTimeZone] abbreviation];
476
- (NSString*) abbreviationForDate: (NSDate*)aDate
478
return [[NSTimeZoneClass defaultTimeZone] abbreviationForDate: aDate];
670
481
- (id) autorelease
488
return [[NSTimeZoneClass defaultTimeZone] data];
675
491
- (void) encodeWithCoder: (NSCoder*)aCoder
677
493
[aCoder encodeObject: @"NSLocalTimeZone"];
501
- (BOOL) isDaylightSavingTime
503
return [[NSTimeZoneClass defaultTimeZone] isDaylightSavingTime];
506
- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate
508
return [[NSTimeZoneClass defaultTimeZone] isDaylightSavingTimeForDate: aDate];
680
511
- (NSString*) name
682
513
return [[NSTimeZoneClass defaultTimeZone] name];
736
591
- (id) initWithOffset: (int)anOffset
738
NSConcreteAbsoluteTimeZone *z;
595
int sign = anOffset >= 0 ? 1 : -1;
598
* Set the uninitialised offset so that dealloc before full
599
* initialisation won't remove the timezeone for offset 0 from cache.
601
offset = uninitialisedOffset;
604
* Round the offset to the nearest minute, (for MacOS-X compatibility)
605
* and ensure it is no more than 18 hours.
608
extra = anOffset % 60;
615
anOffset += 60 - extra;
617
if (anOffset > 64800)
740
624
if (zone_mutex != nil)
742
z = (NSConcreteAbsoluteTimeZone*)NSMapGet(absolutes, (void*)(gsaddr)anOffset);
628
z = (GSAbsTimeZone*)NSMapGet(absolutes, (void*)(gsaddr)anOffset);
745
631
IF_NO_GC(RETAIN(z));
636
if (anOffset % 60 == 0)
638
char s = (anOffset >= 0) ? '+' : '-';
639
int i = (anOffset >= 0) ? anOffset / 60 : -anOffset / 60;
644
sprintf(buf, "GMT%c%02d%02d", s, h, m);
645
name = [[NSString alloc] initWithCString: buf];
650
* Should never happen now we round to the minute
651
* for MacOS-X compatibnility.
753
653
name = [[NSString alloc] initWithFormat: @"NSAbsoluteTimeZone:%d",
755
detail = [[NSConcreteTimeZoneDetail alloc]
756
initWithTimeZone: self withAbbrev: name
757
withOffset: anOffset withDST: NO];
760
NSMapInsert(absolutes, (void*)(gsaddr)anOffset, (void*)z);
761
[zoneDictionary setObject: self forKey: (NSString*)name];
656
detail = [[GSAbsTimeZoneDetail alloc] initWithTimeZone: self];
659
NSMapInsert(absolutes, (void*)(gsaddr)anOffset, (void*)z);
660
[zoneDictionary setObject: self forKey: (NSString*)name];
764
662
if (zone_mutex != nil)
669
- (BOOL) isDaylightSavingTimeZoneForDate: (NSDate*)aDate
769
674
- (NSString*) name
679
- (int) secondsFromGMTForDate: (NSDate*)aDate
774
684
- (NSTimeZone*) timeZoneDetailTimeZone
776
686
return [NSTimeZone arrayWithObject: detail];
768
@implementation GSAbsTimeZoneDetail
770
- (NSString*) abbreviation
775
- (NSString*) abbreviationForDate: (NSDate*)aDate
786
- (id) initWithTimeZone: (GSAbsTimeZone*)aZone
788
zone = RETAIN(aZone);
792
- (BOOL) isDaylightSavingTimeZone
797
- (BOOL) isDaylightSavingTimeZoneForDate: (NSDate*)aDate
807
- (NSString*) timeZoneAbbreviation
812
- (NSArray*) timeZoneDetailArray
814
return [zone timeZoneDetailArray];
817
- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)date
822
- (int) timeZoneSecondsFromGMT
827
- (int) timeZoneSecondsFromGMTForDate: (NSDate*)aDate
838
* If the GNUstep time zone datafiles become too out of date, one
839
* can download an updated database from <uref
840
* url="ftp://elsie.nci.nih.gov/pub/">ftp://elsie.nci.nih.gov/pub/</uref>
841
* and compile it as specified in the README file in the
842
* NSTimeZones directory.
844
* <p>Time zone names in NSDates should be GMT, MET etc. not
845
* Europe/Berlin, America/Washington etc.
847
* <p>The problem with this is that various time zones may use the
848
* same abbreviation (e.g. Australia/Brisbane and
849
* America/New_York both use EST), and some time zones
850
* may have different rules for daylight saving time even if the
851
* abbreviation and offsets from UTC are the same.
853
* <p>The problems with depending on the OS for providing time zone
854
* info are that some methods for the NSTimeZone classes might be
855
* difficult to implement, and also that time zone names may vary
856
* wildly between OSes (this could be a big problem when
857
* archiving is used between different systems).
855
860
@implementation NSTimeZone
857
862
+ (NSDictionary*) abbreviationDictionary
997
1013
NSNonRetainedObjectMapValueCallBacks, 0);
999
1015
localTimeZone = [[NSLocalTimeZone alloc] init];
1000
[self setDefaultTimeZone: [self systemTimeZone]];
1002
1017
fake_abbrev_dict = [[NSInternalAbbrevDict alloc] init];
1003
if ([NSThread isMultiThreaded])
1005
[self _becomeThreaded: nil];
1009
[[NSNotificationCenter defaultCenter]
1011
selector: @selector(_becomeThreaded:)
1012
name: NSWillBecomeMultiThreadedNotification
1018
zone_mutex = [GSLazyRecursiveLock new];
1023
* Return a proxy to the default time zone for this process.
1018
1025
+ (NSTimeZone*) localTimeZone
1020
1027
return localTimeZone;
1031
* Destroy the system time zone so that it will be recreated
1032
* next time it is used.
1023
1034
+ (void) resetSystemTimeZone
1025
1036
if (zone_mutex != nil)
1027
1040
DESTROY(systemTimeZone);
1028
1041
if (zone_mutex != nil)
1029
[zone_mutex unlock];
1043
[zone_mutex unlock];
1048
* Set the default time zone to be used for this process.
1032
1050
+ (void) setDefaultTimeZone: (NSTimeZone*)aTimeZone
1034
if (zone_mutex != nil)
1036
ASSIGN(defaultTimeZone, aTimeZone);
1037
if (zone_mutex != nil)
1038
[zone_mutex unlock];
1052
if (aTimeZone != defaultTimeZone)
1055
* We can't make the localTimeZone the default since that would
1056
* cause recursion ...
1058
if (aTimeZone == localTimeZone)
1060
aTimeZone = [self systemTimeZone];
1062
if (zone_mutex != nil)
1066
ASSIGN(defaultTimeZone, aTimeZone);
1067
if (zone_mutex != nil)
1069
[zone_mutex unlock];
1075
* Returns the current system time zone for the process.
1041
1077
+ (NSTimeZone*) systemTimeZone
1043
1079
NSTimeZone *zone = nil;
1045
1081
if (zone_mutex != nil)
1047
1085
if (systemTimeZone == nil)
1049
1087
NSString *localZoneString = nil;
1154
1200
return regionsArray;
1204
* Return a timezone for the specified offset from GMT.<br />
1205
* The timezone returned does <em>not</em> use daylight savings time.
1206
* The actual timezone returned has an offset rounded to the nearest
1208
* Time zones with an offset of more than +/- 18 hours are disallowed,
1209
* and nil is returned.
1157
1211
+ (NSTimeZone*) timeZoneForSecondsFromGMT: (int)seconds
1159
/* We simply return the following because an existing time zone with
1160
the given offset might not always have the same offset (daylight
1161
savings time, change in standard time, etc.). */
1162
return AUTORELEASE([[NSConcreteAbsoluteTimeZone alloc] initWithOffset: seconds]);
1215
zone = [[GSAbsTimeZone alloc] initWithOffset: seconds];
1216
return AUTORELEASE(zone);
1220
* Returns a timezone for the specified abbrevition,
1165
1222
+ (NSTimeZone*) timeZoneWithAbbreviation: (NSString*)abbreviation
1167
1224
NSTimeZone *zone;
1169
1226
zone = [self timeZoneWithName: [[self abbreviationDictionary]
1170
objectForKey: abbreviation]];
1227
objectForKey: abbreviation] data: nil];
1232
* Returns a timezone for the specified name.
1174
1234
+ (NSTimeZone*) timeZoneWithName: (NSString*)aTimeZoneName
1176
1236
NSTimeZone *zone;
1178
zone = [defaultPlaceholderTimeZone initWithName: aTimeZoneName];
1238
zone = [defaultPlaceholderTimeZone initWithName: aTimeZoneName data: nil];
1179
1239
return AUTORELEASE(zone);
1243
* Returns a timezone for the specified name, created from the supplied data.
1182
1245
+ (NSTimeZone*) timeZoneWithName: (NSString*)name data: (NSData*)data
1185
[self notImplemented: _cmd];
1249
zone = [defaultPlaceholderTimeZone initWithName: name data: nil];
1250
return AUTORELEASE(zone);
1254
* Returns the abbreviation for this timezone now.
1255
* Invokes -abbreviationForDate:
1189
1257
- (NSString*) abbreviation
1191
1259
return [self abbreviationForDate: [NSDate date]];
1194
- (NSString*) abbreviationForDate: (NSDate*)when
1263
* Returns the abbreviation for this timezone at aDate. This may differ
1264
* depending on whether daylight savings time is in effect or not.
1266
- (NSString*) abbreviationForDate: (NSDate*)aDate
1196
1268
NSTimeZoneDetail *detail;
1198
detail = [self timeZoneDetailForDate: when];
1199
return [detail timeZoneAbbreviation];
1271
detail = [self timeZoneDetailForDate: aDate];
1272
abbr = [detail timeZoneAbbreviation];
1202
1277
- (Class) classForCoder
1219
1302
[aCoder encodeObject: [self name]];
1307
return [self initWithName: @"NSLocalTimeZone" data: nil];
1222
1310
- (id) initWithCoder: (NSCoder*)aDecoder
1224
1312
NSString *name;
1226
1314
name = [aDecoder decodeObject];
1227
self = [self initWithName: name];
1315
self = [self initWithName: name data: nil];
1320
* Initialise a timezone with the supplied name. May return a cached
1321
* timezone object rather than the newly created one.
1231
1323
- (id) initWithName: (NSString*)name
1233
[self notImplemented: _cmd];
1325
return [self initWithName: name data: nil];
1329
* Initialises a time zone object using the supplied data object.<br />
1330
* This method is intended for internal use by the NSTimeZone
1332
* Don't use it ... use -initWithName: instead.
1237
1334
- (id) initWithName: (NSString*)name data: (NSData*)data
1239
1336
[self notImplemented: _cmd];
1341
* Returns a boolean indicating whether daylight savings time is in
1342
* effect now. Invokes -isDaylightSavingTimeForDate:
1243
1344
- (BOOL) isDaylightSavingTime
1245
1346
return [self isDaylightSavingTimeForDate: [NSDate date]];
1350
* Returns a boolean indicating whether daylight savings time is in
1351
* effect for this time zone at aDate.
1248
1353
- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate
1250
1355
NSTimeZoneDetail *detail;
1252
1358
detail = [self timeZoneDetailForDate: aDate];
1253
return [detail isDaylightSavingTimeZone];
1359
isDST = [detail isDaylightSavingTimeZone];
1256
1364
- (BOOL) isEqual: (id)other
1349
1478
@implementation NSTimeZone (Private)
1352
* When the system becomes multithreaded, we set a flag to say so
1354
+ (void) _becomeThreaded: (NSNotification*)notification
1356
if (zone_mutex == nil)
1358
zone_mutex = [NSRecursiveLock new];
1360
[[NSNotificationCenter defaultCenter]
1361
removeObserver: self
1362
name: NSWillBecomeMultiThreadedNotification
1366
1480
+ (NSString*) getAbbreviationFile
1368
return [NSBundle pathForGNUstepResource: ABBREV_DICT
1370
inDirectory: TIME_ZONE_DIR];
1482
return _time_zone_path (ABBREV_DICT);
1373
1485
+ (NSString*) getRegionsFile
1375
return [NSBundle pathForGNUstepResource: REGIONS_FILE
1377
inDirectory: TIME_ZONE_DIR];
1487
return _time_zone_path (REGIONS_FILE);
1380
1490
+ (NSString*) getTimeZoneFile: (NSString *)name
1382
NSString *dir = [NSString stringWithFormat: @"%@/%@", TIME_ZONE_DIR, ZONES_DIR];
1383
NSString *path = [NSBundle pathForGNUstepResource: name
1494
dir= _time_zone_path (ZONES_DIR);
1495
return [dir stringByAppendingPathComponent: name];
1502
@implementation GSTimeZone
1505
* Perform a binary search of a transitions table to locate the index
1506
* of the transition to use for a particular time interval since 1970.<br />
1507
* We locate the index of the highest transition before the date, or zero
1508
* if there is no transition before it.
1511
chop(NSTimeInterval since, GSTimeZone *zone)
1513
gss32 when = (gss32)since;
1514
gss32 *trans = zone->trans;
1515
unsigned hi = zone->n_trans;
1519
if (hi == 0 || trans[0] > when)
1521
unsigned n_types = zone->n_types;
1524
* If the first transition is greater than our date,
1525
* we locate the first non-DST transition and use that offset,
1526
* or just use the first transition.
1528
for (i = 0; i < n_types; i++)
1530
if (zone->types[i].isdst == 0)
1532
return &zone->types[i];
1535
return &zone->types[0];
1539
for (i = hi/2; hi != lo; i = (hi + lo)/2)
1541
if (when < trans[i])
1545
else if (when > trans[i])
1555
* If we went off the top of the table or the closest transition
1556
* was later than our date, we step back to find the last
1557
* transition before our date.
1559
if (i > 0 && (i == zone->n_trans || trans[i] > when))
1563
return &zone->types[zone->idxs[i]];
1567
static NSTimeZoneDetail*
1568
newDetailInZoneForType(GSTimeZone *zone, TypeInfo *type)
1570
GSTimeZoneDetail *detail;
1572
detail = [GSTimeZoneDetail alloc];
1573
detail = [detail initWithTimeZone: zone
1574
withAbbrev: type->abbreviation
1575
withOffset: type->offset
1576
withDST: type->isdst];
1580
- (NSString*) abbreviationForDate: (NSDate*)aDate
1582
TypeInfo *type = chop([aDate timeIntervalSince1970], self);
1584
return type->abbreviation;
1589
return timeZoneData;
1594
RELEASE(timeZoneName);
1595
RELEASE(timeZoneData);
1600
for (i = 0; i < n_types; i++)
1602
RELEASE(types[i].abbreviation);
1604
NSZoneFree(NSDefaultMallocZone(), types);
1609
- (id) initWithName: (NSString*)name data: (NSData*)data
1611
static NSString *fileException = @"GSTimeZoneFileException";
1613
timeZoneName = [name copy];
1614
timeZoneData = [data copy];
1617
const void *bytes = [timeZoneData bytes];
1618
unsigned length = [timeZoneData length];
1621
unsigned i, charcnt;
1622
unsigned char *abbr;
1623
struct tzhead *header;
1625
if (length < sizeof(struct tzhead))
1627
[NSException raise: fileException
1628
format: @"File is too small"];
1630
header = (struct tzhead *)(bytes + pos);
1631
pos += sizeof(struct tzhead);
1632
if (memcmp(header->tzh_magic, TZ_MAGIC, strlen(TZ_MAGIC)) != 0)
1634
[NSException raise: fileException
1635
format: @"TZ_MAGIC is incorrect"];
1637
n_trans = GSSwapBigI32ToHost(*(gss32*)header->tzh_timecnt);
1638
n_types = GSSwapBigI32ToHost(*(gss32*)header->tzh_typecnt);
1639
charcnt = GSSwapBigI32ToHost(*(gss32*)header->tzh_charcnt);
1642
i += sizeof(gss32)*n_trans;
1645
[NSException raise: fileException
1646
format: @"Transitions list is truncated"];
1651
[NSException raise: fileException
1652
format: @"Transition indexes are truncated"];
1654
i += sizeof(struct ttinfo)*n_types;
1657
[NSException raise: fileException
1658
format: @"Types list is truncated"];
1660
if (i + charcnt > length)
1662
[NSException raise: fileException
1663
format: @"Abbreviations list is truncated"];
1667
* Now calculate size we need to store the information
1668
* for efficient access ... not the same saze as the data
1671
i = n_trans * (sizeof(gss32)+1) + n_types * sizeof(TypeInfo);
1672
buf = NSZoneMalloc(NSDefaultMallocZone(), i);
1673
types = (TypeInfo*)buf;
1674
buf += (n_types * sizeof(TypeInfo));
1675
trans = (gss32*)buf;
1676
buf += (n_trans * sizeof(gss32));
1677
idxs = (unsigned char*)buf;
1679
/* Read in transitions. */
1680
for (i = 0; i < n_trans; i++)
1682
trans[i] = GSSwapBigI32ToHost(*(gss32*)(bytes + pos));
1683
pos += sizeof(gss32);
1685
for (i = 0; i < n_trans; i++)
1687
idxs[i] = *(unsigned char*)(bytes + pos);
1690
for (i = 0; i < n_types; i++)
1692
struct ttinfo *ptr = (struct ttinfo*)(bytes + pos);
1694
types[i].isdst = (ptr->isdst != 0 ? YES : NO);
1695
types[i].abbr_idx = ptr->abbr_idx;
1696
types[i].offset = decode(ptr->offset);
1697
pos += sizeof(struct ttinfo);
1699
abbr = (char*)(bytes + pos);
1701
id abbrevs[charcnt];
1703
memset(abbrevs, '\0', sizeof(id)*charcnt);
1704
for (i = 0; i < n_types; i++)
1706
int loc = types[i].abbr_idx;
1708
if (abbrevs[loc] == nil)
1710
abbrevs[loc] = [[NSString alloc] initWithCString: abbr + loc];
1714
RETAIN(abbrevs[loc]);
1716
types[i].abbreviation = abbrevs[loc];
1720
if (zone_mutex != nil)
1724
[zoneDictionary setObject: self forKey: timeZoneName];
1725
if (zone_mutex != nil)
1727
[zone_mutex unlock];
1733
NSLog(@"Unable to obtain time zone `%@'... %@", name, localException);
1734
if ([localException name] != fileException)
1736
[localException raise];
1743
- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate
1745
TypeInfo *type = chop([aDate timeIntervalSince1970], self);
1752
return timeZoneName;
1755
- (int) secondsFromGMTForDate: (NSDate*)aDate
1757
TypeInfo *type = chop([aDate timeIntervalSince1970], self);
1759
return type->offset;
1762
- (NSArray*) timeZoneDetailArray
1764
NSTimeZoneDetail *details[n_types];
1768
for (i = 0; i < n_types; i++)
1770
details[i] = newDetailInZoneForType(self, &types[i]);
1772
array = [NSArray arrayWithObjects: details count: n_types];
1773
for (i = 0; i < n_types; i++)
1775
RELEASE(details[i]);
1780
- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)aDate
1783
NSTimeZoneDetail *detail;
1785
type = chop([aDate timeIntervalSince1970], self);
1786
detail = newDetailInZoneForType(self, type);
1787
return AUTORELEASE(detail);
1790
- (NSString*) timeZoneName
1792
return timeZoneName;