~ubuntu-branches/ubuntu/edgy/gnustep-base/edgy

« back to all changes in this revision

Viewing changes to Source/NSTimeZone.m

  • Committer: Bazaar Package Importer
  • Author(s): Eric Heintzmann
  • Date: 2004-04-06 15:21:11 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20040406152111-95pwmfi50qp1s594
Tags: 1.9.1-2
* debian/rules: Update CC.
* Source/NSUserDefaults: apply patch from CVS.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/** Time zone management. -*- Mode: ObjC -*-
2
 
   Copyright (C) 1997 Free Software Foundation, Inc.
 
2
   Copyright (C) 1997-2002 Free Software Foundation, Inc.
3
3
  
4
4
   Written by: Yoo C. Chung <wacko@laplace.snu.ac.kr>
5
5
   Date: June 1997
6
6
  
 
7
   Rewritw large chunks by: Richard Frith-Macdonald <rfm@gnu.org>
 
8
   Date: September 2002
 
9
  
7
10
     This file is part of the GNUstep Base Library.
8
11
  
9
12
   This library is free software; you can redistribute it and/or
21
24
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
22
25
 
23
26
   <title>NSTimeZone class reference</title>
24
 
   $Date: 2001/12/18 16:54:15 $ $Revision: 1.41 $
 
27
   $Date: 2004/02/08 09:42:38 $ $Revision: 1.59 $
25
28
 */
26
29
  
27
30
/* We use a implementation independent of the system, since POSIX
45
48
 
46
49
   FIXME?: use leap seconds? */
47
50
 
48
 
#include <config.h>
49
 
#include <base/preface.h>
 
51
#include "config.h"
 
52
#include "GNUstepBase/preface.h"
 
53
#include "GNUstepBase/GSLock.h"
50
54
#include <limits.h>
51
55
#include <stdio.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>
 
58
#include <time.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"
 
81
#include "GSConfig.h"
 
82
 
74
83
 
75
84
#define NOID
76
85
#include "tzfile.h"
101
110
#define ZONES_DIR @"zones/"
102
111
 
103
112
 
104
 
@class NSInternalTimeTransition;
105
 
@class NSConcreteTimeZone;
106
 
@class NSConcreteAbsoluteTimeZone;
107
 
@class NSConcreteTimeZoneDetail;
 
113
@class GSAbsTimeZone;
 
114
@class GSTimeZoneDetail;
 
115
@class GSAbsTimeZoneDetail;
108
116
 
109
117
@class  GSPlaceholderTimeZone;
110
118
 
114
122
static GSPlaceholderTimeZone    *defaultPlaceholderTimeZone;
115
123
static NSMapTable               *placeholderMap;
116
124
 
117
 
/* Temporary structure for holding time zone details. */
 
125
/*
 
126
 * Temporary structure for holding time zone details.
 
127
 * This is the format in the data object.
 
128
 */
118
129
struct ttinfo
119
130
{
120
131
  char offset[4]; // Seconds east of UTC
121
 
  BOOL isdst; // Daylight savings time?
122
 
  char abbr_idx; // Index into time zone abbreviations string
 
132
  unsigned char isdst; // Daylight savings time?
 
133
  unsigned char abbr_idx; // Index into time zone abbreviations string
123
134
};
124
135
 
 
136
/*
 
137
 * And this is the structure used in the time zone instances.
 
138
 */
 
139
typedef struct {
 
140
  gss32         offset;
 
141
  BOOL          isdst;
 
142
  unsigned char abbr_idx;
 
143
  char          pad[2];
 
144
  NSString      *abbreviation;
 
145
} TypeInfo;
 
146
 
 
147
@interface      GSTimeZone : NSTimeZone
 
148
{
 
149
@public
 
150
  NSString      *timeZoneName;
 
151
  NSData        *timeZoneData;
 
152
  unsigned int  n_trans;
 
153
  unsigned int  n_types;
 
154
  gss32         *trans;
 
155
  TypeInfo      *types;
 
156
  unsigned char *idxs;
 
157
}
 
158
@end
 
159
 
125
160
 
126
161
static NSTimeZone       *defaultTimeZone = nil;
127
162
static NSTimeZone       *localTimeZone = nil;
165
200
#endif /* defined(WORDS_BIGENDIAN) && SIZEOF_INT == 4 */
166
201
}
167
202
 
 
203
/* Return path to a TimeZone directory file */
 
204
static NSString *_time_zone_path(NSString *subpath)
 
205
{
 
206
  NSBundle *gbundle;
 
207
  gbundle = [NSBundle bundleForLibrary: @"gnustep-base"];
 
208
  return [gbundle pathForResource: subpath 
 
209
                           ofType: @"" 
 
210
                      inDirectory: TIME_ZONE_DIR];
 
211
}
168
212
 
169
213
/* Object enumerator for NSInternalAbbrevDict. */
170
214
@interface NSInternalAbbrevDictObjectEnumerator : NSEnumerator
181
225
@end
182
226
  
183
227
  
184
 
@interface NSInternalTimeTransition : NSObject
185
 
{
186
 
  int trans_time; // When the transition occurs
187
 
  char detail_index; // Index of time zone detail
188
 
}
189
 
  
190
 
- (id) initWithTime: (int)aTime withIndex: (char)anIndex;
191
 
- (int) transTime;
192
 
- (char) detailIndex;
193
 
@end
194
 
  
195
228
@interface GSPlaceholderTimeZone : NSTimeZone
196
229
@end
197
230
  
198
 
@interface NSConcreteTimeZone : NSTimeZone
 
231
@interface GSAbsTimeZone : NSTimeZone
199
232
{
 
233
@public
200
234
  NSString      *name;
201
 
  NSArray       *transitions; // Transition times and rules
202
 
  NSArray       *details; // Time zone details
203
 
}
204
 
 
205
 
- (id) initWithName: (NSString*)aName
206
 
    withTransitions: (NSArray*)trans
207
 
        withDetails: (NSArray*)zoneDetails;
208
 
@end
209
 
  
210
 
 
211
 
@interface NSConcreteAbsoluteTimeZone : NSTimeZone
212
 
{
213
 
  NSString *name;
214
 
  id detail;
215
 
  int offset; // Offset from UTC in seconds.
 
235
  id            detail;
 
236
  int           offset; // Offset from UTC in seconds.
216
237
}
217
238
 
218
239
- (id) initWithOffset: (int)anOffset;
221
242
@interface NSLocalTimeZone : NSTimeZone
222
243
@end
223
244
 
224
 
 
225
 
@interface NSConcreteTimeZoneDetail : NSTimeZoneDetail
 
245
@interface GSTimeZoneDetail : NSTimeZoneDetail
226
246
{
227
 
  NSTimeZone *timeZone; // Time zone which created this object.
228
 
  NSString *abbrev; // Abbreviation for time zone detail.
229
 
  int offset; // Offset from UTC in seconds.
230
 
  BOOL is_dst; // Is it daylight savings time?
 
247
  NSTimeZone    *timeZone; // Time zone which created this object.
 
248
  NSString      *abbrev; // Abbreviation for time zone detail.
 
249
  int           offset; // Offset from UTC in seconds.
 
250
  BOOL          is_dst; // Is it daylight savings time?
231
251
}
232
252
 
233
253
- (id) initWithTimeZone: (NSTimeZone*)aZone
236
256
                withDST: (BOOL)isDST;
237
257
@end
238
258
  
 
259
@interface GSAbsTimeZoneDetail : NSTimeZoneDetail
 
260
{
 
261
  GSAbsTimeZone *zone; // Time zone which created this object.
 
262
}
 
263
 
 
264
- (id) initWithTimeZone: (GSAbsTimeZone*)aZone;
 
265
@end
 
266
  
239
267
/* Private methods for obtaining resource file names. */
240
268
@interface NSTimeZone (Private)
241
 
