summaryrefslogtreecommitdiffstats
path: root/xbmc/platform/posix/PosixTimezone.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/platform/posix/PosixTimezone.cpp')
-rw-r--r--xbmc/platform/posix/PosixTimezone.cpp272
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;