~ubuntu-branches/ubuntu/karmic/gnustep-base/karmic

« back to all changes in this revision

Viewing changes to Source/NSTimeZone.m

  • Committer: Bazaar Package Importer
  • Author(s): Eric Heintzmann
  • Date: 2005-04-17 00:14:38 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050417001438-enf0y07c9tku85z1
Tags: 1.10.3-1
New upstream release.

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.
3
 
  
 
2
   Copyright (C) 1997-2002 Free Software Foundation, Inc.
 
3
 
4
4
   Written by: Yoo C. Chung <wacko@laplace.snu.ac.kr>
5
5
   Date: June 1997
6
 
  
 
6
 
 
7
   Rewrite 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
10
13
   modify it under the terms of the GNU Library General Public License
11
14
   as published by the Free Software Foundation; either version 2 of
12
15
   the License, or (at your option) any later version.
13
 
  
 
16
 
14
17
   This library is distributed in the hope that it will be useful, but
15
18
   WITHOUT ANY WARRANTY; without even the implied warranty of
16
19
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
20
   Library General Public License for more details.
18
 
  
 
21
 
19
22
   You should have received a copy of the GNU Library General Public
20
23
   License along with this library; if not, write to the Free Software
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: 2005/02/22 11:22:44 $ $Revision: 1.70 $
25
28
 */
26
 
  
27
 
/* We use a implementation independent of the system, since POSIX
28
 
   functions for time zones are woefully inadequate for implementing
29
 
   NSTimeZone, and time zone names can be different from system to
30
 
   system.
 
29
 
 
30
/* Use the system time zones if available. In other cases, use an
 
31
   implementation independent of the system, since POSIX functions for
 
32
   time zones are woefully inadequate for implementing NSTimeZone.
 
33
   Time zone names can be different from system to system, but usually
 
34
   the user has already set up his timezone independant of GNUstep, so we
 
35
   should respect that information.
31
36
 
32
37
   We do not use a dictionary for storing time zones, since such a
33
38
   dictionary would be VERY large (~500K).  And we would have to use a
36
41
   eventually have to change the implementation to prevent the year
37
42
   2038 problem.)
38
43
 
39
 
   The local time zone can be specified with the user defaults
40
 
   database, the GNUSTEP_TZ environment variable, the file LOCAL_TIME_FILE,
41
 
   the TZ environment variable, or the fallback time zone (which is UTC),
 
44
   The local time zone can be specified with:
 
45
    1) the user defaults database
 
46
    2) the GNUSTEP_TZ environment variable
 
47
    3) the file LOCAL_TIME_FILE in _time_zone_path()
 
48
    4) the TZ environment variable
 
49
    5) TZDEFAULT defined in tzfile.h on platforms which have it
 
50
    6) tzset() & tznam[] for platforms which have it
 
51
    7) Windows registry, for Win32 systems
 
52
    8) or the fallback time zone (which is UTC)
42
53
   with the ones listed first having precedence.
43
54
 
44
55
   Any time zone must be a file name in ZONES_DIR.
45
56
 
 
57
   Files & File System Heirarchy info:
 
58
   ===================================
 
59
 
 
60
   Default place for the NSTimeZone directory is _time_zone_path():
 
61
     {$(GNUSTEP_SYSTEM_ROOT)Libary/Libraries/Resources/TIME_ZONE_DIR}
 
62
 
 
63
   LOCAL_TIME_FILE is a text file with the name of the time zone file.
 
64
 
 
65
   ZONES_DIR is a sub-directory under TIME_ZONE_DIR
 
66
 
 
67
   (dir) ../System/Library/Libraries/Resources/..
 
68
   (dir)     NSTimeZone
 
69
   (file)      localtime {text; time zone eg Australia/Perth}
 
70
   (dir)       zones
 
71
 
 
72
   Note that full zone info is required, especially the various "GMT"
 
73
   files which are created especially for OPENSTEP compatibility.
 
74
   Zone info comes from the Olson time database.
 
75
 
46
76
   FIXME?: use leap seconds? */
47
77
 
48
 
#include <config.h>
49
 
#include <base/preface.h>
 
78
#include "config.h"
 
79
#include "GNUstepBase/preface.h"
 
80
#include "GNUstepBase/GSLock.h"
50
81
#include <limits.h>
51
82
#include <stdio.h>
52
83
#include <stdlib.h>
53
84
#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>
 
85
#include <time.h>
 
86
#include "Foundation/NSArray.h"
 
87
#include "Foundation/NSCoder.h"
 
88
#include "Foundation/NSData.h"
 
89
#include "Foundation/NSDate.h"
 
90
#include "Foundation/NSDictionary.h"
 
91
#include "Foundation/NSException.h"
 
92
#include "Foundation/NSFileManager.h"
 
93
#include "Foundation/NSLock.h"
 
94
#include "Foundation/NSObject.h"
 
95
#include "Foundation/NSProcessInfo.h"
 
96
#include "Foundation/NSString.h"
 
97
#include "Foundation/NSUserDefaults.h"
 
98
#include "Foundation/NSUtilities.h"
 
99
#include "Foundation/NSZone.h"
 
100
#include "Foundation/NSBundle.h"
 
101
#include "Foundation/NSMapTable.h"
 
102
#include "Foundation/NSThread.h"
 
103
#include "Foundation/NSNotification.h"
 
104
#include "Foundation/NSPortCoder.h"
 
105
#include "Foundation/NSTimeZone.h"
 
106
#include "Foundation/NSByteOrder.h"
 
107
#include "Foundation/NSDebug.h"
 
108
#include "GNUstepBase/GSCategories.h"
 
109
#include "GSConfig.h"
74
110
 
 
111
#ifdef HAVE_TZHEAD
 
112
#include <tzfile.h>
 
113
#else
75
114
#define NOID
76
 
#include "tzfile.h"
77
 
 
 
115
#include "nstzfile.h"
 
116
#endif
78
117
 
79
118
/* Key for local time zone in user defaults. */
80
119
#define LOCALDBKEY @"Local Time Zone"
81
120
 
82
 
/* Directory that contains the time zone data. */
 
121
/* Directory that contains the time zone data.
 
122
   Expected in Resources directory for library bundle. */
83
123
#define TIME_ZONE_DIR @"NSTimeZones"
84
124
 
85
 
/* Location of time zone abbreviation dictionary.  It is a text file
 
125
/* Name of time zone abbreviation (plist) dictionary.  */
 
126
#define ABBREV_DICT @"abbreviations"
 
127
 
 
128
/* Name of time zone abbreviation map.  It is a text file
86
129
   with each line comprised of the abbreviation, a whitespace, and the
87
130
   name.  Neither the abbreviation nor the name can contain
88
131
   whitespace, and each line must not be longer than 80 characters. */
89
 
#define ABBREV_DICT @"abbreviations"
 
132
#define ABBREV_MAP @"abbreviations"
90
133
 
91
134
/* File holding regions grouped by latitude.  It is a text file with
92
135
   each line comprised of the latitude region, whitespace, and the
100
143
/* Directory that contains the actual time zones. */
101
144
#define ZONES_DIR @"zones/"
102
145
 
103
 
 
104
 
@class NSInternalTimeTransition;
105
 
@class NSConcreteTimeZone;
106
 
@class NSConcreteAbsoluteTimeZone;
107
 
@class NSConcreteTimeZoneDetail;
108
 
 
109
 
@class  GSPlaceholderTimeZone;
 
146
/* Many systems have this file */
 
147
#define SYSTEM_TIME_FILE @"/etc/localtime"
 
148
 
 
149
/* Possible location of system time zone files */
 
150
static NSString *tzdir = nil;
 
151
 
 
152
@class GSAbsTimeZone;
 
153
@class GSTimeZoneDetail;
 
154
@class GSAbsTimeZoneDetail;
 
155
 
 
156
@class GSPlaceholderTimeZone;
110
157
 
111
158
/*
112
159
 * Information for abstract placeholder class.
114
161
static GSPlaceholderTimeZone    *defaultPlaceholderTimeZone;
115
162
static NSMapTable               *placeholderMap;
116
163
 
117
 
/* Temporary structure for holding time zone details. */
 
164
/*
 
165
 * Temporary structure for holding time zone details.
 
166
 * This is the format in the data object.
 
167
 */
118
168
struct ttinfo
119
169
{
120
 
  char offset[4]; // Seconds east of UTC
121
 
  BOOL isdst; // Daylight savings time?
122
 
  char abbr_idx; // Index into time zone abbreviations string
123
 
};
124
 
 
 
170
  char offset[4];         // Seconds east of UTC
 
171
  unsigned char isdst;    // Daylight savings time?
 
172
  unsigned char abbr_idx; // Index into time zone abbreviations string
 
173
} __attribute__((packed));
 
174
 
 
175
/*
 
176
 * And this is the structure used in the time zone instances.
 
177
 */
 
178
typedef struct {
 
179
  gss32         offset;
 
180
  BOOL          isdst;
 
181
  unsigned char abbr_idx;
 
182
  char          pad[2];
 
183
  NSString      *abbreviation;
 
184
} TypeInfo;
 
185
 
 
186
@interface      GSTimeZone : NSTimeZone
 
187
{
 
188
@public
 
189
  NSString      *timeZoneName;
 
190
  NSData        *timeZoneData;
 
191
  unsigned int  n_trans;
 
192
  unsigned int  n_types;
 
193
  gss32         *trans;
 
194
  TypeInfo      *types;
 
195
  unsigned char *idxs;
 
196
}
 
197
@end
 
198
 
 
199
#ifdef WIN32
 
200
@interface      GSWindowsTimeZone : NSTimeZone
 
201
{
 
202
@public
 
203
  NSString      *timeZoneName;
 
204
  NSString      *daylightZoneName;
 
205
  NSString      *timeZoneNameAbbr;
 
206
  NSString      *daylightZoneNameAbbr;
 
207
  LONG          Bias;
 
208
  LONG          StandardBias;
 
209
  LONG          DaylightBias;
 
210
  SYSTEMTIME    StandardDate;
 
211
  SYSTEMTIME    DaylightDate;
 
212
}
 
213
@end
 
214
#endif
125
215
 
126
216
static NSTimeZone       *defaultTimeZone = nil;
127
217
static NSTimeZone       *localTimeZone = nil;
131
221
   name. */
132
222
static NSMutableDictionary *zoneDictionary;
133
223
 
134
 
/* Fake one-to-one abbreviation to time zone name dictionary. */
135
 
static NSDictionary *fake_abbrev_dict;
 
224
/* one-to-one abbreviation to time zone name dictionary. */
 
225
static NSMutableDictionary *abbreviationDictionary = nil;
 
226
/* one-to-many abbreviation to time zone name dictionary. */
 
227
static NSMutableDictionary *abbreviationMap = nil;
136
228
 
137
229
/* Lock for creating time zones. */
138
230
static NSRecursiveLock *zone_mutex = nil;
165
257
#endif /* defined(WORDS_BIGENDIAN) && SIZEOF_INT == 4 */
166
258
}
167
259
 
168
 
 
169
 
/* Object enumerator for NSInternalAbbrevDict. */
170
 
@interface NSInternalAbbrevDictObjectEnumerator : NSEnumerator
171
 
{
172
 
  NSEnumerator *dict_enum;
173
 
}
174
 
 
175
 
- (id) initWithDict: (NSDictionary*)aDict;
176
 
@end
177
 
 
178
 
 
179
 
/* Front end that actually uses [NSTimeZone abbrebiationMap]. */
180
 
@interface NSInternalAbbrevDict : NSDictionary
181
 
@end
182
 
  
183
 
  
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
 
  
 
260
/* Return path to a TimeZone directory file */
 
261
static NSString *_time_zone_path(NSString *subpath, NSString *type)
 