+ (void) _becomeThreaded: (NSNotification*)notification;
242
269
+ (NSString*) getAbbreviationFile;
243
270
+ (NSString*) getRegionsFile;
244
271
+ (NSString*) getTimeZoneFile: (NSString*)name;
307
334
  
308
335
@end
309
336
 
310
 
 
311
 
@implementation NSInternalTimeTransition
312
 
 
313
 
- (NSString*) description
314
 
{
315
 
  return [NSString stringWithFormat: @"%@(%d, %d)",
316
 
    [self class], trans_time, (int)detail_index];
317
 
}
318
 
 
319
 
- (id) initWithTime: (int)aTime withIndex: (char)anIndex
320
 
{
321
 
  [super init];
322
 
  trans_time = aTime;
323
 
  detail_index = anIndex;
324
 
  return self;
325
 
}
326
 
 
327
 
- (int) transTime
328
 
{
329
 
  return trans_time;
330
 
}
331
 
 
332
 
- (char) detailIndex
333
 
{
334
 
  return detail_index;
335
 
}
336
 
 
337
 
@end
338
 
  
339
337
 
340
338
@implementation GSPlaceholderTimeZone
341
339
 
345
343
  return self;          // placeholders never get released.
346
344
}
347
345
 
348
 
- (id) objectAtIndex: (unsigned)index
349
 
{
350
 
  [NSException raise: NSInternalInconsistencyException
351
 
              format: @"attempt to use uninitialised time zone"];
352
 
  return 0;
353
 
}
354
 
 
355
346
- (void) dealloc
356
347
{
357
348
  return;               // placeholders never get deallocated.
358
349
}
359
350
 
360
 
- (id) initWithName: (NSString*)name
 
351
- (id) initWithName: (NSString*)name data: (NSData*)data
361
352
{
362
353
  NSTimeZone    *zone;
 
354
  unsigned      length = [name length];
363
355
 
364
 
  /*
365
 
   * Quick return if it's the local timezone.
366
 
   */
367
 
  if ([name isEqual: @"NSLocalTimeZone"])
368
 
    {
369
 
      return [self initWithName: name data: nil];
 
356
  if (length == 0)
 
357
    {
 
358
      NSLog(@"Disallowed null time zone name");
 
359
      return nil;
 
360
    }
 
361
  if (length == 15 && [name isEqual: @"NSLocalTimeZone"])
 
362
    {
 
363
      zone = RETAIN(localTimeZone);
 
364
      DESTROY(self);
 
365
      return zone;
370
366
    }
371
367
 
372
368
  /*
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
374
371
   */
375
372
  if (zone_mutex != nil)
376
373
    {
377
374
      [zone_mutex lock];
378
375
    }
379
376
  zone = [zoneDictionary objectForKey: name];
 
377
  if (data != nil && [data isEqual: [zone data]] == NO)
 
378
    {
 
379
      zone = nil;
 
380
    }
380
381
  IF_NO_GC(RETAIN(zone));
381
382
  if (zone_mutex != nil)
382
383
    {
383
384
      [zone_mutex unlock];
384
385
    }
385
 
  if (zone != nil)
386
 
    {
387
 
      return zone;
388
 
    }
389
 
 
390
 
  /*
391
 
   * Now return an absolute time zone or load one from file.
392
 
   */
393
 
  if ([name hasPrefix: @"NSAbsoluteTimeZone:"] == YES)
394
 
    {
395
 
      int       i = [[name substringFromIndex: 19] intValue];
396
 
 
397
 
      zone = [[NSConcreteAbsoluteTimeZone alloc] initWithOffset: i];
398
 
    }
399
 
  else
400
 
    {
401
 
      NSString  *fileName;
402
 
      NSData    *data;
403
 
      unsigned  length;
404
 
 
405
 
      length = [name length];
406
 
      if (length == 0)
407
 
        {
408
 
          NSLog(@"Disallowed null time zone name");
409
 
          return nil;
410
 
        }
411
 
      else
412
 
        {
413
 
          const char    *str = [name lossyCString];
414
 
 
415
 
          /* Make sure that only time zone files are accessed.
416
 
             FIXME: Make this more robust. */
417
 
          if ((str)[0] == '/' || strchr(str, '.') != NULL)
418
 
            {
419
 
              NSLog(@"Disallowed time zone name `%@'.", name);
420
 
              return nil;
421
 
            }
422
 
        }
423
 
 
424
 
      fileName = [NSTimeZoneClass getTimeZoneFile: name];
425
 
      if (fileName == nil)
426
 
        {
427
 
          NSLog(@"Unknown time zone name `%@'.", name);
428
 
          return nil;
429
 
        }
430
 
      data = [NSData dataWithContentsOfFile: fileName];
431
 
      zone = [self initWithName: name data: data];
432
 
    }
433
 
  return zone;
434
 
}
435
 
 
436
 
- (id) initWithName: (NSString*)name data: (NSData*)data
437
 
{
438
 
  NSTimeZone    *zone;
439
 
 
440
 
  if ([name isEqual: @"NSLocalTimeZone"])
441
 
    {
442
 
      zone = RETAIN(localTimeZone);
443
 
    }
444
 
  else if ([name hasPrefix: @"NSAbsoluteTimeZone:"] == YES)
445
 
    {
446
 
      int       i = [[name substringFromIndex: 19] intValue];
447
 
 
448
 
      zone = [[NSConcreteAbsoluteTimeZone alloc] initWithOffset: i];
449
 
    }
450
 
  else
451
 
    {
452
 
      static NSString   *fileException = @"fileException";
453
 
      static NSString   *errMess = @"File read error in NSTimeZone.";
454
 
 
455
 
      zone = nil;
456
 
      NS_DURING
457
 
        {
458
 
          const void    *bytes = [data bytes];
459
 
          unsigned      length = [data length];
460
 
          unsigned      pos = 0;
461
 
          id            transArray;
462
 
          id            detailsArray;
463
 
          int           i, n_trans, n_types, names_size;
464
 
          id            *abbrevsArray;
465
 
          char          *trans;
466
 
          char          *type_idxs;
467
 
          char          *zone_abbrevs;
468
 
          struct tzhead *header;
469
 
          struct ttinfo *types; // Temporary array for details
470
 
 
471
 
          if (length < sizeof(struct tzhead))
472
 
            {
473
 
              [NSException raise: fileException format: errMess];
474
 
            }
475
 
          header = (struct tzhead *)(bytes + pos);
476
 
          pos += sizeof(struct tzhead);
477
 
 
478
 
          n_trans = decode(header->tzh_timecnt);
479
 
          n_types = decode(header->tzh_typecnt);
480
 
          names_size = decode(header->tzh_charcnt);
481
 
 
482
 
          /* Read in transitions. */
483
 
          trans = (char*)(bytes + pos);
484
 
          pos += 4*n_trans;
485
 
          type_idxs = (char*)(bytes + pos);
486
 
          pos += n_trans;
487
 
          if (pos > length)
488
 
            {
489
 
              [NSException raise: fileException format: errMess];
490
 
            }
491
 
          transArray = [NSMutableArray arrayWithCapacity: n_trans];
492
 
          for (i = 0; i < n_trans; i++)
493
 
            {
494
 
              [transArray
495
 
                addObject: [[NSInternalTimeTransition alloc]
496
 
                           initWithTime: decode(trans+(i*4))
497
 
                           withIndex: type_idxs[i]]];
498
 
            }
499
 
 
500
 
          /* Read in time zone details. */
501
 
          types = (struct ttinfo*)(bytes + pos);
502
 
          pos += n_types*sizeof(struct ttinfo);
503
 
          if (pos > length)
504
 
            {
505
 
              [NSException raise: fileException format: errMess];
506
 
            }
507
 
 
508
 
          /* Read in time zone abbreviation strings. */
509
 
          zone_abbrevs = (char*)(bytes + pos);
510
 
          pos += names_size;
511
 
          if (pos > length)
512
 
            {
513
 
              [NSException raise: fileException format: errMess];
514
 
            }
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++)
519
 
            {
520
 
              struct ttinfo     *inf = types + i;
521
 
              int               loc = inf->abbr_idx;
522
 
 
523
 
              if (abbrevsArray[loc] == nil)
524
 
                {
525
 
                  abbrevsArray[loc]
526
 
                    = [NSString stringWithCString: zone_abbrevs+loc];
527
 
                }
528
 
            }
529
 
 
530
 
          zone = [NSConcreteTimeZone alloc];
531
 
 
532
 
          /* Create time zone details. */
533
 
          detailsArray = [NSMutableArray arrayWithCapacity: n_types];
534
 
          for (i = 0; i < n_types; i++)
535
 
            {
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];
542
 
 
543
 
              detail = [[NSConcreteTimeZoneDetail alloc]
544
 
                              initWithTimeZone: zone
545
 
                                    withAbbrev: abr
546
 
                                    withOffset: off
547
 
                                       withDST: dst];
548
 
              [detailsArray addObject: detail];
549
 
              RELEASE(detail);
550
 
            }
551
 
          NSZoneFree(NSDefaultMallocZone(), abbrevsArray);
552
 
 
553
 
          zone = [(id)zone initWithName: name
554
 
                        withTransitions: transArray
555
 
                            withDetails: detailsArray];
556
 
          if (zone_mutex != nil)
557
 
            {
558
 
              [zone_mutex lock];
559
 
            }
560
 
          [zoneDictionary setObject: zone forKey: (NSString*)[zone name]];
561
 
          if (zone_mutex != nil)
562
 
            {
563
 
              [zone_mutex unlock];
564
 
            }
565
 
        }
566
 
      NS_HANDLER
567
 
        {
568
 
          DESTROY(zone);
569
 
          if ([localException name] != fileException)
570
 
            [localException raise];
571
 
          NSLog(@"Unable to obtain time zone `%@'.", name);
572
 
        }
573
 
      NS_ENDHANDLER
574
 
 
575
 
    }
 
