1 module hunt.system.TimeZone; 2 3 import std.string; 4 5 string getSystemTimeZoneId(string homeDir = "") { 6 version (Posix) { 7 return findTZ_md(homeDir); 8 } else version (Windows) { 9 return findTZ_md(homeDir); 10 } else { 11 // return "Asia/Shanghai"; 12 return ""; 13 } 14 } 15 16 // class TimeZone { 17 18 // } 19 20 version (Posix) { 21 import core.sys.posix.stdlib; 22 import core.sys.posix.unistd; 23 import core.sys.posix.fcntl; 24 import core.stdc.errno; 25 import core.sys.linux.unistd; 26 import core.sys.posix.sys.stat; 27 import core.sys.posix.dirent; 28 import std.file; 29 import core.stdc.string; 30 import std.stdio; 31 32 static const char* ETC_TIMEZONE_FILE = "/etc/timezone"; 33 static const char* ZONEINFO_DIR = "/usr/share/zoneinfo"; 34 static const char* DEFAULT_ZONEINFO_FILE = "/etc/localtime"; 35 enum int PATH_MAX = 1024; 36 37 string RESTARTABLE(string _cmd, string _result) { 38 string str; 39 str ~= `do { 40 do { `; 41 str ~= _result ~ "= " ~ _cmd ~ `; 42 } while((` ~ _result 43 ~ `== -1) && (errno == EINTR)); 44 } while(0);`; 45 return str; 46 } 47 48 static char* getPlatformTimeZoneID() { 49 /* struct */ 50 stat_t statbuf; 51 char* tz = null; 52 FILE* fp; 53 int fd; 54 char* buf; 55 size_t size; 56 int res; 57 58 /* #if defined(__linux__) */ /* 59 * Try reading the /etc/timezone file for Debian distros. There's 60 * no spec of the file format available. This parsing assumes that 61 * there's one line of an Olson tzid followed by a '\n', no 62 * leading or trailing spaces, no comments. 63 */ 64 if ((fp = core.stdc.stdio.fopen(ETC_TIMEZONE_FILE, "r")) !is null) { 65 char[256] line; 66 67 if (fgets(line.ptr, (line.sizeof), fp) !is null) { 68 char* p = strchr(line.ptr, '\n'); 69 if (p !is null) { 70 *p = '\0'; 71 } 72 if (strlen(line.ptr) > 0) { 73 tz = strdup(line.ptr); 74 } 75 } 76 /* (void) */ 77 fclose(fp); 78 if (tz !is null) { 79 return tz; 80 } 81 } 82 /* #endif */ /* defined(__linux__) */ 83 84 /* 85 * Next, try /etc/localtime to find the zone ID. 86 */ 87 mixin(RESTARTABLE("lstat(DEFAULT_ZONEINFO_FILE, &statbuf)", "res")); 88 if (res == -1) { 89 return null; 90 } 91 92 /* 93 * If it's a symlink, get the link name and its zone ID part. (The 94 * older versions of timeconfig created a symlink as described in 95 * the Red Hat man page. It was changed in 1999 to create a copy 96 * of a zoneinfo file. It's no longer possible to get the zone ID 97 * from /etc/localtime.) 98 */ 99 if (S_ISLNK(statbuf.st_mode)) { 100 char[PATH_MAX + 1] linkbuf; 101 int len; 102 103 if ((len = cast(int) readlink(DEFAULT_ZONEINFO_FILE, linkbuf.ptr, 104 cast(int)(linkbuf.sizeof) - 1)) == -1) { 105 // /* jio_fprintf */writefln(stderr, cast(const char * ) "can't get a symlink of %s\n", 106 // DEFAULT_ZONEINFO_FILE); 107 return null; 108 } 109 linkbuf[len] = '\0'; 110 tz = getZoneName(linkbuf.ptr); 111 if (tz !is null) { 112 tz = strdup(tz); 113 return tz; 114 } 115 } 116 117 /* 118 * If it's a regular file, we need to find out the same zoneinfo file 119 * that has been copied as /etc/localtime. 120 * If initial symbolic link resolution failed, we should treat target 121 * file as a regular file. 122 */ 123 mixin(RESTARTABLE(`open(DEFAULT_ZONEINFO_FILE, O_RDONLY)`, "fd")); 124 if (fd == -1) { 125 return null; 126 } 127 128 mixin(RESTARTABLE(`fstat(fd, &statbuf)`, "res")); 129 if (res == -1) { 130 /* (void) */ 131 close(fd); 132 return null; 133 } 134 size = cast(size_t) statbuf.st_size; 135 buf = cast(char*) malloc(size); 136 if (buf is null) { 137 /* (void) */ 138 close(fd); 139 return null; 140 } 141 142 mixin(RESTARTABLE(`cast(int)read(fd, buf, size)`, "res")); 143 if (res != cast(int) size) { 144 /* (void) */ 145 close(fd); 146 free(cast(void*) buf); 147 return null; 148 } 149 /* (void) */ 150 close(fd); 151 152 tz = findZoneinfoFile(buf, size, ZONEINFO_DIR); 153 free(cast(void*) buf); 154 return tz; 155 } 156 157 string findTZ_md(string home_dir) { 158 char* tz; 159 char* javatz = null; 160 char* freetz = null; 161 162 tz = getenv("TZ"); 163 164 if (tz is null || *tz == '\0') { 165 tz = getPlatformTimeZoneID(); 166 freetz = tz; 167 } 168 // writeln("tz : ", tz, " freeTz : ", freetz); 169 if (tz !is null) { 170 /* Ignore preceding ':' */ 171 if (*tz == ':') { 172 tz++; 173 } 174 // #if defined(__linux__) 175 /* Ignore "posix/" prefix on Linux. */ 176 if (strncmp(tz, "posix/", 6) == 0) { 177 tz += 6; 178 } 179 // #endif 180 181 // #if defined(_AIX) 182 // /* On AIX do the platform to Java mapping. */ 183 // javatz = mapPlatformToJavaTimezone(home_dir, tz); 184 // if (freetz !is null) { 185 // free((void *) freetz); 186 // } 187 // #else 188 // #if defined(__solaris__) 189 // /* Solaris might use localtime, so handle it here. */ 190 // if (strcmp(tz, "localtime") == 0) { 191 // javatz = getSolarisDefaultZoneID(); 192 // if (freetz !is null) { 193 // free((void *) freetz); 194 // } 195 // } else 196 // #endif 197 if (freetz is null) { 198 /* strdup if we are still working on getenv result. */ 199 javatz = strdup(tz); 200 } else if (freetz != tz) { 201 /* strdup and free the old buffer, if we moved the pointer. */ 202 javatz = strdup(tz); 203 free(cast(void*) freetz); 204 } else { 205 /* we are good if we already work on a freshly allocated buffer. */ 206 javatz = tz; 207 } 208 // #endif 209 } 210 211 return cast(string)fromStringz(javatz); 212 } 213 214 static char* getZoneName(char* str) { 215 static const char* zidir = "zoneinfo/"; 216 217 char* pos = cast(char*) strstr(cast(const char*) str, zidir); 218 if (pos is null) { 219 return null; 220 } 221 return pos + strlen(zidir); 222 } 223 224 static char* getPathName(const char* dir, const char* name) { 225 char* path; 226 227 path = cast(char*) malloc(strlen(dir) + strlen(name) + 2); 228 if (path is null) { 229 return null; 230 } 231 return strcat(strcat(strcpy(path, dir), "/"), name); 232 } 233 234 static char* findZoneinfoFile(char* buf, size_t size, const char* dir) { 235 DIR* dirp = null; 236 /* struct */ 237 stat_t statbuf; 238 /* struct */ 239 dirent* dp = null; 240 char* pathname = null; 241 int fd = -1; 242 char* dbuf = null; 243 char* tz = null; 244 int res; 245 246 dirp = opendir(dir); 247 if (dirp is null) { 248 return null; 249 } 250 251 while ((dp = readdir(dirp)) != null) { 252 /* 253 * Skip '.' and '..' (and possibly other .* files) 254 */ 255 if (dp.d_name[0] == '.') { 256 continue; 257 } 258 259 /* 260 * Skip "ROC", "posixrules", and "localtime". 261 */ 262 if ((strcmp(dp.d_name.ptr, "ROC") == 0) || (strcmp(dp.d_name.ptr, 263 "posixrules") == 0) || (strcmp(dp.d_name.ptr, "localtime") == 0)) { 264 continue; 265 } 266 267 pathname = getPathName(dir, dp.d_name.ptr); 268 if (pathname is null) { 269 break; 270 } 271 mixin(RESTARTABLE(`stat(pathname, &statbuf)`, "res")); 272 if (res == -1) { 273 break; 274 } 275 276 if (S_ISDIR(statbuf.st_mode)) { 277 tz = findZoneinfoFile(buf, size, pathname); 278 if (tz != null) { 279 break; 280 } 281 } else if (S_ISREG(statbuf.st_mode) && cast(size_t) statbuf.st_size == size) { 282 dbuf = cast(char*) malloc(size); 283 if (dbuf is null) { 284 break; 285 } 286 mixin(RESTARTABLE(`open(pathname, O_RDONLY)`, "fd")); 287 if (fd == -1) { 288 break; 289 } 290 mixin(RESTARTABLE(`cast(int)read(fd, dbuf, size)`, "res")); 291 if (res != cast(ssize_t) size) { 292 break; 293 } 294 if (memcmp(buf, dbuf, size) == 0) { 295 tz = getZoneName(pathname); 296 if (tz != null) { 297 tz = strdup(tz); 298 } 299 break; 300 } 301 free(cast(void*) dbuf); 302 dbuf = null; 303 /* (void) */ 304 close(fd); 305 fd = -1; 306 } 307 free(cast(void*) pathname); 308 pathname = null; 309 } 310 311 if (dirp != null) { 312 /* (void) */ 313 closedir(dirp); 314 } 315 if (pathname != null) { 316 free(cast(void*) pathname); 317 } 318 if (fd != -1) { 319 /* (void) */ 320 close(fd); 321 } 322 if (dbuf != null) { 323 free(cast(void*) dbuf); 324 } 325 return tz; 326 } 327 } 328 329 version (Windows) { 330 import core.sys.windows.wtypes; 331 import core.sys.windows.windows; 332 import core.sys.windows.w32api; 333 334 // import core.sys.windows.; 335 import core.stdc.stdio; 336 import core.stdc.stdlib; 337 import core.stdc.string; 338 import core.stdc.time; 339 import core.stdc.wchar_; 340 import core.sys.windows.winnls; 341 import core.sys.windows.winbase; 342 import std.string; 343 344 enum int VALUE_UNKNOWN = 0; 345 enum int VALUE_KEY = 1; 346 enum int VALUE_MAPID = 2; 347 enum int VALUE_GMTOFFSET = 3; 348 349 enum int MAX_ZONE_CHAR = 256; 350 enum int MAX_MAPID_LENGTH = 32; 351 enum int MAX_REGION_LENGTH = 4; 352 353 enum string NT_TZ_KEY = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"; 354 enum string WIN_TZ_KEY = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones"; 355 enum string WIN_CURRENT_TZ_KEY = "System\\CurrentControlSet\\Control\\TimeZoneInformation"; 356 357 struct TziValue { 358 LONG bias; 359 LONG stdBias; 360 LONG dstBias; 361 SYSTEMTIME stdDate; 362 SYSTEMTIME dstDate; 363 }; 364 365 /* 366 * Registry key names 367 */ 368 static string[] keyNames = [ 369 ("StandardName"), ("StandardName"), ("Std"), ("Std") 370 ]; 371 372 /* 373 * Indices to keyNames[] 374 */ 375 enum STANDARD_NAME = 0; 376 enum STD_NAME = 2; 377 378 /* 379 * Calls RegQueryValueEx() to get the value for the specified key. If 380 * the platform is NT, 2000 or XP, it calls the Unicode 381 * version. Otherwise, it calls the ANSI version and converts the 382 * value to Unicode. In this case, it assumes that the current ANSI 383 * Code Page is the same as the native platform code page (e.g., Code 384 * Page 932 for the Japanese Windows systems. 385 * 386 * `keyIndex' is an index value to the keyNames in Unicode 387 * (WCHAR). `keyIndex' + 1 points to its ANSI value. 388 * 389 * Returns the status value. ERROR_SUCCESS if succeeded, a 390 * non-ERROR_SUCCESS value otherwise. 391 */ 392 static LONG getValueInRegistry(HKEY hKey, int keyIndex, LPDWORD typePtr, 393 LPBYTE buf, LPDWORD bufLengthPtr) { 394 LONG ret; 395 DWORD bufLength = *bufLengthPtr; 396 char[MAX_ZONE_CHAR] val; 397 DWORD valSize; 398 int len; 399 400 *typePtr = 0; 401 ret = RegQueryValueExW(hKey, cast(WCHAR*) keyNames[keyIndex], null, 402 typePtr, buf, bufLengthPtr); 403 if (ret == ERROR_SUCCESS && *typePtr == REG_SZ) { 404 return ret; 405 } 406 407 valSize = (val.sizeof); 408 ret = RegQueryValueExA(hKey, cast(char*) keyNames[keyIndex + 1], null, 409 typePtr, val.ptr, &valSize); 410 if (ret != ERROR_SUCCESS) { 411 return ret; 412 } 413 if (*typePtr != REG_SZ) { 414 return ERROR_BADKEY; 415 } 416 417 len = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, 418 cast(LPCSTR) val, -1, cast(LPWSTR) buf, bufLength / (WCHAR.sizeof)); 419 if (len <= 0) { 420 return ERROR_BADKEY; 421 } 422 return ERROR_SUCCESS; 423 } 424 425 /* 426 * Produces custom name "GMT+hh:mm" from the given bias in buffer. 427 */ 428 static void customZoneName(LONG bias, char* buffer) { 429 LONG gmtOffset; 430 int sign; 431 432 if (bias > 0) { 433 gmtOffset = bias; 434 sign = -1; 435 } else { 436 gmtOffset = -bias; 437 sign = 1; 438 } 439 if (gmtOffset != 0) { 440 sprintf(buffer, "GMT%c%02d:%02d", ((sign >= 0) ? '+' : '-'), 441 gmtOffset / 60, gmtOffset % 60); 442 } else { 443 strcpy(buffer, "GMT"); 444 } 445 } 446 447 /* 448 * Gets the current time zone entry in the "Time Zones" registry. 449 */ 450 static int getWinTimeZone(char* winZoneName) { 451 // DYNAMIC_TIME_ZONE_INFORMATION dtzi; 452 DWORD timeType; 453 DWORD bufSize; 454 DWORD val; 455 HANDLE hKey = null; 456 LONG ret; 457 ULONG valueType; 458 459 /* 460 * Get the dynamic time zone information so that time zone redirection 461 * can be supported. (see JDK-7044727) 462 */ 463 // timeType = GetDynamicTimeZoneInformation(&dtzi); 464 // if (timeType == TIME_ZONE_ID_INVALID) 465 // { 466 // goto err; 467 // } 468 469 /* 470 * Make sure TimeZoneKeyName is available from the API call. If 471 * DynamicDaylightTime is disabled, return a custom time zone name 472 * based on the GMT offset. Otherwise, return the TimeZoneKeyName 473 * value. 474 */ 475 // if (dtzi.TimeZoneKeyName[0] != 0) 476 // { 477 // if (dtzi.DynamicDaylightTimeDisabled) 478 // { 479 // customZoneName(dtzi.Bias, winZoneName); 480 // return VALUE_GMTOFFSET; 481 // } 482 // wcstombs(winZoneName, dtzi.TimeZoneKeyName, MAX_ZONE_CHAR); 483 // return VALUE_KEY; 484 // } 485 486 /* 487 * If TimeZoneKeyName is not available, check whether StandardName 488 * is available to fall back to the older API GetTimeZoneInformation. 489 * If not, directly read the value from registry keys. 490 */ 491 // if (dtzi.StandardName[0] == 0) 492 // { 493 // ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0, KEY_READ, cast(PHKEY)&hKey); 494 // if (ret != ERROR_SUCCESS) 495 // { 496 // goto err; 497 // } 498 499 // /* 500 // * Determine if auto-daylight time adjustment is turned off. 501 // */ 502 // bufSize = (val.sizeof); 503 // ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", null, 504 // &valueType, cast(LPBYTE)&val, &bufSize); 505 // if (ret != ERROR_SUCCESS) 506 // { 507 // goto err; 508 // } 509 // /* 510 // * Return a custom time zone name if auto-daylight time adjustment 511 // * is disabled. 512 // */ 513 // if (val == 1) 514 // { 515 // customZoneName(dtzi.Bias, winZoneName); 516 // /* (void) */ 517 // RegCloseKey(hKey); 518 // return VALUE_GMTOFFSET; 519 // } 520 521 // bufSize = MAX_ZONE_CHAR; 522 // ret = RegQueryValueExA(hKey, "TimeZoneKeyName", null, &valueType, 523 // cast(LPBYTE) winZoneName, &bufSize); 524 // if (ret != ERROR_SUCCESS) 525 // { 526 // goto err; 527 // } 528 // /* (void) */ 529 // RegCloseKey(hKey); 530 // return VALUE_KEY; 531 // } 532 // else 533 { 534 /* 535 * Fall back to GetTimeZoneInformation 536 */ 537 TIME_ZONE_INFORMATION tzi; 538 HANDLE hSubKey = null; 539 DWORD nSubKeys, i; 540 ULONG valueType2; 541 TCHAR[MAX_ZONE_CHAR] subKeyName; 542 TCHAR[MAX_ZONE_CHAR] szValue; 543 WCHAR[MAX_ZONE_CHAR] stdNameInReg; 544 TziValue tempTzi; 545 WCHAR* stdNamePtr = tzi.StandardName.ptr; 546 int onlyMapID; 547 548 timeType = GetTimeZoneInformation(&tzi); 549 if (timeType == TIME_ZONE_ID_INVALID) { 550 goto err; 551 } 552 553 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0, 554 KEY_READ, cast(PHKEY)&hKey); 555 if (ret == ERROR_SUCCESS) { 556 /* 557 * Determine if auto-daylight time adjustment is turned off. 558 */ 559 bufSize = (val.sizeof); 560 ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", 561 null, &valueType2, cast(LPBYTE)&val, &bufSize); 562 if (ret == ERROR_SUCCESS) { 563 if (val == 1 && tzi.DaylightDate.wMonth != 0) { 564 /* (void) */ 565 RegCloseKey(hKey); 566 customZoneName(tzi.Bias, winZoneName); 567 return VALUE_GMTOFFSET; 568 } 569 } 570 571 /* 572 * Win32 problem: If the length of the standard time name is equal 573 * to (or probably longer than) 32 in the registry, 574 * GetTimeZoneInformation() on NT returns a null string as its 575 * standard time name. We need to work around this problem by 576 * getting the same information from the TimeZoneInformation 577 * registry. 578 */ 579 if (tzi.StandardName[0] == 0) { 580 bufSize = (stdNameInReg.sizeof); 581 ret = getValueInRegistry(hKey, STANDARD_NAME, &valueType2, 582 cast(LPBYTE) stdNameInReg, &bufSize); 583 if (ret != ERROR_SUCCESS) { 584 goto err; 585 } 586 stdNamePtr = stdNameInReg.ptr; 587 } 588 /* (void) */ 589 RegCloseKey(hKey); 590 } 591 592 /* 593 * Open the "Time Zones" registry. 594 */ 595 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NT_TZ_KEY, 0, KEY_READ, cast(PHKEY)&hKey); 596 if (ret != ERROR_SUCCESS) { 597 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_TZ_KEY, 0, KEY_READ, cast(PHKEY)&hKey); 598 /* 599 * If both failed, then give up. 600 */ 601 if (ret != ERROR_SUCCESS) { 602 return VALUE_UNKNOWN; 603 } 604 } 605 606 /* 607 * Get the number of subkeys of the "Time Zones" registry for 608 * enumeration. 609 */ 610 ret = RegQueryInfoKey(hKey, null, null, null, &nSubKeys, null, 611 null, null, null, null, null, null); 612 if (ret != ERROR_SUCCESS) { 613 goto err; 614 } 615 616 /* 617 * Compare to the "Std" value of each subkey and find the entry that 618 * matches the current control panel setting. 619 */ 620 onlyMapID = 0; 621 for (i = 0; i < nSubKeys; ++i) { 622 DWORD size = (subKeyName.sizeof); 623 ret = RegEnumKeyEx(hKey, i, subKeyName.ptr, &size, null, null, null, null); 624 if (ret != ERROR_SUCCESS) { 625 goto err; 626 } 627 ret = RegOpenKeyEx(hKey, subKeyName.ptr, 0, KEY_READ, cast(PHKEY)&hSubKey); 628 if (ret != ERROR_SUCCESS) { 629 goto err; 630 } 631 632 size = (szValue.sizeof); 633 ret = getValueInRegistry(hSubKey, STD_NAME, &valueType, 634 cast(ubyte*)(szValue.ptr), &size); 635 if (ret != ERROR_SUCCESS) { 636 /* 637 * NT 4.0 SP3 fails here since it doesn't have the "Std" 638 * entry in the Time Zones registry. 639 */ 640 RegCloseKey(hSubKey); 641 onlyMapID = 1; 642 ret = RegOpenKeyExW(hKey, stdNamePtr, 0, KEY_READ, cast(PHKEY)&hSubKey); 643 if (ret != ERROR_SUCCESS) { 644 goto err; 645 } 646 break; 647 } 648 649 if (wcscmp(cast(WCHAR*) szValue, stdNamePtr) == 0) { 650 /* 651 * Some localized Win32 platforms use a same name to 652 * different time zones. So, we can't rely only on the name 653 * here. We need to check GMT offsets and transition dates 654 * to make sure it's the registry of the current time 655 * zone. 656 */ 657 DWORD tziValueSize = (tempTzi.sizeof); 658 ret = RegQueryValueEx(hSubKey, "TZI", null, &valueType, 659 cast(char*)&tempTzi, &tziValueSize); 660 if (ret == ERROR_SUCCESS) { 661 if ((tzi.Bias != tempTzi.bias) 662 || (memcmp(cast(const void*)&tzi.StandardDate, 663 cast(const void*)&tempTzi.stdDate, (SYSTEMTIME.sizeof)) != 0)) { 664 goto exitout; 665 } 666 667 if (tzi.DaylightBias != 0) { 668 if ((tzi.DaylightBias != tempTzi.dstBias) 669 || (memcmp(cast(const void*)&tzi.DaylightDate, 670 cast(const void*)&tempTzi.dstDate, (SYSTEMTIME.sizeof)) != 0)) { 671 goto exitout; 672 } 673 } 674 } 675 676 /* 677 * found matched record, terminate search 678 */ 679 strcpy(winZoneName, cast(const char*)(subKeyName.ptr)); 680 break; 681 } 682 exitout: /* (void) */ 683 RegCloseKey(hSubKey); 684 } 685 686 /* (void) */ 687 RegCloseKey(hKey); 688 } 689 690 return VALUE_KEY; 691 692 err: 693 if (hKey != null) { 694 /* (void) */ 695 RegCloseKey(hKey); 696 } 697 return VALUE_UNKNOWN; 698 } 699 700 /* 701 * The mapping table file name. 702 */ 703 enum string MAPPINGS_FILE = "\\lib\\tzmappings"; 704 705 /* 706 * Index values for the mapping table. 707 */ 708 enum int TZ_WIN_NAME = 0; 709 enum int TZ_REGION = 1; 710 enum int TZ_JAVA_NAME = 2; 711 712 enum int TZ_NITEMS = 3; /* number of items (fields) */ 713 714 /* 715 * Looks up the mapping table (tzmappings) and returns a Java time 716 * zone ID (e.g., "America/Los_Angeles") if found. Otherwise, null is 717 * returned. 718 */ 719 private static char* matchJavaTZ(const char* home_dir, char* tzName) { 720 int line; 721 int IDmatched = 0; 722 FILE* fp; 723 char* javaTZName = null; 724 char*[TZ_NITEMS] items; 725 char* mapFileName; 726 char[MAX_ZONE_CHAR * 4] lineBuffer; 727 int offset = 0; 728 char* errorMessage = cast(char*) toStringz("unknown error"); 729 char[MAX_REGION_LENGTH] region; 730 731 // Get the user's location 732 if (GetGeoInfo(GetUserGeoID(SYSGEOCLASS.GEOCLASS_NATION), 733 SYSGEOTYPE.GEO_ISO2, cast(wchar*) region.ptr, MAX_REGION_LENGTH, 0) == 0) { 734 // If GetGeoInfo fails, fallback to LCID's country 735 LCID lcid = GetUserDefaultLCID(); 736 if (GetLocaleInfo(lcid, LOCALE_SISO3166CTRYNAME, 737 cast(wchar*) region.ptr, MAX_REGION_LENGTH) == 0 /* && GetLocaleInfo(lcid, LOCALE_SISO3166CTRYNAME2, cast(wchar*)region.ptr, MAX_REGION_LENGTH) == 0 */ 738 ) { 739 region[0] = '\0'; 740 } 741 } 742 743 mapFileName = cast(char*) malloc(strlen(home_dir) + strlen(MAPPINGS_FILE) + 1); 744 if (mapFileName == null) { 745 return null; 746 } 747 strcpy(mapFileName, home_dir); 748 strcat(mapFileName, MAPPINGS_FILE); 749 750 if ((fp = fopen(mapFileName, "r")) == null) { 751 // jio_fprintf(stderr, "can't open %s.\n", mapFileName); 752 free(cast(void*) mapFileName); 753 return null; 754 } 755 free(cast(void*) mapFileName); 756 757 line = 0; 758 while (fgets(lineBuffer.ptr, (lineBuffer.sizeof), fp) != null) { 759 char* start; 760 char* idx; 761 char* endp; 762 int itemIndex = 0; 763 764 line++; 765 start = idx = lineBuffer.ptr; 766 endp = &lineBuffer[(lineBuffer.length - 1)]; ///@gxc 767 768 /* 769 * Ignore comment and blank lines. 770 */ 771 if (*idx == '#' || *idx == '\n') { 772 continue; 773 } 774 775 for (itemIndex = 0; itemIndex < TZ_NITEMS; itemIndex++) { 776 items[itemIndex] = start; 777 while (*idx && *idx != ':') { 778 if (++idx >= endp) { 779 errorMessage = cast(char*) toStringz("premature end of line"); 780 offset = cast(int)(idx - lineBuffer.ptr); 781 goto illegal_format; 782 } 783 } 784 if (*idx == '\0') { 785 errorMessage = cast(char*) toStringz("illegal null character found"); 786 offset = cast(int)(idx - lineBuffer.ptr); 787 goto illegal_format; 788 } 789 *idx++ = '\0'; 790 start = idx; 791 } 792 793 if (*idx != '\n') { 794 errorMessage = cast(char*) toStringz("illegal non-newline character found"); 795 offset = cast(int)(idx - lineBuffer.ptr); 796 goto illegal_format; 797 } 798 799 /* 800 * We need to scan items until the 801 * exact match is found or the end of data is detected. 802 */ 803 if (strcmp(items[TZ_WIN_NAME], tzName) == 0) { 804 /* 805 * Found the time zone in the mapping table. 806 * Check the region code and select the appropriate entry 807 */ 808 if (strcmp(items[TZ_REGION], region.ptr) == 0 || strcmp(items[TZ_REGION], "001") == 0) { 809 javaTZName = strdup(items[TZ_JAVA_NAME]); 810 break; 811 } 812 } 813 } 814 fclose(fp); 815 816 return javaTZName; 817 818 illegal_format: 819 /* (void) */ 820 fclose(fp); 821 // jio_fprintf(stderr, "Illegal format in tzmappings file: %s at line %d, offset %d.\n", 822 // errorMessage, line, offset); 823 return null; 824 } 825 826 /* 827 * Detects the platform time zone which maps to a Java time zone ID. 828 */ 829 string findTZ_md(string home_dir) { 830 char[MAX_ZONE_CHAR] winZoneName; 831 char* std_timezone = null; 832 int result; 833 834 result = getWinTimeZone(winZoneName.ptr); 835 836 if (result != VALUE_UNKNOWN) { 837 if (result == VALUE_GMTOFFSET) { 838 std_timezone = strdup(winZoneName.ptr); 839 } else { 840 std_timezone = matchJavaTZ(home_dir.toStringz(), winZoneName.ptr); 841 if (std_timezone == null) { 842 std_timezone = getGMTOffsetID(); 843 } 844 } 845 } 846 return cast(string)fromStringz(std_timezone); 847 } 848 849 /** 850 * Returns a GMT-offset-based time zone ID. 851 */ 852 char* getGMTOffsetID() { 853 LONG bias = 0; 854 LONG ret; 855 HANDLE hKey = null; 856 char[32] zonename; 857 858 // Obtain the current GMT offset value of ActiveTimeBias. 859 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0, KEY_READ, cast(PHKEY)&hKey); 860 if (ret == ERROR_SUCCESS) { 861 DWORD val; 862 DWORD bufSize = (val.sizeof); 863 ULONG valueType = 0; 864 ret = RegQueryValueExA(hKey, "ActiveTimeBias", null, &valueType, 865 cast(LPBYTE)&val, &bufSize); 866 if (ret == ERROR_SUCCESS) { 867 bias = cast(LONG) val; 868 } 869 cast(void) RegCloseKey(hKey); 870 } 871 872 // If we can't get the ActiveTimeBias value, use Bias of TimeZoneInformation. 873 // Note: Bias doesn't reflect current daylight saving. 874 if (ret != ERROR_SUCCESS) { 875 TIME_ZONE_INFORMATION tzi; 876 if (GetTimeZoneInformation(&tzi) != TIME_ZONE_ID_INVALID) { 877 bias = tzi.Bias; 878 } 879 } 880 881 customZoneName(bias, zonename.ptr); 882 return strdup(zonename.ptr); 883 } 884 885 }