262
{
 
263
  NSBundle *gbundle;
 
264
  if (type == nil)
 
265
    type = @"";
 
266
  gbundle = [NSBundle bundleForLibrary: @"gnustep-base"];
 
267
  return [gbundle pathForResource: subpath
 
268
                           ofType: type
 
269
                      inDirectory: TIME_ZONE_DIR];
 
270
}
 
271
 
195
272
@interface GSPlaceholderTimeZone : NSTimeZone
196
273
@end
197
 
  
198
 
@interface NSConcreteTimeZone : NSTimeZone
 
274
 
 
275
@interface GSAbsTimeZone : NSTimeZone
199
276
{
 
277
@public
200
278
  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.
 
279
  id            detail;
 
280
  int           offset; // Offset from UTC in seconds.
216
281
}
217
282
 
218
283
- (id) initWithOffset: (int)anOffset;
219
284
@end
220
 
  
 
285
 
221
286
@interface NSLocalTimeZone : NSTimeZone
222
287
@end
223
288
 
224
 
 
225
 
@interface NSConcreteTimeZoneDetail : NSTimeZoneDetail
 
289
@interface GSTimeZoneDetail : NSTimeZoneDetail
226
290
{
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?
 
291
  NSTimeZone    *timeZone; // Time zone which created this object.
 
292
  NSString      *abbrev; // Abbreviation for time zone detail.
 
293
  int           offset; // Offset from UTC in seconds.
 
294
  BOOL          is_dst; // Is it daylight savings time?
231
295
}
232
296
 
233
297
- (id) initWithTimeZone: (NSTimeZone*)aZone
235
299
             withOffset: (int)anOffset
236
300
                withDST: (BOOL)isDST;
237
301
@end
238
 
  
 
302
 
 
303
@interface GSAbsTimeZoneDetail : NSTimeZoneDetail
 
304
{
 
305
  GSAbsTimeZone *zone; // Time zone which created this object.
 
306
}
 
307
 
 
308
- (id) initWithTimeZone: (GSAbsTimeZone*)aZone;
 
309
@end
 
310
 
239
311
/* Private methods for obtaining resource file names. */
240
312
@interface NSTimeZone (Private)
241
 
+ (void) _becomeThreaded: (NSNotification*)notification;
242
 
+ (NSString*) getAbbreviationFile;
243
 
+ (NSString*) getRegionsFile;
244
313
+ (NSString*) getTimeZoneFile: (NSString*)name;
245
314
@end
246
315
 
247
 
 
248
 
@implementation NSInternalAbbrevDictObjectEnumerator
249
 
 
250
 
- (void) dealloc
251
 
{
252
 
  RELEASE(dict_enum);
253
 
}
254
 
 
255
 
- (id) initWithDict: (NSDictionary*)aDict
256
 
{
257
 
  dict_enum = RETAIN([aDict objectEnumerator]);
258
 
  return self;
259
 
}
260
 
 
261
 
- (id) nextObject
262
 
{
263
 
  id object;
264
 
 
265
 
  object = [dict_enum nextObject];
266
 
  if (object != nil)
267
 
    return [object objectAtIndex: 0];
268
 
  else
269
 
    return nil;
270
 
}
271
 
 
272
 
@end
273
 
 
274
 
 
275
 
@implementation NSInternalAbbrevDict
276
 
 
277
 
+ (id) allocWithZone: (NSZone*)zone
278
 
{
279
 
  return NSAllocateObject(self, 0, zone);
280
 
}
281
 
 
282
 
- (id) init
283
 
{
284
 
  return self;
285
 
}
286
 
 
287
 
- (unsigned) count
288
 
{
289
 
  return [[NSTimeZone abbreviationMap] count];
290
 
}
291
 
 
292
 
- (NSEnumerator*) keyEnumerator
293
 
{
294
 
  return [[NSTimeZone abbreviationMap] keyEnumerator];
295
 
}
296
 
 
297
 
- (NSEnumerator*) objectEnumerator
298
 
{
299
 
  return AUTORELEASE([[NSInternalAbbrevDictObjectEnumerator alloc]
300
 
            initWithDict: [NSTimeZone abbreviationMap]]);
301
 
}
302
 
  
303
 
- (id) objectForKey: (NSString*)key
304
 
{
305
 
  return [[[NSTimeZone abbreviationMap] objectForKey: key] objectAtIndex: 0];
306
 
}
307
 
  
308
 
@end
309
 
 
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
316
 
340
317
@implementation GSPlaceholderTimeZone
341
318
 
345
322
  return self;          // placeholders never get released.
346
323
}
347
324
 
348
 
- (id) objectAtIndex: (unsigned)index
349
 
{
350
 
  [NSException raise: NSInternalInconsistencyException
351
 
              format: @"attempt to use uninitialised time zone"];
352
 
  return 0;
353
 
}
354
 
 
355
325
- (void) dealloc
356
326
{
357
327
  return;               // placeholders never get deallocated.
358
328
}
359
329
 
360
 
- (id) initWithName: (NSString*)name
 
330
- (id) initWithName: (NSString*)name data: (NSData*)data
361
331
{
362
332
  NSTimeZone    *zone;
 
333
  unsigned      length = [name length];
363
334
 
364
 
  /*
365
 
   * Quick return if it's the local timezone.
366
 
   */
367
 
  if ([name isEqual: @"NSLocalTimeZone"])
368
 
    {
369
 
      return [self initWithName: name data: nil];
 
335
  if (length == 0)
 
336
    {
 
337
      NSLog(@"Disallowed null time zone name");
 
338
      return nil;
 
339
    }
 
340
  if (length == 15 && [name isEqual: @"NSLocalTimeZone"])
 
341
    {
 
342
      zone = RETAIN(localTimeZone);
 
343
      DESTROY(self);
 
344
      return zone;
370
345
    }
371
346
 
372
347
  /*
373
348
   * Return a cached time zone if possible.
 
349
   * NB. if data of cached zone does not match new data ... don't use cache
374
350
   */
375
351
  if (zone_mutex != nil)
376
352
    {
377
353
      [zone_mutex lock];
378
354
    }
379
355
  zone = [zoneDictionary objectForKey: name];
 
356
  if (data != nil && [data isEqual: [zone data]] == NO)
 
357
    {
 
358
      zone = nil;
 
359
    }
380
360
  IF_NO_GC(RETAIN(zone));
381
361
  if (zone_mutex != nil)
382
362
    {
383
363
      [zone_mutex unlock];
384
364
    }
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
 
    }
 
365
 
 
366
  if (zone == nil)
 
367
    {
 
368
      unichar   c;
 
369
      unsigned  i;
 
370
 
 
371
      if (length == 8 && [name hasPrefix: @"GMT"] == YES
 
372
        && ((c = [name characterAtIndex: 3]) == '+' || c == '-'))
 
373
        {
 
374
          c = [name characterAtIndex: 4];
 
375
          if (c >= '0' && c <= '9')
 
376
            {
 
377
              i = c - '0';
 
378
              c = [name characterAtIndex: 5];
 
379
              if (c >= '0' && c <= '9')
 
380
                {
 
381
                  i = i * 10 + (c - '0');
 
382
                  c = [name characterAtIndex: 6];
 
383
                  if (c >= '0' && c <= '9')
 
384
                    {
 
385
                      i = i * 6 + (c - '0');
 
386
                      c = [name characterAtIndex: 7];
 
387
                      if (c >= '0' && c <= '9')
 
388
                        {
 
389
                          i = i * 10 + (c - '0');
 
390
                          zone = [[GSAbsTimeZone alloc] initWithOffset: i*60];
 
391
                        }
 
392
                    }
 
393
                }
 
394
            }
 
395
        }
 
396
 
 
397
      if (zone == nil && length > 19
 
398
        && [name hasPrefix: @"NSAbsoluteTimeZone:"] == YES)
 
399
        {
 
400
          i = [[name substringFromIndex: 19] intValue];
 
401
 
 
402
          zone = [[GSAbsTimeZone alloc] initWithOffset: i];
 
403
        }
 
404
 
 
405
      if (zone == nil)
 
406
        {
 
407
          if (data == nil)
 
408
            {
 
409
              NSString          *fileName;
 
410
              const char        *str = [name UTF8String];
 
411
 
 
412
              /* Make sure that only time zone files are accessed.
 
413
                 FIXME: Make this more robust. */
 
414
              if ((str)[0] == '/' || strchr(str, '.') != NULL)
 
415
                {
 
416
                  NSLog(@"Disallowed time zone name `%@'.", name);
 
417
                  return nil;
 
418
                }
 
419
 
 
420
              fileName = [NSTimeZoneClass getTimeZoneFile: name];
 
421
              if (fileName == nil || ![[NSFileManager defaultManager] fileExistsAtPath:fileName])
 
422
#ifdef WIN32
 
423
                {
 
424
                  zone = [[GSWindowsTimeZone alloc] initWithName:name data:0];
 
425
                  RELEASE(self);
 
426
                  return zone;
 
427
                }
 
428
#else
 
429
                {
 
430
                  NSLog(@"Unknown time zone name `%@'.", name);
 
431
                  return nil;
 
432
                }
 
433
#endif
 
434
              data = [NSData dataWithContentsOfFile: fileName];
 
435
            }
 
436
#ifdef WIN32
 
437
        if (!data)
 
438
          zone = [[GSWindowsTimeZone alloc] initWithName: name data: data];
 
439
        else
 
440
#endif
 
441
          zone = [[GSTimeZone alloc] initWithName: name data: data];
 
442
        }
 
443
    }
 
444
  RELEASE(self);
576
445
  return zone;
577
446
}
578
447
 
588
457
@end
589
458
 
590
459
 
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
460
 
668
461
@implementation NSLocalTimeZone
669
 
  
 
462
 
 
463
- (NSString*) abbreviation
 
464
{
 
465
  return [[NSTimeZoneClass defaultTimeZone] abbreviation];
 
466
}
 
467
 
 
468
- (NSString*) abbreviationForDate: (NSDate*)aDate
 
469
{
 
470
  return [[NSTimeZoneClass defaultTimeZone] abbreviationForDate: aDate];
 
471
}
 
472
 
670
473
- (id) autorelease
671
474
{
672
475
  return self;
673
476
}
674
477
 
 
478
- (NSData*) data
 
479
{
 
480
  return [[NSTimeZoneClass defaultTimeZone] data];
 
481
}
 
482
 
675
483
- (void) encodeWithCoder: (NSCoder*)aCoder
676
484
{
677
485
  [aCoder encodeObject: @"NSLocalTimeZone"];
678
486
}
679
487
 
 
488
- (id) init
 
489
{
 
490
  return self;
 
491
}
 
492
 
 
493
- (BOOL) isDaylightSavingTime
 
494
{
 
495
  return [[NSTimeZoneClass defaultTimeZone] isDaylightSavingTime];
 
496
}
 
497
 
 
498
- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate
 
499
{
 
500
  return [[NSTimeZoneClass defaultTimeZone] isDaylightSavingTimeForDate: aDate];
 
501
}
 
502
 
680
503
- (NSString*) name
681
504
{
682
505
  return [[NSTimeZoneClass defaultTimeZone] name];
691
514
  return self;
692
515
}
693
516
 
 
517
- (int) secondsFromGMT
 
518
{
 
519
  return [[NSTimeZoneClass defaultTimeZone] secondsFromGMT];
 
520
}
 
521
 
 
522
- (int) secondsFromGMTForDate: (NSDate*)aDate
 
523
{
 
524
  return [[NSTimeZoneClass defaultTimeZone] secondsFromGMTForDate: aDate];
 
525
}
 
526
 
694
527
- (NSArray*) timeZoneDetailArray
695
528
{
696
529
  return [[NSTimeZoneClass defaultTimeZone] timeZoneDetailArray];
697
530
}
698
 
  
 
531
 
699
532
- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)date
700
533
{
701
534
  return [[NSTimeZoneClass defaultTimeZone] timeZoneDetailForDate: date];
702
535
}
703
536
 
 
537
- (NSString*) timeZoneName
 