386
 
 
387
  if (zone == nil)
 
388
    {
 
389
      unichar   c;
 
390
      unsigned  i;
 
391
 
 
392
      if (length == 8 && [name hasPrefix: @"GMT"] == YES
 
393
        && ((c = [name characterAtIndex: 3]) == '+' || c == '-'))
 
394
        {
 
395
          c = [name characterAtIndex: 4];
 
396
          if (c >= '0' && c <= '9')
 
397
            {
 
398
              i = c - '0';
 
399
              c = [name characterAtIndex: 5];
 
400
              if (c >= '0' && c <= '9')
 
401
                {
 
402
                  i = i * 10 + (c - '0');
 
403
                  c = [name characterAtIndex: 6];
 
404
                  if (c >= '0' && c <= '9')
 
405
                    {
 
406
                      i = i * 6 + (c - '0');
 
407
                      c = [name characterAtIndex: 7];
 
408
                      if (c >= '0' && c <= '9')
 
409
                        {
 
410
                          i = i * 10 + (c - '0');
 
411
                          zone = [[GSAbsTimeZone alloc] initWithOffset: i*60];
 
412
                        }
 
413
                    }
 
414
                }
 
415
            }
 
416
        }
 
417
 
 
418
      if (zone == nil && length > 19
 
419
        && [name hasPrefix: @"NSAbsoluteTimeZone:"] == YES)
 
420
        {
 
421
          i = [[name substringFromIndex: 19] intValue];
 
422
 
 
423
          zone = [[GSAbsTimeZone alloc] initWithOffset: i];
 
424
        }
 
425
 
 
426
      if (zone == nil)
 
427
        {
 
428
          if (data == nil)
 
429
            {
 
430
              NSString          *fileName;
 
431
              const char        *str = [name UTF8String];
 
432
 
 
433
              /* Make sure that only time zone files are accessed.
 
434
                 FIXME: Make this more robust. */
 
435
              if ((str)[0] == '/' || strchr(str, '.') != NULL)
 
436
                {
 
437
                  NSLog(@"Disallowed time zone name `%@'.", name);
 
438
                  return nil;
 
439
                }
 
440
 
 
441
              fileName = [NSTimeZoneClass getTimeZoneFile: name];
 
442
              if (fileName == nil)
 
443
                {
 
444
                  NSLog(@"Unknown time zone name `%@'.", name);
 
445
                  return nil;
 
446
                }
 
447
              data = [NSData dataWithContentsOfFile: fileName];
 
448
            }
 
449
          zone = [[GSTimeZone alloc] initWithName: name data: data];
 
450
        }
 
451
    }
 
452
  RELEASE(self);
576
453
  return zone;
577
454
}
578
455
 
588
465
@end
589
466
 
590
467
 
591
 
  
592
 
@implementation NSConcreteTimeZone
593
 
  
594
 
- (void) dealloc
595
 
{
596
 
  RELEASE(name);
597
 
  RELEASE(transitions);
598
 
  RELEASE(details);
599
 
  [super dealloc];
600
 
}
601
 
 
602
 
- (void) encodeWithCoder: (NSCoder*)aCoder
603
 
{
604
 
  [aCoder encodeObject: name];
605
 
}
606
 
 
607
 
- (id) initWithName: (NSString*)aName
608
 
    withTransitions: (NSArray*)trans
609
 
        withDetails: (NSArray*)zoneDetails
610
 
{
611
 
  self = [super init];
612
 
  if (self != nil)
613
 
    {
614
 
      name = [aName copyWithZone: [self zone]];
615
 
      transitions = RETAIN(trans);
616
 
      details = RETAIN(zoneDetails);
617
 
    }
618
 
  return self;
619
 
}
620
 
 
621
 
- (NSString*) name
622
 
{
623
 
  return name;
624
 
}
625
 
 
626
 
- (NSArray*) timeZoneDetailArray
627
 
{
628
 
  return details;
629
 
}
630
 
  
631
 
- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)date
632
 
