blob: fa8d87434e7d4df1f806ba2935760ddf7c32bd0a [file] [log] [blame]
/*
* R : A Computer Language for Statistical Data Analysis
* Copyright (C) 2007-2018 The R Core Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, a copy is available at
* https://www.R-project.org/Licenses/
*/
#include <string.h>
/*
From http://unicode.org/cldr/data/diff/supplemental/windows_tzid.html
Added some entries from the XP Registry (and checked on Vista).
Table updated from Unicode in August 2015.
*/
const static struct {
const wchar_t * const reg;
const char * const Olson;
} TZtable[] = {
{ L"Afghanistan Standard Time", "Asia/Kabul" },
{ L"Alaskan Standard Time", "America/Anchorage" },
{ L"Arab Standard Time", "Asia/Riyadh" },
{ L"Arabian Standard Time", "Asia/Bahrain" },
{ L"Arabic Standard Time", "Asia/Baghdad" },
{ L"Armenian Standard Time", "Asia/Yerevan" },
{ L"Atlantic Standard Time", "America/Curacao" },
{ L"AUS Central Standard Time", "Australia/Darwin" },
{ L"AUS Eastern Standard Time", "Australia/Sydney" },
{ L"Azerbaijan Standard Time", "Asia/Baku" },
{ L"Azores Standard Time", "Atlantic/Azores" },
{ L"Canada Central Standard Time", "America/Regina" },
{ L"Cape Verde Standard Time", "Atlantic/Cape_Verde" },
{ L"Caucasus Standard Time", "Asia/Yerevan" },
{ L"Cen. Australia Standard Time", "Australia/Adelaide" },
{ L"Central America Standard Time", "America/Guatemala" },
{ L"Central Asia Standard Time", "Asia/Dhaka" },
{ L"Central Brazilian Standard Time", "America/Manaus" },
{ L"Central Europe Standard Time", "Europe/Prague" },
{ L"Central European Standard Time", "Europe/Warsaw" },
{ L"Central Pacific Standard Time", "Pacific/Guadalcanal" },
{ L"Central Standard Time", "America/Chicago" },
{ L"Central Standard Time (Mexico)", "America/Mexico_City" },
{ L"China Standard Time", "Asia/Taipei" },
{ L"Dateline Standard Time", "Pacific/Kwajalein" },
{ L"E. Africa Standard Time", "Africa/Nairobi" },
{ L"E. Australia Standard Time", "Australia/Brisbane" },
{ L"E. Europe Standard Time", "Europe/Minsk" },
{ L"E. South America Standard Time", "America/Sao_Paulo" },
{ L"Eastern Standard Time", "America/New_York" },
{ L"Egypt Standard Time", "Africa/Cairo" },
{ L"Ekaterinburg Standard Time", "Asia/Yekaterinburg" },
{ L"Fiji Standard Time", "Pacific/Fiji" },
{ L"FLE Standard Time", "Europe/Helsinki" },
{ L"Georgian Standard Time", "Asia/Tbilisi" },
{ L"GMT Standard Time", "Europe/London" },
{ L"Greenland Standard Time", "America/Godthab" },
{ L"Greenwich Standard Time", "Africa/Casablanca" },
{ L"GTB Standard Time", "Europe/Istanbul" },
{ L"Hawaiian Standard Time", "Pacific/Honolulu" },
{ L"India Standard Time", "Asia/Calcutta" },
{ L"Iran Standard Time", "Asia/Tehran" },
/* next is name of timezone, not of Std */
{ L"Israel Standard Time", "Asia/Jerusalem" },
{ L"Jerusalem Standard Time", "Asia/Jerusalem" },
{ L"Jordan Standard Time", "Asia/Amman" },
{ L"Korea Standard Time", "Asia/Seoul" },
{ L"Malay Peninsula Standard Time", "Asia/Kuala_Lumpur" },
{ L"Mexico Standard Time", "America/Mexico_City" },
{ L"Mexico Standard Time 2", "America/Chihuahua" },
{ L"Mid-Atlantic Standard Time", "America/Noronha" },
{ L"Middle East Standard Time", "Asia/Beirut" },
{ L"Montevideo Standard Time", "America/Montevideo" },
{ L"Mountain Standard Time", "America/Denver" },
{ L"Mountain Standard Time (Mexico)", "America/Chihuahua" },
{ L"Myanmar Standard Time", "Asia/Rangoon" },
{ L"N. Central Asia Standard Time", "Asia/Novosibirsk" },
{ L"Namibia Standard Time", "Africa/Windhoek" },
{ L"Nepal Standard Time", "Asia/Katmandu" },
{ L"New Zealand Standard Time", "Pacific/Auckland" },
{ L"Newfoundland Standard Time", "America/St_Johns" },
{ L"North Asia East Standard Time", "Asia/Ulaanbaatar" },
{ L"North Asia Standard Time", "Asia/Krasnoyarsk" },
{ L"Pacific SA Standard Time", "America/Santiago" },
{ L"Pacific Standard Time", "America/Los_Angeles" },
{ L"Pacific Standard Time (Mexico)", "America/Tijuana" },
{ L"Romance Standard Time", "Europe/Paris" },
{ L"Russian Standard Time", "Europe/Moscow" },
{ L"SA Eastern Standard Time", "America/Buenos_Aires" },
{ L"SA Pacific Standard Time", "America/Bogota" },
{ L"SA Western Standard Time", "America/Caracas" },
{ L"Samoa Standard Time", "Pacific/Apia" },
{ L"SE Asia Standard Time", "Asia/Bangkok" },
/* next is name of timezone, not of Std */
{ L"Singapore Standard Time", "Asia/Kuala_Lumpur" },
{ L"South Africa Standard Time", "Africa/Johannesburg" },
{ L"Sri Lanka Standard Time", "Asia/Colombo" },
{ L"Taipei Standard Time", "Asia/Taipei" },
{ L"Tasmania Standard Time", "Australia/Hobart" },
{ L"Tokyo Standard Time", "Asia/Tokyo" },
{ L"Tonga Standard Time", "Pacific/Tongatapu" },
{ L"US Eastern Standard Time", "America/Indianapolis" },
{ L"US Mountain Standard Time", "America/Phoenix" },
{ L"Venezuela Standard Time", "America/Caracas" },
{ L"Vladivostok Standard Time", "Asia/Vladivostok" },
{ L"W. Australia Standard Time", "Australia/Perth" },
{ L"W. Central Africa Standard Time", "Africa/Lagos" },
{ L"W. Europe Standard Time", "Europe/Berlin" },
{ L"West Asia Standard Time", "Asia/Karachi" },
{ L"West Pacific Standard Time", "Pacific/Guam" },
{ L"Yakutsk Standard Time", "Asia/Yakutsk" },
/* The ones below do not occur on BDR's XP machine */
{ L"Acre Standard Time", "America/Rio_Branco" },
{ L"Africa_Central Standard Time", "Africa/Kigali" },
{ L"Africa_Eastern Standard Time", "Africa/Kampala" },
{ L"Africa_FarWestern Standard Time", "Africa/El_Aaiun" },
{ L"Africa_Southern Standard Time", "Africa/Johannesburg" },
{ L"Africa_Western Standard Time", "Africa/Niamey" },
{ L"Aktyubinsk Standard Time", "Asia/Aqtobe" },
{ L"Alaska Standard Time", "America/Juneau" },
{ L"Alaska_Hawaii Standard Time", "America/Anchorage" },
{ L"Almaty Standard Time", "Asia/Almaty" },
{ L"Amazon Standard Time", "America/Manaus" },
{ L"America_Central Standard Time", "America/Winnipeg" },
{ L"America_Eastern Standard Time", "America/Panama" },
{ L"America_Mountain Standard Time", "America/Edmonton" },
{ L"America_Pacific Standard Time", "America/Vancouver" },
{ L"Anadyr Standard Time", "Asia/Anadyr" },
{ L"Aqtau Standard Time", "Asia/Aqtau" },
{ L"Aqtobe Standard Time", "Asia/Aqtobe" },
{ L"Argentina Standard Time", "America/Buenos_Aires" },
{ L"Argentina_Western Standard Time", "America/Mendoza" },
{ L"Armenia Standard Time", "Asia/Yerevan" },
{ L"Ashkhabad Standard Time", "Asia/Ashgabat" },
{ L"Australia_Central Standard Time", "Australia/Adelaide" },
{ L"Australia_CentralWestern Standard Time", "Australia/Eucla" },
{ L"Australia_Eastern Standard Time", "Australia/Sydney" },
{ L"Australia_Western Standard Time", "Australia/Perth" },
{ L"Baku Standard Time", "Asia/Baku" },
{ L"Bangladesh Standard Time", "Asia/Dhaka" },
{ L"Bering Standard Time", "America/Adak" },
{ L"Bhutan Standard Time", "Asia/Thimphu" },
{ L"Bolivia Standard Time", "America/La_Paz" },
{ L"Borneo Standard Time", "Asia/Kuching" },
{ L"Brasilia Standard Time", "America/Sao_Paulo" },
{ L"British Standard Time", "Europe/London" },
{ L"Brunei Standard Time", "Asia/Brunei" },
{ L"Chamorro Standard Time", "Pacific/Guam" },
{ L"Changbai Standard Time", "Asia/Harbin" },
{ L"Chatham Standard Time", "Pacific/Chatham" },
{ L"Chile Standard Time", "America/Santiago" },
{ L"Choibalsan Standard Time", "Asia/Choibalsan" },
{ L"Christmas Standard Time", "Indian/Christmas" },
{ L"Cocos Standard Time", "Indian/Cocos" },
{ L"Colombia Standard Time", "America/Bogota" },
{ L"Cook Standard Time", "Pacific/Rarotonga" },
{ L"Cuba Standard Time", "America/Havana" },
{ L"Dacca Standard Time", "Asia/Dhaka" },
{ L"Davis Standard Time", "Antarctica/Davis" },
{ L"Dominican Standard Time", "America/Santo_Domingo" },
{ L"DumontDUrville Standard Time", "Antarctica/DumontDUrville" },
{ L"Dushanbe Standard Time", "Asia/Dushanbe" },
{ L"Dutch_Guiana Standard Time", "America/Paramaribo" },
{ L"East_Timor Standard Time", "Asia/Dili" },
{ L"Easter Standard Time", "Pacific/Easter" },
{ L"Ecuador Standard Time", "America/Guayaquil" },
{ L"Europe_Central Standard Time", "Europe/Oslo" },
{ L"Europe_Eastern Standard Time", "Europe/Vilnius" },
{ L"Europe_Western Standard Time", "Atlantic/Canary" },
{ L"Falkland Standard Time", "Atlantic/Stanley" },
{ L"French_Guiana Standard Time", "America/Cayenne" },
{ L"French_Southern Standard Time", "Indian/Kerguelen" },
{ L"Frunze Standard Time", "Asia/Bishkek" },
{ L"Galapagos Standard Time", "Pacific/Galapagos" },
{ L"Gambier Standard Time", "Pacific/Gambier" },
{ L"Georgia Standard Time", "Asia/Tbilisi" },
{ L"Gilbert_Islands Standard Time", "Pacific/Tarawa" },
{ L"Goose_Bay Standard Time", "America/Goose_Bay" },
{ L"Greenland_Central Standard Time", "America/Scoresbysund" },
{ L"Greenland_Eastern Standard Time", "America/Scoresbysund" },
{ L"Greenland_Western Standard Time", "America/Godthab" },
{ L"Guam Standard Time", "Pacific/Guam" },
{ L"Gulf Standard Time", "Asia/Muscat" },
{ L"Guyana Standard Time", "America/Guyana" },
{ L"Hawaii_Aleutian Standard Time", "Pacific/Honolulu" },
{ L"Hong_Kong Standard Time", "Asia/Hong_Kong" },
{ L"Hovd Standard Time", "Asia/Hovd" },
{ L"Indian_Ocean Standard Time", "Indian/Chagos" },
{ L"Indochina Standard Time", "Asia/Vientiane" },
{ L"Indonesia_Central Standard Time", "Asia/Makassar" },
{ L"Indonesia_Eastern Standard Time", "Asia/Jayapura" },
{ L"Indonesia_Western Standard Time", "Asia/Jakarta" },
{ L"Irish Standard Time", "Europe/Dublin" },
{ L"Irkutsk Standard Time", "Asia/Irkutsk" },
{ L"Japan Standard Time", "Asia/Tokyo" },
{ L"Kamchatka Standard Time", "Asia/Kamchatka" },
{ L"Karachi Standard Time", "Asia/Karachi" },
{ L"Kashgar Standard Time", "Asia/Kashgar" },
{ L"Kazakhstan_Eastern Standard Time", "Asia/Almaty" },
{ L"Kazakhstan_Western Standard Time", "Asia/Aqtobe" },
{ L"Kizilorda Standard Time", "Asia/Qyzylorda" },
{ L"Kosrae Standard Time", "Pacific/Kosrae" },
{ L"Krasnoyarsk Standard Time", "Asia/Krasnoyarsk" },
{ L"Kuybyshev Standard Time", "Europe/Samara" },
{ L"Kwajalein Standard Time", "Pacific/Kwajalein" },
{ L"Kyrgystan Standard Time", "Asia/Bishkek" },
{ L"Lanka Standard Time", "Asia/Colombo" },
{ L"Liberia Standard Time", "Africa/Monrovia" },
{ L"Line_Islands Standard Time", "Pacific/Kiritimati" },
{ L"Long_Shu Standard Time", "Asia/Chongqing" },
{ L"Lord_Howe Standard Time", "Australia/Lord_Howe" },
{ L"Macau Standard Time", "Asia/Macau" },
{ L"Magadan Standard Time", "Asia/Magadan" },
{ L"Malaya Standard Time", "Asia/Kuala_Lumpur" },
{ L"Malaysia Standard Time", "Asia/Kuching" },
{ L"Maldives Standard Time", "Indian/Maldives" },
{ L"Marquesas Standard Time", "Pacific/Marquesas" },
{ L"Marshall_Islands Standard Time", "Pacific/Majuro" },
{ L"Mauritius Standard Time", "Indian/Mauritius" },
{ L"Mawson Standard Time", "Antarctica/Mawson" },
{ L"Mongolia Standard Time", "Asia/Ulaanbaatar" },
{ L"Moscow Standard Time", "Europe/Moscow" },
{ L"Nauru Standard Time", "Pacific/Nauru" },
{ L"New_Caledonia Standard Time", "Pacific/Noumea" },
{ L"New_Zealand Standard Time", "Pacific/Auckland" },
{ L"Niue Standard Time", "Pacific/Niue" },
{ L"Norfolk Standard Time", "Pacific/Norfolk" },
{ L"Noronha Standard Time", "America/Noronha" },
{ L"North_Mariana Standard Time", "Pacific/Saipan" },
{ L"Novosibirsk Standard Time", "Asia/Novosibirsk" },
{ L"Omsk Standard Time", "Asia/Omsk" },
{ L"Oral Standard Time", "Asia/Oral" },
{ L"Pakistan Standard Time", "Asia/Karachi" },
{ L"Palau Standard Time", "Pacific/Palau" },
{ L"Papua_New_Guinea Standard Time", "Pacific/Port_Moresby" },
{ L"Paraguay Standard Time", "America/Asuncion" },
{ L"Peru Standard Time", "America/Lima" },
{ L"Philippines Standard Time", "Asia/Manila" },
{ L"Phoenix_Islands Standard Time", "Pacific/Enderbury" },
{ L"Pierre_Miquelon Standard Time", "America/Miquelon" },
{ L"Pitcairn Standard Time", "Pacific/Pitcairn" },
{ L"Ponape Standard Time", "Pacific/Ponape" },
{ L"Qyzylorda Standard Time", "Asia/Qyzylorda" },
{ L"Reunion Standard Time", "Indian/Reunion" },
{ L"Rothera Standard Time", "Antarctica/Rothera" },
{ L"Sakhalin Standard Time", "Asia/Sakhalin" },
{ L"Samara Standard Time", "Europe/Samara" },
{ L"Samarkand Standard Time", "Asia/Samarkand" },
{ L"Seychelles Standard Time", "Indian/Mahe" },
{ L"Shevchenko Standard Time", "Asia/Aqtau" },
{ L"Solomon Standard Time", "Pacific/Guadalcanal" },
{ L"South_Georgia Standard Time", "Atlantic/South_Georgia" },
{ L"Suriname Standard Time", "America/Paramaribo" },
{ L"Sverdlovsk Standard Time", "Asia/Yekaterinburg" },
{ L"Syowa Standard Time", "Antarctica/Syowa" },
{ L"Tahiti Standard Time", "Pacific/Tahiti" },
{ L"Tajikistan Standard Time", "Asia/Dushanbe" },
{ L"Tashkent Standard Time", "Asia/Tashkent" },
{ L"Tbilisi Standard Time", "Asia/Tbilisi" },
{ L"Tokelau Standard Time", "Pacific/Fakaofo" },
{ L"Truk Standard Time", "Pacific/Truk" },
{ L"Turkey Standard Time", "Europe/Istanbul" },
{ L"Turkmenistan Standard Time", "Asia/Ashgabat" },
{ L"Tuvalu Standard Time", "Pacific/Funafuti" },
{ L"Uralsk Standard Time", "Asia/Oral" },
{ L"Uruguay Standard Time", "America/Montevideo" },
{ L"Urumqi Standard Time", "Asia/Urumqi" },
{ L"Uzbekistan Standard Time", "Asia/Tashkent" },
{ L"Vanuatu Standard Time", "Pacific/Efate" },
{ L"Volgograd Standard Time", "Europe/Volgograd" },
{ L"Vostok Standard Time", "Antarctica/Vostok" },
{ L"Wake Standard Time", "Pacific/Wake" },
{ L"Wallis Standard Time", "Pacific/Wallis" },
{ L"Yekaterinburg Standard Time", "Asia/Yekaterinburg" },
{ L"Yerevan Standard Time", "Asia/Yerevan" },
{ L"Yukon Standard Time", "America/Yakutat" },
/* 2015 additions, seen in then-current Windows 7 */
{ L"Argentina Standard Time", "America/Buenos_Aires" },
{ L"Bahia Standard Time", "America/Bahia" },
{ L"Belarus Standard Time", "Europe/Minsk" },
{ L"Kaliningrad Standard Time", "Europe/Kaliningrad" },
{ L"Kamchatka Standard Time", "Asia/Kamchatka" },
{ L"Libya Standard Time", "Africa/Tripoli" },
{ L"Morocco Standard Time", "Africa/Casablanca" },
{ L"Syria Standard Time", "Asia/Damascus" },
{ L"Ulaanbaatar Standard Time", "Asia/Ulaanbaatar" },
// See https://support.microsoft.com/en-gb/gp/cp_dst
{ L"Russia Time Zone 1", "Europe/Kaliningrad" },
{ L"Russia Time Zone 2", "Europe/Moscow" },
{ L"Russia Time Zone 3", "Europe/Samara" },
{ L"Russia Time Zone 4", "Asia/Yekaterinburg" },
{ L"Russia Time Zone 5", "Asia/Novosibirsk" },
{ L"Russia Time Zone 6", "Asia/Krasnoyarsk" },
{ L"Russia Time Zone 7", "Asia/Irkutsk" },
{ L"Russia Time Zone 8", "Asia/Yakutsk" },
{ L"Russia Time Zone 9", "Asia/Magadan" },
{ L"Russia Time Zone 10", "Asia/Srednekolymsk" },
{ L"Russia Time Zone 11", "Asia/Kamchatka" },
{ NULL, "" }
};
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <wchar.h>
#include <stdlib.h> /* for wcstombs */
/* #define DEBUG */
#ifdef DEBUG
# include <stdio.h>
#endif
extern void Rf_warning(const char *, ...);
/* Since the registry entries can be localized and we might not be
running in the native charset, use Unicode here. */
static wchar_t basekey[] =
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\";
static char tzikey[] =
"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation";
static const char *reg2Olson(const wchar_t *s)
{
int i;
LONG rc;
HKEY hkey;
DWORD type, size;
BYTE regdata[1000]; /* 64 was not enough on Vista */
/* Try for English names first */
for (i = 0; TZtable[i].reg; i++)
if (!wcscmp(s, TZtable[i].reg)) return TZtable[i].Olson;
/* On Vista, the English name of the current timezone is in the registry */
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, tzikey, 0, KEY_QUERY_VALUE, &hkey)
== ERROR_SUCCESS) {
size = 1000;
rc = RegQueryValueExW(hkey, L"TimeZoneKeyName", NULL, &type,
regdata, &size);
RegCloseKey(hkey);
if(rc == ERROR_SUCCESS) {
for (i = 0; TZtable[i].reg; i++)
if (!wcscmp((wchar_t *) regdata, TZtable[i].reg))
return TZtable[i].Olson;
}
}
{
wchar_t keyname[100];
/* That failed, so we are not on English Windows, nor on
Vista, probably. However, the registry time zone key names
are in English and a subset of the above list (and need to
be for use to identify an Olson name). So try them all for
a match to StandardName.
*/
for (i = 0; TZtable[i].reg; i++) {
/* Retrieve
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\TZtable[i].reg\Std
*/
wcscpy(keyname, basekey);
wcscat(keyname, TZtable[i].reg);
rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyname, 0,
KEY_QUERY_VALUE, &hkey);
if (rc != ERROR_SUCCESS) continue;
size = 1000;
rc = RegQueryValueExW(hkey, L"Std", NULL, &type, regdata, &size);
RegCloseKey(hkey);
if (rc != ERROR_SUCCESS) continue;
if (!wcscmp(s, (wchar_t *) regdata)) return TZtable[i].Olson;
}
}
Rf_warning("unable to identify current timezone '%s':\nplease set environment variable 'TZ'", s);
return "unknown";
}
static TIME_ZONE_INFORMATION tzi;
/* Longest currently is 31 chars */
static char StandardName[64], DaylightName[64], Olson[64] = "";
/* Uwe Ligges says these entries are localized in the Windows' base language */
const char *getTZinfo(void)
{
if(!Olson[0]) {
const char *p = getenv("TZ");
if(p) {
strncpy(Olson, p, 63);
Olson[63] = '\0';
} else {
GetTimeZoneInformation(&tzi);
wcstombs(StandardName, tzi.StandardName, 64);
wcstombs(DaylightName, tzi.DaylightName, 64);
strncpy(Olson, reg2Olson(tzi.StandardName), 63);
Olson[63] = '\0';
}
#ifdef DEBUG
printf("names %s, %s\n", StandardName, DaylightName);
printf("TZ = %s\n", Olson);
#endif
}
return Olson;
}