1997-09-01 21:59:51 +00:00
|
|
|
/* Time zone management. -*- Mode: ObjC -*-
|
|
|
|
Copyright (C) 1997 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
Written by: Yoo C. Chung <wacko@laplace.snu.ac.kr>
|
|
|
|
Date: June 1997
|
|
|
|
|
|
|
|
This file is part of the GNUstep Base Library.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2 of
|
|
|
|
the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful, but
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
License along with this library; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
|
|
|
|
/* We use a implementation independent of the system, since POSIX
|
|
|
|
functions for time zones are woefully inadequate for implementing
|
|
|
|
NSTimeZone, and time zone names can be different from system to
|
|
|
|
system.
|
|
|
|
|
|
|
|
We do not use a dictionary for storing time zones, since such a
|
|
|
|
dictionary would be VERY large (~500K). And we would have to use a
|
|
|
|
complicated object determining whether we're using daylight savings
|
|
|
|
time and such for every entry in the dictionary.
|
|
|
|
|
|
|
|
The local time zone can be specified with the user defaults
|
|
|
|
database (when it's properly implemented, that is), the TZ
|
|
|
|
environment variable, or the fallback local time zone, with the
|
|
|
|
ones listed first having precedence.
|
|
|
|
|
|
|
|
Any time zone must be a file name in ZONES_DIR.
|
|
|
|
|
|
|
|
FIXME?: use leap seconds? */
|
|
|
|
|
|
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <Foundation/NSArray.h>
|
|
|
|
#include <Foundation/NSCoder.h>
|
|
|
|
#include <Foundation/NSDate.h>
|
|
|
|
#include <Foundation/NSDictionary.h>
|
|
|
|
#include <Foundation/NSException.h>
|
|
|
|
#include <Foundation/NSLock.h>
|
|
|
|
#include <Foundation/NSObject.h>
|
|
|
|
#include <Foundation/NSProcessInfo.h>
|
|
|
|
#include <Foundation/NSString.h>
|
|
|
|
#include <Foundation/NSUserDefaults.h>
|
|
|
|
#include <Foundation/NSUtilities.h>
|
|
|
|
#include <Foundation/NSZone.h>
|
|
|
|
#include <gnustep/base/config.h>
|
|
|
|
|
|
|
|
#define NOID
|
|
|
|
#include "tzfile.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* Key for local time zone in user defaults. */
|
|
|
|
#define LOCALDBKEY "Local Time Zone"
|
1995-08-23 16:13:42 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
/* Fallback local time zone. */
|
|
|
|
#ifndef LOCAL_TIME_ZONE
|
|
|
|
#define LOCAL_TIME_ZONE "Universal"
|
|
|
|
#endif
|
1995-08-23 16:13:42 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
/* Directory that contains the time zone data. */
|
|
|
|
#define TIME_ZONE_DIR GNUSTEP_INSTALL_LIBDIR "/NSTimeZones/"
|
1995-08-23 16:13:42 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
/* Location of time zone abbreviation dictionary. It is a text file
|
|
|
|
with each line comprised of the abbreviation, a whitespace, and the
|
|
|
|
name. Neither the abbreviation nor the name can contain
|
|
|
|
whitespace, and each line must not be longer than 80 characters. */
|
|
|
|
#define ABBREV_DICT TIME_ZONE_DIR "abbreviations"
|
1995-08-23 16:13:42 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
/* File holding regions grouped by latitude. It is a text file with
|
|
|
|
each line comprised of the latitude region, whitespace, and the
|
|
|
|
name. Neither the abbreviation not the name can contain
|
|
|
|
whitespace, and each line must not be longer than 80 characters. */
|
|
|
|
#define REGIONS_FILE TIME_ZONE_DIR "regions"
|
1995-08-23 16:13:42 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
/* Directory that contains the actual time zones. */
|
|
|
|
#define ZONES_DIR TIME_ZONE_DIR "zones/"
|
1995-08-23 16:13:42 +00:00
|
|
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
@class NSInternalTimeTransition;
|
|
|
|
@class NSConcreteTimeZone;
|
|
|
|
@class NSConcreteAbsoluteTimeZone;
|
|
|
|
@class NSConcreteTimeZoneDetail;
|
1995-08-23 16:13:42 +00:00
|
|
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
/* Temporary structure for holding time zone details. */
|
|
|
|
struct ttinfo
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
int offset; // Seconds east of UTC
|
|
|
|
BOOL isdst; // Daylight savings time?
|
|
|
|
char abbr_idx; // Index into time zone abbreviations string
|
|
|
|
};
|
1995-08-23 16:13:42 +00:00
|
|
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
/* The local time zone. */
|
|
|
|
static id localTimeZone;
|
1995-08-23 16:13:42 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
/* Dictionary for time zones. Each time zone must have a unique
|
|
|
|
name. */
|
|
|
|
static NSMutableDictionary *zoneDictionary;
|
1995-08-23 16:13:42 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
/* Fake one-to-one abbreviation to time zone name dictionary. */
|
|
|
|
static NSDictionary *fake_abbrev_dict;
|
1995-08-23 16:13:42 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
/* Lock for creating time zones. */
|
|
|
|
static NSLock *zone_mutex;
|
1995-08-23 16:13:42 +00:00
|
|
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
/* Decode the four bytes at PTR as a signed integer in network byte order.
|
|
|
|
Based on code included in the GNU C Library 2.0.3. */
|
|
|
|
static inline int
|
|
|
|
decode (const void *ptr)
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
#if defined(WORDS_BIGENDIAN) && SIZEOF_INT == 4
|
|
|
|
return *(const int *) ptr;
|
|
|
|
#else /* defined(WORDS_BIGENDIAN) && SIZEOF_INT == 4 */
|
|
|
|
const unsigned char *p = ptr;
|
|
|
|
int result = *p & (1 << (CHAR_BIT - 1)) ? ~0 : 0;
|
|
|
|
|
|
|
|
result = (result << 8) | *p++;
|
|
|
|
result = (result << 8) | *p++;
|
|
|
|
result = (result << 8) | *p++;
|
|
|
|
result = (result << 8) | *p++;
|
|
|
|
return result;
|
|
|
|
#endif /* defined(WORDS_BIGENDIAN) && SIZEOF_INT == 4 */
|
|
|
|
}
|
1995-08-23 16:13:42 +00:00
|
|
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
/* Front end that actually uses [NSTimeZone abbrebiationMap]. */
|
|
|
|
@interface NSInternalAbbrevDict : NSDictionary
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@interface NSInternalTimeTransition : NSObject
|
|
|
|
{
|
|
|
|
int trans_time; // When the transition occurs
|
|
|
|
char detail_index; // Index of time zone detail
|
|
|
|
}
|
|
|
|
|
|
|
|
- initWithTime: (int)aTime withIndex: (char)anIndex;
|
|
|
|
- (int)transTime;
|
|
|
|
- (char)detailIndex;
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@interface NSConcreteTimeZone : NSTimeZone
|
|
|
|
{
|
|
|
|
NSString *name;
|
|
|
|
NSArray *transitions; // Transition times and rules
|
|
|
|
NSArray *details; // Time zone details
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
- initWithName: (NSString*)aName withTransitions: (NSArray*)trans
|
|
|
|
withDetails: (NSArray*)zoneDetails;
|
|
|
|
@end
|
|
|
|
|
1995-08-23 16:13:42 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
@interface NSConcreteAbsoluteTimeZone : NSTimeZone
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
NSString *name;
|
|
|
|
int offset; // Offset from UTC in seconds.
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
+ timeZoneWithOffset: (int)anOffset;
|
|
|
|
|
|
|
|
- initWithOffset: (int)anOffset withName: (NSString*)aName;
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@interface NSConcreteTimeZoneDetail : NSTimeZoneDetail
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
NSString *abbrev; // Abbreviation for time zone detail.
|
|
|
|
int offset; // Offset from UTC in seconds.
|
|
|
|
BOOL is_dst; // Is it daylight savings time?
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
- initWithAbbrev: (NSString*)anAbbrev withOffset: (int)anOffset
|
|
|
|
withDST: (BOOL)isDST;
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NSInternalAbbrevDict
|
|
|
|
|
|
|
|
+ allocWithZone: (NSZone*)zone
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
return NSAllocateObject(self, 0, zone);
|
|
|
|
}
|
|
|
|
|
|
|
|
- init
|
|
|
|
{
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (unsigned)count
|
|
|
|
{
|
|
|
|
return [[NSTimeZone abbreviationMap] count];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSEnumerator*)keyEnumerator
|
|
|
|
{
|
|
|
|
return [[NSTimeZone abbreviationMap] keyEnumerator];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSEnumerator*)objectEnumerator
|
|
|
|
{
|
|
|
|
/* FIXME: this is a memory hungry implementation */
|
|
|
|
id e, name, a;
|
|
|
|
|
|
|
|
a = [NSMutableArray array];
|
|
|
|
e = [[NSTimeZone abbreviationMap] keyEnumerator];
|
|
|
|
while ((name = [e nextObject]) != nil)
|
|
|
|
[a addObject: [[[NSTimeZone abbreviationMap] objectForKey: name]
|
|
|
|
objectAtIndex: 0]];
|
|
|
|
return [a objectEnumerator];
|
|
|
|
}
|
|
|
|
|
|
|
|
- objectForKey: key
|
|
|
|
{
|
|
|
|
return [[[NSTimeZone abbreviationMap] objectForKey: key] objectAtIndex: 0];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NSInternalTimeTransition
|
|
|
|
|
|
|
|
- (NSString*)description
|
|
|
|
{
|
|
|
|
return [NSString
|
|
|
|
stringWithFormat: @"(trans: %d, idx: %d)",
|
|
|
|
trans_time, (int)detail_index];
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
- initWithTime: (int)aTime withIndex: (char)anIndex
|
|
|
|
{
|
|
|
|
[super init];
|
|
|
|
trans_time = aTime;
|
|
|
|
detail_index = anIndex;
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int)transTime
|
|
|
|
{
|
|
|
|
return trans_time;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (char)detailIndex
|
|
|
|
{
|
|
|
|
return detail_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NSConcreteTimeZone
|
|
|
|
|
|
|
|
- initWithName: (NSString*)aName withTransitions: (NSArray*)trans
|
|
|
|
withDetails: (NSArray*)zoneDetails
|
|
|
|
{
|
|
|
|
[super init];
|
|
|
|
name = [aName retain];
|
|
|
|
transitions = [trans retain];
|
|
|
|
details = [zoneDetails retain];
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dealloc
|
|
|
|
{
|
|
|
|
[name release];
|
|
|
|
[transitions release];
|
|
|
|
[details release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)encodeWithCoder: aCoder
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
[super encodeWithCoder: aCoder];
|
|
|
|
[aCoder encodeObject: name];
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
- initWithDecoder: aDecoder
|
|
|
|
{
|
|
|
|
/* FIXME?: is this right? */
|
|
|
|
self = [super initWithCoder: aDecoder];
|
|
|
|
return (self = (id)[NSTimeZone timeZoneWithName: [aDecoder decodeObject]]);
|
|
|
|
}
|
|
|
|
|
1995-08-23 16:13:42 +00:00
|
|
|
- (NSString*)description
|
1997-09-01 21:59:51 +00:00
|
|
|
{
|
|
|
|
return [NSString stringWithFormat: @"(trans: %@, details: %@)",
|
|
|
|
[transitions description], [details description]];
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
- (NSTimeZoneDetail*)timeZoneDetailForDate: (NSDate*)date
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
unsigned index;
|
|
|
|
int the_time;
|
|
|
|
unsigned count;
|
|
|
|
|
|
|
|
the_time = (int)[date timeIntervalSince1970];
|
|
|
|
count = [transitions count];
|
|
|
|
if (count == 0
|
|
|
|
|| the_time < [[transitions objectAtIndex: 0] transTime])
|
|
|
|
/* Either DATE is before any transitions or there is no transition.
|
|
|
|
Return the first non-DST type, or the first one if they are all DST. */
|
|
|
|
{
|
|
|
|
unsigned detail_count;
|
|
|
|
|
|
|
|
detail_count = [details count];
|
|
|
|
index = 0;
|
|
|
|
while (index < detail_count
|
|
|
|
&& [[details objectAtIndex: index] isDaylightSavingTimeZone])
|
|
|
|
index++;
|
|
|
|
if (index == detail_count)
|
|
|
|
index = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
/* Find the first transition after DATE, and then pick the type of
|
|
|
|
the transition before it. */
|
|
|
|
{
|
|
|
|
for (index = 1; index < count; index++)
|
|
|
|
if (the_time < [[transitions objectAtIndex: index] transTime])
|
|
|
|
break;
|
|
|
|
index = [[transitions objectAtIndex: index-1] detailIndex];
|
|
|
|
}
|
|
|
|
return [details objectAtIndex: index];
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
- (NSArray*)timeZoneDetailArray
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
return details;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)timeZoneName
|
|
|
|
{
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NSConcreteAbsoluteTimeZone
|
|
|
|
|
|
|
|
+ timeZoneWithOffset: (int)anOffset
|
|
|
|
{
|
|
|
|
id newName, zone;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
newName = [NSString stringWithFormat: @"%d", anOffset];
|
|
|
|
zone = [zoneDictionary objectForKey: newName];
|
|
|
|
if (zone == nil)
|
|
|
|
{
|
|
|
|
zone = [[self alloc] initWithOffset: anOffset withName: newName];
|
|
|
|
[zoneDictionary setObject: zone forKey: newName];
|
|
|
|
}
|
|
|
|
return zone;
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
- initWithOffset: (int)anOffset withName: (NSString*)aName
|
|
|
|
{
|
|
|
|
[super init];
|
|
|
|
name = [aName retain];
|
|
|
|
offset = anOffset;
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dealloc
|
|
|
|
{
|
|
|
|
[name release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)encodeWithCoder: aCoder
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
[super encodeWithCoder: aCoder];
|
|
|
|
[aCoder encodeObject: name];
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
- initWithCoder: aDecoder
|
|
|
|
{
|
|
|
|
self = [super initWithCoder: aDecoder];
|
|
|
|
name = [aDecoder decodeObject];
|
|
|
|
offset = [name intValue];
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)description
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
return [NSString stringWithFormat: @"(offset: %d)", offset];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSTimeZoneDetail*)timeZoneDetailForDate: (NSDate*)date
|
|
|
|
{
|
|
|
|
return [[[NSConcreteTimeZoneDetail alloc]
|
|
|
|
initWithAbbrev: name withOffset: offset withDST: NO]
|
|
|
|
autorelease];
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
- (NSString*)timeZoneName
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)timeZoneDetailArray
|
|
|
|
{
|
|
|
|
return [NSArray arrayWithObject: [self timeZoneDetailForDate: nil]];
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NSConcreteTimeZoneDetail
|
|
|
|
|
|
|
|
- initWithAbbrev: (NSString*)anAbbrev withOffset: (int)anOffset
|
|
|
|
withDST: (BOOL)isDST
|
|
|
|
{
|
|
|
|
[super init];
|
|
|
|
abbrev = [anAbbrev retain];
|
|
|
|
offset = anOffset;
|
|
|
|
is_dst = isDST;
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)dealloc
|
|
|
|
{
|
|
|
|
[abbrev release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)encodeWithCoder: aCoder
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
[super encodeWithCoder: aCoder];
|
|
|
|
[aCoder encodeObject: abbrev];
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(int) at: &offset];
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &is_dst];
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
- initWithCoder: aDecoder
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
self = [super initWithCoder: aDecoder];
|
|
|
|
abbrev = [aDecoder decodeObject];
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(int) at: &offset];
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &is_dst];
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)description
|
|
|
|
{
|
|
|
|
return [NSString stringWithFormat: @"(abbrev: %@, offset: %d, is_dst: %d)",
|
|
|
|
abbrev, offset, (int)is_dst];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isDaylightSavingTimeZone
|
|
|
|
{
|
|
|
|
return is_dst;
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
- (NSString*)timeZoneAbbreviation
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
return abbrev;
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
- (int)timeZoneSecondsFromGMT
|
|
|
|
{
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
1995-08-23 16:13:42 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
@implementation NSTimeZone
|
1995-08-23 16:13:42 +00:00
|
|
|
|
|
|
|
+ (void)initialize
|
1997-09-01 21:59:51 +00:00
|
|
|
{
|
|
|
|
if (self == [NSTimeZone class])
|
|
|
|
{
|
|
|
|
id localZoneString = nil;
|
1995-08-23 16:13:42 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
zone_mutex = [NSLock new];
|
|
|
|
|
|
|
|
/* Don't use this for now. */
|
1995-08-23 16:13:42 +00:00
|
|
|
#if 0
|
1997-09-01 21:59:51 +00:00
|
|
|
localZoneString = [[NSUserDefaults standardUserDefaults]
|
|
|
|
stringForKey: @LOCALDBKEY];
|
1995-08-23 16:13:42 +00:00
|
|
|
#endif
|
1997-09-01 21:59:51 +00:00
|
|
|
if (localZoneString == nil)
|
|
|
|
localZoneString = [[[NSProcessInfo processInfo]
|
|
|
|
environment] objectForKey: @"TZ"];
|
|
|
|
if (localZoneString != nil)
|
|
|
|
localTimeZone = [NSTimeZone timeZoneWithName: localZoneString];
|
|
|
|
else
|
|
|
|
localTimeZone = [NSTimeZone timeZoneWithName: @LOCAL_TIME_ZONE];
|
|
|
|
|
|
|
|
/* If local time zone fails to allocate, then allocate something
|
|
|
|
that is sure to succeed (unless we run out of memory, of
|
|
|
|
course). */
|
|
|
|
if (localTimeZone == nil)
|
|
|
|
localTimeZone = [NSTimeZone timeZoneForSecondsFromGMT: 0];
|
|
|
|
|
|
|
|
[localTimeZone retain];
|
|
|
|
|
|
|
|
fake_abbrev_dict = [[NSInternalAbbrevDict alloc] init];
|
|
|
|
zoneDictionary = [[NSMutableDictionary dictionary] retain];
|
|
|
|
[zoneDictionary setObject: localTimeZone
|
|
|
|
forKey: [localTimeZone timeZoneName]];
|
|
|
|
}
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
+ (NSTimeZoneDetail*)defaultTimeZone
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
return [localTimeZone timeZoneDetailForDate: [NSDate date]];
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
+ (NSTimeZone*)localTimeZone
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
return localTimeZone;
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
+ (NSTimeZone*)timeZoneForSecondsFromGMT: (int)seconds
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
/* We simply return the following because an existing time zone with
|
|
|
|
the given offset might not always have the same offset (daylight
|
|
|
|
savings time, change in standard time, etc.). */
|
|
|
|
return [NSConcreteAbsoluteTimeZone timeZoneWithOffset: seconds];
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
+ (NSTimeZoneDetail*)timeZoneWithAbbreviation: (NSString*)abbreviation
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
/* We obtain a time zone from the abbreviation dictionary, get the
|
|
|
|
time zone detail array, and then obtain the time zone detail we
|
|
|
|
want from that array. This convulated and twisted method is used
|
|
|
|
because there is no way to directly obtain the time zone
|
|
|
|
detail. */
|
|
|
|
id zone, detailArray, e, object;
|
|
|
|
|
|
|
|
zone = [self timeZoneWithName: [[self abbreviationDictionary]
|
|
|
|
objectForKey: abbreviation]];
|
|
|
|
if (zone == nil)
|
|
|
|
return nil;
|
|
|
|
detailArray = [zone timeZoneDetailArray];
|
|
|
|
e = [detailArray objectEnumerator];
|
|
|
|
while ((object = [e nextObject]) != nil)
|
|
|
|
if ([[object timeZoneAbbreviation] isEqualToString: abbreviation])
|
|
|
|
return object;
|
|
|
|
|
|
|
|
/* If we reach here, we've got an inconsistency in our time zone
|
|
|
|
database. */
|
|
|
|
[NSException
|
|
|
|
raise: NSInternalInconsistencyException
|
|
|
|
format: @"Time zone abbreviation %@ inconsistent", abbreviation];
|
|
|
|
return nil;
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
+ (NSTimeZone*)timeZoneWithName: (NSString*)aTimeZoneName
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
static NSString *fileException = @"fileException";
|
|
|
|
id zone, fileName, transArray, detailsArray;
|
|
|
|
int i, n_trans, n_types, names_size;
|
|
|
|
id *abbrevsArray;
|
|
|
|
char *trans, *type_idxs, *zone_abbrevs;
|
|
|
|
struct tzhead header;
|
|
|
|
struct ttinfo *types; // Temporary array for details
|
|
|
|
FILE *file;
|
|
|
|
|
|
|
|
[zone_mutex lock];
|
|
|
|
zone = [zoneDictionary objectForKey: aTimeZoneName];
|
|
|
|
if (zone != nil)
|
|
|
|
{
|
|
|
|
[zone_mutex unlock];
|
|
|
|
return zone;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure that only time zone files are accessed.
|
|
|
|
FIXME: Make this more robust. */
|
|
|
|
if ([aTimeZoneName length] == 0
|
|
|
|
|| ([aTimeZoneName cStringNoCopy])[0] == '/'
|
|
|
|
|| strchr([aTimeZoneName cStringNoCopy], '.') != NULL)
|
|
|
|
{
|
|
|
|
[zone_mutex unlock];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
fileName = [NSString stringWithFormat: @"%s%@", ZONES_DIR, aTimeZoneName];
|
|
|
|
file = fopen([fileName cStringNoCopy], "rb");
|
|
|
|
if (file == NULL)
|
|
|
|
{
|
|
|
|
[zone_mutex unlock];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_DURING
|
|
|
|
/* Read header. */
|
|
|
|
if (fread(&header, sizeof(struct tzhead), 1, file) != 1)
|
|
|
|
[NSException raise: fileException format: nil];
|
|
|
|
|
|
|
|
n_trans = decode(header.tzh_timecnt);
|
|
|
|
n_types = decode(header.tzh_typecnt);
|
|
|
|
names_size = decode(header.tzh_charcnt);
|
|
|
|
|
|
|
|
/* Read in transitions. */
|
|
|
|
trans = NSZoneMalloc(NSDefaultMallocZone(), 4*n_trans);
|
|
|
|
type_idxs = NSZoneMalloc(NSDefaultMallocZone(), n_trans);
|
|
|
|
if (fread(trans, 4, n_trans, file) != n_trans
|
|
|
|
|| fread(type_idxs, 1, n_trans, file) != n_trans)
|
|
|
|
[NSException raise: fileException format: nil];
|
|
|
|
transArray = [NSMutableArray array];
|
|
|
|
for (i = 0; i < n_trans; i++)
|
|
|
|
[transArray
|
|
|
|
addObject: [[NSInternalTimeTransition alloc]
|
|
|
|
initWithTime: decode(trans+(i*4))
|
|
|
|
withIndex: type_idxs[i]]];
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), trans);
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), type_idxs);
|
|
|
|
|
|
|
|
/* Read in time zone details. */
|
|
|
|
types =
|
|
|
|
NSZoneMalloc(NSDefaultMallocZone(), sizeof(struct ttinfo)*n_types);
|
|
|
|
for (i = 0; i < n_types; i++)
|
|
|
|
{
|
|
|
|
unsigned char x[4];
|
|
|
|
|
|
|
|
if (fread(x, 1, 4, file) != 4
|
|
|
|
|| fread(&types[i].isdst, 1, 1, file) != 1
|
|
|
|
|| fread(&types[i].abbr_idx, 1, 1, file) != 1)
|
|
|
|
[NSException raise: fileException format: nil];
|
|
|
|
types[i].offset = decode(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read in time zone abbreviation strings. */
|
|
|
|
zone_abbrevs = NSZoneMalloc(NSDefaultMallocZone(), names_size);
|
|
|
|
if (fread(zone_abbrevs, 1, names_size, file) != names_size)
|
|
|
|
[NSException raise: fileException format: nil];
|
|
|
|
abbrevsArray = NSZoneMalloc(NSDefaultMallocZone(), sizeof(id)*names_size);
|
|
|
|
i = 0;
|
|
|
|
while (i < names_size)
|
|
|
|
{
|
|
|
|
abbrevsArray[i] = [NSString stringWithCString: zone_abbrevs+i];
|
|
|
|
i = (strchr(zone_abbrevs+i, '\0')-zone_abbrevs)+1;
|
|
|
|
}
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), zone_abbrevs);
|
|
|
|
|
|
|
|
/* Create time zone details. */
|
|
|
|
detailsArray = [NSMutableArray array];
|
|
|
|
for (i = 0; i < n_types; i++)
|
|
|
|
[detailsArray
|
|
|
|
addObject: [[NSConcreteTimeZoneDetail alloc]
|
|
|
|
initWithAbbrev: abbrevsArray[types[i].abbr_idx]
|
|
|
|
withOffset: types[i].offset
|
|
|
|
withDST: (types[i].isdst > 0)]];
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), abbrevsArray);
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), types);
|
|
|
|
zone = [[NSConcreteTimeZone alloc]
|
|
|
|
initWithName: aTimeZoneName
|
|
|
|
withTransitions: transArray
|
|
|
|
withDetails: detailsArray];
|
|
|
|
[zoneDictionary setObject: zone forKey: aTimeZoneName];
|
|
|
|
fclose(file);
|
|
|
|
NS_HANDLER
|
|
|
|
if ([localException name] != fileException)
|
|
|
|
[localException raise];
|
|
|
|
fclose(file);
|
|
|
|
zone = nil;
|
|
|
|
NS_ENDHANDLER
|
|
|
|
|
|
|
|
[zone_mutex unlock];
|
|
|
|
return zone;
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
- (NSTimeZoneDetail*)timeZoneDetailForDate: (NSDate*)date
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
return [self subclassResponsibility: _cmd];
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
+ (void)setDefaultTimeZone: (NSTimeZone*)aTimeZone
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
if (aTimeZone == nil)
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
format: @"Nil time zone specified"];
|
|
|
|
[localTimeZone release];
|
|
|
|
localTimeZone = [aTimeZone retain];
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
+ (NSDictionary*)abbreviationDictionary
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
return fake_abbrev_dict;
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
+ (NSDictionary*)abbreviationMap
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
/* Instead of creating the abbreviation dictionary when the class is
|
|
|
|
initialized, we create it when we first need it, since the
|
|
|
|
dictionary can be potentially very large, considering that it's
|
|
|
|
almost never used. */
|
|
|
|
|
|
|
|
static NSMutableDictionary *abbreviationDictionary = nil;
|
|
|
|
FILE *file; // For the file containing the abbreviation dictionary
|
|
|
|
char abbrev[80], name[80];
|
|
|
|
|
|
|
|
if (abbreviationDictionary != nil)
|
|
|
|
return abbreviationDictionary;
|
|
|
|
|
|
|
|
/* Read dictionary from file. */
|
|
|
|
abbreviationDictionary = [[NSMutableDictionary dictionary] retain];
|
|
|
|
file = fopen(ABBREV_DICT, "r");
|
|
|
|
if (file == NULL)
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
format: @"Failed to open time zone abbreviation dictionary"];
|
|
|
|
while (fscanf(file, "%s %s", abbrev, name) == 2)
|
|
|
|
{
|
|
|
|
id a, the_name, the_abbrev;
|
|
|
|
|
|
|
|
the_name = [NSString stringWithCString: name];
|
|
|
|
the_abbrev = [NSString stringWithCString: abbrev];
|
|
|
|
a = [abbreviationDictionary objectForKey: the_abbrev];
|
|
|
|
if (a == nil)
|
|
|
|
{
|
|
|
|
a = [NSMutableArray array];
|
|
|
|
[abbreviationDictionary setObject: a forKey: the_abbrev];
|
|
|
|
}
|
|
|
|
[a addObject: the_name];
|
|
|
|
}
|
|
|
|
fclose(file);
|
1995-08-23 16:13:42 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
return abbreviationDictionary;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)timeZoneName
|
|
|
|
{
|
|
|
|
return [self subclassResponsibility: _cmd];
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSArray*)timeZoneArray
|
|
|
|
{
|
|
|
|
/* We create the array only when we need it to reduce overhead. */
|
|
|
|
|
|
|
|
static NSArray *regionsArray = nil;
|
|
|
|
int index, i;
|
|
|
|
char name[80];
|
|
|
|
FILE *file;
|
|
|
|
id temp_array[24];
|
|
|
|
|
|
|
|
if (regionsArray != nil)
|
|
|
|
return regionsArray;
|
|
|
|
|
|
|
|
for (i = 0; i < 24; i++)
|
|
|
|
temp_array[i] = [NSMutableArray array];
|
|
|
|
|
|
|
|
file = fopen(REGIONS_FILE, "r");
|
|
|
|
if (file == NULL)
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
format: @"Failed to open regions array file"];
|
|
|
|
while (fscanf(file, "%d %s", &index, name) == 2)
|
|
|
|
[temp_array[index] addObject: [NSString stringWithCString: name]];
|
|
|
|
fclose(file);
|
|
|
|
regionsArray = [[NSArray alloc] initWithObjects: temp_array count: 24];
|
|
|
|
return regionsArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray*)timeZoneDetailArray
|
|
|
|
{
|
|
|
|
return [self subclassResponsibility: _cmd];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
1995-08-23 16:13:42 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
@implementation NSTimeZoneDetail
|
1995-08-23 16:13:42 +00:00
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
- (NSString*)timeZoneName
|
1995-08-23 16:13:42 +00:00
|
|
|
{
|
1997-09-01 21:59:51 +00:00
|
|
|
return [self shouldNotImplement: _cmd];
|
1995-08-23 16:13:42 +00:00
|
|
|
}
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
- (BOOL)isDaylightSavingTimeZone
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*)timeZoneAbbreviation
|
|
|
|
{
|
|
|
|
return [self subclassResponsibility: _cmd];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int)timeZoneSecondsFromGMT
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return 0;
|
|
|
|
}
|