{
633
 
  unsigned index;
634
 
  int the_time;
635
 
  unsigned count;
636
 
 
637
 
  the_time = (int)[date timeIntervalSince1970];
638
 
  count = [transitions count];
639
 
  if (count == 0
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. */
643
 
    {
644
 
      unsigned detail_count;
645
 
 
646
 
      detail_count = [details count];
647
 
      index = 0;
648
 
      while (index < detail_count
649
 
        && [[details objectAtIndex: index] isDaylightSavingTimeZone])
650
 
        index++;
651
 
      if (index == detail_count)
652
 
        index = 0;
653
 
    }
654
 
  else
655
 
    /* Find the first transition after DATE, and then pick the type of
656
 
       the transition before it. */
657
 
    {
658
 
      for (index = 1; index < count; index++)
659
 
        if (the_time < [[transitions objectAtIndex: index] transTime])
660
 
          break;
661
 
      index = [[transitions objectAtIndex: index-1] detailIndex];
662
 
    }
663
 
  return [details objectAtIndex: index];
664
 
}
665
 
  
666
 
@end
667
468
 
668
469
@implementation NSLocalTimeZone
669
470
  
 
471
- (NSString*) abbreviation
 
472
{
 
473
  return [[NSTimeZoneClass defaultTimeZone] abbreviation];
 
474
}
 
475
 
 
476
- (NSString*) abbreviationForDate: (NSDate*)aDate
 
477
{
 
478
  return [[NSTimeZoneClass defaultTimeZone] abbreviationForDate: aDate];
 
479
}
 
480
 
670
481
- (id) autorelease
671
482
{
672
483
  return self;
673
484
}
674
485
 
 
486
- (NSData*) data
 
487
{
 
488
  return [[NSTimeZoneClass defaultTimeZone] data];
 
489
}
 
490
 
675
491
- (void) encodeWithCoder: (NSCoder*)aCoder
676
492
{
677
493
  [aCoder encodeObject: @"NSLocalTimeZone"];
678
494
}
679
495
 
 
496
- (id) init
 
497
{
 
498
  return self;
 
499
}
 
500
 
 
501
- (BOOL) isDaylightSavingTime
 
502
{
 
503
  return [[NSTimeZoneClass defaultTimeZone] isDaylightSavingTime];
 
504
}
 
505
 
 
506
- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate
 
507
{
 
508
  return [[NSTimeZoneClass defaultTimeZone] isDaylightSavingTimeForDate: aDate];
 
509
}
 
510
 
680
511
- (NSString*) name
681
512
{
682
513
  return [[NSTimeZoneClass defaultTimeZone] name];
691
522
  return self;
692
523
}
693
524
 
 
525
- (int) secondsFromGMT
 
526
{
 
527
  return [[NSTimeZoneClass defaultTimeZone] secondsFromGMT];
 
528
}
 
529
 
 
530
- (int) secondsFromGMTForDate: (NSDate*)aDate
 
531
{
 
532
  return [[NSTimeZoneClass defaultTimeZone] secondsFromGMTForDate: aDate];
 
533
}
 
534
 
694
535
- (NSArray*) timeZoneDetailArray
695
536
{
696
537
  return [[NSTimeZoneClass defaultTimeZone] timeZoneDetailArray];
701
542
  return [[NSTimeZoneClass defaultTimeZone] timeZoneDetailForDate: date];
702
543
}
703
544
 
 
545
- (NSString*) timeZoneName
 
546
{
 
547
  return [[NSTimeZoneClass defaultTimeZone] timeZoneName];
 
548
}
 
549
 
704
550
@end
705
551
  
706
 
@implementation NSConcreteAbsoluteTimeZone
 
552
@implementation GSAbsTimeZone
707
553
 
 
554
static int              uninitialisedOffset = 100000;
708
555
static NSMapTable       *absolutes = 0;
709
556
 
710
557
+ (void) initialize
711
558
{
712
 
  if (self == [NSConcreteAbsoluteTimeZone class])
 
559
  if (self == [GSAbsTimeZone class])
713
560
    {
714
561
      absolutes = NSCreateMapTable(NSIntMapKeyCallBacks,
715
 
                NSNonOwnedPointerMapValueCallBacks, 0);
 
562
        NSNonOwnedPointerMapValueCallBacks, 0);
716
563
    }
717
564
}
718
565
 
 
566
- (NSString*) abbreviationForDate: (NSDate*)aDate
 
567
{
 
568
  return name;
 
569
}
 
570
 
719
571
- (void) dealloc
720
572
{
721
 
  if (zone_mutex != nil)
722
 
    [zone_mutex lock];
723
 
  NSMapRemove(absolutes, (void*)(gsaddr)offset);
724
 
  if (zone_mutex != nil)
725
 
    [zone_mutex unlock];
 
573
  if (offset != uninitialisedOffset)
 
574
    {
 
575
      if (zone_mutex != nil)
 
576
        [zone_mutex lock];
 
577
      NSMapRemove(absolutes, (void*)(gsaddr)offset);
 
578
      if (zone_mutex != nil)
 
579
        [zone_mutex unlock];
 
580
    }
726
581
  RELEASE(name);
727
582
  RELEASE(detail);
728
583
  [super dealloc];
735
590
 
736
591
- (id) initWithOffset: (int)anOffset
737
592
{
738
 
  NSConcreteAbsoluteTimeZone    *z;
 
593
  GSAbsTimeZone *z;
 
594
  int           extra;
 
595
  int           sign = anOffset >= 0 ? 1 : -1;
 
596
 
 
597
  /*
 
598
   * Set the uninitialised offset so that dealloc before full
 
599
   * initialisation won't remove the timezeone for offset 0 from cache.
 
600
   */
 
601
  offset = uninitialisedOffset;
 
602
 
 
603
  /*
 
604
   * Round the offset to the nearest minute, (for MacOS-X compatibility)
 
605
   * and ensure it is no more than 18 hours.
 
606
   */
 
607
  anOffset *= sign;
 
608
  extra = anOffset % 60;
 
609
  if (extra < 30)
 
610
    {
 
611
      anOffset -= extra;
 
612
    }
 
613
  else
 
614
    {
 
615
      anOffset += 60 - extra;
 
616
    }
 
617
  if (anOffset > 64800)
 
618
    {
 
619
      RELEASE(self);
 
620
      return nil;
 
621
    }
 
622
  anOffset *= sign;
739
623
 
740
624
  if (zone_mutex != nil)
741
 
    [zone_mutex lock];
742
 
  z = (NSConcreteAbsoluteTimeZone*)NSMapGet(absolutes, (void*)(gsaddr)anOffset);
743
 
  if (z)
 
625
    {
 
626
      [zone_mutex lock];
 
627
    }
 
628
  z = (GSAbsTimeZone*)NSMapGet(absolutes, (void*)(gsaddr)anOffset);
 
629
  if (z != nil)
744
630
    {
745
631
      IF_NO_GC(RETAIN(z));
746
632
      RELEASE(self);
747
633
    }
748
634
  else
749
635
    {
750
 
      self = [super init];
751
 
      if (self != nil)
752
 
        {
 
636
      if (anOffset % 60 == 0)
 
637
        {
 
638
          char  s = (anOffset >= 0) ? '+' : '-';
 
639
          int   i = (anOffset >= 0) ? anOffset / 60 : -anOffset / 60;
 
640
          int   h = i / 60;
 
641
          int   m = i % 60;
 
642
          char  buf[9];
 
643
 
 
644
          sprintf(buf, "GMT%c%02d%02d", s, h, m);
 
645
          name = [[NSString alloc] initWithCString: buf];
 
646
        }
 
647
      else
 
648
        {
 
649
          /*
 
650
           * Should never happen now we round to the minute
 
651
           * for MacOS-X compatibnility.
 
652
           */
753
653
          name = [[NSString alloc] initWithFormat: @"NSAbsoluteTimeZone:%d",
754
654
            anOffset];
755
 
          detail = [[NSConcreteTimeZoneDetail alloc]
756
 
            initWithTimeZone: self withAbbrev: name
757
 
              withOffset: anOffset withDST: NO];
758
 
          offset = anOffset;
759
 
          z = self;
760
 
          NSMapInsert(absolutes, (void*)(gsaddr)anOffset, (void*)z);
761
 
          [zoneDictionary setObject: self forKey: (NSString*)name];
762
655
        }
 
656
      detail = [[GSAbsTimeZoneDetail alloc] initWithTimeZone: self];
 
657
      offset = anOffset;
 
658
      z = self;
 
659
      NSMapInsert(absolutes, (void*)(gsaddr)anOffset, (void*)z);
 
660
      [zoneDictionary setObject: self forKey: (NSString*)name];
763
661
    }
764
662
  if (zone_mutex != nil)
765
 
    [zone_mutex unlock];
 
663
    {
 
664
      [zone_mutex unlock];
 
665
    }
766
666
  return z;
767
667
}
768
668
 
 
669
- (BOOL) isDaylightSavingTimeZoneForDate: (NSDate*)aDate
 
670
{
 
671
  return NO;
 
672
}
 
673
 
769
674
- (NSString*) name
770
675
{
771
676
  return name;
772
677
}
773
678
 
 
679
- (int) secondsFromGMTForDate: (NSDate*)aDate
 
680
{
 
681
  return offset;
 
682
}
 
683
 
774
684
- (NSTimeZone*) timeZoneDetailTimeZone
775
685
{
776
686
  return [NSTimeZone arrayWithObject: detail];
781
691
  return detail;
782
692
}
783
693
  
 
694
- (NSString*) timeZoneName
 
695
{
 
696
  return name;
 
697
}
784
698
@end
785
699
  
786
700
  
787
 
@implementation NSConcreteTimeZoneDetail
 
701
@implementation GSTimeZoneDetail
788
702
  
789
703
- (void) dealloc
790
704
{
791
705
  RELEASE(timeZone);
792
 
  RELEASE(abbrev);
793
706
  [super dealloc];
794
707
}
795
708
 
806
719
             withOffset: (int)anOffset
807
720
                withDST: (BOOL)isDST
808
721
{
809
 
  [super init];
810
722
  timeZone = RETAIN(aZone);
811
 
  abbrev = RETAIN(anAbbrev);
 
723
  abbrev = anAbbrev;            // NB. Depend on this being retained in aZone
812
724
  offset = anOffset;
813
725
  is_dst = isDST;
814
726
  return self;
851
763
  
852
764
@end
853
765
 
854
 
 
 
766
 
 
767
 
 
768
@implementation GSAbsTimeZoneDetail
 
769
  
 
770
- (NSString*) abbreviation
 
771
{
 
772
  return zone->name;
 
773
}
 
774
 
 
775
- (NSString*) abbreviationForDate: (NSDate*)aDate
 
776
{
 
777
  return zone->name;
 
778
}
 
779
 
 
780
- (void) dealloc
 
781
{
 
782
  RELEASE(zone);
 
783
  [super dealloc];
 
784
}
 
785
 
 
786
- (id) initWithTimeZone: (GSAbsTimeZone*)aZone
 
787
{
 
788
  zone = RETAIN(aZone);
 
789
  return self;
 
790
}
 
791
  
 
792
- (BOOL) isDaylightSavingTimeZone
 
793
{
 
794
  return NO;
 
795
}
 
796
  
 
797
- (BOOL) isDaylightSavingTimeZoneForDate: (NSDate*)aDate
 
798
{
 
799
  return NO;
 
800
}
 
801
  
 
802
- (NSString*) name
 
803
{
 
804
  return zone->name;
 
805
}
 
806
 
 
807
- (NSString*) timeZoneAbbreviation
 
808
{
 
809
  return zone->name;
 
810
}
 
811
  
 
812
- (NSArray*) timeZoneDetailArray
 
813
{
 
814
  return [zone timeZoneDetailArray];
 
815
}
 
816
 
 
817
- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)date
 
818
{
 
819
  return self;
 
820
}
 
821
 
 
822
- (int) timeZoneSecondsFromGMT
 
823
{
 
824
  return zone->offset;
 
825
}
 
826
 
 
827
- (int) timeZoneSecondsFromGMTForDate: (NSDate*)aDate
 
828
{
 
829
  return zone->offset;
 
830
}
 
831
  
 
832
@end
 
833
 
 
834
 
 
835
 
 
836
/**
 
837
 * <p>
 
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.
 
843
 * </p>
 
844
 * <p>Time zone names in NSDates should be GMT, MET etc. not
 
845
 * Europe/Berlin, America/Washington etc.
 
846
 * </p>
 
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.
 
852
 * </p>
 
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).
 
858
 * </p>
 
859
 */
855
860
@implementation NSTimeZone
856
861
 
857
862
+ (NSDictionary*) abbreviationDictionary
958
963
    }
959
964
}
960
965
 
 
966
/**
 
967
 * Return the default time zone for this process.
 
968
 */
