| Index: chrome/browser/chromeos/system/timezone_settings.cc
|
| diff --git a/chrome/browser/chromeos/system/timezone_settings.cc b/chrome/browser/chromeos/system/timezone_settings.cc
|
| index d5c505458c5aa6429eb87194fdf9a10d163641d7..39d38677670d053845189fad8b875c2de5854db7 100644
|
| --- a/chrome/browser/chromeos/system/timezone_settings.cc
|
| +++ b/chrome/browser/chromeos/system/timezone_settings.cc
|
| @@ -4,6 +4,8 @@
|
|
|
| #include "chrome/browser/chromeos/system/timezone_settings.h"
|
|
|
| +#include <string>
|
| +
|
| #include "base/bind.h"
|
| #include "base/chromeos/chromeos_version.h"
|
| #include "base/file_path.h"
|
| @@ -12,15 +14,14 @@
|
| #include "base/memory/scoped_ptr.h"
|
| #include "base/memory/singleton.h"
|
| #include "base/observer_list.h"
|
| +#include "base/stl_util.h"
|
| #include "base/string_util.h"
|
| #include "base/utf_string_conversions.h"
|
| #include "content/public/browser/browser_thread.h"
|
| +#include "unicode/timezone.h"
|
|
|
| using content::BrowserThread;
|
|
|
| -namespace chromeos {
|
| -namespace system {
|
| -
|
| namespace {
|
|
|
| // The filepath to the timezone file that symlinks to the actual timezone file.
|
| @@ -34,31 +35,104 @@ const char kTimezoneFilesDir[] = "/usr/share/zoneinfo/";
|
| // Fallback time zone ID used in case of an unexpected error.
|
| const char kFallbackTimeZoneId[] = "America/Los_Angeles";
|
|
|
| -} // namespace
|
| -
|
| -// The TimezoneSettings implementation used in production.
|
| -class TimezoneSettingsImpl : public TimezoneSettings {
|
| - public:
|
| - // TimezoneSettings implementation:
|
| - virtual const icu::TimeZone& GetTimezone();
|
| - virtual void SetTimezone(const icu::TimeZone& timezone);
|
| - virtual void AddObserver(Observer* observer);
|
| - virtual void RemoveObserver(Observer* observer);
|
| -
|
| - static TimezoneSettingsImpl* GetInstance();
|
| -
|
| - private:
|
| - friend struct DefaultSingletonTraits<TimezoneSettingsImpl>;
|
| -
|
| - TimezoneSettingsImpl();
|
| -
|
| - scoped_ptr<icu::TimeZone> timezone_;
|
| - ObserverList<Observer> observers_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsImpl);
|
| +// TODO(jungshik): Using Enumerate method in ICU gives 600+ timezones.
|
| +// Even after filtering out duplicate entries with a strict identity check,
|
| +// we still have 400+ zones. Relaxing the criteria for the timezone
|
| +// identity is likely to cut down the number to < 100. Until we
|
| +// come up with a better list, we hard-code the following list as used by
|
| +// Android.
|
| +static const char* kTimeZones[] = {
|
| + "Pacific/Majuro",
|
| + "Pacific/Midway",
|
| + "Pacific/Honolulu",
|
| + "America/Anchorage",
|
| + "America/Los_Angeles",
|
| + "America/Tijuana",
|
| + "America/Denver",
|
| + "America/Phoenix",
|
| + "America/Chihuahua",
|
| + "America/Chicago",
|
| + "America/Mexico_City",
|
| + "America/Costa_Rica",
|
| + "America/Regina",
|
| + "America/New_York",
|
| + "America/Bogota",
|
| + "America/Caracas",
|
| + "America/Barbados",
|
| + "America/Manaus",
|
| + "America/Santiago",
|
| + "America/St_Johns",
|
| + "America/Sao_Paulo",
|
| + "America/Araguaina",
|
| + "America/Argentina/Buenos_Aires",
|
| + "America/Godthab",
|
| + "America/Montevideo",
|
| + "Atlantic/South_Georgia",
|
| + "Atlantic/Azores",
|
| + "Atlantic/Cape_Verde",
|
| + "Africa/Casablanca",
|
| + "Europe/London",
|
| + "Europe/Amsterdam",
|
| + "Europe/Belgrade",
|
| + "Europe/Brussels",
|
| + "Europe/Sarajevo",
|
| + "Africa/Windhoek",
|
| + "Africa/Brazzaville",
|
| + "Asia/Amman",
|
| + "Europe/Athens",
|
| + "Asia/Beirut",
|
| + "Africa/Cairo",
|
| + "Europe/Helsinki",
|
| + "Asia/Jerusalem",
|
| + "Europe/Minsk",
|
| + "Africa/Harare",
|
| + "Asia/Baghdad",
|
| + "Europe/Moscow",
|
| + "Asia/Kuwait",
|
| + "Africa/Nairobi",
|
| + "Asia/Tehran",
|
| + "Asia/Baku",
|
| + "Asia/Tbilisi",
|
| + "Asia/Yerevan",
|
| + "Asia/Dubai",
|
| + "Asia/Kabul",
|
| + "Asia/Karachi",
|
| + "Asia/Oral",
|
| + "Asia/Yekaterinburg",
|
| + "Asia/Calcutta",
|
| + "Asia/Colombo",
|
| + "Asia/Katmandu",
|
| + "Asia/Almaty",
|
| + "Asia/Rangoon",
|
| + "Asia/Krasnoyarsk",
|
| + "Asia/Bangkok",
|
| + "Asia/Shanghai",
|
| + "Asia/Hong_Kong",
|
| + "Asia/Irkutsk",
|
| + "Asia/Kuala_Lumpur",
|
| + "Australia/Perth",
|
| + "Asia/Taipei",
|
| + "Asia/Seoul",
|
| + "Asia/Tokyo",
|
| + "Asia/Yakutsk",
|
| + "Australia/Adelaide",
|
| + "Australia/Darwin",
|
| + "Australia/Brisbane",
|
| + "Australia/Hobart",
|
| + "Australia/Sydney",
|
| + "Asia/Vladivostok",
|
| + "Pacific/Guam",
|
| + "Asia/Magadan",
|
| + "Pacific/Auckland",
|
| + "Pacific/Fiji",
|
| + "Pacific/Tongatapu",
|
| };
|
|
|
| std::string GetTimezoneIDAsString() {
|
| + // Compare with chromiumos/src/platform/init/ui.conf which fixes certain
|
| + // incorrect states of the timezone symlink on startup. Thus errors occuring
|
| + // here should be rather contrived.
|
| +
|
| // Look at kTimezoneSymlink, see which timezone we are symlinked to.
|
| char buf[256];
|
| const ssize_t len = readlink(kTimezoneSymlink, buf,
|
| @@ -115,93 +189,204 @@ void SetTimezoneIDFromString(const std::string& id) {
|
| }
|
| }
|
|
|
| -const icu::TimeZone& TimezoneSettingsImpl::GetTimezone() {
|
| +// Common code of the TimezoneSettings implementations.
|
| +class TimezoneSettingsBaseImpl : public chromeos::system::TimezoneSettings {
|
| + public:
|
| + virtual ~TimezoneSettingsBaseImpl();
|
| +
|
| + // TimezoneSettings implementation:
|
| + virtual const icu::TimeZone& GetTimezone() OVERRIDE;
|
| + virtual string16 GetCurrentTimezoneID() OVERRIDE;
|
| + virtual void SetTimezoneFromID(const string16& timezone_id) OVERRIDE;
|
| + virtual void AddObserver(Observer* observer) OVERRIDE;
|
| + virtual void RemoveObserver(Observer* observer) OVERRIDE;
|
| + virtual const std::vector<icu::TimeZone*>& GetTimezoneList() const OVERRIDE;
|
| +
|
| + protected:
|
| + TimezoneSettingsBaseImpl();
|
| +
|
| + // Returns |timezone| if it is an element of |timezones_|.
|
| + // Otherwise, returns a timezone from |timezones_|, if such exists, that has
|
| + // the same rule as the given |timezone|.
|
| + // Otherwise, returns NULL.
|
| + // Note multiple timezones with the same time zone offset may exist
|
| + // e.g.
|
| + // US/Pacific == America/Los_Angeles
|
| + const icu::TimeZone* GetKnownTimezoneOrNull(
|
| + const icu::TimeZone& timezone) const;
|
| +
|
| + ObserverList<Observer> observers_;
|
| + std::vector<icu::TimeZone*> timezones_;
|
| + scoped_ptr<icu::TimeZone> timezone_;
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsBaseImpl);
|
| +};
|
| +
|
| +// The TimezoneSettings implementation used in production.
|
| +class TimezoneSettingsImpl : public TimezoneSettingsBaseImpl {
|
| + public:
|
| + // TimezoneSettings implementation:
|
| + virtual void SetTimezone(const icu::TimeZone& timezone) OVERRIDE;
|
| +
|
| + static TimezoneSettingsImpl* GetInstance();
|
| +
|
| + private:
|
| + friend struct DefaultSingletonTraits<TimezoneSettingsImpl>;
|
| +
|
| + TimezoneSettingsImpl();
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsImpl);
|
| +};
|
| +
|
| +// The stub TimezoneSettings implementation used on Linux desktop.
|
| +class TimezoneSettingsStubImpl : public TimezoneSettingsBaseImpl {
|
| + public:
|
| + // TimezoneSettings implementation:
|
| + virtual void SetTimezone(const icu::TimeZone& timezone) OVERRIDE;
|
| +
|
| + static TimezoneSettingsStubImpl* GetInstance();
|
| +
|
| + private:
|
| + friend struct DefaultSingletonTraits<TimezoneSettingsStubImpl>;
|
| +
|
| + TimezoneSettingsStubImpl();
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsStubImpl);
|
| +};
|
| +
|
| +TimezoneSettingsBaseImpl::~TimezoneSettingsBaseImpl() {
|
| + STLDeleteElements(&timezones_);
|
| +}
|
| +
|
| +const icu::TimeZone& TimezoneSettingsBaseImpl::GetTimezone() {
|
| return *timezone_.get();
|
| }
|
|
|
| -void TimezoneSettingsImpl::SetTimezone(const icu::TimeZone& timezone) {
|
| - timezone_.reset(timezone.clone());
|
| - icu::UnicodeString unicode;
|
| - timezone.getID(unicode);
|
| - std::string id;
|
| - UTF16ToUTF8(unicode.getBuffer(), unicode.length(), &id);
|
| - VLOG(1) << "Setting timezone to " << id;
|
| - // Change the timezone config files on the FILE thread. It's safe to do this
|
| - // in the background as the following operations don't depend on the
|
| - // completion of the config change.
|
| - BrowserThread::PostTask(BrowserThread::FILE,
|
| - FROM_HERE,
|
| - base::Bind(&SetTimezoneIDFromString, id));
|
| - icu::TimeZone::setDefault(timezone);
|
| - FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(timezone));
|
| +string16 TimezoneSettingsBaseImpl::GetCurrentTimezoneID() {
|
| + return chromeos::system::TimezoneSettings::GetTimezoneID(GetTimezone());
|
| +}
|
| +
|
| +void TimezoneSettingsBaseImpl::SetTimezoneFromID(const string16& timezone_id) {
|
| + scoped_ptr<icu::TimeZone> timezone(icu::TimeZone::createTimeZone(
|
| + icu::UnicodeString(timezone_id.c_str(), timezone_id.size())));
|
| + SetTimezone(*timezone);
|
| }
|
|
|
| -void TimezoneSettingsImpl::AddObserver(Observer* observer) {
|
| +void TimezoneSettingsBaseImpl::AddObserver(Observer* observer) {
|
| observers_.AddObserver(observer);
|
| }
|
|
|
| -void TimezoneSettingsImpl::RemoveObserver(Observer* observer) {
|
| +void TimezoneSettingsBaseImpl::RemoveObserver(Observer* observer) {
|
| observers_.RemoveObserver(observer);
|
| }
|
|
|
| -TimezoneSettingsImpl::TimezoneSettingsImpl() {
|
| - // Get Timezone
|
| - std::string id = GetTimezoneIDAsString();
|
| - if (id.empty()) {
|
| - id = kFallbackTimeZoneId;
|
| - LOG(ERROR) << "Got an empty string for timezone, default to " << id;
|
| +const std::vector<icu::TimeZone*>&
|
| +TimezoneSettingsBaseImpl::GetTimezoneList() const {
|
| + return timezones_;
|
| +}
|
| +
|
| +TimezoneSettingsBaseImpl::TimezoneSettingsBaseImpl() {
|
| + for (size_t i = 0; i < arraysize(kTimeZones); ++i) {
|
| + timezones_.push_back(icu::TimeZone::createTimeZone(
|
| + icu::UnicodeString(kTimeZones[i], -1, US_INV)));
|
| }
|
| - icu::TimeZone* timezone =
|
| - icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(id));
|
| - timezone_.reset(timezone);
|
| - icu::TimeZone::setDefault(*timezone);
|
| - VLOG(1) << "Timezone is " << id;
|
| }
|
|
|
| +const icu::TimeZone* TimezoneSettingsBaseImpl::GetKnownTimezoneOrNull(
|
| + const icu::TimeZone& timezone) const {
|
| + const icu::TimeZone* known_timezone = NULL;
|
| + for (std::vector<icu::TimeZone*>::const_iterator iter = timezones_.begin();
|
| + iter != timezones_.end(); ++iter) {
|
| + const icu::TimeZone* entry = *iter;
|
| + if (*entry == timezone)
|
| + return entry;
|
| + if (entry->hasSameRules(timezone))
|
| + known_timezone = entry;
|
| + }
|
| +
|
| + // May return NULL if we did not find a matching timezone in our list.
|
| + return known_timezone;
|
| +}
|
| +
|
| +void TimezoneSettingsImpl::SetTimezone(const icu::TimeZone& timezone) {
|
| + // Replace |timezone| by a known timezone with the same rules. If none exists
|
| + // go on with |timezone|.
|
| + const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(timezone);
|
| + if (!known_timezone)
|
| + known_timezone = &timezone;
|
| +
|
| + timezone_.reset(known_timezone->clone());
|
| + std::string id = UTF16ToUTF8(GetTimezoneID(*known_timezone));
|
| + VLOG(1) << "Setting timezone to " << id;
|
| + // It's safe to change the timezone config files in the background as the
|
| + // following operations don't depend on the completion of the config change.
|
| + BrowserThread::PostBlockingPoolTask(FROM_HERE,
|
| + base::Bind(&SetTimezoneIDFromString, id));
|
| + icu::TimeZone::setDefault(*known_timezone);
|
| + FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(*known_timezone));
|
| +}
|
| +
|
| +// static
|
| TimezoneSettingsImpl* TimezoneSettingsImpl::GetInstance() {
|
| return Singleton<TimezoneSettingsImpl,
|
| DefaultSingletonTraits<TimezoneSettingsImpl> >::get();
|
| }
|
|
|
| -// The stub TimezoneSettings implementation used on Linux desktop.
|
| -class TimezoneSettingsStubImpl : public TimezoneSettings {
|
| - public:
|
| - // TimezoneSettings implementation:
|
| - virtual const icu::TimeZone& GetTimezone() {
|
| - return *timezone_.get();
|
| +TimezoneSettingsImpl::TimezoneSettingsImpl() {
|
| + std::string id = GetTimezoneIDAsString();
|
| + if (id.empty()) {
|
| + id = kFallbackTimeZoneId;
|
| + LOG(ERROR) << "Got an empty string for timezone, default to '" << id;
|
| }
|
|
|
| - virtual void SetTimezone(const icu::TimeZone& timezone) {
|
| - icu::TimeZone::setDefault(timezone);
|
| - FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(timezone));
|
| - }
|
| + timezone_.reset(icu::TimeZone::createTimeZone(
|
| + icu::UnicodeString::fromUTF8(id)));
|
|
|
| - virtual void AddObserver(Observer* observer) {
|
| - observers_.AddObserver(observer);
|
| - }
|
| + // Store a known timezone equivalent to id in |timezone_|.
|
| + const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(*timezone_);
|
| + if (known_timezone != NULL && *known_timezone != *timezone_)
|
| + // Not necessary to update the filesystem because |known_timezone| has the
|
| + // same rules.
|
| + timezone_.reset(known_timezone->clone());
|
|
|
| - virtual void RemoveObserver(Observer* observer) {
|
| - observers_.RemoveObserver(observer);
|
| - }
|
| + icu::TimeZone::setDefault(*timezone_);
|
| + VLOG(1) << "Timezone initially set to " << id;
|
| +}
|
|
|
| - static TimezoneSettingsStubImpl* GetInstance() {
|
| - return Singleton<TimezoneSettingsStubImpl,
|
| - DefaultSingletonTraits<TimezoneSettingsStubImpl> >::get();
|
| - }
|
| +void TimezoneSettingsStubImpl::SetTimezone(const icu::TimeZone& timezone) {
|
| + // Replace |timezone| by a known timezone with the same rules. If none exists
|
| + // go on with |timezone|.
|
| + const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(timezone);
|
| + if (!known_timezone)
|
| + known_timezone = &timezone;
|
|
|
| - private:
|
| - friend struct DefaultSingletonTraits<TimezoneSettingsStubImpl>;
|
| + timezone_.reset(known_timezone->clone());
|
| + icu::TimeZone::setDefault(*known_timezone);
|
| + FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(*known_timezone));
|
| +}
|
|
|
| - TimezoneSettingsStubImpl() {
|
| - timezone_.reset(icu::TimeZone::createDefault());
|
| - }
|
| +// static
|
| +TimezoneSettingsStubImpl* TimezoneSettingsStubImpl::GetInstance() {
|
| + return Singleton<TimezoneSettingsStubImpl,
|
| + DefaultSingletonTraits<TimezoneSettingsStubImpl> >::get();
|
| +}
|
|
|
| - scoped_ptr<icu::TimeZone> timezone_;
|
| - ObserverList<Observer> observers_;
|
| +TimezoneSettingsStubImpl::TimezoneSettingsStubImpl() {
|
| + timezone_.reset(icu::TimeZone::createDefault());
|
| + const icu::TimeZone* known_timezone = GetKnownTimezoneOrNull(*timezone_);
|
| + if (known_timezone != NULL && *known_timezone != *timezone_)
|
| + timezone_.reset(known_timezone->clone());
|
| +}
|
|
|
| - DISALLOW_COPY_AND_ASSIGN(TimezoneSettingsStubImpl);
|
| -};
|
| +} // namespace
|
|
|
| +namespace chromeos {
|
| +namespace system {
|
| +
|
| +TimezoneSettings::Observer::~Observer() {}
|
| +
|
| +// static
|
| TimezoneSettings* TimezoneSettings::GetInstance() {
|
| if (base::chromeos::IsRunningOnChromeOS()) {
|
| return TimezoneSettingsImpl::GetInstance();
|
| @@ -210,5 +395,12 @@ TimezoneSettings* TimezoneSettings::GetInstance() {
|
| }
|
| }
|
|
|
| +// static
|
| +string16 TimezoneSettings::GetTimezoneID(const icu::TimeZone& timezone) {
|
| + icu::UnicodeString id;
|
| + timezone.getID(id);
|
| + return string16(id.getBuffer(), id.length());
|
| +}
|
| +
|
| } // namespace system
|
| } // namespace chromeos
|
|
|