diff options
Diffstat (limited to 'xbmc/platform/posix/PosixTimezone.cpp')
-rw-r--r-- | xbmc/platform/posix/PosixTimezone.cpp | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/xbmc/platform/posix/PosixTimezone.cpp b/xbmc/platform/posix/PosixTimezone.cpp new file mode 100644 index 0000000..6f276a3 --- /dev/null +++ b/xbmc/platform/posix/PosixTimezone.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include <time.h> +#ifdef TARGET_ANDROID +#include "platform/android/bionic_supplement/bionic_supplement.h" +#endif +#include "PlatformDefs.h" +#include "PosixTimezone.h" +#include "utils/SystemInfo.h" + +#include "ServiceBroker.h" +#include "utils/StringUtils.h" +#include "XBDateTime.h" +#include "settings/lib/Setting.h" +#include "settings/lib/SettingDefinitions.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include <stdlib.h> + +#include <algorithm> + +CPosixTimezone::CPosixTimezone() +{ + char* line = NULL; + size_t linelen = 0; + int nameonfourthfield = 0; + std::string s; + std::vector<std::string> tokens; + + // Load timezones + FILE* fp = fopen("/usr/share/zoneinfo/zone.tab", "r"); + if (fp) + { + std::string countryCode; + std::string timezoneName; + + while (getdelim(&line, &linelen, '\n', fp) > 0) + { + tokens.clear(); + s = line; + StringUtils::Trim(s); + + if (s.length() == 0) + continue; + + if (s[0] == '#') + continue; + + StringUtils::Tokenize(s, tokens, " \t"); + if (tokens.size() < 3) + continue; + + countryCode = tokens[0]; + timezoneName = tokens[2]; + + if (m_timezonesByCountryCode.count(countryCode) == 0) + { + std::vector<std::string> timezones; + timezones.push_back(timezoneName); + m_timezonesByCountryCode[countryCode] = timezones; + } + else + { + std::vector<std::string>& timezones = m_timezonesByCountryCode[countryCode]; + timezones.push_back(timezoneName); + } + + m_countriesByTimezoneName[timezoneName] = countryCode; + } + fclose(fp); + } + + if (line) + { + free(line); + line = NULL; + linelen = 0; + } + + // Load countries + fp = fopen("/usr/share/zoneinfo/iso3166.tab", "r"); + if (!fp) + { + fp = fopen("/usr/share/misc/iso3166", "r"); + nameonfourthfield = 1; + } + if (fp) + { + std::string countryCode; + std::string countryName; + + while (getdelim(&line, &linelen, '\n', fp) > 0) + { + s = line; + StringUtils::Trim(s); + + //! @todo STRING_CLEANUP + if (s.length() == 0) + continue; + + if (s[0] == '#') + continue; + + // Search for the first non space from the 2nd character and on + int i = 2; + while (s[i] == ' ' || s[i] == '\t') i++; + + if (nameonfourthfield) + { + // skip three letter + while (s[i] != ' ' && s[i] != '\t') i++; + while (s[i] == ' ' || s[i] == '\t') i++; + // skip number + while (s[i] != ' ' && s[i] != '\t') i++; + while (s[i] == ' ' || s[i] == '\t') i++; + } + + countryCode = s.substr(0, 2); + countryName = s.substr(i); + + m_counties.push_back(countryName); + m_countryByCode[countryCode] = countryName; + m_countryByName[countryName] = countryCode; + } + sort(m_counties.begin(), m_counties.end(), sortstringbyname()); + fclose(fp); + } + free(line); +} + +void CPosixTimezone::OnSettingChanged(const std::shared_ptr<const CSetting>& setting) +{ + if (setting == NULL) + return; + + const std::string &settingId = setting->GetId(); + if (settingId == CSettings::SETTING_LOCALE_TIMEZONE) + { + SetTimezone(std::static_pointer_cast<const CSettingString>(setting)->GetValue()); + + CDateTime::ResetTimezoneBias(); + } + else if (settingId == CSettings::SETTING_LOCALE_TIMEZONECOUNTRY) + { + // nothing to do here. Changing locale.timezonecountry will trigger an + // update of locale.timezone and automatically adjust its value + // and execute OnSettingChanged() for it as well (see above) + } +} + +void CPosixTimezone::OnSettingsLoaded() +{ + SetTimezone(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_TIMEZONE)); + CDateTime::ResetTimezoneBias(); +} + +std::vector<std::string> CPosixTimezone::GetCounties() +{ + return m_counties; +} + +std::vector<std::string> CPosixTimezone::GetTimezonesByCountry(const std::string& country) +{ + return m_timezonesByCountryCode[m_countryByName[country]]; +} + +std::string CPosixTimezone::GetCountryByTimezone(const std::string& timezone) +{ +#if defined(TARGET_DARWIN) + return "?"; +#else + return m_countryByCode[m_countriesByTimezoneName[timezone]]; +#endif +} + +void CPosixTimezone::SetTimezone(const std::string& timezoneName) +{ +#if !defined(TARGET_DARWIN) + bool use_timezone = true; +#else + bool use_timezone = false; +#endif + + if (use_timezone) + { + static char env_var[255]; + sprintf(env_var, "TZ=:%s", timezoneName.c_str()); + putenv(env_var); + tzset(); + } +} + +std::string CPosixTimezone::GetOSConfiguredTimezone() +{ + char timezoneName[255]; + + // try Slackware approach first + ssize_t rlrc = readlink("/etc/localtime-copied-from" + , timezoneName, sizeof(timezoneName)-1); + + // RHEL and maybe other distros make /etc/localtime a symlink + if (rlrc == -1) + rlrc = readlink("/etc/localtime", timezoneName, sizeof(timezoneName)-1); + + if (rlrc != -1) + { + timezoneName[rlrc] = '\0'; + + char* p = strrchr(timezoneName,'/'); + if (p) + { // we want the previous '/' + char* q = p; + *q = 0; + p = strrchr(timezoneName,'/'); + *q = '/'; + if (p) + p++; + } + return p; + } + + // now try Debian approach + timezoneName[0] = 0; + FILE* fp = fopen("/etc/timezone", "r"); + if (fp) + { + if (fgets(timezoneName, sizeof(timezoneName), fp)) + timezoneName[strlen(timezoneName)-1] = '\0'; + fclose(fp); + } + + return timezoneName; +} + +void CPosixTimezone::SettingOptionsTimezoneCountriesFiller( + const std::shared_ptr<const CSetting>& setting, + std::vector<StringSettingOption>& list, + std::string& current, + void* data) +{ + std::vector<std::string> countries = g_timezone.GetCounties(); + for (unsigned int i = 0; i < countries.size(); i++) + list.emplace_back(countries[i], countries[i]); +} + +void CPosixTimezone::SettingOptionsTimezonesFiller(const std::shared_ptr<const CSetting>& setting, + std::vector<StringSettingOption>& list, + std::string& current, + void* data) +{ + current = std::static_pointer_cast<const CSettingString>(setting)->GetValue(); + bool found = false; + std::vector<std::string> timezones = g_timezone.GetTimezonesByCountry(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_TIMEZONECOUNTRY)); + for (unsigned int i = 0; i < timezones.size(); i++) + { + if (!found && StringUtils::EqualsNoCase(timezones[i], current)) + found = true; + + list.emplace_back(timezones[i], timezones[i]); + } + + if (!found && !timezones.empty()) + current = timezones[0]; +} + +CPosixTimezone g_timezone; |