961
969
+ (NSTimeZone*) defaultTimeZone
962
970
{
963
971
  NSTimeZone    *zone;
964
972
 
965
973
  if (zone_mutex != nil)
966
 
    [zone_mutex lock];
 
974
    {
 
975
      [zone_mutex lock];
 
976
    }
967
977
  if (defaultTimeZone == nil)
968
978
    {
969
979
      zone = [self systemTimeZone];
971
981
  else
972
982
    {
973
983
      if (zone_mutex != nil)
974
 
        zone = AUTORELEASE(RETAIN(defaultTimeZone));
 
984
        {
 
985
          zone = AUTORELEASE(RETAIN(defaultTimeZone));
 
986
        }
975
987
      else
976
 
        zone = defaultTimeZone;
 
988
        {
 
989
          zone = defaultTimeZone;
 
990
        }
977
991
    }
978
992
  if (zone_mutex != nil)
979
 
    [zone_mutex unlock];
 
993
    {
 
994
      [zone_mutex unlock];
 
995
    }
980
996
  return zone;
981
997
}
982
998
 
997
1013
        NSNonRetainedObjectMapValueCallBacks, 0);
998
1014
 
999
1015
      localTimeZone = [[NSLocalTimeZone alloc] init];
1000
 
      [self setDefaultTimeZone: [self systemTimeZone]];
1001
1016
 
1002
1017
      fake_abbrev_dict = [[NSInternalAbbrevDict alloc] init];
1003
 
      if ([NSThread isMultiThreaded])
1004
 
        {
1005
 
          [self _becomeThreaded: nil];
1006
 
        }
1007
 
      else
1008
 
        {
1009
 
          [[NSNotificationCenter defaultCenter]
1010
 
            addObserver: self
1011
 
               selector: @selector(_becomeThreaded:)
1012
 
                   name: NSWillBecomeMultiThreadedNotification
1013
 
                 object: nil];
1014
 
        }
 
1018
      zone_mutex = [GSLazyRecursiveLock new];
1015
1019
    }
1016
1020
}
1017
1021
 
 
1022
/**
 
1023
 * Return a proxy to the default time zone for this process.
 
1024
 */
1018
1025
+ (NSTimeZone*) localTimeZone
1019
1026
{
1020
1027
  return localTimeZone;
1021
1028
}
1022
1029
 
 
1030
/**
 
1031
 * Destroy the system time zone so that it will be recreated
 
1032
 * next time it is used.
 
1033
 */
1023
1034
+ (void) resetSystemTimeZone
1024
1035
{
1025
1036
  if (zone_mutex != nil)
1026
 
    [zone_mutex lock];
 
1037
    {
 
1038
      [zone_mutex lock];
 
1039
    }
1027
1040
  DESTROY(systemTimeZone);
1028
1041
  if (zone_mutex != nil)
1029
 
    [zone_mutex unlock];
 
1042
    {
 
1043
      [zone_mutex unlock];
 
1044
    }
1030
1045
}
1031
1046
 
 
1047
/**
 
1048
 * Set the default time zone to be used for this process.
 
1049
 */
1032
1050
+ (void) setDefaultTimeZone: (NSTimeZone*)aTimeZone
1033
1051
{
1034
 
  if (zone_mutex != nil)
1035
 
    [zone_mutex lock];
1036
 
  ASSIGN(defaultTimeZone, aTimeZone);
1037
 
  if (zone_mutex != nil)
1038
 
    [zone_mutex unlock];
 
1052
  if (aTimeZone != defaultTimeZone)
 
1053
    {
 
1054
      /*
 
1055
       * We can't make the localTimeZone the default since that would
 
1056
       * cause recursion ...
 
1057
       */
 
1058
      if (aTimeZone == localTimeZone)
 
1059
        {
 
1060
          aTimeZone = [self systemTimeZone];
 
1061
        }
 
1062
      if (zone_mutex != nil)
 
1063
        {
 
1064
          [zone_mutex lock];
 
1065
        }
 
1066
      ASSIGN(defaultTimeZone, aTimeZone);
 
1067
      if (zone_mutex != nil)
 
1068
        {
 
1069
          [zone_mutex unlock];
 
1070
        }
 
1071
    }
1039
1072
}
1040
1073
 
 
1074
/**
 
1075
 * Returns the current system time zone for the process.
 
1076
 */
