1/** Time zone management. -*- Mode: ObjC -*- 2 Copyright (C) 1997-20 11Free Software Foundation, Inc. 3 4 Written by: Yoo C. Chung <wacko@laplace.snu.ac.kr> 5 Date: June 1997 6 7 Rewrite large chunks by: Richard Frith-Macdonald <rfm@gnu.org> 8 Date: September 2002 9 10 This file is part of the GNUstep Base Library. 11 12 This library is free software; you can redistribute it and/or 13 modify it under the terms of the GNU Lesser General Public License 14 as published by the Free Software Foundation; either 15 version 2 of the License, or (at your option) any later version. 16 17 This library is distributed in the hope that it will be useful, but 18 WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 Lesser General Public License for more details. 21 22 You should have received a copy of the GNU Lesser General Public 23 License along with this library; if not, write to the Free Software 24 Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 Boston, MA 02110 USA. 26 27 <title>NSTimeZone class reference</title> 28 $Date$ $Revision$ 29 */ 30 31/* Use the system time zones if available. In other cases, use an 32 implementation independent of the system, since POSIX functions for 33 time zones are woefully inadequate for implementing NSTimeZone. 34 Time zone names can be different from system to system, but usually 35 the user has already set up his timezone independant of GNUstep, so we 36 should respect that information. 37 38 We do not use a dictionary for storing time zones, since such a 39 dictionary would be VERY large (~500K). And we would have to use a 40 complicated object determining whether we're using daylight savings 41 time and such for every entry in the dictionary. (Though we will 42 eventually have to change the implementation to prevent the year 43 2038 problem.) 44 45 The local time zone can be specified (with the ones listed first 46 having precedence) by: 47 48 1) the user defaults database 49 2) the GNUSTEP_TZ environment variable 50 3) the file LOCAL_TIME_FILE in _time_zone_path() 51 52 Failing that, the time zone may be guessed from system dependent sources 53 such as: 54 55 the windows registry 56 the /etc/timezone file 57 the /etc/sysconfig/clock file 58 TZDEFAULT defined in tzfile.h 59 the TZ environment variable 60 tzset() & tznam[]/daylight 61 62 If all else faile, the fallback time zone (which is GMT/UTC) 63 64 Any time zone must be a file name in ZONES_DIR. 65 66 Files & File System Heirarchy info: 67 =================================== 68 69 Default place for the NSTimeZone directory is _time_zone_path(): 70 {$(GNUSTEP_SYSTEM_LIBRARY)/Libraries/gnustep-base/Versions/???/Resources/TIME_ZONE_DIR) 71 72 LOCAL_TIME_FILE is a text file with the name of the time zone file. 73 74 ZONES_DIR is a sub-directory under TIME_ZONE_DIR 75 76 (dir) ../System/Library/Libraries/gnustep-base/Versions/???/Resources/.. 77 (dir) NSTimeZone 78 (file) localtime {text; time zone eg Australia/Perth} 79 (dir) zones 80 81 Note that full zone info is required, especially the various "GMT" 82 files which are created especially for OPENSTEP compatibility. 83 Zone info comes from the Olson time database. 84 85 FIXME?: use leap seconds? */ 86 87#import "common.h" 88#define EXPOSE_NSTimeZone_IVARS 1 89#import "GNUstepBase/GSLock.h" 90#include <stdio.h> 91#include <time.h> 92#import "Foundation/NSArray.h" 93#import "Foundation/NSCoder.h" 94#import "Foundation/NSData.h" 95#import "Foundation/NSDate.h" 96#import "Foundation/NSDictionary.h" 97#import "Foundation/NSException.h" 98#import "Foundation/NSFileManager.h" 99#import "Foundation/NSLock.h" 100#import "Foundation/NSProcessInfo.h" 101#import "Foundation/NSUserDefaults.h" 102#import "Foundation/NSMapTable.h" 103#import "Foundation/NSThread.h" 104#import "Foundation/NSNotification.h" 105#import "Foundation/NSPortCoder.h" 106#import "Foundation/NSTimeZone.h" 107#import "Foundation/NSByteOrder.h" 108#import "Foundation/NSLocale.h" 109#import "GNUstepBase/NSString+GNUstepBase.h" 110#import "GSPrivate.h" 111#import "GSPThread.h" 112 113#ifdef HAVE_TZHEAD 114#include <tzfile.h> 115#else 116#include "nstzfile.h" 117#endif 118 119#if defined(HAVE_UNICODE_UCAL_H) 120#define id id_ucal 121#include <unicode/ucal.h> 122#undef id 123#endif 124 125NSString * const NSSystemTimeZoneDidChangeNotification 126 = @"NSSystemTimeZoneDidChangeNotification"; 127 128/* Key for local time zone in user defaults. */ 129#define LOCALDBKEY @"Local Time Zone" 130 131/* Directory that contains the time zone data. 132 Expected in Resources directory for library bundle. */ 133#define TIME_ZONE_DIR @"NSTimeZones" 134 135/* Name of time zone abbreviation (plist) dictionary. */ 136#define ABBREV_DICT @"abbreviations" 137 138/* Name of time zone abbreviation map. It is a text file 139 with each line comprised of the abbreviation, a whitespace, and the 140 name. Neither the abbreviation nor the name can contain 141 whitespace, and each line must not be longer than 80 characters. */ 142#define ABBREV_MAP @"abbreviations" 143 144/* File holding regions grouped by latitude. It is a text file with 145 each line comprised of the latitude region, whitespace, and the 146 name. Neither the abbreviation nor the name can contain 147 whitespace, and each line must not be longer than 80 characters. */ 148#define REGIONS_FILE @"regions" 149 150/* Name of the file that contains the name of the local time zone. */ 151#define LOCAL_TIME_FILE @"localtime" 152 153/* Directory that contains the actual time zones. */ 154#define ZONES_DIR @"zones" 155 156/* Many systems have this file */ 157#define SYSTEM_TIME_FILE @"/etc/localtime" 158 159/* If TZDIR told us where the zoneinfo files are, don't append anything else */ 160#ifdef TZDIR 161#define POSIX_TZONES @"" 162#else 163#define POSIX_TZONES @"posix/" 164#endif 165 166#define BUFFER_SIZE 512 167#define WEEK_MILLISECONDS (7.0*24.0*60.0*60.0*1000.0) 168 169#if GS_USE_ICU == 1 170static inline int 171_NSToICUTZDisplayStyle(NSTimeZoneNameStyle style) 172{ 173 switch (style) 174 { 175 case NSTimeZoneNameStyleStandard: 176 return UCAL_STANDARD; 177 case NSTimeZoneNameStyleShortStandard: 178 return UCAL_SHORT_STANDARD; 179 case NSTimeZoneNameStyleDaylightSaving: 180 return UCAL_DST; 181 case NSTimeZoneNameStyleShortDaylightSaving: 182 return UCAL_SHORT_DST; 183 default: 184 return -1; 185 } 186} 187 188static inline UCalendar * 189ICUCalendarSetup (NSTimeZone *tz, NSLocale *locale) 190{ 191 NSString *tzStr; 192 int32_t tzLen; 193 const char *cLocale; 194 UChar tzName[BUFFER_SIZE]; 195 UCalendar *cal; 196 UErrorCode err = U_ZERO_ERROR; 197 198 tzStr = [tz name]; 199 if ((tzLen = [tzStr length]) > BUFFER_SIZE) 200 tzLen = BUFFER_SIZE; 201 [tzStr getCharacters: tzName range: NSMakeRange(0, tzLen)]; 202 cLocale = [[locale localeIdentifier] UTF8String]; 203 204 cal = ucal_open(tzName, tzLen, cLocale, UCAL_TRADITIONAL, &err); 205 if (U_FAILURE(err)) 206 return NULL; 207 208 return cal; 209} 210#endif 211 212/* Possible location of system time zone files */ 213static NSString *tzdir = nil; 214 215@class GSAbsTimeZone; 216@class GSTimeZoneDetail; 217@class GSAbsTimeZoneDetail; 218 219@class GSPlaceholderTimeZone; 220 221/* 222 * Information for abstract placeholder class. 223 */ 224static GSPlaceholderTimeZone *defaultPlaceholderTimeZone; 225static NSMapTable *placeholderMap; 226static GSAbsTimeZone *commonAbsolutes[145] = { 0 }; 227 228/* 229 * Temporary structure for holding time zone details. 230 * This is the format in the data object. 231 */ 232struct ttinfo 233{ 234 char offset[4]; // Seconds east of UTC 235 unsigned char isdst; // Daylight savings time? 236 unsigned char abbr_idx; // Index into time zone abbreviations string 237} __attribute__((packed)); 238 239/* 240 * And this is the structure used in the time zone instances. 241 */ 242typedef struct { 243 int32_t offset; 244 BOOL isdst; 245 unsigned char abbr_idx; 246 char pad[2]; 247 NSString *abbreviation; 248} TypeInfo; 249 250@interface GSTimeZone : NSTimeZone 251{ 252@public 253 NSString *timeZoneName; 254 NSArray *abbreviations; 255 NSData *timeZoneData; 256 unsigned int n_trans; 257 unsigned int n_types; 258 int32_t *trans; 259 TypeInfo *types; 260 unsigned char *idxs; 261} 262@end 263 264#if defined(_WIN32) 265@interface GSWindowsTimeZone : NSTimeZone 266{ 267@public 268 NSString *timeZoneName; 269 NSString *daylightZoneName; 270 NSString *timeZoneNameAbbr; 271 NSString *daylightZoneNameAbbr; 272 LONG Bias; 273 LONG StandardBias; 274 LONG DaylightBias; 275 SYSTEMTIME StandardDate; 276 SYSTEMTIME DaylightDate; 277} 278@end 279#endif 280 281static NSTimeZone *defaultTimeZone = nil; 282static NSTimeZone *localTimeZone = nil; 283static NSTimeZone *systemTimeZone = nil; 284 285/* Dictionary for time zones. Each time zone must have a unique 286 name. */ 287static NSMutableDictionary *zoneDictionary; 288 289/* one-to-one abbreviation to time zone name dictionary. */ 290static NSMutableDictionary *abbreviationDictionary = nil; 291/* one-to-many abbreviation to time zone name dictionary. */ 292static NSMutableDictionary *abbreviationMap = nil; 293 294/* Lock for creating time zones. */ 295static pthread_mutex_t zone_mutex; 296 297static Class NSTimeZoneClass; 298static Class GSPlaceholderTimeZoneClass; 299 300 301/* Return path to a TimeZone directory file */ 302static NSString *_time_zone_path(NSString *subpath, NSString *type) 303{ 304 NSBundle *gbundle; 305 if (type == nil) 306 type = @""; 307 gbundle = [NSBundle bundleForClass: [NSObject class]]; 308 return [gbundle pathForResource: subpath 309 ofType: type 310 inDirectory: TIME_ZONE_DIR]; 311} 312 313@interface GSPlaceholderTimeZone : NSTimeZone 314@end 315 316@interface GSAbsTimeZone : NSTimeZone 317{ 318@public 319 NSString *name; 320 id detail; 321 int offset; // Offset from UTC in seconds. 322} 323 324- (id) initWithOffset: (NSInteger)anOffset name: (NSString*)aName; 325@end 326 327@interface NSLocalTimeZone : NSTimeZone 328@end 329 330@interface GSTimeZoneDetail : NSTimeZoneDetail 331{ 332 NSTimeZone *timeZone; // Time zone which created this object. 333 NSString *abbrev; // Abbreviation for time zone detail. 334 int offset; // Offset from UTC in seconds. 335 BOOL is_dst; // Is it daylight savings time? 336} 337 338- (id) initWithTimeZone: (NSTimeZone*)aZone 339 withAbbrev: (NSString*)anAbbrev 340 withOffset: (NSInteger)anOffset 341 withDST: (BOOL)isDST; 342@end 343 344@interface GSAbsTimeZoneDetail : NSTimeZoneDetail 345{ 346 GSAbsTimeZone *zone; // Time zone which created this object. 347} 348 349- (id) initWithTimeZone: (GSAbsTimeZone*)aZone; 350@end 351 352/* Private methods for obtaining resource file names. */ 353@interface NSTimeZone (Private) 354+ (NSString*) _getTimeZoneFile: (NSString*)name; 355+ (void) _notified: (NSNotification*)n; 356@end 357 358 359@implementation GSPlaceholderTimeZone 360 361- (id) autorelease 362{ 363 NSWarnLog(@"-autorelease sent to uninitialised time zone"); 364 return self; // placeholders never get released. 365} 366 367- (void) dealloc 368{ 369 GSNOSUPERDEALLOC; // placeholders never get deallocated. 370} 371 372- (id) initWithName: (NSString*)name data: (NSData*)data 373{ 374 NSTimeZone *zone; 375 unsigned length = [name length]; 376 377 if (length == 0) 378 { 379 NSLog(@"Disallowed null time zone name"); 380 return nil; 381 } 382 if (length == 15 && [name isEqual: @"NSLocalTimeZone"]) 383 { 384 zone = RETAIN(localTimeZone); 385 DESTROY(self); 386 return (GSPlaceholderTimeZone*)zone; 387 } 388 389 /* 390 * Return a cached time zone if possible. 391 * NB. if data of cached zone does not match new data ... don't use cache 392 */ 393 pthread_mutex_lock(&zone_mutex); 394 zone = [zoneDictionary objectForKey: name]; 395 if (data != nil && [data isEqual: [zone data]] == NO) 396 { 397 zone = nil; 398 } 399 IF_NO_GC(RETAIN(zone)); 400 pthread_mutex_unlock(&zone_mutex); 401 402 if (zone == nil) 403 { 404 unichar c; 405 int i; 406 407 if ((length == 3 408 && ([name isEqualToString: @"GMT"] == YES 409 || [name isEqualToString: @"UTC"] == YES 410 || [name isEqualToString: @"UCT"] == YES)) 411 || (length == 4 412 && ([name isEqualToString: @"GMT0"] == YES 413 || [name isEqualToString: @"Zulu"] == YES)) 414 || (length == 9 && [name isEqualToString: @"Universal"] == YES)) 415 { 416 // Synonyms for GMT 417 zone = [[GSAbsTimeZone alloc] initWithOffset: 0 name: name]; 418 } 419 else if (length == 5 && [name hasPrefix: @"GMT"] == YES 420 && ((c = [name characterAtIndex: 3]) == '+' || c == '-') 421 && ((c = [name characterAtIndex: 4]) >= '0' && c <= '9')) 422 { 423 // GMT-9 to GMT+9 424 i = (c - '0') * 60 * 60; 425 if ([name characterAtIndex: 3] == '-') 426 { 427 i = -i; 428 } 429 zone = [[GSAbsTimeZone alloc] initWithOffset: i name: name]; 430 } 431 else if (length == 6 && [name hasPrefix: @"GMT"] == YES 432 && ((c = [name characterAtIndex: 3]) == '+' || c == '-') 433 && ((c = [name characterAtIndex: 4]) == '0' || c == '1') 434 && ((c = [name characterAtIndex: 5]) >= '0' && c <= '4')) 435 { 436 // GMT-14 to GMT-10 and GMT+10 to GMT+14 437 i = (c - '0') * 60 * 60; 438 if ([name characterAtIndex: 4] == '1') 439 { 440 i += 60 * 60 * 10; 441 } 442 if ([name characterAtIndex: 3] == '-') 443 { 444 i = -i; 445 } 446 zone = [[GSAbsTimeZone alloc] initWithOffset: i name: name]; 447 } 448 else if (length == 8 && [name hasPrefix: @"GMT"] == YES 449 && ((c = [name characterAtIndex: 3]) == '+' || c == '-')) 450 { 451 // GMT+NNNN and GMT-NNNN 452 c = [name characterAtIndex: 4]; 453 if (c >= '0' && c <= '9') 454 { 455 i = c - '0'; 456 c = [name characterAtIndex: 5]; 457 if (c >= '0' && c <= '9') 458 { 459 i = i * 10 + (c - '0'); 460 c = [name characterAtIndex: 6]; 461 if (c >= '0' && c <= '9') 462 { 463 i = i * 6 + (c - '0'); 464 c = [name characterAtIndex: 7]; 465 if (c >= '0' && c <= '9') 466 { 467 i = i * 10 + (c - '0'); 468 i = i * 60; 469 if ([name characterAtIndex: 3] == '-') 470 { 471 i = -i; 472 } 473 zone = [[GSAbsTimeZone alloc] initWithOffset: i 474 name: nil]; 475 } 476 } 477 } 478 } 479 } 480 481 if (zone == nil && length > 19 482 && [name hasPrefix: @"NSAbsoluteTimeZone: "] == YES) 483 { 484 i = [[name substringFromIndex: 19] intValue]; 485 486 zone = [[GSAbsTimeZone alloc] initWithOffset: i name: nil]; 487 } 488 489 if (zone == nil) 490 { 491 if (data == nil) 492 { 493 NSString *fileName; 494 BOOL isDir; 495 496 fileName = [NSTimeZoneClass _getTimeZoneFile: name]; 497 if (fileName == nil 498 || ![[NSFileManager defaultManager] fileExistsAtPath: fileName 499 isDirectory: &isDir] || YES == isDir) 500 { 501 data = nil; 502 } 503 else 504 { 505 data = [NSData dataWithContentsOfFile: fileName]; 506 } 507 if (nil == data) 508#if defined(_WIN32) 509 { 510 zone = [[GSWindowsTimeZone alloc] initWithName: name data: 0]; 511 DESTROY(self); 512 return zone; 513 } 514#else 515 { 516 return nil; 517 } 518#endif 519 } 520#if defined(_WIN32) 521 if (!data) 522 zone = [[GSWindowsTimeZone alloc] initWithName: name data: data]; 523 else 524#endif 525 zone = [[GSTimeZone alloc] initWithName: name data: data]; 526 } 527 } 528 DESTROY(self); 529 return (GSPlaceholderTimeZone*)zone; 530} 531 532- (oneway void) release 533{ 534 return; // placeholders never get released. 535} 536 537- (id) retain 538{ 539 return self; // placeholders never get retained. 540} 541@end 542 543 544 545@implementation NSLocalTimeZone 546 547- (NSString*) abbreviation 548{ 549 return [[NSTimeZoneClass defaultTimeZone] abbreviation]; 550} 551 552- (NSString*) abbreviationForDate: (NSDate*)aDate 553{ 554 return [[NSTimeZoneClass defaultTimeZone] abbreviationForDate: aDate]; 555} 556 557- (id) autorelease 558{ 559 return self; 560} 561 562- (id) copy 563{ 564 return self; 565} 566 567- (id) copyWithZone: (NSZone*)z 568{ 569 return self; 570} 571 572- (NSData*) data 573{ 574 return [[NSTimeZoneClass defaultTimeZone] data]; 575} 576 577- (void) encodeWithCoder: (NSCoder*)aCoder 578{ 579 [aCoder encodeObject: @"NSLocalTimeZone"]; 580} 581 582- (id) init 583{ 584 return self; 585} 586 587- (BOOL) isDaylightSavingTime 588{ 589 return [[NSTimeZoneClass defaultTimeZone] isDaylightSavingTime]; 590} 591 592- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate 593{ 594 return [[NSTimeZoneClass defaultTimeZone] isDaylightSavingTimeForDate: aDate]; 595} 596 597- (NSString*) name 598{ 599 return [[NSTimeZoneClass defaultTimeZone] name]; 600} 601 602- (oneway void) release 603{ 604} 605 606- (id) retain 607{ 608 return self; 609} 610 611- (NSInteger) secondsFromGMT 612{ 613 return [[NSTimeZoneClass defaultTimeZone] secondsFromGMT]; 614} 615 616- (NSInteger) secondsFromGMTForDate: (NSDate*)aDate 617{ 618 return [[NSTimeZoneClass defaultTimeZone] secondsFromGMTForDate: aDate]; 619} 620 621- (NSArray*) timeZoneDetailArray 622{ 623 return [[NSTimeZoneClass defaultTimeZone] timeZoneDetailArray]; 624} 625 626- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)date 627{ 628 return [[NSTimeZoneClass defaultTimeZone] timeZoneDetailForDate: date]; 629} 630 631- (NSString*) timeZoneName 632{ 633 return [[NSTimeZoneClass defaultTimeZone] timeZoneName]; 634} 635 636@end 637 638 639@implementation GSAbsTimeZone 640 641static int uninitialisedOffset = 100000; 642static NSMapTable *absolutes = 0; 643 644+ (void) initialize 645{ 646 if (self == [GSAbsTimeZone class]) 647 { 648 absolutes = NSCreateMapTable(NSIntegerMapKeyCallBacks, 649 NSNonOwnedPointerMapValueCallBacks, 0); 650 [[NSObject leakAt: (id*)&absolutes] release]; 651 } 652} 653 654- (NSString*) abbreviationForDate: (NSDate*)aDate 655{ 656 return name; 657} 658 659- (void) dealloc 660{ 661 if (offset != uninitialisedOffset) 662 { 663 pthread_mutex_lock(&zone_mutex); 664 NSMapRemove(absolutes, (void*)(uintptr_t)offset); 665 pthread_mutex_unlock(&zone_mutex); 666 } 667 RELEASE(name); 668 RELEASE(detail); 669 [super dealloc]; 670} 671 672- (void) encodeWithCoder: (NSCoder*)aCoder 673{ 674 [aCoder encodeObject: name]; 675} 676 677- (id) initWithOffset: (NSInteger)anOffset name: (NSString*)aName 678{ 679 GSAbsTimeZone *z; 680 int extra; 681 int sign = anOffset >= 0 ? 1 : -1; 682 683 /* 684 * Set the uninitialised offset so that dealloc before full 685 * initialisation won't remove the timezeone for offset 0 from cache. 686 */ 687 offset = uninitialisedOffset; 688 689 /* 690 * Round the offset to the nearest minute, (for MacOS-X compatibility) 691 * and ensure it is no more than 18 hours. 692 */ 693 anOffset *= sign; 694 extra = anOffset % 60; 695 if (extra < 30) 696 { 697 anOffset -= extra; 698 } 699 else 700 { 701 anOffset += 60 - extra; 702 } 703 if (anOffset > 64800) 704 { 705 DESTROY(self); 706 return nil; 707 } 708 anOffset *= sign; 709 710 if (anOffset % 900 == 0) 711 { 712 z = commonAbsolutes[anOffset/900 + 72]; 713 if (z != nil) 714 { 715 IF_NO_GC(RETAIN(z)); 716 DESTROY(self); 717 return z; 718 } 719 } 720 721 pthread_mutex_lock(&zone_mutex); 722 z = (GSAbsTimeZone*)NSMapGet(absolutes, (void*)(uintptr_t)anOffset); 723 if (z != nil) 724 { 725 IF_NO_GC(RETAIN(z)); 726 DESTROY(self); 727 } 728 else 729 { 730 if (aName == nil) 731 { 732 if (anOffset % 60 == 0) 733 { 734 char s = (anOffset >= 0) ? '+' : '-'; 735 unsigned i = (anOffset >= 0) ? anOffset / 60 : -anOffset / 60; 736 unsigned h = (i / 60) % 24; 737 unsigned m = i % 60; 738 char buf[9]; 739 740 snprintf(buf, sizeof(buf), "GMT%c%02u%02u", s, h, m); 741 name = [[NSString alloc] initWithUTF8String: buf]; 742 } 743 else 744 { 745 /* 746 * Should never happen now we round to the minute 747 * for MacOS-X compatibnility. 748 */ 749 name = [[NSString alloc] 750 initWithFormat: @"NSAbsoluteTimeZone:%"PRIdPTR, anOffset]; 751 } 752 } 753 else 754 { 755 name = [aName copy]; 756 } 757 detail = [[GSAbsTimeZoneDetail alloc] initWithTimeZone: self]; 758 offset = anOffset; 759 z = self; 760 NSMapInsert(absolutes, (void*)(uintptr_t)anOffset, (void*)z); 761 [zoneDictionary setObject: self forKey: (NSString*)name]; 762 } 763 if (anOffset % 900 == 0) 764 { 765 int index = anOffset/900 + 72; 766 767 if (nil == commonAbsolutes[index]) 768 { 769 commonAbsolutes[index] = RETAIN(self); 770 } 771 } 772 pthread_mutex_unlock(&zone_mutex); 773 return z; 774} 775 776- (BOOL) isDaylightSavingTimeZoneForDate: (NSDate*)aDate 777{ 778 return NO; 779} 780 781- (NSString*) name 782{ 783 return name; 784} 785 786- (NSInteger) secondsFromGMTForDate: (NSDate*)aDate 787{ 788 return offset; 789} 790 791- (NSArray*) timeZoneDetailArray 792{ 793 return [NSArray arrayWithObject: detail]; 794} 795 796- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)date 797{ 798 return detail; 799} 800 801- (NSString*) timeZoneName 802{ 803 return name; 804} 805@end 806 807 808@implementation GSTimeZoneDetail 809 810- (void) dealloc 811{ 812 RELEASE(timeZone); 813 [super dealloc]; 814} 815 816- (id) initWithCoder: (NSCoder*)aDecoder 817{ 818 [aDecoder decodeValueOfObjCType: @encode(id) at: &abbrev]; 819 [aDecoder decodeValueOfObjCType: @encode(int) at: &offset]; 820 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &is_dst]; 821 return self; 822} 823 824- (id) initWithTimeZone: (NSTimeZone*)aZone 825 withAbbrev: (NSString*)anAbbrev 826 withOffset: (NSInteger)anOffset 827 withDST: (BOOL)isDST 828{ 829 timeZone = RETAIN(aZone); 830 abbrev = anAbbrev; // NB. Depend on this being retained in aZone 831 offset = anOffset; 832 is_dst = isDST; 833 return self; 834} 835 836- (BOOL) isDaylightSavingTimeZone 837{ 838 return is_dst; 839} 840 841- (NSString*) name 842{ 843 return [timeZone name]; 844} 845 846- (NSString*) timeZoneAbbreviation 847{ 848 return abbrev; 849} 850 851- (NSArray*) timeZoneDetailArray 852{ 853 return [timeZone timeZoneDetailArray]; 854} 855 856- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)date 857{ 858 return [timeZone timeZoneDetailForDate: date]; 859} 860 861- (NSInteger) timeZoneSecondsFromGMT 862{ 863 return offset; 864} 865 866- (NSInteger) timeZoneSecondsFromGMTForDate: (NSDate*)aDate 867{ 868 return offset; 869} 870 871@end 872 873 874@implementation GSAbsTimeZoneDetail 875 876- (NSString*) abbreviation 877{ 878 return zone->name; 879} 880 881- (NSString*) abbreviationForDate: (NSDate*)aDate 882{ 883 return zone->name; 884} 885 886- (void) dealloc 887{ 888 RELEASE(zone); 889 [super dealloc]; 890} 891 892- (id) initWithTimeZone: (GSAbsTimeZone*)aZone 893{ 894 zone = RETAIN(aZone); 895 return self; 896} 897 898- (BOOL) isDaylightSavingTimeZone 899{ 900 return NO; 901} 902 903- (BOOL) isDaylightSavingTimeZoneForDate: (NSDate*)aDate 904{ 905 return NO; 906} 907 908- (NSString*) name 909{ 910 return zone->name; 911} 912 913- (NSString*) timeZoneAbbreviation 914{ 915 return zone->name; 916} 917 918- (NSArray*) timeZoneDetailArray 919{ 920 return [zone timeZoneDetailArray]; 921} 922 923- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)date 924{ 925 return self; 926} 927 928- (NSInteger) timeZoneSecondsFromGMT 929{ 930 return zone->offset; 931} 932 933- (NSInteger) timeZoneSecondsFromGMTForDate: (NSDate*)aDate 934{ 935 return zone->offset; 936} 937 938@end 939 940 941/** 942 * <p> 943 * The local time zone is obtained from, in order of preference:<br/ > 944 * 1) the user defaults database: NSGlobalDomain "Local Time Zone"<br/ > 945 * 2) the GNUSTEP_TZ environment variable<br/ > 946 * 3) the file "localtime" in System/Library/Libraries/Resources/NSTimeZone<br/ > 947 * 4) the TZ environment variable<br/ > 948 * 5) The system zone settings (typically in /etc/localtime)<br/ > 949 * 6) tzset and tznam on platforms which have it<br/ > 950 * 7) Windows registry, on Win32 systems<br/ > 951 * 8) or the fallback time zone (which is UTC)<br/ > 952 * </p> 953 * <p>If the GNUstep time zone datafiles become too out of date, one 954 * can download an updated database from <uref 955 * url="ftp://elsie.nci.nih.gov/pub/">ftp://elsie.nci.nih.gov/pub/</uref> 956 * and compile it as specified in the README file in the 957 * NSTimeZones directory. 958 * </p> 959 * <p>Time zone names in NSDates should be GMT, MET etc. not 960 * Europe/Berlin, America/Washington etc. 961 * </p> 962 * <p>The problem with this is that various time zones may use the 963 * same abbreviation (e.g. Australia/Brisbane and 964 * America/New_York both use EST), and some time zones 965 * may have different rules for daylight saving time even if the 966 * abbreviation and offsets from UTC are the same. 967 * </p> 968 * <p>The problems with depending on the OS for providing time zone 969 * info are that time zone names may vary 970 * wildly between OSes (this could be a big problem when 971 * archiving is used between different systems). 972 * </p> 973 * <p>Win32: Time zone names read from the registry are different 974 * from other GNUstep installations. Be careful when moving data 975 * between platforms in this case. 976 * </p> 977 */ 978@implementation NSTimeZone 979 980/** 981 * Returns a dictionary containing time zone abbreviations and their 982 * corresponding time zone names. More than one time zone may be associated 983 * with a single abbreviation. In this case, the dictionary contains only 984 * one (usually the most common) time zone name for the abbreviation. 985 */ 986+ (NSDictionary*) abbreviationDictionary 987{ 988 if (abbreviationDictionary != nil) 989 { 990 return abbreviationDictionary; 991 } 992 pthread_mutex_lock(&zone_mutex); 993 if (abbreviationDictionary == nil) 994 { 995 NSAutoreleasePool *pool = [NSAutoreleasePool new]; 996 NSString *path; 997 998 path = _time_zone_path (ABBREV_DICT, @"plist"); 999 if (path != nil) 1000 { 1001 /* 1002 * Fast mechanism ... load prebuilt data from file so we don't 1003 * need to load in all time zones. 1004 */ 1005 abbreviationDictionary 1006 = RETAIN([[NSString stringWithContentsOfFile: path] propertyList]); 1007 } 1008 if (abbreviationDictionary == nil) 1009 { 1010 NSMutableDictionary *md; 1011 NSString *name; 1012 NSEnumerator *names; 1013 1014 /* 1015 * Slow fallback ... load all time zones and generate 1016 * abbreviation dictionary from them. 1017 */ 1018 md = [[NSMutableDictionary alloc] init]; 1019 names = [[NSTimeZone knownTimeZoneNames] objectEnumerator]; 1020 while ((name = [names nextObject]) != nil) 1021 { 1022 NSTimeZone *zone; 1023 1024 if ((zone = [NSTimeZone timeZoneWithName: name])) 1025 { 1026 NSEnumerator *details; 1027 NSTimeZoneDetail *detail; 1028 1029 details = [[zone timeZoneDetailArray] objectEnumerator]; 1030 while ((detail = [details nextObject]) != nil) 1031 { 1032 [md setObject: name 1033 forKey: [detail timeZoneAbbreviation]]; 1034 } 1035 } 1036 } 1037 if ([md makeImmutable] == YES) 1038 { 1039 abbreviationDictionary = md; 1040 } 1041 else 1042 { 1043 abbreviationDictionary = [md copy]; 1044 RELEASE(md); 1045 } 1046 } 1047 [pool drain]; 1048 } 1049 pthread_mutex_unlock(&zone_mutex); 1050 return abbreviationDictionary; 1051} 1052 1053/** 1054 * Returns a dictionary that maps abbreviations to the array 1055 * containing all the time zone names that use the abbreviation. 1056 */ 1057+ (NSDictionary*) abbreviationMap 1058{ 1059 /* Instead of creating the abbreviation dictionary when the class is 1060 initialized, we create it when we first need it, since the 1061 dictionary can be potentially very large, considering that it's 1062 almost never used. */ 1063 if (abbreviationMap != nil) 1064 { 1065 return abbreviationMap; 1066 } 1067 pthread_mutex_lock(&zone_mutex); 1068 if (abbreviationMap == nil) 1069 { 1070 NSAutoreleasePool *pool = [NSAutoreleasePool new]; 1071 NSMutableDictionary *md; 1072 NSMutableArray *ma; 1073 NSString *the_name; 1074 NSString *the_abbrev; 1075 FILE *file; 1076 char abbrev[80]; 1077 char name[80]; 1078 NSString *path; 1079 1080 /* 1081 * Read dictionary from file... fast mechanism because we don't have 1082 * to create all timezoneas and parse all their data files. 1083 */ 1084 md = [NSMutableDictionary dictionaryWithCapacity: 100]; 1085 path = _time_zone_path (ABBREV_MAP, nil); 1086 if (path != nil) 1087 { 1088#if defined(_WIN32) 1089 unichar mode[3]; 1090 1091 mode[0] = 'r'; 1092 mode[1] = 'b'; 1093 mode[2] = '\0'; 1094 1095 file = _wfopen((const unichar*)[path fileSystemRepresentation], mode); 1096#else 1097 file = fopen([path fileSystemRepresentation], "r"); 1098#endif 1099 if (file == NULL) 1100 { 1101 pthread_mutex_unlock(&zone_mutex); 1102 [NSException 1103 raise: NSInternalInconsistencyException 1104 format: @"Failed to open time zone abbreviation map."]; 1105 } 1106 while (fscanf(file, "%79s %79s", abbrev, name) == 2) 1107 { 1108 the_name = [[NSString alloc] initWithUTF8String: name]; 1109 the_abbrev = [[NSString alloc] initWithUTF8String: abbrev]; 1110 ma = [md objectForKey: the_abbrev]; 1111 if (ma == nil) 1112 { 1113 ma = [[NSMutableArray alloc] initWithCapacity: 1]; 1114 [md setObject: ma forKey: the_abbrev]; 1115 RELEASE(ma); 1116 } 1117 RELEASE(the_abbrev); 1118 if ([ma containsObject: the_name] == NO) 1119 { 1120 [ma addObject: the_name]; 1121 } 1122 RELEASE(the_name); 1123 } 1124 fclose(file); 1125 } 1126 else 1127 { 1128 NSString *name; 1129 NSEnumerator *names; 1130 1131 /* 1132 * Slow fallback mechanism ... go through all time names 1133 * so we load all the time zone data and generate the info 1134 * we need from it. 1135 */ 1136 names = [[NSTimeZone knownTimeZoneNames] objectEnumerator]; 1137 while ((name = [names nextObject]) != nil) 1138 { 1139 NSTimeZone *zone; 1140 1141 if ((zone = [NSTimeZone timeZoneWithName: name]) != nil) 1142 { 1143 NSEnumerator *details; 1144 NSTimeZoneDetail *detail; 1145 1146 details = [[zone timeZoneDetailArray] objectEnumerator]; 1147 while ((detail = [details nextObject]) != nil) 1148 { 1149 the_abbrev = [detail timeZoneAbbreviation]; 1150 ma = [md objectForKey: the_abbrev]; 1151 if (ma == nil) 1152 { 1153 ma = [[NSMutableArray alloc] initWithCapacity: 1]; 1154 [md setObject: ma forKey: the_abbrev]; 1155 RELEASE(ma); 1156 } 1157 if ([ma containsObject: name] == NO) 1158 { 1159 [ma addObject: name]; 1160 } 1161 } 1162 } 1163 } 1164 } 1165 1166 /* Special case: Add the system time zone if 1167 * it doesn't exist in the map */ 1168 the_abbrev = [systemTimeZone abbreviation]; 1169 ma = [md objectForKey: the_abbrev]; 1170 if (ma == nil) 1171 { 1172 ma = [NSMutableArray new]; 1173 [md setObject: ma forKey: the_abbrev]; 1174 RELEASE(ma); 1175 } 1176 the_name = [systemTimeZone timeZoneName]; 1177 if ([ma containsObject: the_name] == NO) 1178 { 1179 [ma addObject: the_name]; 1180 } 1181 1182 if ([md makeImmutable] == YES) 1183 { 1184 abbreviationMap = RETAIN(md); 1185 } 1186 else 1187 { 1188 abbreviationMap = [md copy]; 1189 } 1190 [pool drain]; 1191 } 1192 pthread_mutex_unlock(&zone_mutex); 1193 1194 return abbreviationMap; 1195} 1196 1197/** 1198 * Returns an array of all known time zone names. 1199 */ 1200+ (NSArray*) knownTimeZoneNames 1201{ 1202 static NSArray *namesArray = nil; 1203 1204 /* We create the array only when we need it to reduce overhead. */ 1205 if (namesArray != nil) 1206 { 1207 return namesArray; 1208 } 1209 1210 pthread_mutex_lock(&zone_mutex); 1211 if (namesArray == nil) 1212 { 1213 unsigned i; 1214 NSMutableArray *ma; 1215 NSArray *regionsArray; 1216 1217 ma = [NSMutableArray new]; 1218 regionsArray = [self timeZoneArray]; 1219 1220 for (i = 0; i < [regionsArray count]; i++) 1221 { 1222 NSArray *names = [regionsArray objectAtIndex: i]; 1223 1224 [ma addObjectsFromArray: names]; 1225 } 1226 if ([ma makeImmutable] == YES) 1227 { 1228 namesArray = ma; 1229 } 1230 else 1231 { 1232 namesArray = [ma copy]; 1233 RELEASE(ma); 1234 } 1235 } 1236 pthread_mutex_unlock(&zone_mutex); 1237 return namesArray; 1238} 1239 1240+ (id) allocWithZone: (NSZone*)z 1241{ 1242 if (self == NSTimeZoneClass) 1243 { 1244 /* 1245 * We return a placeholder object that can 1246 * be converted to a real object when its initialisation method 1247 * is called. 1248 */ 1249 if (z == NSDefaultMallocZone() || z == 0) 1250 { 1251 /* 1252 * As a special case, we can return a placeholder for a time zone 1253 * in the default zone extremely efficiently. 1254 */ 1255 return defaultPlaceholderTimeZone; 1256 } 1257 else 1258 { 1259 id obj; 1260 1261 /* 1262 * For anything other than the default zone, we need to 1263 * locate the correct placeholder in the (lock protected) 1264 * table of placeholders. 1265 */ 1266 pthread_mutex_lock(&zone_mutex); 1267 obj = (id)NSMapGet(placeholderMap, (void*)z); 1268 if (obj == nil) 1269 { 1270 /* 1271 * There is no placeholder object for this zone, so we 1272 * create a new one and use that. 1273 */ 1274 obj = (id)NSAllocateObject(GSPlaceholderTimeZoneClass, 0, z); 1275 NSMapInsert(placeholderMap, (void*)z, (void*)obj); 1276 } 1277 pthread_mutex_unlock(&zone_mutex); 1278 return obj; 1279 } 1280 } 1281 else 1282 { 1283 return NSAllocateObject(self, 0, z); 1284 } 1285} 1286 1287/** 1288 * Return the default time zone for this process. 1289 */ 1290+ (NSTimeZone*) defaultTimeZone 1291{ 1292 NSTimeZone *zone; 1293 1294 pthread_mutex_lock(&zone_mutex); 1295 if (defaultTimeZone == nil) 1296 { 1297 zone = [self systemTimeZone]; 1298 } 1299 else 1300 { 1301 zone = AUTORELEASE(RETAIN(defaultTimeZone)); 1302 } 1303 pthread_mutex_unlock(&zone_mutex); 1304 return zone; 1305} 1306 1307+ (void) initialize 1308{ 1309 if (self == [NSTimeZone class]) 1310 { 1311 NSTimeZoneClass = self; 1312 GS_INIT_RECURSIVE_MUTEX(zone_mutex); 1313 GSPlaceholderTimeZoneClass = [GSPlaceholderTimeZone class]; 1314 zoneDictionary = [[NSMutableDictionary alloc] init]; 1315 [[NSObject leakAt: &zoneDictionary] release]; 1316 1317 /* 1318 * Set up infrastructure for placeholder timezones. 1319 */ 1320 defaultPlaceholderTimeZone = (GSPlaceholderTimeZone*) 1321 NSAllocateObject(GSPlaceholderTimeZoneClass, 0, NSDefaultMallocZone()); 1322 [[NSObject leakAt: &defaultPlaceholderTimeZone] release]; 1323 placeholderMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, 1324 NSNonRetainedObjectMapValueCallBacks, 0); 1325 [[NSObject leakAt: (id*)&placeholderMap] release]; 1326 1327 localTimeZone = [[NSLocalTimeZone alloc] init]; 1328 [[NSObject leakAt: (id*)&localTimeZone] release]; 1329 1330 [[NSObject leakAt: (id*)&defaultTimeZone] release]; 1331 [[NSObject leakAt: (id*)&systemTimeZone] release]; 1332 [[NSObject leakAt: (id*)&abbreviationDictionary] release]; 1333 [[NSObject leakAt: (id*)&abbreviationMap] release]; 1334 [[NSObject leakAt: (id*)&absolutes] release]; 1335 1336 [[NSNotificationCenter defaultCenter] addObserver: self 1337 selector: @selector(_notified:) 1338 name: NSUserDefaultsDidChangeNotification 1339 object: nil]; 1340 } 1341} 1342 1343/** 1344 * Return a proxy to the default time zone for this process. 1345 */ 1346+ (NSTimeZone*) localTimeZone 1347{ 1348 return localTimeZone; 1349} 1350 1351/** 1352 * Destroy the system time zone so that it will be recreated 1353 * next time it is used. 1354 */ 1355+ (void) resetSystemTimeZone 1356{ 1357 pthread_mutex_lock(&zone_mutex); 1358 DESTROY(systemTimeZone); 1359 pthread_mutex_unlock(&zone_mutex); 1360 [[NSNotificationCenter defaultCenter] 1361 postNotificationName: NSSystemTimeZoneDidChangeNotification 1362 object: nil]; 1363} 1364 1365/** 1366 * Set the default time zone to be used for this process. 1367 */ 1368+ (void) setDefaultTimeZone: (NSTimeZone*)aTimeZone 1369{ 1370 if (aTimeZone != defaultTimeZone) 1371 { 1372 /* 1373 * We can't make the localTimeZone the default since that would 1374 * cause recursion ... 1375 */ 1376 if (aTimeZone == localTimeZone) 1377 { 1378 aTimeZone = [self systemTimeZone]; 1379 } 1380 pthread_mutex_lock(&zone_mutex); 1381 ASSIGN(defaultTimeZone, aTimeZone); 1382 pthread_mutex_unlock(&zone_mutex); 1383 } 1384} 1385 1386/** 1387 * Returns the current system time zone for the process. 1388 */ 1389+ (NSTimeZone*) systemTimeZone 1390{ 1391 NSTimeZone *zone = nil; 1392 1393 pthread_mutex_lock(&zone_mutex); 1394 if (systemTimeZone == nil) 1395 { 1396 NSFileManager *dflt = [NSFileManager defaultManager]; 1397 NSString *localZoneString = nil; 1398 NSString *localZoneSource = nil; 1399 1400 /* 1401 * setup default value in case something goes wrong. 1402 */ 1403 systemTimeZone = RETAIN([NSTimeZoneClass timeZoneForSecondsFromGMT: 0]); 1404 1405 /* 1406 * Try to get timezone from user defaults database 1407 */ 1408 localZoneSource = [NSString stringWithFormat: 1409 @"NSUserDefaults: '%@'", LOCALDBKEY]; 1410 localZoneString = [[NSUserDefaults standardUserDefaults] 1411 stringForKey: LOCALDBKEY]; 1412 1413 /* 1414 * Try to get timezone from GNUSTEP_TZ environment variable. 1415 */ 1416 if (localZoneString == nil) 1417 { 1418 localZoneSource = _(@"environment variable: 'GNUSTEP_TZ'"); 1419 localZoneString = [[[NSProcessInfo processInfo] 1420 environment] objectForKey: @"GNUSTEP_TZ"]; 1421 } 1422 1423 /* 1424 * Try to get timezone from LOCAL_TIME_FILE. 1425 */ 1426 if (localZoneString == nil) 1427 { 1428 NSString *f = _time_zone_path(LOCAL_TIME_FILE, nil); 1429 1430 localZoneSource = [NSString stringWithFormat: @"file: '%@'", f]; 1431 if (f != nil) 1432 { 1433 localZoneString = [NSString stringWithContentsOfFile: f]; 1434 localZoneString = [localZoneString stringByTrimmingSpaces]; 1435 } 1436 } 1437 1438#if defined(_WIN32) 1439 /* 1440 * Try to get timezone from windows system call. 1441 */ 1442 { 1443 TIME_ZONE_INFORMATION tz; 1444 DWORD DST = GetTimeZoneInformation(&tz); 1445 1446 localZoneSource = @"function: 'GetTimeZoneInformation()'"; 1447 if (DST == TIME_ZONE_ID_DAYLIGHT) 1448 { 1449 localZoneString = [NSString stringWithCharacters: tz.DaylightName 1450 length: wcslen(tz.DaylightName)]; 1451 } 1452 else 1453 { 1454 localZoneString = [NSString stringWithCharacters: tz.StandardName 1455 length: wcslen(tz.StandardName)]; 1456 } 1457 } 1458#endif 1459 1460 if (localZoneString == nil) 1461 { 1462 if (YES == [dflt isReadableFileAtPath: @"/etc/timezone"]) 1463 { 1464 NSString *s; 1465 1466 s = [NSString stringWithContentsOfFile: @"/etc/timezone"]; 1467 s = [s stringByTrimmingSpaces]; 1468 if (0 != [s length]) 1469 { 1470 localZoneSource = _(@"/etc/timezone file"); 1471 localZoneString = s; 1472 } 1473 } 1474 } 1475 1476 if (localZoneString == nil) 1477 { 1478 if (YES == [dflt isReadableFileAtPath: @"/etc/sysconfig/clock"]) 1479 { 1480 NSString *s; 1481 NSEnumerator *e; 1482 1483 s = [NSString stringWithContentsOfFile: 1484 @"/etc/sysconfig/clock"]; 1485 e = [[s componentsSeparatedByString: @"\n"] objectEnumerator]; 1486 while (nil != (s = [e nextObject])) 1487 { 1488 s = [s stringByTrimmingSpaces]; 1489 // OpenSuse uses the non-standard key TIMEZONE 1490 if ([s hasPrefix: @"ZONE"] || [s hasPrefix: @"TIMEZONE"]) 1491 { 1492 if ([s hasPrefix: @"ZONE"]) 1493 s = [s substringFromIndex: 4]; 1494 else 1495 s = [s substringFromIndex: 8]; 1496 1497 s = [s stringByTrimmingSpaces]; 1498 if ([s hasPrefix: @"="]) 1499 { 1500 s = [s substringFromIndex: 1]; 1501 s = [s stringByTrimmingSpaces]; 1502 if ([s hasPrefix: @"\""]) 1503 { 1504 s = [s substringFromIndex: 1]; 1505 } 1506 if ([s hasSuffix: @"\""]) 1507 { 1508 s = [s substringToIndex: [s length] - 1]; 1509 } 1510 s = [s stringByTrimmingSpaces]; 1511 if ([s length] > 0) 1512 { 1513 localZoneSource = _(@"/etc/sysconfig/clock file"); 1514 localZoneString = s; 1515 } 1516 } 1517 } 1518 } 1519 } 1520 } 1521 1522 if (localZoneString == nil) 1523 { 1524 /* Get the zone name from the localtime file, assuming the file 1525 is a symlink to the time zone. Getting the actual data (which 1526 is easier) doesn't help, since we won't know the name itself. */ 1527#if defined(HAVE_TZHEAD) && defined(TZDEFAULT) 1528 tzdir = RETAIN([NSString stringWithUTF8String: TZDIR]); 1529 localZoneString = [NSString stringWithUTF8String: TZDEFAULT]; 1530 localZoneSource = [NSString stringWithFormat: 1531 @"file (TZDEFAULT): '%@'", localZoneString]; 1532 localZoneString = [localZoneString stringByResolvingSymlinksInPath]; 1533#else 1534 if ([dflt fileExistsAtPath: SYSTEM_TIME_FILE]) 1535 { 1536 localZoneString = SYSTEM_TIME_FILE; 1537 localZoneSource = [NSString stringWithFormat: 1538 @"file (SYSTEM_TIME_FILE): '%@'", localZoneString]; 1539 localZoneString 1540 = [localZoneString stringByResolvingSymlinksInPath]; 1541 /* Guess what tzdir is */ 1542 tzdir = [localZoneString stringByDeletingLastPathComponent]; 1543 while ([tzdir length] > 2 1544 && [dflt fileExistsAtPath: 1545 [tzdir stringByAppendingPathComponent: @"GMT"]] == NO) 1546 { 1547 tzdir = [tzdir stringByDeletingLastPathComponent]; 1548 } 1549 if ([tzdir length] <= 2) 1550 { 1551 localZoneString = tzdir = nil; 1552 } 1553 else 1554 { 1555 [tzdir retain]; 1556 } 1557 } 1558#endif 1559 if (localZoneString != nil && [localZoneString hasPrefix: tzdir]) 1560 { 1561 /* This must be the time zone name */ 1562 localZoneString = AUTORELEASE([localZoneString mutableCopy]); 1563 [(NSMutableString*)localZoneString deleteCharactersInRange: 1564 NSMakeRange(0, [tzdir length])]; 1565 while ([localZoneString hasPrefix: @"/"]) 1566 { 1567 [(NSMutableString*)localZoneString deleteCharactersInRange: 1568 NSMakeRange(0, 1)]; 1569 } 1570 } 1571 else 1572 { 1573 localZoneString = nil; 1574 } 1575 } 1576 1577 /* Try to get timezone from standard unix environment variable. 1578 * This is often an ambiguous abbreviation :-( 1579 */ 1580 if (localZoneString == nil) 1581 { 1582 localZoneSource = _(@"environment variable: 'TZ'"); 1583 localZoneString = [[[NSProcessInfo processInfo] 1584 environment] objectForKey: @"TZ"]; 1585 } 1586 1587 1588#if HAVE_TZSET && !defined(__FreeBSD__) && !defined(__OpenBSD__) 1589 /* 1590 * Try to get timezone from tzset and tzname/daylight. 1591 * If daylight is non-zero, then tzname[0] is only the name 1592 * the the zone for part of the year, so we can't use it as 1593 * the definitive zone. 1594 * 1595 * FreeBSD doesn't implement TZSet fully, so we can't use it there. 1596 * Apparently, OpenBSD neither. 1597 */ 1598 if (localZoneString == nil) 1599 { 1600 localZoneSource = @"function: 'tzset()/tzname'"; 1601 tzset(); 1602 if (NULL != tzname[0] && '\0' != *tzname[0] && 0 == daylight) 1603 localZoneString = [NSString stringWithUTF8String: tzname[0]]; 1604 } 1605#endif 1606 1607 if (localZoneString != nil) 1608 { 1609 NSDebugLLog (@"NSTimeZone", @"Using zone %@", localZoneString); 1610 zone = [defaultPlaceholderTimeZone initWithName: localZoneString]; 1611 if (zone == nil) 1612 { 1613 NSArray *possibleZoneNames; 1614 1615 /* 1616 It is not guaranteed on some systems (e.g., Ubuntu) that 1617 SYSTEM_TIME_FILE is a symlink. This file is more probably 1618 a copy of a zoneinfo file. The above time zone detecting 1619 approach can lead to the situation when we can only know 1620 about the time zone abbreviation (localZoneString) and 1621 (for some time zone abbreviations) the corresponding list 1622 of possible time zone names (e.g. SAMT is valid for 1623 Pacific/Samoa, Pacific/Pago_Pago, Pacific/Apia, 1624 Asia/Samarkand, Europe/Samara, US/Samoa). 1625 In such a case the time zone can be selected 1626 from the list by comparing the content of SYSTEM_TIME_FILE 1627 and the content of zoneinfo files corresponding to the items 1628 of that list. 1629 */ 1630 possibleZoneNames = [[self abbreviationMap] 1631 objectForKey: localZoneString]; 1632 if (possibleZoneNames != nil) 1633 { 1634 NSEnumerator *en = [possibleZoneNames objectEnumerator]; 1635 NSString *zoneName; 1636 NSFileManager *dflt = [NSFileManager defaultManager]; 1637 1638 while ((zoneName = [en nextObject]) != nil) 1639 { 1640 NSString *fileName = [self _getTimeZoneFile: zoneName]; 1641 1642 if (fileName != nil 1643 && [dflt contentsEqualAtPath: fileName 1644 andPath: SYSTEM_TIME_FILE]) 1645 { 1646 zone = [[self timeZoneWithName: zoneName] retain]; 1647 1648 if (zone != nil) 1649 { 1650 GSPrintf(stderr, 1651@"\nIt seems that your operating system does not have a valid timezone name\n" 1652@"configured and is using an abbreviation instead. By comparing timezone\n" 1653@"file data it is has been possible to find the actual timezone used, but\n" 1654@"doing that is a slow process.\n" 1655@"\nYou can avoid slowness of this time zone detecting approach\n" 1656@"by setting the environment variable TZ='%@'\n" 1657@"Or You can override the timezone name by setting the '%@'\n" 1658@"NSUserDefault via the 'defaults' command line utility, a Preferences\n" 1659@"application, or some other utility.\n" 1660@"eg \"defaults write NSGlobalDomain '%@' '%@'\"\n\n", 1661zoneName, LOCALDBKEY, LOCALDBKEY, zoneName); 1662 break; 1663 } 1664 } 1665 } 1666 } 1667 } 1668 if (zone == nil) 1669 { 1670 if (zone == nil) 1671 { 1672 GSPrintf(stderr, 1673@"\nUnable to create time zone for name: '%@'\n" 1674@"(source '%@').\n", localZoneString, localZoneSource); 1675 } 1676 if ([localZoneSource hasPrefix: @"file"] 1677 || [localZoneSource hasPrefix: @"function"]) 1678 { 1679 GSPrintf(stderr, 1680@"\nIt seems that your operating system does not have a valid timezone name\n" 1681@"configured (it could be that some other software has set a, possibly\n" 1682@"ambiguous, timezone abbreviation rather than a name) ... please correct\n" 1683@"that or override by setting a timezone name (such as 'Europe/London'\n" 1684@"or 'America/Chicago').\n"); 1685 } 1686 GSPrintf(stderr, 1687@"\nYou can override the timezone name by setting the '%@'\n" 1688@"NSUserDefault via the 'defaults' command line utility, a Preferences\n" 1689@"application, or some other utility.\n" 1690@"eg \"defaults write NSGlobalDomain '%@' 'Africa/Nairobi'\"\n" 1691@"See '%@'\n" 1692@"for the standard timezones such as 'GB-Eire' or 'America/Chicago'.\n", 1693LOCALDBKEY, LOCALDBKEY, _time_zone_path (ZONES_DIR, nil)); 1694 zone = [[self timeZoneWithAbbreviation: localZoneString] retain]; 1695 if (zone != nil) 1696 { 1697 NSInteger s; 1698 char sign = '+'; 1699 1700 s = [zone secondsFromGMT]; 1701 if (s < 0) 1702 { 1703 sign = '-'; 1704 s = -s; 1705 } 1706 GSPrintf(stderr, 1707@"\nSucceeded in treating '%@' as a timezone abbreviation,\n" 1708@"but abbreviations do not uniquely represent timezones, so this may\n" 1709@"not have found the timezone you were expecting. The timezone found\n" 1710@"was '%@' (currently UTC%c%02d%02d)\n\n", 1711localZoneString, [zone name], sign, s/3600, (s/60)%60); 1712 } 1713 } 1714 } 1715 else 1716 { 1717 NSLog(@"No local time zone specified."); 1718 } 1719 1720 /* 1721 * If local time zone fails to allocate, then allocate something 1722 * that is sure to succeed (unless we run out of memory, of 1723 * course). 1724 */ 1725 if (zone == nil) 1726 { 1727 NSLog(@"Using time zone with absolute offset 0."); 1728 zone = systemTimeZone; 1729 } 1730 ASSIGN(systemTimeZone, zone); 1731 } 1732 zone = AUTORELEASE(RETAIN(systemTimeZone)); 1733 pthread_mutex_unlock(&zone_mutex); 1734 return zone; 1735} 1736 1737/** 1738 * Returns an array of all the known regions.<br /> 1739 * There are 24 elements, of course, one for each time zone. 1740 * Each element contains an array of NSStrings which are 1741 * the region names. 1742 */ 1743+ (NSArray*) timeZoneArray 1744{ 1745 static NSArray *regionsArray = nil; 1746 1747 /* We create the array only when we need it to reduce overhead. */ 1748 if (regionsArray != nil) 1749 { 1750 return regionsArray; 1751 } 1752 pthread_mutex_lock(&zone_mutex); 1753 if (regionsArray == nil) 1754 { 1755 NSAutoreleasePool *pool = [NSAutoreleasePool new]; 1756 int index; 1757 int i; 1758 char name[80]; 1759 FILE *fp; 1760 NSMutableArray *temp_array[24]; 1761 NSString *path; 1762 1763 for (i = 0; i < 24; i++) 1764 { 1765 temp_array[i] = [NSMutableArray array]; 1766 } 1767 1768 path = _time_zone_path (REGIONS_FILE, nil); 1769 if (path != nil) 1770 { 1771#if defined(_WIN32) 1772 unichar mode[3]; 1773 1774 mode[0] = 'r'; 1775 mode[1] = 'b'; 1776 mode[2] = '\0'; 1777 1778 fp = _wfopen((const unichar*)[path fileSystemRepresentation], mode); 1779#else 1780 fp = fopen([path fileSystemRepresentation], "r"); 1781#endif 1782 if (fp == NULL) 1783 { 1784 pthread_mutex_unlock(&zone_mutex); 1785 [NSException 1786 raise: NSInternalInconsistencyException 1787 format: @"Failed to open time zone regions array file."]; 1788 } 1789 while (fscanf(fp, "%d %79s", &index, name) == 2) 1790 { 1791 if (index < 0) 1792 index = 0; 1793 else 1794 index %= 24; 1795 [temp_array[index] 1796 addObject: [NSString stringWithUTF8String: name]]; 1797 } 1798 fclose(fp); 1799 } 1800 else 1801 { 1802 NSString *zonedir = [NSTimeZone _getTimeZoneFile: @"WET"]; 1803 1804 if (tzdir != nil) 1805 { 1806 NSFileManager *mgr = [NSFileManager defaultManager]; 1807 NSDirectoryEnumerator *enumerator; 1808 NSString *name; 1809 1810 zonedir = [zonedir stringByDeletingLastPathComponent]; 1811 enumerator = [mgr enumeratorAtPath: zonedir]; 1812 while ((name = [enumerator nextObject]) != nil) 1813 { 1814 NSTimeZone *zone = nil; 1815 BOOL isDir; 1816 1817 path = [zonedir stringByAppendingPathComponent: name]; 1818 if ([mgr fileExistsAtPath: path isDirectory: &isDir] 1819 && isDir == NO 1820 && [[path pathExtension] isEqual: @"tab"] == NO) 1821 { 1822 zone = [zoneDictionary objectForKey: name]; 1823 if (zone == nil) 1824 { 1825 NSData *data; 1826 1827 data = [NSData dataWithContentsOfFile: path]; 1828 /* We should really make sure this is a real 1829 zone file and not something extra that happens 1830 to be in this directory, but initWithName:data: 1831 will do this anyway and log a message if not. */ 1832 zone = [[self alloc] initWithName: name data: data]; 1833 IF_NO_GC([zone autorelease];) 1834 } 1835 if (zone != nil) 1836 { 1837 int offset; 1838 NSArray *details; 1839 NSTimeZoneDetail *detail; 1840 NSEnumerator *e; 1841 1842 details = [zone timeZoneDetailArray]; 1843 e = [details objectEnumerator]; 1844 1845 while ((detail = [e nextObject]) != nil) 1846 { 1847 if ([detail isDaylightSavingTime] == NO) 1848 { 1849 break; // Found a standard time 1850 } 1851 } 1852 if (detail == nil && [details count] > 0) 1853 { 1854 // If no standard time 1855 detail = [details objectAtIndex: 0]; 1856 } 1857 1858 offset = [detail secondsFromGMT]; 1859 if (offset < 0) 1860 { 1861 offset = -offset; 1862 offset %= (60 * 60 * 24); 1863 if (offset > 0) 1864 { 1865 offset = -offset; 1866 offset += (60 * 60 * 24); 1867 } 1868 } 1869 else 1870 { 1871 offset %= (60 * 60 * 24); 1872 } 1873 offset /= (60 * 60); 1874 1875 [temp_array[offset] addObject: name]; 1876 } 1877 } 1878 } 1879 } 1880 } 1881 regionsArray = [[NSArray alloc] initWithObjects: temp_array count: 24]; 1882 [pool drain]; 1883 } 1884 pthread_mutex_unlock(&zone_mutex); 1885 return regionsArray; 1886} 1887 1888/** 1889 * Return a timezone for the specified offset from GMT.<br /> 1890 * The timezone returned does <em>not</em> use daylight savings time. 1891 * The actual timezone returned has an offset rounded to the nearest 1892 * minute.<br /> 1893 * Time zones with an offset of more than +/- 18 hours are disallowed, 1894 * and nil is returned. 1895 */ 1896+ (NSTimeZone*) timeZoneForSecondsFromGMT: (NSInteger)seconds 1897{ 1898 NSTimeZone *zone; 1899 int sign = seconds >= 0 ? 1 : -1; 1900 int extra; 1901 1902 /* 1903 * Round the offset to the nearest minute, (for MacOS-X compatibility) 1904 * and ensure it is no more than 18 hours. 1905 */ 1906 seconds *= sign; 1907 extra = seconds % 60; 1908 if (extra < 30) 1909 { 1910 seconds -= extra; 1911 } 1912 else 1913 { 1914 seconds += 60 - extra; 1915 } 1916 if (seconds > 64800) 1917 { 1918 return nil; 1919 } 1920 seconds *= sign; 1921 if (seconds % 900 == 0) 1922 { 1923 zone = commonAbsolutes[seconds/900 + 72]; 1924 } 1925 else 1926 { 1927 pthread_mutex_lock(&zone_mutex); 1928 zone = (NSTimeZone*)NSMapGet(absolutes, (void*)(uintptr_t)seconds); 1929 pthread_mutex_unlock(&zone_mutex); 1930 } 1931 if (nil == zone) 1932 { 1933 zone = [[GSAbsTimeZone alloc] initWithOffset: seconds name: nil]; 1934 zone = AUTORELEASE(zone); 1935 } 1936 return zone; 1937} 1938 1939/** 1940 * Returns a timezone for the specified abbreviation. The same abbreviations 1941 * are used in different regions so this isn't particularly useful.<br /> 1942 * Calls NSTimeZone-abbreviation dictionary an so uses a lot of memory. 1943 */ 1944+ (NSTimeZone*) timeZoneWithAbbreviation: (NSString*)abbreviation 1945{ 1946 NSTimeZone *zone; 1947 NSString *name; 1948 1949 name = [[self abbreviationDictionary] objectForKey: abbreviation]; 1950 if (name == nil) 1951 { 1952 zone = nil; 1953 } 1954 else 1955 { 1956 zone = [self timeZoneWithName: name data: nil]; 1957 } 1958 return zone; 1959} 1960 1961/** 1962 * Returns a timezone for the specified name. 1963 */ 1964+ (NSTimeZone*) timeZoneWithName: (NSString*)aTimeZoneName 1965{ 1966 NSTimeZone *zone; 1967 1968 zone = [defaultPlaceholderTimeZone initWithName: aTimeZoneName data: nil]; 1969 return AUTORELEASE(zone); 1970} 1971 1972/** 1973 * Returns a timezone for aTimeZoneName, created from the supplied 1974 * time zone data. Data must be in TZ format as per the Olson database. 1975 */ 1976+ (NSTimeZone*) timeZoneWithName: (NSString*)name data: (NSData*)data 1977{ 1978 NSTimeZone *zone; 1979 1980 zone = [defaultPlaceholderTimeZone initWithName: name data: data]; 1981 return AUTORELEASE(zone); 1982} 1983 1984/** 1985 * Returns the abbreviation for this timezone now. 1986 * Invokes -abbreviationForDate: 1987 */ 1988- (NSString*) abbreviation 1989{ 1990 return [self abbreviationForDate: [NSDate date]]; 1991} 1992 1993/** 1994 * Returns the abbreviation for this timezone at aDate. This may differ 1995 * depending on whether daylight savings time is in effect or not. 1996 */ 1997- (NSString*) abbreviationForDate: (NSDate*)aDate 1998{ 1999 NSTimeZoneDetail *detail; 2000 NSString *abbr; 2001 2002 detail = [self timeZoneDetailForDate: aDate]; 2003 abbr = [detail timeZoneAbbreviation]; 2004 2005 return abbr; 2006} 2007 2008/** 2009 * Returns the Class for this object 2010 */ 2011- (Class) classForCoder 2012{ 2013 return NSTimeZoneClass; 2014} 2015 2016- (id) copyWithZone: (NSZone*)z 2017{ 2018 return RETAIN(self); 2019} 2020 2021/** 2022 * Returns the data with which the receiver was initialised. 2023 */ 2024- (NSData*) data 2025{ 2026 return nil; 2027} 2028 2029/** 2030 * Returns the name of this object. 2031 */ 2032- (NSString*) description 2033{ 2034 return [self name]; 2035} 2036 2037- (void) encodeWithCoder: (NSCoder*)aCoder 2038{ 2039 [aCoder encodeObject: [self name]]; 2040} 2041 2042- (NSUInteger) hash 2043{ 2044 return [[self name] hash]; 2045} 2046 2047- (id) init 2048{ 2049 return [self initWithName: @"NSLocalTimeZone" data: nil]; 2050} 2051 2052- (id) initWithCoder: (NSCoder*)aDecoder 2053{ 2054 NSString *name; 2055 2056 name = [aDecoder decodeObject]; 2057 self = [self initWithName: name data: nil]; 2058 return self; 2059} 2060 2061/** 2062 * Initialise a timezone with the supplied name. May return a cached 2063 * timezone object rather than the newly created one. 2064 */ 2065- (id) initWithName: (NSString*)name 2066{ 2067 return [self initWithName: name data: nil]; 2068} 2069 2070/** 2071 * Initialises a time zone object using the supplied data object.<br /> 2072 * This method is intended for internal use by the NSTimeZone 2073 * class cluster. 2074 * Don't use it ... use -initWithName: instead. 2075 */ 2076- (id) initWithName: (NSString*)name data: (NSData*)data 2077{ 2078 [self notImplemented: _cmd]; 2079 return nil; 2080} 2081 2082/** 2083 * Returns a boolean indicating whether daylight savings time is in 2084 * effect now. Invokes -isDaylightSavingTimeForDate: 2085 */ 2086- (BOOL) isDaylightSavingTime 2087{ 2088 return [self isDaylightSavingTimeForDate: [NSDate date]]; 2089} 2090 2091/** 2092 * Returns a boolean indicating whether daylight savings time is in 2093 * effect for this time zone at aDate. 2094 */ 2095- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate 2096{ 2097 NSTimeZoneDetail *detail; 2098 BOOL isDST; 2099 2100 detail = [self timeZoneDetailForDate: aDate]; 2101 isDST = [detail isDaylightSavingTimeZone]; 2102 2103 return isDST; 2104} 2105 2106- (BOOL) isEqual: (id)other 2107{ 2108 if (other == self) 2109 return YES; 2110 if ([other isKindOfClass: NSTimeZoneClass] == NO) 2111 return NO; 2112 return [self isEqualToTimeZone: other]; 2113} 2114 2115/** 2116 * Returns YES if the time zones have the same name. 2117 */ 2118- (BOOL) isEqualToTimeZone: (NSTimeZone*)aTimeZone 2119{ 2120 if (aTimeZone == self) 2121 return YES; 2122 if ([[self name] isEqual: [aTimeZone name]] == NO) 2123 return NO; 2124 if (([self data] == nil && [aTimeZone data] == nil) 2125 || [[self name] isEqual: [aTimeZone name]] == YES) 2126 return YES; 2127 return NO; 2128} 2129 2130/** 2131 * Returns the name of the timezone 2132 */ 2133- (NSString*) name 2134{ 2135 return [self subclassResponsibility: _cmd]; 2136} 2137 2138- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder 2139{ 2140 if ([aCoder isByref] == NO) 2141 { 2142 return self; 2143 } 2144 return [super replacementObjectForPortCoder: aCoder]; 2145} 2146 2147/** 2148 * Returns the number of seconds by which the receiver differs 2149 * from Greenwich Mean Time at the current date and time.<br /> 2150 * Invokes -secondsFromGMTForDate: 2151 */ 2152- (NSInteger) secondsFromGMT 2153{ 2154 return [self secondsFromGMTForDate: [NSDate date]]; 2155} 2156 2157/** 2158 * Returns the number of seconds by which the receiver differs 2159 * from Greenwich Mean Time at the date aDate.<br /> 2160 * If the time zone uses daylight savings time, the returned value 2161 * will vary at different times of year. 2162 */ 2163- (NSInteger) secondsFromGMTForDate: (NSDate*)aDate 2164{ 2165 NSTimeZoneDetail *detail; 2166 int offset; 2167 2168 detail = [self timeZoneDetailForDate: aDate]; 2169 offset = [detail timeZoneSecondsFromGMT]; 2170 2171 return offset; 2172} 2173 2174/** 2175 * DEPRECATED: see NSTimeZoneDetail 2176 */ 2177- (NSArray*) timeZoneDetailArray 2178{ 2179 return [self subclassResponsibility: _cmd]; 2180} 2181 2182/** 2183 * DEPRECATED: see NSTimeZoneDetail 2184 */ 2185- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)date 2186{ 2187 return [self subclassResponsibility: _cmd]; 2188} 2189 2190/** 2191 * Returns the name of this timezone. 2192 */ 2193- (NSString*) timeZoneName 2194{ 2195 return [self name]; 2196} 2197 2198- (NSTimeInterval) daylightSavingTimeOffsetForDate: (NSDate *)aDate 2199{ 2200#if GS_USE_ICU == 1 2201 NSTimeInterval result; 2202 UCalendar *cal; 2203 UErrorCode err = U_ZERO_ERROR; 2204 2205 cal = ICUCalendarSetup (self, nil); 2206 if (cal == NULL) 2207 return 0.0; 2208 2209 ucal_setMillis (cal, ([aDate timeIntervalSince1970] * 1000.0), &err); 2210 result = (double)ucal_get (cal, UCAL_DST_OFFSET, &err) / 1000.0; 2211 if (U_FAILURE(err)) 2212 result = 0.0; 2213 ucal_close (cal); 2214 2215 return result; 2216#else 2217 return 0.0; // FIXME 2218#endif 2219} 2220 2221- (NSDate *) nextDaylightSavingTimeTransitionAfterDate: (NSDate *)aDate 2222{ 2223#if GS_USE_ICU == 1 2224 /* ICU doesn't provide transition information per se. 2225 * The canonical method of retrieving this piece of information is to 2226 * use binary search. 2227 */ 2228 2229 int32_t originalOffset, currentOffset; 2230 UCalendar *cal; 2231 UErrorCode err = U_ZERO_ERROR; 2232 UDate currentTime; 2233 int i; 2234 NSDate* result = nil; 2235 2236 cal = ICUCalendarSetup (self, nil); 2237 if (cal == NULL) 2238 return nil; 2239 2240 currentTime = [aDate timeIntervalSince1970] * 1000.0; 2241 ucal_setMillis (cal, currentTime, &err); 2242 originalOffset = ucal_get (cal, UCAL_DST_OFFSET, &err); 2243 if (U_FAILURE(err)) 2244 return nil; 2245 2246 /* First try to find the next transition by adding a week at a time */ 2247 /* Avoid ending in an infinite loop in case there is no transition at all */ 2248 2249 for (i = 0; i < 53; i++) 2250 { 2251 /* Add a single week */ 2252 currentTime += WEEK_MILLISECONDS; 2253 2254 ucal_setMillis (cal, currentTime, &err); 2255 if (U_FAILURE(err)) 2256 break; 2257 2258 currentOffset = ucal_get (cal, UCAL_DST_OFFSET, &err); 2259 if (U_FAILURE(err)) 2260 break; 2261 2262 if (currentOffset != originalOffset) 2263 { 2264 double interval = WEEK_MILLISECONDS / 2.0; 2265 /* Now use bisection to determine the exact moment */ 2266 2267 while (interval >= 1.0) 2268 { 2269 ucal_setMillis (cal, currentTime - interval, &err); 2270 2271 currentOffset = ucal_get (cal, UCAL_DST_OFFSET, &err); 2272 2273 if (currentOffset != originalOffset) 2274 currentTime -= interval; /* it is in the lower half */ 2275 2276 interval /= 2.0; 2277 } 2278 2279 result = 2280 [NSDate dateWithTimeIntervalSince1970: floor(currentTime/1000.0)]; 2281 } 2282 } 2283 2284 ucal_close (cal); 2285 2286 return result; 2287#else 2288 return nil; // FIXME; 2289#endif 2290} 2291 2292- (NSTimeInterval) daylightSavingTimeOffset 2293{ 2294 return [self daylightSavingTimeOffsetForDate: [NSDate date]]; 2295} 2296 2297- (NSDate *) nextDaylightSavingTimeTransition 2298{ 2299 return [self nextDaylightSavingTimeTransitionAfterDate: [NSDate date]]; 2300} 2301 2302- (NSString *)localizedName: (NSTimeZoneNameStyle)style 2303 locale: (NSLocale *)locale 2304{ 2305#if GS_USE_ICU == 1 2306 UChar *result; 2307 const char *cLocale; 2308 int32_t len; 2309 UCalendar *cal; 2310 UErrorCode err = U_ZERO_ERROR; 2311 2312 cal = ICUCalendarSetup (self, locale); 2313 if (cal == NULL) 2314 return nil; 2315 2316 cLocale = [[locale localeIdentifier] UTF8String]; 2317 result = NSZoneMalloc ([self zone], BUFFER_SIZE * sizeof(UChar)); 2318 len = ucal_getTimeZoneDisplayName (cal, _NSToICUTZDisplayStyle(style), 2319 cLocale, result, BUFFER_SIZE, &err); 2320 if (len > BUFFER_SIZE) 2321 { 2322 result = NSZoneRealloc ([self zone], result, len * sizeof(UChar)); 2323 ucal_getTimeZoneDisplayName (cal, _NSToICUTZDisplayStyle(style), 2324 cLocale, result, len, &err); 2325 } 2326 2327 return AUTORELEASE([[NSString alloc] initWithCharactersNoCopy: result 2328 length: len freeWhenDone: YES]); 2329#else 2330 return nil; // FIXME; 2331#endif 2332} 2333 2334@end 2335 2336/** 2337 * This class serves no useful purpose in GNUstep other than to provide 2338 * a backup mechanism for handling abbreviations where the precomputed 2339 * data files cannot be found. It is provided primarily for backward 2340 * compatibility with the OpenStep spec. It is missing entirely from MacOS-X. 2341 */ 2342@implementation NSTimeZoneDetail 2343 2344- (NSString*) description 2345{ 2346 return [NSString stringWithFormat: @"%@(%@, %s%"PRIdPTR")", [self name], 2347 [self timeZoneAbbreviation], 2348 ([self isDaylightSavingTimeZone]? "IS_DST, ": ""), 2349 [self timeZoneSecondsFromGMT]]; 2350} 2351 2352/** 2353 * DEPRECATED: Class is no longer used. 2354 */ 2355- (BOOL) isDaylightSavingTimeZone 2356{ 2357 [self subclassResponsibility: _cmd]; 2358 return NO; 2359} 2360 2361/** 2362 * DEPRECATED: Class is no longer used. 2363 */ 2364- (NSString*) timeZoneAbbreviation 2365{ 2366 return [self subclassResponsibility: _cmd]; 2367} 2368 2369/** 2370 * DEPRECATED: Class is no longer used. 2371 */ 2372- (NSInteger) timeZoneSecondsFromGMT 2373{ 2374 [self subclassResponsibility: _cmd]; 2375 return 0; 2376} 2377 2378@end 2379 2380 2381@implementation NSTimeZone (Private) 2382 2383/** 2384 * Common locations for timezone info on unix systems. 2385 */ 2386static NSString *zoneDirs[] = { 2387#ifdef TZDIR 2388 @TZDIR, 2389#endif 2390 @"/usr/share/zoneinfo", 2391 @"/usr/lib/zoneinfo", 2392 @"/usr/local/share/zoneinfo", 2393 @"/usr/local/lib/zoneinfo", 2394 @"/etc/zoneinfo", 2395 @"/usr/local/etc/zoneinfo" 2396}; 2397 2398/** 2399 * Returns the path to the named zone info file. 2400 */ 2401+ (NSString*) _getTimeZoneFile: (NSString *)name 2402{ 2403 static BOOL beenHere = NO; 2404 NSString *dir = nil; 2405 BOOL isDir; 2406 2407 if (beenHere == NO && tzdir == nil) 2408 { 2409 pthread_mutex_lock(&zone_mutex); 2410 if (beenHere == NO && tzdir == nil) 2411 { 2412 NSFileManager *mgr = [NSFileManager defaultManager]; 2413 NSString *zonedir = nil; 2414 unsigned i; 2415 2416 for (i = 0; i < sizeof(zoneDirs)/sizeof(zoneDirs[0]); i++) 2417 { 2418 BOOL isDir; 2419 2420 zonedir 2421 = [zoneDirs[i] stringByAppendingPathComponent: POSIX_TZONES]; 2422 if ([mgr fileExistsAtPath: zonedir isDirectory: &isDir] && isDir) 2423 { 2424 tzdir = RETAIN(zonedir); 2425 break; // use first one 2426 } 2427 } 2428 beenHere = YES; 2429 } 2430 pthread_mutex_unlock(&zone_mutex); 2431 } 2432 /* Use the system zone info if possible, otherwise, use our installed 2433 info. */ 2434 if (tzdir && [[NSFileManager defaultManager] fileExistsAtPath: 2435 [tzdir stringByAppendingPathComponent: name] isDirectory: &isDir] == YES 2436 && isDir == NO) 2437 { 2438 dir = tzdir; 2439 } 2440 if (dir == nil) 2441 { 2442 dir = _time_zone_path (ZONES_DIR, nil); 2443 } 2444 return [dir stringByAppendingPathComponent: name]; 2445} 2446 2447+ (void) _notified: (NSNotification*)n 2448{ 2449 NSString *name; 2450 2451 /* If the name of the system time zone has changed ... 2452 * get a new system time zone. 2453 */ 2454 name = [[NSUserDefaults standardUserDefaults] stringForKey: LOCALDBKEY]; 2455 if ([name length] > 0 && [name isEqual: [[self systemTimeZone] name]] == NO) 2456 { 2457 [self resetSystemTimeZone]; 2458 [self systemTimeZone]; 2459 } 2460} 2461@end 2462 2463 2464#if defined(_WIN32) 2465/* Timezone information data as stored in the registry */ 2466typedef struct TZI_format { 2467 LONG Bias; 2468 LONG StandardBias; 2469 LONG DaylightBias; 2470 SYSTEMTIME StandardDate; 2471 SYSTEMTIME DaylightDate; 2472} TZI; 2473 2474static inline unsigned int 2475lastDayOfGregorianMonth(int month, int year) 2476{ 2477 switch (month) 2478 { 2479 case 2: 2480 if ((((year % 4) == 0) && ((year % 100) != 0)) 2481 || ((year % 400) == 0)) 2482 return 29; 2483 else 2484 return 28; 2485 case 4: 2486 case 6: 2487 case 9: 2488 case 11: return 30; 2489 default: return 31; 2490 } 2491} 2492 2493/* IMPORT from NSCalendar date */ 2494void 2495GSBreakTime(NSTimeInterval when, 2496 NSInteger *year, NSInteger *month, NSInteger *day, 2497 NSInteger *hour, NSInteger *minute, NSInteger *second, NSInteger *mil); 2498 2499 2500@implementation GSWindowsTimeZone 2501 2502- (NSString*) abbreviationForDate: (NSDate*)aDate 2503{ 2504 if ([self isDaylightSavingTimeForDate: aDate]) 2505 return daylightZoneNameAbbr; 2506 return timeZoneNameAbbr; 2507} 2508 2509- (NSData*) data 2510{ 2511 return 0; 2512} 2513 2514- (void) dealloc 2515{ 2516 RELEASE(timeZoneName); 2517 RELEASE(daylightZoneName); 2518 RELEASE(timeZoneNameAbbr); 2519 RELEASE(daylightZoneNameAbbr); 2520 [super dealloc]; 2521} 2522 2523- (id) initWithName: (NSString*)name data: (NSData*)data 2524{ 2525 HKEY regDirKey; 2526 BOOL isNT = NO; 2527 BOOL regFound=NO; 2528 BOOL tzFound = NO; 2529 2530 /* Open the key in the local machine hive where 2531 * the time zone data is stored. */ 2532 if (ERROR_SUCCESS == RegOpenKeyExW(HKEY_LOCAL_MACHINE, 2533 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones", 2534 0, 2535 KEY_READ, 2536 ®DirKey)) 2537 { 2538 isNT = YES; 2539 regFound = YES; 2540 } 2541 else 2542 { 2543 if (ERROR_SUCCESS == RegOpenKeyExW(HKEY_LOCAL_MACHINE, 2544 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones", 2545 0, 2546 KEY_READ, 2547 ®DirKey)) 2548 { 2549 regFound = YES; 2550 } 2551 } 2552 2553 if (regFound) 2554 { 2555 /* Iterate over all subKeys in the registry to find the right one. 2556 Unfortunately name is a localized value. The keys in the registry are 2557 unlocalized names. */ 2558 wchar_t achKey[255]; // buffer for subkey name 2559 DWORD cbName; // size of name string 2560 wchar_t achClass[MAX_PATH] = L""; // buffer for class name 2561 DWORD cchClassName = MAX_PATH; // size of class string 2562 DWORD cSubKeys = 0; // number of subkeys 2563 DWORD cbMaxSubKey; // longest subkey size 2564 DWORD cchMaxClass; // longest class string 2565 DWORD cValues; // number of values for key 2566 DWORD cchMaxValue; // longest value name 2567 DWORD cbMaxValueData; // longest value data 2568 DWORD cbSecurityDescriptor; // size of security descriptor 2569 FILETIME ftLastWriteTime; // last write time 2570 DWORD i, retCode; 2571 2572 /* Get the class name and the value count. */ 2573 retCode = RegQueryInfoKeyW( 2574 regDirKey, // key handle 2575 achClass, // buffer for class name 2576 &cchClassName, // size of class string 2577 NULL, // reserved 2578 &cSubKeys, // number of subkeys 2579 &cbMaxSubKey, // longest subkey size 2580 &cchMaxClass, // longest class string 2581 &cValues, // number of values for this key 2582 &cchMaxValue, // longest value name 2583 &cbMaxValueData, // longest value data 2584 &cbSecurityDescriptor, // security descriptor 2585 &ftLastWriteTime); // last write time 2586 2587 if (cSubKeys && (retCode == ERROR_SUCCESS)) 2588 { 2589 unsigned wLen = [name length]; 2590 wchar_t *wName = malloc((wLen+1) * sizeof(wchar_t)); 2591 2592 if (wName) 2593 { 2594 [name getCharacters: wName]; 2595 wName[wLen] = 0; 2596 for (i = 0; i < cSubKeys && !tzFound; i++) 2597 { 2598 cbName = 255; 2599 2600 retCode = RegEnumKeyExW(regDirKey, i, achKey, &cbName, 2601 NULL, NULL, NULL, &ftLastWriteTime); 2602 if (retCode == ERROR_SUCCESS) 2603 { 2604 wchar_t keyBuffer[16384]; 2605 HKEY regKey; 2606 2607 if (isNT) 2608 wcscpy(keyBuffer, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\"); 2609 else 2610 wcscpy(keyBuffer, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones\\"); 2611 2612 wcscat(keyBuffer, achKey); 2613 if (ERROR_SUCCESS == RegOpenKeyExW(HKEY_LOCAL_MACHINE, 2614 keyBuffer, 0, KEY_READ, ®Key)) 2615 { 2616 wchar_t buf[256]; 2617 wchar_t standardName[256]; 2618 wchar_t daylightName[256]; 2619 DWORD bufsize; 2620 DWORD type; 2621 2622 /* check standardname */ 2623 standardName[0] = L'\0'; 2624 bufsize = sizeof(buf); 2625 if (ERROR_SUCCESS == RegQueryValueExW(regKey, 2626 L"Std", 0, &type, (BYTE *)buf, &bufsize)) 2627 { 2628 wcscpy(standardName, buf); 2629 if (wcscmp(standardName, wName) == 0) 2630 tzFound = YES; 2631 } 2632 2633 /* check daylightname */ 2634 daylightName[0] = L'\0'; 2635 bufsize = sizeof(buf); 2636 if (ERROR_SUCCESS == RegQueryValueExW(regKey, 2637 L"Dlt", 0, &type, (BYTE *)buf, &bufsize)) 2638 { 2639 wcscpy(daylightName, buf); 2640 if (wcscmp(daylightName, wName) == 0) 2641 tzFound = YES; 2642 } 2643 2644 if (tzFound) 2645 { 2646 /* Read in the time zone data */ 2647 bufsize = sizeof(buf); 2648 if (ERROR_SUCCESS == RegQueryValueExW(regKey, 2649 L"TZI", 0, &type, (BYTE *)buf, &bufsize)) 2650 { 2651 TZI *tzi = (void*)buf; 2652 Bias = tzi->Bias; 2653 StandardBias = tzi->StandardBias; 2654 DaylightBias = tzi->DaylightBias; 2655 StandardDate = tzi->StandardDate; 2656 DaylightDate = tzi->DaylightDate; 2657 } 2658 2659 /* Set the standard name for the time zone. */ 2660 if (wcslen(standardName)) 2661 { 2662 int a, b; 2663 2664 ASSIGN(timeZoneName, 2665 [NSString stringWithCharacters: standardName 2666 length: wcslen(standardName)]); 2667 2668 /* Abbr generated here is IMHO 2669 * a bit suspicous but I kept it */ 2670 for (a = 0, b = 0; standardName[a]; a++) 2671 { 2672 if (iswupper(standardName[a])) 2673 standardName[b++] = standardName[a]; 2674 } 2675 standardName[b] = L'\0'; 2676 ASSIGN(timeZoneNameAbbr, 2677 [NSString stringWithCharacters: standardName 2678 length: wcslen(standardName)]); 2679 } 2680 2681 /* Set the daylight savings name 2682 * for the time zone. */ 2683 if (wcslen(daylightName)) 2684 { 2685 int a, b; 2686 2687 ASSIGN(daylightZoneName, 2688 [NSString stringWithCharacters: daylightName 2689 length: wcslen(daylightName)]); 2690 2691 /* Abbr generated here is IMHO 2692 * a bit suspicous but I kept it */ 2693 for (a = 0, b = 0; daylightName[a]; a++) 2694 { 2695 if (iswupper(daylightName[a])) 2696 daylightName[b++] = daylightName[a]; 2697 } 2698 daylightName[b] = L'\0'; 2699 ASSIGN(daylightZoneNameAbbr, 2700 [NSString stringWithCharacters: daylightName 2701 length: wcslen(daylightName)]); 2702 } 2703 } 2704 RegCloseKey(regKey); 2705 } 2706 } 2707 } 2708 free(wName); 2709 } 2710 } 2711 RegCloseKey(regDirKey); 2712 } 2713 if (NO == tzFound) 2714 { 2715 DESTROY(self); 2716 } 2717 return self; 2718} 2719 2720- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate 2721{ 2722 NSInteger year, month, day, hour, minute, second, mil; 2723 int dow; 2724 int daylightdate, count, maxdate; 2725 NSTimeInterval when; 2726 2727 if (DaylightDate.wMonth == 0) 2728 return NO; 2729 2730 when = [aDate timeIntervalSinceReferenceDate] - Bias*60; 2731 2732 GSBreakTime(when, &year, &month, &day, &hour, &minute, &second, &mil); 2733 2734 // Check north globe 2735 if (StandardDate.wMonth >= DaylightDate.wMonth) 2736 { 2737 // Before April or after October is Std 2738 if (month < DaylightDate.wMonth || month > StandardDate.wMonth) 2739 { 2740 return NO; 2741 } 2742 // After April and before October is DST 2743 if (month > DaylightDate.wMonth && month < StandardDate.wMonth) 2744 { 2745 return YES; 2746 } 2747 } 2748 else 2749 { 2750 /* check south globe 2751 * Before April or after October is DST 2752 */ 2753 if (month < StandardDate.wMonth || month > DaylightDate.wMonth) 2754 { 2755 return YES; 2756 } 2757 // After April and before October is Std 2758 if (month > StandardDate.wMonth && month < DaylightDate.wMonth) 2759 { 2760 return NO; 2761 } 2762 } 2763 2764 dow = ((NSInteger)((when / 86400.0) + GREGORIAN_REFERENCE)) % 7; 2765 if (dow < 0) 2766 dow += 7; 2767 2768 if (month == DaylightDate.wMonth /* April */) 2769 { 2770 daylightdate = day - dow + DaylightDate.wDayOfWeek; 2771 maxdate = lastDayOfGregorianMonth(DaylightDate.wMonth, year)-7; 2772 while (daylightdate > 7) 2773 daylightdate -= 7; 2774 if (daylightdate < 1) 2775 daylightdate += 7; 2776 count = DaylightDate.wDay; 2777 while (count > 1 && daylightdate < maxdate) 2778 { 2779 daylightdate += 7; 2780 count--; 2781 } 2782 if (day > daylightdate) 2783 return YES; 2784 if (day < daylightdate) 2785 return NO; 2786 if (hour > DaylightDate.wHour) 2787 return YES; 2788 if (hour < DaylightDate.wHour) 2789 return NO; 2790 if (minute > DaylightDate.wMinute) 2791 return YES; 2792 if (minute < DaylightDate.wMinute) 2793 return NO; 2794 if (second > DaylightDate.wSecond) 2795 return YES; 2796 if (second < DaylightDate.wSecond) 2797 return NO; 2798 if (mil >= DaylightDate.wMilliseconds) 2799 return YES; 2800 return NO; 2801 } 2802 if (month == StandardDate.wMonth /* October */) 2803 { 2804 daylightdate = day - dow + StandardDate.wDayOfWeek; 2805 maxdate = lastDayOfGregorianMonth(StandardDate.wMonth, year)-7; 2806 while (daylightdate > 7) 2807 daylightdate -= 7; 2808 if (daylightdate < 1) 2809 daylightdate += 7; 2810 count = StandardDate.wDay; 2811 while (count > 1 && daylightdate < maxdate) 2812 { 2813 daylightdate += 7; 2814 count--; 2815 } 2816 if (day > daylightdate) 2817 return NO; 2818 if (day < daylightdate) 2819 return YES; 2820 if (hour > StandardDate.wHour) 2821 return NO; 2822 if (hour < StandardDate.wHour) 2823 return YES; 2824 if (minute > StandardDate.wMinute) 2825 return NO; 2826 if (minute < StandardDate.wMinute) 2827 return YES; 2828 if (second > StandardDate.wSecond) 2829 return NO; 2830 if (second < StandardDate.wSecond) 2831 return YES; 2832 if (mil >= StandardDate.wMilliseconds) 2833 return NO; 2834 return YES; 2835 } 2836 return NO; // Never reached 2837} 2838 2839- (NSString*) name 2840{ 2841 TIME_ZONE_INFORMATION tz; 2842 DWORD DST = GetTimeZoneInformation(&tz); 2843 2844 if (DST == TIME_ZONE_ID_DAYLIGHT) 2845 { 2846 return daylightZoneName; 2847 } 2848 else 2849 { 2850 return timeZoneName; 2851 } 2852} 2853 2854- (NSInteger) secondsFromGMTForDate: (NSDate*)aDate 2855{ 2856 if ([self isDaylightSavingTimeForDate: aDate]) 2857 return -Bias*60 - DaylightBias*60; 2858 return -Bias*60 - StandardBias*60; 2859} 2860 2861- (NSArray*) timeZoneDetailArray 2862{ 2863 return [NSArray arrayWithObjects: 2864 [[[GSTimeZoneDetail alloc] initWithTimeZone: self 2865 withAbbrev: timeZoneNameAbbr 2866 withOffset: -Bias*60 - StandardBias*60 2867 withDST: NO] autorelease], 2868 [[[GSTimeZoneDetail alloc] initWithTimeZone: self 2869 withAbbrev: daylightZoneNameAbbr 2870 withOffset: -Bias*60 - DaylightBias*60 2871 withDST: YES] autorelease], 0]; 2872} 2873 2874- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)aDate 2875{ 2876 GSTimeZoneDetail *detail; 2877 int offset; 2878 BOOL isDST = [self isDaylightSavingTimeForDate: aDate]; 2879 NSString *abbr; 2880 2881 if (isDST) 2882 { 2883 offset = -Bias*60 - DaylightBias*60; 2884 abbr = daylightZoneNameAbbr; 2885 } 2886 else 2887 { 2888 offset = -Bias*60 - StandardBias*60; 2889 abbr = timeZoneNameAbbr; 2890 } 2891 detail = [GSTimeZoneDetail alloc]; 2892 detail = [detail initWithTimeZone: self 2893 withAbbrev: abbr 2894 withOffset: offset 2895 withDST: isDST]; 2896 return detail; 2897} 2898 2899- (NSString*) timeZoneName 2900{ 2901 return [self name]; 2902} 2903@end 2904#endif // _WIN32 2905 2906 2907@implementation GSTimeZone 2908 2909/** 2910 * Perform a binary search of a transitions table to locate the index 2911 * of the transition to use for a particular time interval since 1970.<br /> 2912 * We locate the index of the highest transition before the date, or zero 2913 * if there is no transition before it. 2914 */ 2915static TypeInfo* 2916chop(NSTimeInterval since, GSTimeZone *zone) 2917{ 2918 int32_t when = (int32_t)since; 2919 int32_t *trans = zone->trans; 2920 unsigned hi = zone->n_trans; 2921 unsigned lo = 0; 2922 unsigned int i; 2923 2924 if (hi == 0 || trans[0] > when) 2925 { 2926 unsigned n_types = zone->n_types; 2927 2928 /* 2929 * If the first transition is greater than our date, 2930 * we locate the first non-DST transition and use that offset, 2931 * or just use the first transition. 2932 */ 2933 for (i = 0; i < n_types; i++) 2934 { 2935 if (zone->types[i].isdst == 0) 2936 { 2937 return &zone->types[i]; 2938 } 2939 } 2940 return &zone->types[0]; 2941 } 2942 else 2943 { 2944 for (i = hi/2; hi != lo; i = (hi + lo)/2) 2945 { 2946 if (when < trans[i]) 2947 { 2948 hi = i; 2949 } 2950 else if (when > trans[i]) 2951 { 2952 lo = ++i; 2953 } 2954 else 2955 { 2956 break; 2957 } 2958 } 2959 /* 2960 * If we went off the top of the table or the closest transition 2961 * was later than our date, we step back to find the last 2962 * transition before our date. 2963 */ 2964 if (i > 0 && (i == zone->n_trans || trans[i] > when)) 2965 { 2966 i--; 2967 } 2968 return &zone->types[zone->idxs[i]]; 2969 } 2970} 2971 2972static NSTimeZoneDetail* 2973newDetailInZoneForType(GSTimeZone *zone, TypeInfo *type) 2974{ 2975 GSTimeZoneDetail *detail; 2976 2977 detail = [GSTimeZoneDetail alloc]; 2978 detail = [detail initWithTimeZone: zone 2979 withAbbrev: type->abbreviation 2980 withOffset: type->offset 2981 withDST: type->isdst]; 2982 return detail; 2983} 2984 2985- (NSString*) abbreviationForDate: (NSDate*)aDate 2986{ 2987 TypeInfo *type = chop([aDate timeIntervalSince1970], self); 2988 2989 return type->abbreviation; 2990} 2991 2992- (NSData*) data 2993{ 2994 return timeZoneData; 2995} 2996 2997- (void) dealloc 2998{ 2999 RELEASE(timeZoneName); 3000 RELEASE(timeZoneData); 3001 RELEASE(abbreviations); 3002 if (types != 0) 3003 { 3004 NSZoneFree(NSDefaultMallocZone(), types); 3005 } 3006 [super dealloc]; 3007} 3008 3009- (id) initWithName: (NSString*)name data: (NSData*)data 3010{ 3011 static NSString *fileException = @"GSTimeZoneFileException"; 3012 3013 timeZoneName = [name copy]; 3014 timeZoneData = [data copy]; 3015 NS_DURING 3016 { 3017 const void *bytes = [timeZoneData bytes]; 3018 unsigned length = [timeZoneData length]; 3019 void *buf; 3020 unsigned pos = 0; 3021 unsigned i, charcnt; 3022 unsigned char *abbr; 3023 struct tzhead *header; 3024 3025 if (length < sizeof(struct tzhead)) 3026 { 3027 [NSException raise: fileException 3028 format: @"File is too small"]; 3029 } 3030 header = (struct tzhead *)(bytes + pos); 3031 pos += sizeof(struct tzhead); 3032#ifdef TZ_MAGIC 3033 if (memcmp(header->tzh_magic, TZ_MAGIC, strlen(TZ_MAGIC)) != 0) 3034 { 3035 [NSException raise: fileException 3036 format: @"TZ_MAGIC is incorrect"]; 3037 } 3038#endif 3039 n_trans = GSSwapBigI32ToHost(*(int32_t*)(void*)header->tzh_timecnt); 3040 n_types = GSSwapBigI32ToHost(*(int32_t*)(void*)header->tzh_typecnt); 3041 charcnt = GSSwapBigI32ToHost(*(int32_t*)(void*)header->tzh_charcnt); 3042 3043 i = pos; 3044 i += sizeof(int32_t)*n_trans; 3045 if (i > length) 3046 { 3047 [NSException raise: fileException 3048 format: @"Transitions list is truncated"]; 3049 } 3050 i += n_trans; 3051 if (i > length) 3052 { 3053 [NSException raise: fileException 3054 format: @"Transition indexes are truncated"]; 3055 } 3056 i += sizeof(struct ttinfo)*n_types; 3057 if (i > length) 3058 { 3059 [NSException raise: fileException 3060 format: @"Types list is truncated"]; 3061 } 3062 if (i + charcnt > length) 3063 { 3064 [NSException raise: fileException 3065 format: @"Abbreviations list is truncated"]; 3066 } 3067 3068 /* 3069 * Now calculate size we need to store the information 3070 * for efficient access ... not the same saze as the data 3071 * we received. 3072 */ 3073 i = n_trans * (sizeof(int32_t)+1) + n_types * sizeof(TypeInfo); 3074 buf = NSZoneMalloc(NSDefaultMallocZone(), i); 3075 types = (TypeInfo*)buf; 3076 buf += (n_types * sizeof(TypeInfo)); 3077 trans = (int32_t*)buf; 3078 buf += (n_trans * sizeof(int32_t)); 3079 idxs = (unsigned char*)buf; 3080 3081 /* Read in transitions. */ 3082 for (i = 0; i < n_trans; i++) 3083 { 3084 trans[i] = GSSwapBigI32ToHost(*(int32_t*)(bytes + pos)); 3085 pos += sizeof(int32_t); 3086 } 3087 for (i = 0; i < n_trans; i++) 3088 { 3089 idxs[i] = *(unsigned char*)(bytes + pos); 3090 pos++; 3091 } 3092 for (i = 0; i < n_types; i++) 3093 { 3094 struct ttinfo *ptr = (struct ttinfo*)(bytes + pos); 3095 uint32_t off; 3096 3097 types[i].isdst = (ptr->isdst != 0 ? YES : NO); 3098 types[i].abbr_idx = ptr->abbr_idx; 3099 memcpy(&off, ptr->offset, 4); 3100 types[i].offset = GSSwapBigI32ToHost(off); 3101 pos += sizeof(struct ttinfo); 3102 } 3103 abbr = (unsigned char*)(bytes + pos); 3104 { 3105 id abbrevs[charcnt]; 3106 unsigned count = 0; 3107 unsigned used = 0; 3108 3109 memset(abbrevs, '\0', sizeof(id)*charcnt); 3110 for (i = 0; i < n_types; i++) 3111 { 3112 int loc = types[i].abbr_idx; 3113 3114 if (abbrevs[loc] == nil) 3115 { 3116 abbrevs[loc] 3117 = [[NSString alloc] initWithUTF8String: (char*)abbr + loc]; 3118 count++; 3119 } 3120 types[i].abbreviation = abbrevs[loc]; 3121 } 3122 /* 3123 * Now we have created all the abbreviations, we put them in an 3124 * array for easy access later and easy deallocation if/when 3125 * the receiver is deallocated. 3126 */ 3127 i = charcnt; 3128 while (i-- > count) 3129 { 3130 if (abbrevs[i] != nil) 3131 { 3132 while (abbrevs[used] != nil) 3133 { 3134 used++; 3135 } 3136 abbrevs[used] = abbrevs[i]; 3137 abbrevs[i] = nil; 3138 if (++used >= count) 3139 { 3140 break; 3141 } 3142 } 3143 } 3144 abbreviations = [[NSArray alloc] initWithObjects: abbrevs count: count]; 3145 while (count-- > 0) 3146 { 3147 RELEASE(abbrevs[count]); 3148 } 3149 } 3150 3151 pthread_mutex_lock(&zone_mutex); 3152 [zoneDictionary setObject: self forKey: timeZoneName]; 3153 pthread_mutex_unlock(&zone_mutex); 3154 } 3155 NS_HANDLER 3156 { 3157 DESTROY(self); 3158 NSLog(@"Unable to obtain time zone `%@'... %@", name, localException); 3159 if ([localException name] != fileException) 3160 { 3161 [localException raise]; 3162 } 3163 } 3164 NS_ENDHANDLER 3165 return self; 3166} 3167 3168- (BOOL) isDaylightSavingTimeForDate: (NSDate*)aDate 3169{ 3170 TypeInfo *type = chop([aDate timeIntervalSince1970], self); 3171 3172 return type->isdst; 3173} 3174 3175- (NSString*) name 3176{ 3177 return timeZoneName; 3178} 3179 3180- (NSInteger) secondsFromGMTForDate: (NSDate*)aDate 3181{ 3182 TypeInfo *type = chop([aDate timeIntervalSince1970], self); 3183 3184 return type->offset; 3185} 3186 3187- (NSArray*) timeZoneDetailArray 3188{ 3189 NSTimeZoneDetail *details[n_types]; 3190 unsigned i; 3191 NSArray *array; 3192 3193 for (i = 0; i < n_types; i++) 3194 { 3195 details[i] = newDetailInZoneForType(self, &types[i]); 3196 } 3197 array = [NSArray arrayWithObjects: details count: n_types]; 3198 for (i = 0; i < n_types; i++) 3199 { 3200 RELEASE(details[i]); 3201 } 3202 return array; 3203} 3204 3205- (NSTimeZoneDetail*) timeZoneDetailForDate: (NSDate*)aDate 3206{ 3207 TypeInfo *type; 3208 NSTimeZoneDetail *detail; 3209 3210 type = chop([aDate timeIntervalSince1970], self); 3211 detail = newDetailInZoneForType(self, type); 3212 return AUTORELEASE(detail); 3213} 3214 3215- (NSString*) timeZoneName 3216{ 3217 return timeZoneName; 3218} 3219 3220@end 3221 3222