538
{
 
539
  return [[NSTimeZoneClass defaultTimeZone] timeZoneName];
 
540
}
 
541
 
704
542
@end
705
 
  
706
 
@implementation NSConcreteAbsoluteTimeZone
707
 
 
 
543
 
 
544
 
 
545
@implementation GSAbsTimeZone
 
546
 
 
547
static int              uninitialisedOffset = 100000;
708
548
static NSMapTable       *absolutes = 0;
709
549
 
710
550
+ (void) initialize
711
551
{
712
 
  if (self == [NSConcreteAbsoluteTimeZone class])
 
552
  if (self == [GSAbsTimeZone class])
713
553
    {
714
554
      absolutes = NSCreateMapTable(NSIntMapKeyCallBacks,
715
 
                NSNonOwnedPointerMapValueCallBacks, 0);
 
555
        NSNonOwnedPointerMapValueCallBacks, 0);
716
556
    }
717
557
}
718
558
 
 
559
- (NSString*) abbreviationForDate: (NSDate*)aDate
 
560
{
 
561
  return name;
 
562
}
 
563
 
719
564
- (void) dealloc
720
565
{
721
 
  if (zone_mutex != nil)
722
 
    [zone_mutex lock];
723
 
  NSMapRemove(absolutes, (void*)(gsaddr)offset);
724
 
  if (zone_mutex != nil)
725
 
    [zone_mutex unlock];
 
566
  if (offset != uninitialisedOffset)
 
567
    {
 
568
      if (zone_mutex != nil)
 
569
        [zone_mutex lock];
 
570
      NSMapRemove(absolutes, (void*)(gsaddr)offset);
 
571
      if (zone_mutex != nil)
 
572
        [zone_mutex unlock];
 
573
    }
726
574
  RELEASE(name);
727
575
  RELEASE(detail);
728
576
  [super dealloc];
735
583
 
736
584
- (id) initWithOffset: (int)anOffset
737
585
{
738
 
  NSConcreteAbsoluteTimeZone    *z;
 
586
  GSAbsTimeZone *z;
 
587
  int           extra;
 
588
  int           sign = anOffset >= 0 ? 1 : -1;
 
589
 
 
590
  /*
 
591
   * Set the uninitialised offset so that dealloc before full
 
592
   * initialisation won't remove the timezeone for offset 0 from cache.
 
593
   */
 
594
  offset = uninitialisedOffset;
 
595
 
 
596
  /*
 
597
   * Round the offset to the nearest minute, (for MacOS-X compatibility)
 
598
   * and ensure it is no more than 18 hours.
 
599
   */
 
600
  anOffset *= sign;
 
601
  extra = anOffset % 60;
 
602
  if (extra < 30)
 
603
    {
 
604
      anOffset -= extra;
 
605
    }
 
606
  else
 
607
    {
 
608
      anOffset += 60 - extra;
 
609
    }
 
610
  if (anOffset > 64800)
 
611
    {
 
612
      RELEASE(self);
 
613
      return nil;
 
614
    }
 
615
  anOffset *= sign;
739
616
 
740
617
  if (zone_mutex != nil)
741
 
    [zone_mutex lock];
742
 
  z = (NSConcreteAbsoluteTimeZone*)NSMapGet(absolutes, (void*)(gsaddr)anOffset);
743
 
  if (z)
 
618
    {
 
619
      [zone_mutex lock];
 
620
    }
 
621
  z = (GSAbsTimeZone*)NSMapGet(absolutes, (void*)(gsaddr)anOffset);
 
622
  if (z != nil)
744
623
    {
745
624
      IF_NO_GC(RETAIN(z));
746
625
      RELEASE(self);
747
626
    }
748
627
  else
749
628
    {
750
 
      self = [super init];
751
 
      if (self != nil)
752
 
        {
 
629
      if (anOffset % 60 == 0)
 
630
        {
 
631
          char  s = (anOffset >= 0) ? '+' : '-';
 
632
          int   i = (anOffset >= 0) ? anOffset / 60 : -anOffset / 60;
 
633
          int   h = i / 60;
 
634
          int   m = i % 60;
 
635
          char  buf[9];
 
636
 
 
637
          sprintf(buf, "GMT%c%02d%02d", s, h, m);
 
638
          name = [[NSString alloc] initWithCString: buf];
 
639
        }
 
640
      else
 
641
        {
 
642
          /*
 
643
           * Should never happen now we round to the minute
 
644
           * for MacOS-X compatibnility.
 
645
           */
753
646
          name = [[NSString alloc] initWithFormat: @"NSAbsoluteTimeZone:%d",
754
647
            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
648
        }
 
649
      detail = [[GSAbsTimeZoneDetail alloc] initWithTimeZone: self];
 
650
      offset = anOffset;
 
651
      z = self;
 
652
      NSMapInsert(absolutes, (void*)(gsaddr)anOffset, (void*)z);
 
653
      [zoneDictionary setObject: self forKey: (NSString*)name];
763
654
    }
764
655
  if (zone_mutex != nil)
765
 
    [zone_mutex unlock];
 
656
    {
 
657
      [zone_mutex unlock];
 
658
    }
766
659
  return z;
767
660
}
768
661
 
 
662
- (BOOL) isDaylightSavingTimeZoneForDate: (NSDate*)aDate
 
663
{
 
664
  return NO;
 
665
}
 
666
 
769
667
- (NSString*) name
770
668
{
771
669
  return name;
772
670
}
773
671
 
 
672
- (int) secondsFromGMTForDate: (NSDate*)aDate
 
673
{
 
674
  return offset;
 
675
}
 
676
 
774
677
- (NSTimeZone*) timeZoneDetailTimeZone
775
678
{
776
679
  return [NSTimeZone arrayWithObject: detail];
777
680
}
778
 
  
 
681
 
779
682
- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)date
780
683
{
781
684
  return detail;
782
685
}
783
 
  
 
686
 
 
687
- (NSString*) timeZoneName
 
688
{
 
689
  return name;
 
690
}
784
691
@end
785
 
  
786
 
  
787
 
@implementation NSConcreteTimeZoneDetail
788
 
  
 
692
 
 
693
 
 
694
@implementation GSTimeZoneDetail
 
695
 
789
696
- (void) dealloc
790
697
{
791
698
  RELEASE(timeZone);
792
 
  RELEASE(abbrev);
793
699
  [super dealloc];
794
700
}
795
701
 
806
712
             withOffset: (int)anOffset
807
713
                withDST: (BOOL)isDST
808
714
{
809
 
  [super init];
810
715
  timeZone = RETAIN(aZone);
811
 
  abbrev = RETAIN(anAbbrev);
 
716
  abbrev = anAbbrev;            // NB. Depend on this being retained in aZone
812
717
  offset = anOffset;
813
718
  is_dst = isDST;
814
719
  return self;
815
720
}
816
 
  
 
721
 
817
722
- (BOOL) isDaylightSavingTimeZone
818
723
{
819
724
  return is_dst;
820
725
}
821
 
  
 
726
 
822
727
- (NSString*) name
823
728
{
824
729
  return [timeZone name];
825
730
}
826
 
 
 
731
 
827
732
- (NSString*) timeZoneAbbreviation
828
733
{
829
734
  return abbrev;
830
735
}
831
 
  
 
736
 
832
737
- (NSArray*) timeZoneDetailArray
833
738
{
834
739
  return [timeZone timeZoneDetailArray];
848
753
{
849
754
  return offset;
850
755
}
851
 
  
852
 
@end
853
 
 
854
 
 
 
756
 
 
757
@end
 
758
 
 
759
 
 
760
@implementation GSAbsTimeZoneDetail
 
761
 
 
762
- (NSString*) abbreviation
 
763
{
 
764
  return zone->name;
 
765
}
 
766
 
 
767
- (NSString*) abbreviationForDate: (NSDate*)aDate
 
768
{
 
769
  return zone->name;
 
770
}
 
771
 
 
772
- (void) dealloc
 
773
{
 
774
  RELEASE(zone);
 
775
  [super dealloc];
 
776
}
 
777
 
 
778
- (id) initWithTimeZone: (GSAbsTimeZone*)aZone
 
779
{
 
780
  zone = RETAIN(aZone);
 
781
  return self;
 
782
}
 
783
 
 
784
- (BOOL) isDaylightSavingTimeZone
 
785
{
 
786
  return NO;
 
787
}
 
788
 
 
789
- (BOOL) isDaylightSavingTimeZoneForDate: (NSDate*)aDate
 
790
{
 
791
  return NO;
 
792
}
 
793
 
 
794
- (NSString*) name
 
795
{
 
796
  return zone->name;
 
797
}
 
798
 
 
799
- (NSString*) timeZoneAbbreviation
 
800
{
 
801
  return zone->name;
 
802
}
 
803
 
 
804
- (NSArray*) timeZoneDetailArray
 
805
{
 
806
  return [zone timeZoneDetailArray];
 
807
}
 
808
 
 
809
- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)date
 
810
{
 
811
  return self;
 
812
}
 
813
 
 
814
- (int) timeZoneSecondsFromGMT
 
815
{
 
816
  return zone->offset;
 
817
}
 
818
 
 
819
- (int) timeZoneSecondsFromGMTForDate: (NSDate*)aDate
 
820
{
 
821
  return zone->offset;
 
822
}
 
823
 
 
824
@end
 
825
 
 
826
 
 
827
/**
 
828
 * <p>
 
829
 * The local time zone is obtained from, in order of preference:<br/ >
 
830
 *  1) the user defaults database: NSGlobalDomain "Local Time Zone"<br/ >
 
831
 *  2) the GNUSTEP_TZ environment variable<br/ >
 
832
 *  3) the file "localtime" in System/Library/Libraries/Resources/NSTimeZone<br/ >
 
833
 *  4) the TZ environment variable<br/ >
 
834
 *  5) The system zone settings (typically in /etc/localtime)<br/ >
 
835
 *  6) tzset and tznam on platforms which have it<br/ >
 
836
 *  7) Windows registry, on Win32 systems<br/ >
 
837
 *  8) or the fallback time zone (which is UTC)<br/ >
 
838
 * </p>
 
839
 * <p>If the GNUstep time zone datafiles become too out of date, one
 
840
 * can download an updated database from <uref
 
841
 * url="ftp://elsie.nci.nih.gov/pub/">ftp://elsie.nci.nih.gov/pub/</uref>
 
842
 * and compile it as specified in the README file in the
 
843
 * NSTimeZones directory.
 
844
 * </p>
 
845
 * <p>Time zone names in NSDates should be GMT, MET etc. not
 
846
 * Europe/Berlin, America/Washington etc.
 
847
 * </p>
 
848
 * <p>The problem with this is that various time zones may use the
 
849
 * same abbreviation (e.g. Australia/Brisbane and
 
850
 * America/New_York both use EST), and some time zones
 
851
 * may have different rules for daylight saving time even if the
 
852
 * abbreviation and offsets from UTC are the same.
 
853
 * </p>
 
854
 * <p>The problems with depending on the OS for providing time zone
 
855
 * info are 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
 * <p>Win32:  Time zone names read from the registry are different
 
860
 * from other GNUstep installations. Be careful when moving data
 
861
 * between platforms in this case.
 
862
 * </p>
 
863
 */
855
864
@implementation NSTimeZone
856
865
 
 
866
/**
 
867
 * Returns a dictionary containing time zone abbreviations and their
 
868
 * corresponding time zone names. More than one time zone may be associated
 
869
 * with a single abbreviation. In this case, the dictionary contains only
 
870
 * one (usually the most common) time zone name for the abbreviation.
 
871
 */
857
872
+ (NSDictionary*) abbreviationDictionary
858
873
{
859
 
  return fake_abbrev_dict;
 
874
  NSString *path;
 
875
  if (abbreviationDictionary != nil)
 
876
    return abbreviationDictionary;
 
877
 
 
878
  path = _time_zone_path (ABBREV_DICT, @"plist");
 
879
  if (path == NULL)
 
880
    {
 
881
      [NSException
 
882
        raise: NSInternalInconsistencyException
 
883
        format: @"Failed to open time zone abbreviation dictionary."];
 
884
    }
 
885
  abbreviationDictionary =
 
886
    [[NSString stringWithContentsOfFile: path] propertyList];
 
887
  abbreviationDictionary =
 
888
    [abbreviationDictionary makeImmutableCopyOnFail: NO];
 
889
  return RETAIN(abbreviationDictionary);
860
890
}
861
891
 
 
892
/**
 
893
 * Returns a dictionary that maps abbreviations to the array
 
894
 * containing all the time zone names that use the abbreviation.
 
895
 */
862
896
+ (NSDictionary*) abbreviationMap
863
897
{
 
898
  FILE *file; // For the file containing the abbreviation dictionary
 
899
  char abbrev[80], name[80];
 
900
  NSString *path;
 
901
 
864
902
  /* Instead of creating the abbreviation dictionary when the class is
865
903
     initialized, we create it when we first need it, since the
866
904
     dictionary can be potentially very large, considering that it's
867
905
     almost never used. */
868
 
 
869
 
  static NSMutableDictionary *abbreviationDictionary = nil;
870
 
  FILE *file; // For the file containing the abbreviation dictionary
871
 
  char abbrev[80], name[80];
872
 
  NSString *fileName;
873
 
 
874
 
  if (abbreviationDictionary != nil)
875
 
    return abbreviationDictionary;
 
906
  if (abbreviationMap != nil)
 
907
    return abbreviationMap;
876
908
 
877
909
  /* Read dictionary from file. */
878
 
  abbreviationDictionary = [[NSMutableDictionary alloc] init];
879
 
  fileName = [NSTimeZone getAbbreviationFile];
 
910
  abbreviationMap = [[NSMutableDictionary alloc] init];
 
911
  path = _time_zone_path (ABBREV_MAP, nil);
880
912
#if     defined(__WIN32__)
881
 
  file = fopen([fileName fileSystemRepresentation], "rb");
 
913
  file = fopen([path fileSystemRepresentation], "rb");
882
914
#else
883
 
  file = fopen([fileName fileSystemRepresentation], "r");
 
915
  file = fopen([path fileSystemRepresentation], "r");
884
916
#endif
885
917
  if (file == NULL)
886
918
    [NSException
887
919
      raise: NSInternalInconsistencyException
888
 
      format: @"Failed to open time zone abbreviation dictionary."];
 
920
      format: @"Failed to open time zone abbreviation map."];
889
921
  while (fscanf(file, "%79s %79s", abbrev, name) == 2)
890
922
    {
891
923
      id a, the_name, the_abbrev;
892
924
 
893
925
      the_name = [NSString stringWithCString: name];
894
926
      the_abbrev = [NSString stringWithCString: abbrev];
895
 
      a = [abbreviationDictionary objectForKey: the_abbrev];
 
927
      a = [abbreviationMap objectForKey: the_abbrev];
896
928
      if (a == nil)
897
929
        {
898
 
          a = [[NSMutableArray alloc] init];
899
 
          [abbreviationDictionary setObject: a forKey: the_abbrev];
 
930
          a = AUTORELEASE([NSMutableArray new]);
 
931
          [abbreviationMap setObject: a forKey: the_abbrev];
900
932
        }
901
933
      [a addObject: the_name];
902
934
    }
903
935
  fclose(file);
904
936
 
905
 
  return abbreviationDictionary;
 
937
  /* Special case: Add the system time zone if it doesn't exist in the map */
 
938
  {
 
939
    id array;
 
940
    id the_abbrev = [systemTimeZone abbreviation];
 
941
    array = [abbreviationMap objectForKey: the_abbrev];
 
942
    if (array == nil)
 
943
      {
 
944
        array = AUTORELEASE([NSMutableArray new]);
 
945
        [abbreviationMap setObject: array forKey: the_abbrev];
 
946
      }
 
947
    if ([array containsObject: [systemTimeZone timeZoneName]] == NO)
 
948
      [array addObject: [systemTimeZone timeZoneName]];
 
949
  }
 
950
 
 
951
  return abbreviationMap;
 
952
}
 
953
 
 
954
/**
 
955
 * Returns an array of all known time zone names.
 
956
 */
 
957
+ (NSArray*) knownTimeZoneNames
 
958
{
 
959
  static NSArray *namesArray = nil;
 
960
  int i;
 
961
  id temp_array;
 
962
  NSArray *regionsArray, *array;
 
963
 
 
964
  /* We create the array only when we need it to reduce overhead. */
 
965
  if (namesArray != nil)
 
966
    return namesArray;
 
967
 
 
968
  temp_array = [NSMutableArray array];
 
969
  regionsArray = [self timeZoneArray];
 
970
 
 
971
  for (i = 0; i < [regionsArray count]; i++)
 
972
    {
 
973
      array = [regionsArray objectAtIndex:i];
 
974
      [temp_array addObjectsFromArray: array];
 
975
    }
 
976
 
 
977
  namesArray = [[NSArray alloc] initWithArray: temp_array];
 
978
  return namesArray;
906
979
}
907
980
 
908
981
+ (id) allocWithZone: (NSZone*)z
958
1031
    }