1041
1077
+ (NSTimeZone*) systemTimeZone
1042
1078
{
1043
1079
  NSTimeZone    *zone = nil;
1044
1080
 
1045
1081
  if (zone_mutex != nil)
1046
 
    [zone_mutex lock];
 
1082
    {
 
1083
      [zone_mutex lock];
 
1084
    }
1047
1085
  if (systemTimeZone == nil)
1048
1086
    {
1049
1087
      NSString  *localZoneString = nil;
1068
1106
          /*
1069
1107
           * Try to get timezone from LOCAL_TIME_FILE.
1070
1108
           */
1071
 
          NSString      *f;
1072
 
 
1073
 
          f = [NSBundle pathForGNUstepResource: LOCAL_TIME_FILE
1074
 
                                        ofType: @""
1075
 
                                   inDirectory: TIME_ZONE_DIR];
 
1109
          NSString      *f = _time_zone_path(LOCAL_TIME_FILE);
1076
1110
          if (f != nil)
1077
1111
            {
1078
1112
              localZoneString = [NSString stringWithContentsOfFile: f];
1079
1113
              localZoneString = [localZoneString stringByTrimmingSpaces];
1080
1114
            }
1081
1115
        }
 
1116
#if HAVE_TZSET
 
1117
      if (localZoneString == nil)
 
1118
        {
 
1119
          /*
 
1120
           * Try to get timezone from tzset and tzname
 
1121
           */
 
1122
          tzset();
 
1123
          if (tzname[0] != NULL && *tzname[0] != '\0')
 
1124
            localZoneString = [NSString stringWithCString: tzname[0]];
 
1125
        }
 
1126
#else
1082
1127
      if (localZoneString == nil)
1083
1128
        {
1084
1129
          /*
1087
1132
          localZoneString = [[[NSProcessInfo processInfo]
1088
1133
            environment] objectForKey: @"TZ"];
1089
1134
        }
 
1135
#endif
1090
1136
      if (localZoneString != nil)
1091
1137
        {
1092
1138
          zone = [defaultPlaceholderTimeZone initWithName: localZoneString];
1154
1200
  return regionsArray;
1155
1201
}
1156
1202
 
 
1203
/**
 
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
 
1207
 * minute.<br />
 
1208
 * Time zones with an offset of more than +/- 18 hours  are disallowed,
 
1209
 * and nil is returned.
 
1210
 */
1157
1211
+ (NSTimeZone*) timeZoneForSecondsFromGMT: (int)seconds
1158
1212
{
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]);
 
1213
  NSTimeZone    *zone;
 
1214
 
 
1215
  zone = [[GSAbsTimeZone alloc] initWithOffset: seconds];
 
1216
  return AUTORELEASE(zone);
1163
1217
}
1164
1218
 
 
1219
/**
 
1220
 * Returns a timezone for the specified abbrevition,
 
1221
 */
1165
1222
+ (NSTimeZone*) timeZoneWithAbbreviation: (NSString*)abbreviation
1166
1223
{
1167
1224
  NSTimeZone    *zone;
1168
1225
 
1169
1226
  zone = [self timeZoneWithName: [[self abbreviationDictionary]
1170
 
                                   objectForKey: abbreviation]];
 
1227
                                   objectForKey: abbreviation] data: nil];
1171
1228
  return zone;
1172
1229
}
1173
1230
 
 
1231
/**
 
1232
 * Returns a timezone for the specified name.
 
1233
 */
1174
1234
+ (NSTimeZone*) timeZoneWithName: (NSString*)aTimeZoneName
1175
1235
{
1176
1236
  NSTimeZone    *zone;
1177
1237
 
1178
 
  zone = [defaultPlaceholderTimeZone initWithName: aTimeZoneName];
 
1238
  zone = [defaultPlaceholderTimeZone initWithName: aTimeZoneName data: nil];
1179
1239
  return AUTORELEASE(zone);
1180
1240
}
1181
1241
 
 
1242
/**
 
1243
 * Returns a timezone for the specified name, created from the supplied data.
 
1244
 */
1182
1245
+ (NSTimeZone*) timeZoneWithName: (NSString*)name data: (NSData*)data
 
1246
{
 
1247
  NSTimeZone    *zone;
1183
1248
 
1184
 
{
1185
 
  [self notImplemented: _cmd];
1186
 
  return nil;
 
1249
  zone = [defaultPlaceholderTimeZone initWithName: name data: nil];
 
1250
  return AUTORELEASE(zone);
1187
1251
}
1188
1252
 
 
1253
/**
 
1254
 * Returns the abbreviation for this timezone now.
 
1255
 * Invokes -abbreviationForDate:
 
1256
 */
1189
1257
- (NSString*) abbreviation
1190
1258
{
1191
1259
  return [self abbreviationForDate: [NSDate date]];
1192
1260
}
1193
1261
 
1194
 
- (NSString*) abbreviationForDate: (NSDate*)when
 
1262
/**
 
1263
 * Returns the abbreviation for this timezone at aDate.  This may differ
 
1264
 * depending on whether daylight savings time is in effect or not.
 
1265
 */
 
1266
- (NSString*) abbreviationForDate: (NSDate*)aDate
1195
1267
{
1196
1268
  NSTimeZoneDetail      *detail;
1197
 
 
1198
 
  detail = [self timeZoneDetailForDate: when];
1199
 
  return [detail timeZoneAbbreviation];
 
1269
  NSString              *abbr;
 
1270
 
 
1271
  detail = [self timeZoneDetailForDate: aDate];
 
1272
  abbr = [detail timeZoneAbbreviation];
 
1273
 
 
1274
  return abbr;
1200
1275
}
1201
1276
 
1202
1277
- (Class) classForCoder
1209
1284
  return RETAIN(self);
1210
1285
}
1211
1286
 
 
1287
/**
 
1288
 * Returns the data with which the receiver was initialised.
 
1289
 */
 
1290
- (NSData*) data
 
1291
{
 
1292
  return nil;
 
1293
}
 
1294
 
1212
1295
- (NSString*) description
1213
1296
{
1214
1297
  return [self name];
1219
1302
  [aCoder encodeObject: [self name]];
1220
1303
}
1221
1304
 
 
1305
- (id) init
 
1306
{
 
1307
  return [self initWithName: @"NSLocalTimeZone" data: nil];
 
1308
}
 
1309
 
1222
1310
- (id) initWithCoder: (NSCoder*)aDecoder
1223
1311
{
1224
1312
  NSString      *name;
1225
1313
 
1226
1314
  name = [aDecoder decodeObject];
1227
 
  self = [self initWithName: name];
 
1315
  self = [self initWithName: name data: nil];
1228
1316
  return self;
1229
1317
}
1230
1318
 
 
1319
/**
 
1320
 * Initialise a timezone with the supplied name.  May return a cached
 
1321
 * timezone object rather than the newly created one.
 
1322
 */
1231
1323
- (id) initWithName: (NSString*)name
1232
1324
{
1233
 
  [self notImplemented: _cmd];
1234
 
  return nil;
 
1325
  return [self initWithName: name data: nil];
1235
1326
}
1236
1327
 
 
1328
/**
 
1329
 * Initialises a time zone object using the supplied data object.<br />
 
1330
 * This method is intended for internal use by the NSTimeZone
 
1331
 * class cluster.
 
1332
 * Don't use it ... use -initWithName: instead.
 
1333
 */
1237
1334
- (id) initWithName: (NSString*)name data: (NSData*)data
1238
1335
{
1239
1336
  [self notImplemented: _cmd];
1240
1337
  return nil;
1241
1338
}
1242
1339
 
 
1340
/**
 
1341
 * Returns a boolean indicating whether daylight savings time is in
 
1342
 * effect now.  Invokes -isDaylightSavingTimeForDate:
 
1343
 */
1243
1344
- (BOOL) isDaylightSavingTime
1244
1345
{
1245
1346
  return [self isDaylightSavingTimeForDate: [NSDate date]];
1246
1347
}
1247
1348
 
 
1349
/**
 
1350
 * Returns a boolean indicating whether daylight savings time is in
 
1351
 * effect for this time zone at aDate.
 
1352
 */
1248
1353
- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate
1249
1354
{
1250
1355
  NSTimeZoneDetail      *detail;
 
1356
  BOOL                  isDST;
1251
1357
 
1252
1358
  detail = [self timeZoneDetailForDate: aDate];
1253
 
  return [detail isDaylightSavingTimeZone];
 
1359
  isDST = [detail isDaylightSavingTimeZone];
 
1360
 
 
1361
  return isDST;
1254
1362
}
1255
1363
 
1256
1364
- (BOOL) isEqual: (id)other
1266
1374
{
1267
1375
  if (aTimeZone == self)
1268
1376
    return YES;
1269
 
  if ([[self name] isEqual: [aTimeZone name]] == YES)
 
1377
  if ([[self name] isEqual: [aTimeZone name]] == NO)
 
1378
    return NO;
 
1379
  if (([self data] == nil && [aTimeZone data] == nil)
 
1380
    || [[self name] isEqual: [aTimeZone name]] == YES)
1270
1381
    return YES;
1271
1382
  return NO;
1272
1383
}
1279
1390
- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder
1280
1391
{
1281
1392
  if ([aCoder isByref] == NO)
1282
 
    return self;
 
1393
    {
 
1394
      return self;
 
1395
    }
1283
1396
  return [super replacementObjectForPortCoder: aCoder];
1284
1397
}
1285
1398
 
 
1399
/**
 
1400
 * Returns the number of seconds by which the receiver differs
 
1401
 * from Greenwich Mean Time at the current date and time.<br />
 
1402
 * Invokes -secondsFromGMTForDate:
 
1403
 */
1286
1404
- (int) secondsFromGMT
1287
1405
{
1288
1406
  return [self secondsFromGMTForDate: [NSDate date]];
1289
1407
}
1290
1408
 
1291
 
- (int) secondsFromGMTForDate: (NSDate*)when
 
1409
/**
 
1410
 * Returns the number of seconds by which the receiver differs
 
1411
 * from Greenwich Mean Time at the date aDate.<br />
 
1412
 * If the time zone uses dayl;ight savings time, the returned value
 
1413
 * will vary at different times of year.
 
1414
 */
 
1415
- (int) secondsFromGMTForDate: (NSDate*)aDate
1292
1416
{
1293
1417
  NSTimeZoneDetail      *detail;
1294
 
 
1295
 
  detail = [self timeZoneDetailForDate: when];
1296
 
  return [detail timeZoneSecondsFromGMT];
 
1418
  int                   offset;
 
1419
 
 
1420
  detail = [self timeZoneDetailForDate: aDate];
 
1421
  offset = [detail timeZoneSecondsFromGMT];
 
1422
 
 
1423
  return offset;
1297
1424
}
1298
1425
 
1299
1426
- (NSArray*) timeZoneDetailArray
1313
1440
 
1314
1441
@end
1315
1442
  
1316
 
 
 
1443
/**
 
1444
 * This class serves no useful purpose in GNUstep, and is provided
 
1445
 * solely for backward compatibility with the OpenStep spec.  It is
 
1446
 * missing entirely from MacOS-X.
 
1447
 */
1317
1448
@implementation NSTimeZoneDetail
1318
1449
 
1319
1450
- (NSString*) description
1320
1451
{
1321
 
  return [NSString
1322
 
           stringWithFormat: @"%@(%@, %s%d)",
1323
 
           [self name],
1324
 
           [self timeZoneAbbreviation],
1325
 
           ([self isDaylightSavingTimeZone]? "IS_DST, ": ""),
1326
 
           [self timeZoneSecondsFromGMT]];
 
1452
  return [NSString stringWithFormat: @"%@(%@, %s%d)", [self name],
 
1453
    [self timeZoneAbbreviation],
 
1454
    ([self isDaylightSavingTimeZone]? "IS_DST, ": ""),
 
1455
    [self timeZoneSecondsFromGMT]];
1327
1456
}
1328
1457
 
1329
1458
- (BOOL) isDaylightSavingTimeZone
1348
1477
 
1349
1478
@implementation NSTimeZone (Private)
1350
1479
 
1351
 
/*
1352
 
 *      When the system becomes multithreaded, we set a flag to say so
1353
 
 */
1354
 
+ (void) _becomeThreaded: (NSNotification*)notification
1355
 
{
1356
 
  if (zone_mutex == nil)
1357
 
    {
1358
 
      zone_mutex = [NSRecursiveLock new];
1359
 
    }
1360
 
  [[NSNotificationCenter defaultCenter]
1361
 
    removeObserver: self
1362
 
              name: NSWillBecomeMultiThreadedNotification
1363
 
            object: nil];
1364
 
}
1365
 
 
1366
1480
+ (NSString*) getAbbreviationFile
1367
1481
{
1368
 
  return [NSBundle pathForGNUstepResource: ABBREV_DICT
1369
 
                   ofType: @""
1370
 
                   inDirectory: TIME_ZONE_DIR];
 
1482
  return _time_zone_path (ABBREV_DICT);
1371
1483
}
1372
1484
 
1373
1485
+ (NSString*) getRegionsFile
1374
1486
{
1375
 
  return [NSBundle pathForGNUstepResource: REGIONS_FILE
1376
 
                   ofType: @""
1377
 
                   inDirectory: TIME_ZONE_DIR];
 
1487
  return _time_zone_path (REGIONS_FILE);
1378
1488
}
1379
1489
 
1380
1490
+ (NSString*) getTimeZoneFile: (NSString *)name
1381
1491
{
1382
 
  NSString *dir = [NSString stringWithFormat: @"%@/%@", TIME_ZONE_DIR, ZONES_DIR];
1383
 
  NSString *path = [NSBundle pathForGNUstepResource: name
1384
 
                                             ofType: @""
1385
 
                                        inDirectory: dir];
1386
 
  return path;
1387
 
}
1388
 
 
1389
 
@end
 
1492
  NSString      *dir;
 
1493
 
 
1494
  dir= _time_zone_path (ZONES_DIR);
 
1495
  return [dir stringByAppendingPathComponent: name];
 
1496
}
 
1497
 
 
1498
@end
 
1499
 
 
1500
 
 
1501
 
 
1502
@implementation GSTimeZone
 
1503
 
 
1504
/**
 
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.
 
1509
 */
 
1510
static TypeInfo*
 
1511
chop(NSTimeInterval since, GSTimeZone *zone)
 
1512
{
 
1513
  gss32                 when = (gss32)since;
 
1514
  gss32                 *trans = zone->trans;
 
1515
  unsigned              hi = zone->n_trans;
 
1516
  unsigned              lo = 0;
 
1517
  unsigned int          i;
 
1518
 
 
1519
  if (hi == 0 || trans[0] > when)
 
1520
    {
 
1521
      unsigned  n_types = zone->n_types;
 
1522
 
 
1523
      /*
 
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.
 
1527
       */
 
1528
      for (i = 0; i < n_types; i++)
 
1529
        {
 
1530
          if (zone->types[i].isdst == 0)
 
1531
            {
 
1532
              return &zone->types[i];
 
1533
            }
 
1534
        }
 
1535
      return &zone->types[0];
 
1536
    }
 
1537
  else
 
1538
    {
 
1539
      for (i = hi/2; hi != lo; i = (hi + lo)/2)
 
1540
        {
 
1541
          if (when < trans[i])
 
1542
            {
 
1543
              hi = i;
 
1544
            }
 
1545
          else if (when > trans[i])
 
1546
            {
 
1547
              lo = ++i;
 
1548
            }
 
1549
          else
 
1550
            {
 
1551
              break;
 
1552
            }
 
1553
        }
 
1554
      /*
 
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.
 
1558
       */
 
1559
      if (i > 0 && (i == zone->n_trans || trans[i] > when))
 
1560
        {
 
1561
          i--;
 
1562
        }
 
1563
      return &zone->types[zone->idxs[i]];
 
1564
    }
 
1565
}
 
1566
 
 
1567
static NSTimeZoneDetail*
 
1568
newDetailInZoneForType(GSTimeZone *zone, TypeInfo *type)
 
1569
{
 
1570
  GSTimeZoneDetail      *detail;
 
1571
 
 
1572
  detail = [GSTimeZoneDetail alloc];
 
1573
  detail = [detail initWithTimeZone: zone
 
1574
                         withAbbrev: type->abbreviation
 
1575
                         withOffset: type->offset
 
1576
                            withDST: type->isdst];
 
1577
  return detail;
 
1578
}
 