959
1032
}
960
1033
 
 
1034
/**
 
1035
 * Return the default time zone for this process.
 
1036
 */
961
1037
+ (NSTimeZone*) defaultTimeZone
962
1038
{
963
1039
  NSTimeZone    *zone;
964
1040
 
965
1041
  if (zone_mutex != nil)
966
 
    [zone_mutex lock];
 
1042
    {
 
1043
      [zone_mutex lock];
 
1044
    }
967
1045
  if (defaultTimeZone == nil)
968
1046
    {
969
1047
      zone = [self systemTimeZone];
971
1049
  else
972
1050
    {
973
1051
      if (zone_mutex != nil)
974
 
        zone = AUTORELEASE(RETAIN(defaultTimeZone));
 
1052
        {
 
1053
          zone = AUTORELEASE(RETAIN(defaultTimeZone));
 
1054
        }
975
1055
      else
976
 
        zone = defaultTimeZone;
 
1056
        {
 
1057
          zone = defaultTimeZone;
 
1058
        }
977
1059
    }
978
1060
  if (zone_mutex != nil)
979
 
    [zone_mutex unlock];
 
1061
    {
 
1062
      [zone_mutex unlock];
 
1063
    }
980
1064
  return zone;
981
1065
}
982
1066
 
997
1081
        NSNonRetainedObjectMapValueCallBacks, 0);
998
1082
 
999
1083
      localTimeZone = [[NSLocalTimeZone alloc] init];
1000
 
      [self setDefaultTimeZone: [self systemTimeZone]];
1001
1084
 
1002
 
      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
 
        }
 
1085
      zone_mutex = [GSLazyRecursiveLock new];
1015
1086
    }
1016
1087
}
1017
1088
 
 
1089
/**
 
1090
 * Return a proxy to the default time zone for this process.
 
1091
 */
1018
1092
+ (NSTimeZone*) localTimeZone
1019
1093
{
1020
1094
  return localTimeZone;
1021
1095
}
1022
1096
 
 
1097
/**
 
1098
 * Destroy the system time zone so that it will be recreated
 
1099
 * next time it is used.
 
1100
 */
1023
1101
+ (void) resetSystemTimeZone
1024
1102
{
1025
1103
  if (zone_mutex != nil)
1026
 
    [zone_mutex lock];
 
1104
    {
 
1105
      [zone_mutex lock];
 
1106
    }
1027
1107
  DESTROY(systemTimeZone);
1028
1108
  if (zone_mutex != nil)
1029
 
    [zone_mutex unlock];
 
1109
    {
 
1110
      [zone_mutex unlock];
 
1111
    }
1030
1112
}
1031
1113
 
 
1114
/**
 
1115
 * Set the default time zone to be used for this process.
 
1116
 */
1032
1117
+ (void) setDefaultTimeZone: (NSTimeZone*)aTimeZone
1033
1118
{
1034
 
  if (zone_mutex != nil)
1035
 
    [zone_mutex lock];
1036
 
  ASSIGN(defaultTimeZone, aTimeZone);
1037
 
  if (zone_mutex != nil)
1038
 
    [zone_mutex unlock];
 
1119
  if (aTimeZone != defaultTimeZone)
 
1120
    {
 
1121
      /*
 
1122
       * We can't make the localTimeZone the default since that would
 
1123
       * cause recursion ...
 
1124
       */
 
1125
      if (aTimeZone == localTimeZone)
 
1126
        {
 
1127
          aTimeZone = [self systemTimeZone];
 
1128
        }
 
1129
      if (zone_mutex != nil)
 
1130
        {
 
1131
          [zone_mutex lock];
 
1132
        }
 
1133
      ASSIGN(defaultTimeZone, aTimeZone);
 
1134
      if (zone_mutex != nil)
 
1135
        {
 
1136
          [zone_mutex unlock];
 
1137
        }
 
1138
    }
1039
1139
}
1040
1140
 
 
1141
/**
 
1142
 * Returns the current system time zone for the process.
 
1143
 */