1579
 
 
1580
- (NSString*) abbreviationForDate: (NSDate*)aDate
 
1581
{
 
1582
  TypeInfo      *type = chop([aDate timeIntervalSince1970], self);
 
1583
 
 
1584
  return type->abbreviation;
 
1585
}
 
1586
 
 
1587
- (NSData*) data
 
1588
{
 
1589
  return timeZoneData;
 
1590
}
 
1591
 
 
1592
- (void) dealloc
 
1593
{
 
1594
  RELEASE(timeZoneName);
 
1595
  RELEASE(timeZoneData);
 
1596
  if (types != 0)
 
1597
    {
 
1598
      unsigned  i;
 
1599
 
 
1600
      for (i = 0; i < n_types; i++)
 
1601
        {
 
1602
          RELEASE(types[i].abbreviation);
 
1603
        }
 
1604
      NSZoneFree(NSDefaultMallocZone(), types);
 
1605
    }
 
1606
  [super dealloc];
 
1607
}
 
1608
 
 
1609
- (id) initWithName: (NSString*)name data: (NSData*)data
 
1610
{
 
1611
  static NSString       *fileException = @"GSTimeZoneFileException";
 
1612
 
 
1613
  timeZoneName = [name copy];
 
1614
  timeZoneData = [data copy];
 
1615
  NS_DURING
 
1616
    {
 
1617
      const void        *bytes = [timeZoneData bytes];
 
1618
      unsigned          length = [timeZoneData length];
 
1619
      void              *buf;
 
1620
      unsigned          pos = 0;
 
1621
      unsigned          i, charcnt;
 
1622
      unsigned char     *abbr;
 
1623
      struct tzhead     *header;
 
1624
 
 
1625
      if (length < sizeof(struct tzhead))
 
1626
        {
 
1627
          [NSException raise: fileException
 
1628
                      format: @"File is too small"];
 
1629
        }
 
1630
      header = (struct tzhead *)(bytes + pos);
 
1631
      pos += sizeof(struct tzhead);
 
1632
      if (memcmp(header->tzh_magic, TZ_MAGIC, strlen(TZ_MAGIC)) != 0)
 
1633
        {
 
1634
          [NSException raise: fileException
 
1635
                      format: @"TZ_MAGIC is incorrect"];
 
1636
        }
 
1637
      n_trans = GSSwapBigI32ToHost(*(gss32*)header->tzh_timecnt);
 
1638
      n_types = GSSwapBigI32ToHost(*(gss32*)header->tzh_typecnt);
 
1639
      charcnt = GSSwapBigI32ToHost(*(gss32*)header->tzh_charcnt);
 
1640
 
 
1641
      i = pos;
 
1642
      i += sizeof(gss32)*n_trans;
 
1643
      if (i > length)
 
1644
        {
 
1645
          [NSException raise: fileException
 
1646
                      format: @"Transitions list is truncated"];
 
1647
        }
 
1648
      i += n_trans;
 
1649
      if (i > length)
 
1650
        {
 
1651
          [NSException raise: fileException
 
1652
                      format: @"Transition indexes are truncated"];
 
1653
        }
 
1654
      i += sizeof(struct ttinfo)*n_types;
 
1655
      if (i > length)
 
1656
        {
 
1657
          [NSException raise: fileException
 
1658
                      format: @"Types list is truncated"];
 
1659
        }
 
1660
      if (i + charcnt > length)
 
1661
        {
 
1662
          [NSException raise: fileException
 
1663
                      format: @"Abbreviations list is truncated"];
 
1664
        }
 
1665
 
 
1666
      /*
 
1667
       * Now calculate size we need to store the information
 
1668
       * for efficient access ... not the same saze as the data
 
1669
       * we received.
 
1670
       */
 
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;
 
1678
 
 
1679
      /* Read in transitions. */
 
1680
      for (i = 0; i < n_trans; i++)
 
1681
        {
 
1682
          trans[i] = GSSwapBigI32ToHost(*(gss32*)(bytes + pos));
 
1683
          pos += sizeof(gss32);
 
1684
        }
 
1685
      for (i = 0; i < n_trans; i++)
 
1686
        {
 
1687
          idxs[i] = *(unsigned char*)(bytes + pos);
 
1688
          pos++;
 
1689
        }
 
1690
      for (i = 0; i < n_types; i++)
 
1691
        {
 
1692
          struct ttinfo *ptr = (struct ttinfo*)(bytes + pos);
 
1693
 
 
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);
 
1698
        }
 
1699
      abbr = (char*)(bytes + pos);
 
1700
      {
 
1701
        id      abbrevs[charcnt];
 
1702
 
 
1703
        memset(abbrevs, '\0', sizeof(id)*charcnt);
 
1704
        for (i = 0; i < n_types; i++)
 
1705
          {
 
1706
            int loc = types[i].abbr_idx;
 
1707
 
 
1708
            if (abbrevs[loc] == nil)
 
1709
              {
 
1710
                abbrevs[loc] = [[NSString alloc] initWithCString: abbr + loc];
 
1711
              }
 
1712
            else
 
1713
              {
 
1714
                RETAIN(abbrevs[loc]);
 
1715
              }
 
1716
            types[i].abbreviation = abbrevs[loc];
 
1717
          }
 
1718
      }
 
1719
 
 
1720
      if (zone_mutex != nil)
 
1721
        {
 
1722
          [zone_mutex lock];
 
1723
        }
 
1724
      [zoneDictionary setObject: self forKey: timeZoneName];
 
1725
      if (zone_mutex != nil)
 
1726
        {
 
1727
          [zone_mutex unlock];
 
1728
        }
 
1729
    }
 
1730
  NS_HANDLER
 
1731
    {
 
1732
      DESTROY(self);
 
1733
      NSLog(@"Unable to obtain time zone `%@'... %@", name, localException);
 
1734
      if ([localException name] != fileException)
 
1735
        {
 
1736
          [localException raise];
 
1737
        }
 
1738
    }
 
1739
  NS_ENDHANDLER
 
1740
  return self;
 
1741
}
 
1742
 
 
1743
- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate
 
1744
{
 
1745
  TypeInfo      *type = chop([aDate timeIntervalSince1970], self);
 
1746
 
 
1747
  return type->isdst;
 
1748
}
 
1749
 
 
1750
- (NSString*) name
 
1751
{
 
1752
  return timeZoneName;
 
1753
}
 
1754
 
 
1755
- (int) secondsFromGMTForDate: (NSDate*)aDate
 
1756
{
 
1757
  TypeInfo      *type = chop([aDate timeIntervalSince1970], self);
 
1758
 
 
1759
  return type->offset;
 
1760
}
 
1761
 
 
1762
- (NSArray*) timeZoneDetailArray
 
1763
{
 
1764
  NSTimeZoneDetail      *details[n_types];
 
1765
  unsigned              i;
 
1766
  NSArray               *array;
 
1767
 
 
1768
  for (i = 0; i < n_types; i++)
 
1769
    {
 
1770
      details[i] = newDetailInZoneForType(self, &types[i]);
 
1771
    }
 
1772
  array = [NSArray arrayWithObjects: details count: n_types];
 
1773
  for (i = 0; i < n_types; i++)
 
1774
    {
 
1775
      RELEASE(details[i]);
 
1776
    }
 
1777
  return array;
 
1778
}
 
1779
 
 
1780
- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)aDate
 
1781
{
 
1782
  TypeInfo              *type;
 
1783
  NSTimeZoneDetail      *detail;
 
1784
 
 
1785
  type = chop([aDate timeIntervalSince1970], self);
 
1786
  detail = newDetailInZoneForType(self, type);
 
1787
  return AUTORELEASE(detail);
 
1788
}
 
1789
 
 
1790
- (NSString*) timeZoneName
 
1791
{
 
1792
  return timeZoneName;
 
1793
}
 
1794
 
 
1795
@end
 
1796