1041
1144
+ (NSTimeZone*) systemTimeZone
1042
1145
{
1043
1146
  NSTimeZone    *zone = nil;
1044
1147
 
1045
1148
  if (zone_mutex != nil)
1046
 
    [zone_mutex lock];
 
1149
    {
 
1150
      [zone_mutex lock];
 
1151
    }
1047
1152
  if (systemTimeZone == nil)
1048
1153
    {
1049
1154
      NSString  *localZoneString = nil;
1053
1158
       */
1054
1159
      systemTimeZone = RETAIN([NSTimeZoneClass timeZoneForSecondsFromGMT: 0]);
1055
1160
 
 
1161
      /*
 
1162
       * Try to get timezone from user defaults database
 
1163
       */
1056
1164
      localZoneString = [[NSUserDefaults standardUserDefaults]
1057
1165
        stringForKey: LOCALDBKEY];
 
1166
 
 
1167
      /*
 
1168
       * Try to get timezone from GNUSTEP_TZ environment variable.
 
1169
       */
1058
1170
      if (localZoneString == nil)
1059
1171
        {
1060
 
          /*
1061
 
           * Try to get timezone from GNUSTEP_TZ environment variable.
1062
 
           */
1063
1172
          localZoneString = [[[NSProcessInfo processInfo]
1064
1173
            environment] objectForKey: @"GNUSTEP_TZ"];
1065
1174
        }
 
1175
      /*
 
1176
       * Try to get timezone from LOCAL_TIME_FILE.
 
1177
       */
1066
1178
      if (localZoneString == nil)
1067
1179
        {
1068
 
          /*
1069
 
           * Try to get timezone from LOCAL_TIME_FILE.
1070
 
           */
1071
 
          NSString      *f;
1072
 
 
1073
 
          f = [NSBundle pathForGNUstepResource: LOCAL_TIME_FILE
1074
 
                                        ofType: @""
1075
 
                                   inDirectory: TIME_ZONE_DIR];
 
1180
          NSString      *f = _time_zone_path(LOCAL_TIME_FILE, nil);
1076
1181
          if (f != nil)
1077
1182
            {
1078
1183
              localZoneString = [NSString stringWithContentsOfFile: f];
1079
1184
              localZoneString = [localZoneString stringByTrimmingSpaces];
1080
1185
            }
1081
1186
        }
 
1187
      /*
 
1188
       * Try to get timezone from standard unix environment variable.
 
1189
       */
1082
1190
      if (localZoneString == nil)
1083
1191
        {
1084
 
          /*
1085
 
           * Try to get timezone from standard unix environment variable.
1086
 
           */
1087
1192
          localZoneString = [[[NSProcessInfo processInfo]
1088
1193
            environment] objectForKey: @"TZ"];
1089
1194
        }
 
1195
      if (localZoneString == nil)
 
1196
        {
 
1197
          /* Get the zone name from the localtime file, assuming the file
 
1198
             is a symlink to the time zone. Getting the actual data (which
 
1199
             is easier) doesn't help, since we won't know the name itself.  */
 
1200
#if defined(HAVE_TZHEAD) && defined(TZDEFAULT)
 
1201
          tzdir = RETAIN([NSString stringWithCString: TZDIR]);
 
1202
          localZoneString = [NSString stringWithCString: TZDEFAULT];
 
1203
          localZoneString = [localZoneString stringByResolvingSymlinksInPath];
 
1204
#else
 
1205
          NSFileManager *dflt = [NSFileManager defaultManager];
 
1206
          if ([dflt fileExistsAtPath: SYSTEM_TIME_FILE])
 
1207
            {
 
1208
              localZoneString = SYSTEM_TIME_FILE;
 
1209
              localZoneString = [localZoneString stringByResolvingSymlinksInPath];
 
1210
              /* Guess what tzdir is */
 
1211
              tzdir = [localZoneString stringByDeletingLastPathComponent];
 
1212
              while ([tzdir length] > 2
 
1213
                     && [dflt fileExistsAtPath: [tzdir stringByAppendingPathComponent: @"GMT"]] == NO)
 
1214
                tzdir = [tzdir stringByDeletingLastPathComponent];
 
1215
              if ([tzdir length] > 2)
 
1216
                RETAIN(tzdir);
 
1217
              else
 
1218
                {
 
1219
                  localZoneString = tzdir = nil;
 
1220
                }
 
1221
            }
 
1222
#endif
 
1223
          if (localZoneString != nil && [localZoneString hasPrefix: tzdir])
 
1224
            {
 
1225
              /* This must be the time zone name */
 
1226
              localZoneString = [[localZoneString mutableCopy] autorelease];
 
1227
              [(NSMutableString *)localZoneString deletePrefix: tzdir];
 
1228
              if ([localZoneString hasPrefix: @"/"])
 
1229
                {
 
1230
                  [(NSMutableString *)localZoneString deletePrefix: @"/"];
 
1231
                }
 
1232
            }
 
1233
          else
 
1234
            localZoneString = nil;
 
1235
        }
 
1236
#if HAVE_TZSET
 
1237
      /*
 
1238
       * Try to get timezone from tzset and tzname
 
1239
       */
 
1240
      if (localZoneString == nil)
 
1241
        {
 
1242
          tzset();
 
1243
          if (tzname[0] != NULL && *tzname[0] != '\0')
 
1244
            localZoneString = [NSString stringWithCString: tzname[0]];
 
1245
        }
 
1246
#endif
 
1247
 
 
1248
#ifdef WIN32
 
1249
      /*
 
1250
       * Try to get timezone from windows registry.
 
1251
       */
 
1252
      {
 
1253
        HKEY regkey;
 
1254
 
 
1255
        if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, \
 
1256
        "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation", 0, KEY_READ, &regkey))
 
1257
          {
 
1258
            char buf[255];
 
1259
            DWORD bufsize=255;
 
1260
            DWORD type;
 
1261
            if (ERROR_SUCCESS==RegQueryValueEx(regkey, "StandardName", 0, &type, buf, &bufsize))
 
1262
              {
 
1263
                bufsize=strlen(buf);
 
1264
                while (bufsize && isspace(buf[bufsize-1]))
 
1265
                  {
 
1266
                    bufsize--;
 
1267
                  }
 
1268
                  localZoneString = [NSString stringWithCString:buf length:bufsize];
 
1269
              }
 
1270
            RegCloseKey(regkey);
 
1271
          }
 
1272
      }
 
1273
#endif
 
1274
 
1090
1275
      if (localZoneString != nil)
1091
1276
        {
 
1277
          NSDebugLLog (@"NSTimeZone", @"Using zone %@", localZoneString);
1092
1278
          zone = [defaultPlaceholderTimeZone initWithName: localZoneString];
1093
1279
        }
1094
1280
      else
1120
1306
  return zone;
1121
1307
}
1122
1308
 
 
1309
/**
 
1310
 * Returns an array of all the known regions.<br />
 
1311
 * There are 24 elements, of course, one for each time zone.
 
1312
 * Each element contains an array of NSStrings which are
 
1313
 * the region names.
 
1314
 */
1123
1315
+ (NSArray*) timeZoneArray
1124
1316
{
1125
 
  /* We create the array only when we need it to reduce overhead. */
1126
 
 
1127
1317
  static NSArray *regionsArray = nil;
1128
1318
  int index, i;
1129
1319
  char name[80];
1131
1321
  id temp_array[24];
1132
1322
  NSString *fileName;
1133
1323
 
 
1324
  /* We create the array only when we need it to reduce overhead. */
1134
1325
  if (regionsArray != nil)
1135
1326
    return regionsArray;
1136
1327
 
1137
1328
  for (i = 0; i < 24; i++)
1138
1329
    temp_array[i] = [NSMutableArray array];
1139
1330
 
1140
 
  fileName = [NSTimeZoneClass getRegionsFile];
 
1331
  fileName = _time_zone_path (REGIONS_FILE, nil);
1141
1332
#if     defined(__WIN32__)
1142
1333
  file = fopen([fileName fileSystemRepresentation], "rb");
1143
1334
#else
1154
1345
  return regionsArray;
1155
1346
}
1156
1347
 
 
1348
/**
 
1349
 * Return a timezone for the specified offset from GMT.<br />
 
1350
 * The timezone returned does <em>not</em> use daylight savings time.
 
1351
 * The actual timezone returned has an offset rounded to the nearest
 
1352
 * minute.<br />
 
1353
 * Time zones with an offset of more than +/- 18 hours  are disallowed,
 
1354
 * and nil is returned.
 
1355
 */
1157
1356
+ (NSTimeZone*) timeZoneForSecondsFromGMT: (int)seconds
1158
1357
{
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]);
 
1358
  NSTimeZone    *zone;
 
1359
 
 
1360
  zone = [[GSAbsTimeZone alloc] initWithOffset: seconds];
 
1361
  return AUTORELEASE(zone);
1163
1362
}
1164
1363
 
 
1364
/**
 
1365
 * Returns a timezone for the specified abbrevition. The same abbreviations
 
1366
 * are used in different regions so this isn't particularly useful.<br />
 
1367
 * Calls NSTimeZone-abbreviation dictionary an so uses a lot of memory.
 
1368
 */
1165
1369
+ (NSTimeZone*) timeZoneWithAbbreviation: (NSString*)abbreviation
1166
1370
{
1167
1371
  NSTimeZone    *zone;
1168
1372
 
1169
1373
  zone = [self timeZoneWithName: [[self abbreviationDictionary]
1170
 
                                   objectForKey: abbreviation]];
 
1374
                                   objectForKey: abbreviation] data: nil];
1171
1375
  return zone;
1172
1376
}
1173
1377
 
 
1378
/**
 
1379
 * Returns a timezone for the specified name.
 
1380
 */
1174
1381
+ (NSTimeZone*) timeZoneWithName: (NSString*)aTimeZoneName
1175
1382
{
1176
1383
  NSTimeZone    *zone;
1177
1384
 
1178
 
  zone = [defaultPlaceholderTimeZone initWithName: aTimeZoneName];
 
1385
  zone = [defaultPlaceholderTimeZone initWithName: aTimeZoneName data: nil];
1179
1386
  return AUTORELEASE(zone);
1180
1387
}
1181
1388
 
 
1389
/**
 
1390
 * Returns a timezone for aTimeZoneName, created from the supplied
 
1391
 * time zone data. Data must be in TZ format as per the Olson database.
 
1392
 */
1182
1393
+ (NSTimeZone*) timeZoneWithName: (NSString*)name data: (NSData*)data
 
1394
{
 
1395
  NSTimeZone    *zone;
1183
1396
 
1184
 
{
1185
 
  [self notImplemented: _cmd];
1186
 
  return nil;
 
1397
  zone = [defaultPlaceholderTimeZone initWithName: name data: data];
 
1398
  return AUTORELEASE(zone);
1187
1399
}
1188
1400
 
 
1401
/**
 
1402
 * Returns the abbreviation for this timezone now.
 
1403
 * Invokes -abbreviationForDate:
 
1404
 */
1189
1405
- (NSString*) abbreviation
1190
1406
{
1191
1407
  return [self abbreviationForDate: [NSDate date]];
1192
1408
}
1193
1409
 
1194
 
- (NSString*) abbreviationForDate: (NSDate*)when
 
1410
/**
 
1411
 * Returns the abbreviation for this timezone at aDate.  This may differ
 
1412
 * depending on whether daylight savings time is in effect or not.
 
1413
 */
 
1414
- (NSString*) abbreviationForDate: (NSDate*)aDate
1195
1415
{
1196
1416
  NSTimeZoneDetail      *detail;
1197
 
 
1198
 
  detail = [self timeZoneDetailForDate: when];
1199
 
  return [detail timeZoneAbbreviation];
 
1417
  NSString              *abbr;
 
1418
 
 
1419
  detail = [self timeZoneDetailForDate: aDate];
 
1420
  abbr = [detail timeZoneAbbreviation];
 
1421
 
 
1422
  return abbr;
1200
1423
}
1201
1424
 
 
1425
/**
 
1426
 * Returns the Class for this object
 
1427
 */
1202
1428
- (Class) classForCoder
1203
1429
{
1204
1430
  return NSTimeZoneClass;
1209
1435
  return RETAIN(self);
1210
1436
}
1211
1437
 
 
1438
/**
 
1439
 * Returns the data with which the receiver was initialised.
 
1440
 */
 
1441
- (NSData*) data
 
1442
{
 
1443
  return nil;
 
1444
}
 
1445
 
 
1446
/**
 
1447
 * Returns the name of this object.
 
1448
 */
1212
1449
- (NSString*) description
1213
1450
{
1214
1451
  return [self name];
1219
1456
  [aCoder encodeObject: [self name]];
1220
1457
}
1221
1458
 
 
1459
- (id) init
 
1460
{
 
1461
  return [self initWithName: @"NSLocalTimeZone" data: nil];
 
1462
}
 
1463
 
1222
1464
- (id) initWithCoder: (NSCoder*)aDecoder
1223
1465
{
1224
1466
  NSString      *name;
1225
1467
 
1226
1468
  name = [aDecoder decodeObject];
1227
 
  self = [self initWithName: name];
 
1469
  self = [self initWithName: name data: nil];
1228
1470
  return self;
1229
1471
}
1230
1472
 
 
1473
/**
 
1474
 * Initialise a timezone with the supplied name.  May return a cached
 
1475
 * timezone object rather than the newly created one.
 
1476
 */
1231
1477
- (id) initWithName: (NSString*)name
1232
1478
{
1233
 
  [self notImplemented: _cmd];
1234
 
  return nil;
 
1479
  return [self initWithName: name data: nil];
1235
1480
}
1236
1481
 
 
1482
/**
 
1483
 * Initialises a time zone object using the supplied data object.<br />
 
1484
 * This method is intended for internal use by the NSTimeZone
 
1485
 * class cluster.
 
1486
 * Don't use it ... use -initWithName: instead.
 
1487
 */
1237
1488
- (id) initWithName: (NSString*)name data: (NSData*)data
1238
1489
{
1239
1490
  [self notImplemented: _cmd];
1240
1491
  return nil;
1241
1492
}
1242
1493
 
 
1494
/**
 
1495
 * Returns a boolean indicating whether daylight savings time is in
 
1496
 * effect now.  Invokes -isDaylightSavingTimeForDate:
 
1497
 */
1243
1498
- (BOOL) isDaylightSavingTime
1244
1499
{
1245
1500
  return [self isDaylightSavingTimeForDate: [NSDate date]];
1246
1501
}
1247
1502
 
 
1503
/**
 
1504
 * Returns a boolean indicating whether daylight savings time is in
 
1505
 * effect for this time zone at aDate.
 
1506
 */
1248
1507
- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate
1249
1508
{
1250
1509
  NSTimeZoneDetail      *detail;
 
1510
  BOOL                  isDST;
1251
1511
 
1252
1512
  detail = [self timeZoneDetailForDate: aDate];
1253
 
  return [detail isDaylightSavingTimeZone];
 
1513
  isDST = [detail isDaylightSavingTimeZone];
 
1514
 
 
1515
  return isDST;
1254
1516
}
1255
1517
 
1256
1518
- (BOOL) isEqual: (id)other
1262
1524
  return [self isEqualToTimeZone: other];
1263
1525
}
1264
1526
 
 
1527
/**
 
1528
 * Returns TRUE if the time zones have the same name.
 
1529
 */
1265
1530
- (BOOL) isEqualToTimeZone: (NSTimeZone*)aTimeZone
1266
1531
{
1267
1532
  if (aTimeZone == self)
1268
1533
    return YES;
1269
 
  if ([[self name] isEqual: [aTimeZone name]] == YES)
 
1534
  if ([[self name] isEqual: [aTimeZone name]] == NO)
 
1535
    return NO;
 
1536
  if (([self data] == nil && [aTimeZone data] == nil)
 
1537
    || [[self name] isEqual: [aTimeZone name]] == YES)
1270
1538
    return YES;
1271
1539
  return NO;
1272
1540
}
1273
1541
 
 
1542
/**
 
1543
 * Returns the name of the timezone
 
1544
 */
1274
1545
- (NSString*) name
1275
1546
{
1276
1547
  return [self subclassResponsibility: _cmd];
1279
1550
- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder
1280
1551
{
1281
1552
  if ([aCoder isByref] == NO)
1282
 
    return self;
 
1553
    {
 
1554
      return self;
 
1555
    }
1283
1556
  return [super replacementObjectForPortCoder: aCoder];
1284
1557
}
1285
1558
 
 
1559
/**
 
1560
 * Returns the number of seconds by which the receiver differs
 
1561
 * from Greenwich Mean Time at the current date and time.<br />
 
1562
 * Invokes -secondsFromGMTForDate:
 
1563
 */
1286
1564
- (int) secondsFromGMT
1287
1565
{
1288
1566
  return [self secondsFromGMTForDate: [NSDate date]];
1289
1567
}
1290
1568
 
1291
 
- (int) secondsFromGMTForDate: (NSDate*)when
 
1569
/**
 
1570
 * Returns the number of seconds by which the receiver differs
 
1571
 * from Greenwich Mean Time at the date aDate.<br />
 
1572
 * If the time zone uses dayl;ight savings time, the returned value
 
1573
 * will vary at different times of year.
 
1574
 */
 
1575
- (int) secondsFromGMTForDate: (NSDate*)aDate
1292
1576
{
1293
1577
  NSTimeZoneDetail      *detail;
1294
 
 
1295
 
  detail = [self timeZoneDetailForDate: when];
1296
 
  return [detail timeZoneSecondsFromGMT];
 
1578
  int                   offset;
 
1579
 
 
1580
  detail = [self timeZoneDetailForDate: aDate];
 
1581
  offset = [detail timeZoneSecondsFromGMT];
 
1582
 
 
1583
  return offset;
1297
1584
}
1298
1585
 
 
1586
/**
 
1587
 * DEPRECATED:  see NSTimeZoneDetail
 
1588
 */
1299
1589
- (NSArray*) timeZoneDetailArray
1300
1590
{
1301
1591
  return [self subclassResponsibility: _cmd];
1302
1592
}
1303
1593
 
 
1594
/**
 
1595
 * DEPRECATED:  see NSTimeZoneDetail
 
1596
 */
1304
1597
- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)date
1305
1598
{
1306
1599
  return [self subclassResponsibility: _cmd];
1307
1600
}
1308
1601
 
 
1602
/**
 
1603
 * Returns the name of this timezone.
 
1604
 */
1309
1605
- (NSString*) timeZoneName
1310
1606
{
1311
1607
  return [self name];
1312
1608
}
1313
1609
 
1314
1610
@end
1315
 
  
1316
1611
 
 
1612
/**
 
1613
 * This class serves no useful purpose in GNUstep, and is provided
 
1614
 * solely for backward compatibility with the OpenStep spec.  It is
 
1615
 * missing entirely from MacOS-X.
 
1616
 */
1317
1617
@implementation NSTimeZoneDetail
1318
1618
 
1319
1619
- (NSString*) description
1320
1620
{
1321
 
  return [NSString
1322
 
           stringWithFormat: @"%@(%@, %s%d)",
1323
 
           [self name],
1324
 
           [self timeZoneAbbreviation],
1325
 
           ([self isDaylightSavingTimeZone]? "IS_DST, ": ""),
1326
 
           [self timeZoneSecondsFromGMT]];
 
1621
  return [NSString stringWithFormat: @"%@(%@, %s%d)", [self name],
 
1622
    [self timeZoneAbbreviation],
 
1623
    ([self isDaylightSavingTimeZone]? "IS_DST, ": ""),
 
1624
    [self timeZoneSecondsFromGMT]];
1327
1625
}
1328
1626
 
 
1627
/**
 
1628
 * DEPRECATED: Class is no longer used.
 
1629
 */
1329
1630
- (BOOL) isDaylightSavingTimeZone
1330
1631
{
1331
1632
  [self subclassResponsibility: _cmd];
1332
1633
  return NO;
1333
1634
}
1334
 
  
 
1635
 
 
1636
/**
 
1637
 * DEPRECATED: Class is no longer used.
 
1638
 */
1335
1639
- (NSString*) timeZoneAbbreviation
1336
1640
{
1337
1641
  return [self subclassResponsibility: _cmd];
1338
1642
}
1339
 
  
 
1643
 
 
1644
/**
 
1645
 * DEPRECATED: Class is no longer used.
 
1646
 */
1340
1647
- (int) timeZoneSecondsFromGMT
1341
1648
{
1342
1649
  [self subclassResponsibility: _cmd];
1348
1655
 
1349
1656
@implementation NSTimeZone (Private)
1350
1657
 
1351
 
/*
1352
 
 *      When the system becomes multithreaded, we set a flag to say so
 
1658
/**
 
1659
 * Returns the path to the named zone info file.
1353
1660
 */
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
 
+ (NSString*) getAbbreviationFile
1367
 
{
1368
 
  return [NSBundle pathForGNUstepResource: ABBREV_DICT
1369
 
                   ofType: @""
1370
 
                   inDirectory: TIME_ZONE_DIR];
1371
 
}
1372
 
 
1373
 
+ (NSString*) getRegionsFile
1374
 
{
1375
 
  return [NSBundle pathForGNUstepResource: REGIONS_FILE
1376
 
                   ofType: @""
1377
 
                   inDirectory: TIME_ZONE_DIR];
1378
 
}
1379
 
 
1380
1661
+ (NSString*) getTimeZoneFile: (NSString *)name
1381
1662
{
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
 
1663
  NSString      *dir = nil;
 
1664
 
 
1665
  /* Use the system zone info if possible, otherwise, use our installed
 
1666
     info.  */
 
1667
  if (tzdir && [[NSFileManager defaultManager] fileExistsAtPath:
 
1668
        [tzdir stringByAppendingPathComponent: name]] == NO)
 
1669
    dir = nil;
 
1670
  if (dir == nil)
 
1671
    dir= _time_zone_path (ZONES_DIR, nil);
 
1672
  return [dir stringByAppendingPathComponent: name];
 
1673
}
 
1674
 
 
1675
@end
 
1676
 
 
1677
 
 
1678
#ifdef WIN32
 
1679
/* Timezone information data as stored in the registry */
 
1680
typedef struct TZI_format {
 
1681
        LONG       Bias;
 
1682
        LONG       StandardBias;
 
1683
        LONG       DaylightBias;
 
1684
        SYSTEMTIME StandardDate;
 
1685
        SYSTEMTIME DaylightDate;
 
1686
} TZI;
 
1687
 
 
1688
static inline unsigned int
 
1689
lastDayOfGregorianMonth(int month, int year)
 
1690
{
 
1691
  switch (month)
 
1692
    {
 
1693
      case 2:
 
1694
        if ((((year % 4) == 0) && ((year % 100) != 0))
 
1695
          || ((year % 400) == 0))
 
1696
          return 29;
 
1697
        else
 
1698
          return 28;
 
1699
      case 4:
 
1700
      case 6:
 
1701
      case 9:
 
1702
      case 11: return 30;
 
1703
      default: return 31;
 
1704
    }
 
1705
}
 
1706
 
 
1707
/* IMPORT from NSCalendar date */
 
1708
void
 
1709
GSBreakTime(NSTimeInterval when, int *year, int *month, int *day,
 
1710
  int *hour, int *minute, int *second, int *mil);
 
1711
int dayOfCommonEra(NSTimeInterval when);
 
1712
 
 
1713
@implementation GSWindowsTimeZone
 
1714
 
 
1715
- (NSString*) abbreviationForDate: (NSDate*)aDate
 
1716
{
 
1717
  if ([self isDaylightSavingTimeForDate:aDate])
 
1718
    return daylightZoneNameAbbr;
 
1719
  return timeZoneNameAbbr;
 
1720
}
 
1721
 
 
1722
- (NSData*) data
 
1723
{
 
1724
        return 0;
 
1725
}
 
1726
 
 
1727
- (void) dealloc
 
1728
{
 
1729
  RELEASE(timeZoneName);
 
1730
  RELEASE(daylightZoneName);
 
1731
  RELEASE(timeZoneNameAbbr);
 
1732
  RELEASE(daylightZoneNameAbbr);
 
1733
  [super dealloc];
 
1734
}
 
1735
 
 
1736
- (id) initWithName: (NSString*)name data: (NSData*)data
 
1737
{
 
1738
  HKEY     regDirKey;
 
1739
  BOOL     isNT = NO,regFound=NO;
 
1740
 
 
1741
  /* Open the key in the local machine hive where the time zone data is stored. */
 
1742
  if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones", 0, KEY_READ, &regDirKey))
 
1743
  {
 
1744
    isNT=YES;
 
1745
    regFound=YES;
 
1746
  }
 
1747
  else
 
1748
  {
 
1749
    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones", 0, KEY_READ, &regDirKey))
 
1750
    {
 
1751
        regFound=YES;
 
1752
    }
 
1753
  }
 
1754
 
 
1755
  if (regFound)
 
1756
  {
 
1757
        /* Iterate over all subKeys in the registry to find the right one.
 
1758
           Unfortunately name is a localized value. The keys in the registry are
 
1759
           unlocalized names. */
 
1760
           CHAR     achKey[255];              // buffer for subkey name
 
1761
        DWORD    cbName;                   // size of name string
 
1762
        CHAR     achClass[MAX_PATH] = "";  // buffer for class name
 
1763
        DWORD    cchClassName = MAX_PATH;  // size of class string
 
1764
        DWORD    cSubKeys=0;               // number of subkeys
 
1765
        DWORD    cbMaxSubKey;              // longest subkey size
 
1766
        DWORD    cchMaxClass;              // longest class string
 
1767
        DWORD    cValues;                  // number of values for key
 
1768
        DWORD    cchMaxValue;              // longest value name
 
1769
        DWORD    cbMaxValueData;           // longest value data
 
1770
        DWORD    cbSecurityDescriptor;     // size of security descriptor
 
1771
        FILETIME ftLastWriteTime;          // last write time
 
1772
                
 
1773
                DWORD i, retCode;
 
1774
                BOOL    tzFound = NO;
 
1775
                
 
1776
                /* Get the class name and the value count. */
 
1777
            retCode = RegQueryInfoKey(
 
1778
                regDirKey,               // key handle
 
1779
                achClass,                // buffer for class name
 
1780
                &cchClassName,           // size of class string
 
1781
                NULL,                    // reserved
 
1782
                &cSubKeys,               // number of subkeys
 
1783
                &cbMaxSubKey,            // longest subkey size
 
1784
                &cchMaxClass,            // longest class string
 
1785
                &cValues,                // number of values for this key
 
1786
                &cchMaxValue,            // longest value name
 
1787
                &cbMaxValueData,         // longest value data
 
1788
                &cbSecurityDescriptor,   // security descriptor
 
1789
                &ftLastWriteTime);       // last write time
 
1790
 
 
1791
        if (cSubKeys && (retCode == ERROR_SUCCESS))
 
1792
        {
 
1793
                const char *cName = [name cString];
 
1794
 
 
1795
                        for (i=0; i<cSubKeys && !tzFound; i++)
 
1796
                        {
 
1797
                           cbName = 255;
 
1798
                        
 
1799
                           retCode = RegEnumKeyEx(regDirKey, i, achKey, &cbName, NULL, NULL, NULL, &ftLastWriteTime);
 
1800
                           if (retCode == ERROR_SUCCESS)
 
1801
                           {
 
1802
                                 char keyBuffer[16384];
 
1803
                                 HKEY regKey;
 
1804
                        
 
1805
                               if (isNT)
 
1806
                                sprintf(keyBuffer,"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\%s",achKey);
 
1807
                                         else
 
1808
                                sprintf(keyBuffer,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones\\%s",achKey);
 
1809
                        
 
1810
                          if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyBuffer, 0, KEY_READ, &regKey))
 
1811
                               {
 
1812
                                      char buf[256];
 
1813
                                      char standardName[256];
 
1814
                                      char daylightName[256];
 
1815
                                      DWORD bufsize;
 
1816
                                      DWORD type;
 
1817
 
 
1818
                                                /* check standardname */
 
1819
                                                standardName[0]='\0';
 
1820
                                      bufsize=sizeof(buf);
 
1821
                                      if (ERROR_SUCCESS==RegQueryValueEx(regKey, "Std", 0, &type, buf, &bufsize))
 
1822
                                      {
 
1823
                                        strcpy(standardName,buf);
 
1824
                                        if (strcmp(standardName,cName) == 0)
 
1825
                                                tzFound = YES;
 
1826
                                        }
 
1827
                
 
1828
                                                /* check daylightname */
 
1829
                                                daylightName[0]='\0';
 
1830
                                      bufsize=sizeof(buf);
 
1831
                                      if (ERROR_SUCCESS==RegQueryValueEx(regKey, "Dlt", 0, &type, buf, &bufsize))
 
1832
                                      {
 
1833
                                        strcpy(daylightName,buf);
 
1834
                                        if (strcmp(daylightName,cName) == 0)
 
1835
                                                tzFound = YES;
 
1836
                                        }
 
1837
 
 
1838
                                                if (tzFound)
 
1839
                                                {
 
1840
                                              /* Read in the time zone data */
 
1841
                                              bufsize=sizeof(buf);
 
1842
                                              if (ERROR_SUCCESS==RegQueryValueEx(regKey, "TZI", 0, &type, buf, &bufsize))
 
1843
                                              {
 
1844
                                                  TZI *tzi = (void*)buf;
 
1845
                                                  Bias = tzi->Bias;
 
1846
                                                  StandardBias = tzi->StandardBias;
 
1847
                                                  DaylightBias = tzi->DaylightBias;
 
1848
                                                  StandardDate = tzi->StandardDate;
 
1849
                                                  DaylightDate = tzi->DaylightDate;
 
1850
                                              }
 
1851
                                        
 
1852
                                              /* Set the standard name for the time zone. */
 
1853
                                              if (strlen(standardName))
 
1854
                                              {
 
1855
                                                  int a, b;
 
1856
                                                  [timeZoneName release];
 
1857
                                                  timeZoneName = [[NSString stringWithCString:standardName] retain];
 
1858
 
 
1859
                                                  /* Abbr generated here is IMHO a bit suspicous but I kept it */
 
1860
                                                  for (a=0,b=0;standardName[a];a++)
 
1861
                                                  {
 
1862
                                                    if (isupper(standardName[a]))
 
1863
                                                      standardName[b++]=standardName[a];
 
1864
                                                  }
 
1865
                                                  standardName[b]=0;
 
1866
                                                  [timeZoneNameAbbr release];
 
1867
                                                  timeZoneNameAbbr = [[NSString stringWithCString:standardName] retain];
 
1868
                                              }
 
1869
 
 
1870
                                              /* Set the daylight savings name for the time zone. */
 
1871
                                              if (strlen(daylightName))
 
1872
                                              {
 
1873
                                                  int a,b;
 
1874
                                                  [daylightZoneName release];
 
1875
                                                  daylightZoneName = [[NSString stringWithCString:daylightName] retain];
 
1876
 
 
1877
                                                  /* Abbr generated here is IMHO a bit suspicous but I kept it */
 
1878
                                                  for (a=0,b=0;daylightName[a];a++)
 
1879
                                                  {
 
1880
                                                      if (isupper(daylightName[a]))
 
1881
                                                        daylightName[b++]=daylightName[a];
 
1882
                                                  }
 
1883
                                                  daylightName[b]=0;
 
1884
                                                  [daylightZoneNameAbbr release];
 
1885
                                                  daylightZoneNameAbbr = [[NSString stringWithCString:daylightName] retain];
 
1886
                                        }
 
1887
                                        }
 
1888
                                      RegCloseKey(regKey);
 
1889
                                }                       
 
1890
                           }
 
1891
                        }
 
1892
        }
 
1893
      RegCloseKey(regDirKey);
 
1894
  }
 
1895
 
 
1896
  return self;
 
1897
}
 
1898
 
 
1899
- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate
 
1900
{
 
1901
  int year, month, day, hour, minute, second, mil;
 
1902
  int   dow;
 
1903
  int daylightdate, count, maxdate;
 
1904
  NSTimeInterval when;
 
1905
 
 
1906
  if (DaylightDate.wMonth == 0)
 
1907
    return NO;
 
1908
 
 
1909
  when = [aDate timeIntervalSinceReferenceDate] - Bias*60;
 
1910
 
 
1911
  GSBreakTime(when, &year, &month, &day, &hour, &minute, &second, &mil);
 
1912
 
 
1913
  // Before April or after October is Std
 
1914
  if (month < DaylightDate.wMonth || month > StandardDate.wMonth)
 
1915
    return NO;
 
1916
  // After April and before October is DST
 
1917
  if (month > DaylightDate.wMonth && month < StandardDate.wMonth)
 
1918
    return YES;
 
1919
  dow = dayOfCommonEra(when);
 
1920
 
 
1921
  dow = dow % 7;
 
1922
  if (dow < 0)
 
1923
    dow += 7;
 
1924
 
 
1925
  if (month == DaylightDate.wMonth /* April */)
 
1926
    {
 
1927
      daylightdate = day - dow + DaylightDate.wDayOfWeek;
 
1928
      maxdate = lastDayOfGregorianMonth(DaylightDate.wMonth, year)-7;
 
1929
      while (daylightdate > 7)
 
1930
        daylightdate-=7;
 
1931
      if (daylightdate < 1)
 
1932
        daylightdate += 7;
 
1933
      count=DaylightDate.wDay;
 
1934
      while (count>1 && daylightdate < maxdate)
 
1935
        {
 
1936
          daylightdate+=7;
 
1937
          count--;
 
1938
        }
 
1939
      if (day > daylightdate)
 
1940
        return YES;
 
1941
      if (day < daylightdate)
 
1942
        return NO;
 
1943
      if (hour > DaylightDate.wHour)
 
1944
        return YES;
 
1945
      if (hour < DaylightDate.wHour)
 
1946
        return NO;
 
1947
      if (minute > DaylightDate.wMinute)
 
1948
        return YES;
 
1949
      if (minute < DaylightDate.wMinute)
 
1950
        return NO;
 
1951
      if (second > DaylightDate.wSecond)
 
1952
        return YES;
 
1953
      if (second < DaylightDate.wSecond)
 
1954
        return NO;
 
1955
      if (mil >= DaylightDate.wMilliseconds)
 
1956
        return YES;
 
1957
      return NO;
 
1958
  }
 
1959
  if (month == StandardDate.wMonth /* October */)
 
1960
    {
 
1961
      daylightdate = day - dow + StandardDate.wDayOfWeek;
 
1962
      maxdate = lastDayOfGregorianMonth(StandardDate.wMonth, year)-7;
 
1963
      while (daylightdate > 7)
 
1964
        daylightdate-=7;
 
1965
      if (daylightdate < 1)
 
1966
        daylightdate += 7;
 
1967
      count=StandardDate.wDay;
 
1968
      while (count>1 && daylightdate < maxdate)
 
1969
        {
 
1970
          daylightdate+=7;
 
1971
          count--;
 
1972
        }
 
1973
      if (day > daylightdate)
 
1974
        return NO;
 
1975
      if (day < daylightdate)
 
1976
        return YES;
 
1977
      if (hour > StandardDate.wHour)
 
1978
        return NO;
 
1979
      if (hour < StandardDate.wHour)
 
1980
        return YES;
 
1981
      if (minute > StandardDate.wMinute)
 
1982
        return NO;
 
1983
      if (minute < StandardDate.wMinute)
 
1984
        return YES;
 
1985
      if (second > StandardDate.wSecond)
 
1986
        return NO;
 
1987
      if (second < StandardDate.wSecond)
 
1988
        return YES;
 
1989
      if (mil >= StandardDate.wMilliseconds)
 
1990
        return NO;
 
1991
      return YES;
 
1992
    }
 
1993
  return NO; // Never reached
 
1994
}
 
1995
 
 
1996
- (NSString*) name
 
1997
{
 
1998
  return timeZoneName;
 
1999
}
 
2000
 
 
2001
- (int) secondsFromGMTForDate: (NSDate*)aDate
 
2002
{
 
2003
  if ([self isDaylightSavingTimeForDate:aDate])
 
2004
    return -Bias*60 - DaylightBias*60;
 
2005
  return -Bias*60 - StandardBias*60;
 
2006
}
 
2007
 
 
2008
- (NSArray*) timeZoneDetailArray
 
2009
{
 
2010
        return [NSArray arrayWithObjects:
 
2011
                 [[[GSTimeZoneDetail alloc] initWithTimeZone:self
 
2012
                            withAbbrev:timeZoneNameAbbr
 
2013
                            withOffset:-Bias*60 - StandardBias*60
 
2014
                            withDST:NO] autorelease],
 
2015
                 [[[GSTimeZoneDetail alloc] initWithTimeZone:self
 
2016
                            withAbbrev:daylightZoneNameAbbr
 
2017
                            withOffset:-Bias*60 - DaylightBias*60
 
2018
                            withDST:YES] autorelease], 0];
 
2019
}
 
2020
 
 
2021
- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)aDate
 
2022
{
 
2023
  GSTimeZoneDetail      *detail;
 
2024
  int offset;
 
2025
  BOOL isDST = [self isDaylightSavingTimeForDate:aDate];
 
2026
  NSString *abbr;
 
2027
 
 
2028
  if (isDST)
 
2029
    {
 
2030
      offset = -Bias*60 - DaylightBias*60;
 
2031
      abbr = daylightZoneNameAbbr;
 
2032
    }
 
2033
  else
 
2034
    {
 
2035
      offset = -Bias*60 - StandardBias*60;
 
2036
      abbr = timeZoneNameAbbr;
 
2037
    }
 
2038
  detail = [GSTimeZoneDetail alloc];
 
2039
  detail = [detail initWithTimeZone: self
 
2040
                         withAbbrev: abbr
 
2041
                         withOffset: offset
 
2042
                            withDST: isDST];
 
2043
  return detail;
 
2044
}
 
2045
 
 
2046
- (NSString*) timeZoneName
 
2047
{
 
2048
  return timeZoneName;
 
2049
}
 
2050
@end
 
2051
#endif // WIN32
 
2052
 
 
2053
 
 
2054
@implementation GSTimeZone
 
2055
 
 
2056
/**
 
2057
 * Perform a binary search of a transitions table to locate the index
 
2058
 * of the transition to use for a particular time interval since 1970.<br />
 
2059
 * We locate the index of the highest transition before the date, or zero
 
2060
 * if there is no transition before it.
 
2061
 */
 
2062
static TypeInfo*
 
2063
chop(NSTimeInterval since, GSTimeZone *zone)
 
2064
{
 
2065
  gss32                 when = (gss32)since;
 
2066
  gss32                 *trans = zone->trans;
 
2067
  unsigned              hi = zone->n_trans;
 
2068
  unsigned              lo = 0;
 
2069
  unsigned int          i;
 
2070
 
 
2071
  if (hi == 0 || trans[0] > when)
 
2072
    {
 
2073
      unsigned  n_types = zone->n_types;
 
2074
 
 
2075
      /*
 
2076
       * If the first transition is greater than our date,
 
2077
       * we locate the first non-DST transition and use that offset,
 
2078
       * or just use the first transition.
 
2079
       */
 
2080
      for (i = 0; i < n_types; i++)
 
2081
        {
 
2082
          if (zone->types[i].isdst == 0)
 
2083
            {
 
2084
              return &zone->types[i];
 
2085
            }
 
2086
        }
 
2087
      return &zone->types[0];
 
2088
    }
 
2089
  else
 
2090
    {
 
2091
      for (i = hi/2; hi != lo; i = (hi + lo)/2)
 
2092
        {
 
2093
          if (when < trans[i])
 
2094
            {
 
2095
              hi = i;
 
2096
            }
 
2097
          else if (when > trans[i])
 
2098
            {
 
2099
              lo = ++i;
 
2100
            }
 
2101
          else
 
2102
            {
 
2103
              break;
 
2104
            }
 
2105
        }
 
2106
      /*
 
2107
       * If we went off the top of the table or the closest transition
 
2108
       * was later than our date, we step back to find the last
 
2109
       * transition before our date.
 
2110
       */
 
2111
      if (i > 0 && (i == zone->n_trans || trans[i] > when))
 
2112
        {
 
2113
          i--;
 
2114
        }
 
2115
      return &zone->types[zone->idxs[i]];
 
2116
    }
 
2117
}
 
2118
 
 
2119
static NSTimeZoneDetail*
 
2120
newDetailInZoneForType(GSTimeZone *zone, TypeInfo *type)
 
2121
{
 
2122
  GSTimeZoneDetail      *detail;
 
2123
 
 
2124
  detail = [GSTimeZoneDetail alloc];
 
2125
  detail = [detail initWithTimeZone: zone
 
2126
                         withAbbrev: type->abbreviation
 
2127
                         withOffset: type->offset
 
2128
                            withDST: type->isdst];
 
2129
  return detail;
 
2130
}
 
2131
 
 
2132
- (NSString*) abbreviationForDate: (NSDate*)aDate
 
2133
{
 
2134
  TypeInfo      *type = chop([aDate timeIntervalSince1970], self);
 
2135
 
 
2136
  return type->abbreviation;
 
2137
}
 
2138
 
 
2139
- (NSData*) data
 
2140
{
 
2141
  return timeZoneData;
 
2142
}
 
2143
 
 
2144
- (void) dealloc
 
2145
{
 
2146
  RELEASE(timeZoneName);
 
2147
  RELEASE(timeZoneData);
 
2148
  if (types != 0)
 
2149
    {
 
2150
      unsigned  i;
 
2151
 
 
2152
      for (i = 0; i < n_types; i++)
 
2153
        {
 
2154
          RELEASE(types[i].abbreviation);
 
2155
        }
 
2156
      NSZoneFree(NSDefaultMallocZone(), types);
 
2157
    }
 
2158
  [super dealloc];
 
2159
}
 
2160
 
 
2161
- (id) initWithName: (NSString*)name data: (NSData*)data
 
2162
{
 
2163
  static NSString       *fileException = @"GSTimeZoneFileException";
 
2164
 
 
2165
  timeZoneName = [name copy];
 
2166
  timeZoneData = [data copy];
 
2167
  NS_DURING
 
2168
    {
 
2169
      const void        *bytes = [timeZoneData bytes];
 
2170
      unsigned          length = [timeZoneData length];
 
2171
      void              *buf;
 
2172
      unsigned          pos = 0;
 
2173
      unsigned          i, charcnt;
 
2174
      unsigned char     *abbr;
 
2175
      struct tzhead     *header;
 
2176
 
 
2177
      if (length < sizeof(struct tzhead))
 
2178
        {
 
2179
          [NSException raise: fileException
 
2180
                      format: @"File is too small"];
 
2181
        }
 
2182
      header = (struct tzhead *)(bytes + pos);
 
2183
      pos += sizeof(struct tzhead);
 
2184
#ifdef TZ_MAGIC
 
2185
      if (memcmp(header->tzh_magic, TZ_MAGIC, strlen(TZ_MAGIC)) != 0)
 
2186
        {
 
2187
          [NSException raise: fileException
 
2188
                      format: @"TZ_MAGIC is incorrect"];
 
2189
        }
 
2190
#endif
 
2191
      n_trans = GSSwapBigI32ToHost(*(gss32*)header->tzh_timecnt);
 
2192
      n_types = GSSwapBigI32ToHost(*(gss32*)header->tzh_typecnt);
 
2193
      charcnt = GSSwapBigI32ToHost(*(gss32*)header->tzh_charcnt);
 
2194
 
 
2195
      i = pos;
 
2196
      i += sizeof(gss32)*n_trans;
 
2197
      if (i > length)
 
2198
        {
 
2199
          [NSException raise: fileException
 
2200
                      format: @"Transitions list is truncated"];
 
2201
        }
 
2202
      i += n_trans;
 
2203
      if (i > length)
 
2204
        {
 
2205
          [NSException raise: fileException
 
2206
                      format: @"Transition indexes are truncated"];
 
2207
        }
 
2208
      i += sizeof(struct ttinfo)*n_types;
 
2209
      if (i > length)
 
2210
        {
 
2211
          [NSException raise: fileException
 
2212
                      format: @"Types list is truncated"];
 
2213
        }
 
2214
      if (i + charcnt > length)
 
2215
        {
 
2216
          [NSException raise: fileException
 
2217
                      format: @"Abbreviations list is truncated"];
 
2218
        }
 
2219
 
 
2220
      /*
 
2221
       * Now calculate size we need to store the information
 
2222
       * for efficient access ... not the same saze as the data
 
2223
       * we received.
 
2224
       */
 
2225
      i = n_trans * (sizeof(gss32)+1) + n_types * sizeof(TypeInfo);
 
2226
      buf = NSZoneMalloc(NSDefaultMallocZone(), i);
 
2227
      types = (TypeInfo*)buf;
 
2228
      buf += (n_types * sizeof(TypeInfo));
 
2229
      trans = (gss32*)buf;
 
2230
      buf += (n_trans * sizeof(gss32));
 
2231
      idxs = (unsigned char*)buf;
 
2232
 
 
2233
      /* Read in transitions. */
 
2234
      for (i = 0; i < n_trans; i++)
 
2235
        {
 
2236
          trans[i] = GSSwapBigI32ToHost(*(gss32*)(bytes + pos));
 
2237
          pos += sizeof(gss32);
 
2238
        }
 
2239
      for (i = 0; i < n_trans; i++)
 
2240
        {
 
2241
          idxs[i] = *(unsigned char*)(bytes + pos);
 
2242
          pos++;
 
2243
        }
 
2244
      for (i = 0; i < n_types; i++)
 
2245
        {
 
2246
          struct ttinfo *ptr = (struct ttinfo*)(bytes + pos);
 
2247
 
 
2248
          types[i].isdst = (ptr->isdst != 0 ? YES : NO);
 
2249
          types[i].abbr_idx = ptr->abbr_idx;
 
2250
          types[i].offset = decode(ptr->offset);
 
2251
          pos += sizeof(struct ttinfo);
 
2252
        }
 
2253
      abbr = (char*)(bytes + pos);
 
2254
      {
 
2255
        id      abbrevs[charcnt];
 
2256
 
 
2257
        memset(abbrevs, '\0', sizeof(id)*charcnt);
 
2258
        for (i = 0; i < n_types; i++)
 
2259
          {
 
2260
            int loc = types[i].abbr_idx;
 
2261
 
 
2262
            if (abbrevs[loc] == nil)
 
2263
              {
 
2264
                abbrevs[loc] = [[NSString alloc] initWithCString: abbr + loc];
 
2265
              }
 
2266
            else
 
2267
              {
 
2268
                RETAIN(abbrevs[loc]);
 
2269
              }
 
2270
            types[i].abbreviation = abbrevs[loc];
 
2271
          }
 
2272
      }
 
2273
 
 
2274
      if (zone_mutex != nil)
 
2275
        {
 
2276
          [zone_mutex lock];
 
2277
        }
 
2278
      [zoneDictionary setObject: self forKey: timeZoneName];
 
2279
      if (zone_mutex != nil)
 
2280
        {
 
2281
          [zone_mutex unlock];
 
2282
        }
 
2283
    }
 
2284
  NS_HANDLER
 
2285
    {
 
2286
      DESTROY(self);
 
2287
      NSLog(@"Unable to obtain time zone `%@'... %@", name, localException);
 
2288
      if ([localException name] != fileException)
 
2289
        {
 
2290
          [localException raise];
 
2291
        }
 
2292
    }
 
2293
  NS_ENDHANDLER
 
2294
  return self;
 
2295
}
 
2296
 
 
2297
- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate
 
2298
{
 
2299
  TypeInfo      *type = chop([aDate timeIntervalSince1970], self);
 
2300
 
 
2301
  return type->isdst;
 
2302
}
 
2303
 
 
2304
- (NSString*) name
 
2305
{
 
2306
  return timeZoneName;
 
2307
}
 
2308
 
 
2309
- (int) secondsFromGMTForDate: (NSDate*)aDate
 
2310
{
 
2311
  TypeInfo      *type = chop([aDate timeIntervalSince1970], self);
 
2312
 
 
2313
  return type->offset;
 
2314
}
 
2315
 
 
2316
- (NSArray*) timeZoneDetailArray
 
2317
{
 
2318
  NSTimeZoneDetail      *details[n_types];
 
2319
  unsigned              i;
 
2320
  NSArray               *array;
 
2321
 
 
2322
  for (i = 0; i < n_types; i++)
 
2323
    {
 
2324
      details[i] = newDetailInZoneForType(self, &types[i]);
 
2325
    }
 
2326
  array = [NSArray arrayWithObjects: details count: n_types];
 
2327
  for (i = 0; i < n_types; i++)
 
2328
    {
 
2329
      RELEASE(details[i]);
 
2330
    }
 
2331
  return array;
 
2332
}
 
2333
 
 
2334
- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)aDate
 
2335
{
 
2336
  TypeInfo              *type;
 
2337
  NSTimeZoneDetail      *detail;
 
2338
 
 
2339
  type = chop([aDate timeIntervalSince1970], self);
 
2340
  detail = newDetailInZoneForType(self, type);
 
2341
  return AUTORELEASE(detail);
 
2342
}
 
2343
 
 
2344
- (NSString*) timeZoneName
 
2345
{
 
2346
  return timeZoneName;
 
2347
}
 
2348
 
 
2349
@end
 
2350