Home | History | Annotate | Download | only in chromeos
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/webui/options/chromeos/timezone_options_util.h"
      6 
      7 #include <string>
      8 
      9 #include "base/i18n/rtl.h"
     10 #include "base/lazy_instance.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "base/synchronization/lock.h"
     15 #include "base/values.h"
     16 #include "chrome/browser/chromeos/system/timezone_settings.h"
     17 #include "grit/generated_resources.h"
     18 #include "third_party/icu/source/common/unicode/ures.h"
     19 #include "third_party/icu/source/common/unicode/utypes.h"
     20 #include "third_party/icu/source/i18n/unicode/calendar.h"
     21 #include "third_party/icu/source/i18n/unicode/timezone.h"
     22 #include "ui/base/l10n/l10n_util.h"
     23 
     24 namespace {
     25 
     26 struct UResClose {
     27   inline void operator() (UResourceBundle* b) const {
     28     ures_close(b);
     29   }
     30 };
     31 
     32 static base::LazyInstance<base::Lock>::Leaky
     33     g_timezone_bundle_lock = LAZY_INSTANCE_INITIALIZER;
     34 
     35 // Returns an exemplary city in the given timezone.
     36 string16 GetExemplarCity(const icu::TimeZone& zone) {
     37   // TODO(jungshik): After upgrading to ICU 4.6, use U_ICUDATA_ZONE
     38   static const char* zone_bundle_name = NULL;
     39 
     40   // These will be leaked at the end.
     41   static UResourceBundle *zone_bundle = NULL;
     42   static UResourceBundle *zone_strings = NULL;
     43 
     44   UErrorCode status = U_ZERO_ERROR;
     45   {
     46     base::AutoLock lock(g_timezone_bundle_lock.Get());
     47     if (zone_bundle == NULL)
     48       zone_bundle = ures_open(zone_bundle_name, uloc_getDefault(), &status);
     49 
     50     if (zone_strings == NULL)
     51       zone_strings = ures_getByKey(zone_bundle, "zone_strings", NULL, &status);
     52   }
     53 
     54   icu::UnicodeString zone_id;
     55   zone.getID(zone_id);
     56   std::string zone_id_str;
     57   zone_id.toUTF8String(zone_id_str);
     58 
     59   // Resource keys for timezones use ':' in place of '/'.
     60   ReplaceSubstringsAfterOffset(&zone_id_str, 0, "/", ":");
     61   scoped_ptr_malloc<UResourceBundle, UResClose> zone_item(
     62       ures_getByKey(zone_strings, zone_id_str.c_str(), NULL, &status));
     63   icu::UnicodeString city;
     64   if (!U_FAILURE(status)) {
     65     city = icu::ures_getUnicodeStringByKey(zone_item.get(), "ec", &status);
     66     if (U_SUCCESS(status))
     67       return string16(city.getBuffer(), city.length());
     68   }
     69 
     70   // Fallback case in case of failure.
     71   ReplaceSubstringsAfterOffset(&zone_id_str, 0, ":", "/");
     72   // Take the last component of a timezone id (e.g. 'Baz' in 'Foo/Bar/Baz').
     73   // Depending on timezones, keeping all but the 1st component
     74   // (e.g. Bar/Baz) may be better, but our current list does not have
     75   // any timezone for which that's the case.
     76   std::string::size_type slash_pos = zone_id_str.rfind('/');
     77   if (slash_pos != std::string::npos && slash_pos < zone_id_str.size())
     78     zone_id_str.erase(0, slash_pos + 1);
     79   // zone id has '_' in place of ' '.
     80   ReplaceSubstringsAfterOffset(&zone_id_str, 0, "_", " ");
     81   return ASCIIToUTF16(zone_id_str);
     82 }
     83 
     84 // Gets the given timezone's name for visualization.
     85 string16 GetTimezoneName(const icu::TimeZone& timezone) {
     86   // Instead of using the raw_offset, use the offset in effect now.
     87   // For instance, US Pacific Time, the offset shown will be -7 in summer
     88   // while it'll be -8 in winter.
     89   int raw_offset, dst_offset;
     90   UDate now = icu::Calendar::getNow();
     91   UErrorCode status = U_ZERO_ERROR;
     92   timezone.getOffset(now, false, raw_offset, dst_offset, status);
     93   DCHECK(U_SUCCESS(status));
     94   int offset = raw_offset + dst_offset;
     95   // |offset| is in msec.
     96   int minute_offset = std::abs(offset) / 60000;
     97   int hour_offset = minute_offset / 60;
     98   int min_remainder = minute_offset % 60;
     99   // Some timezones have a non-integral hour offset. So, we need to use hh:mm
    100   // form.
    101   std::string  offset_str = base::StringPrintf(offset >= 0 ?
    102       "UTC+%d:%02d" : "UTC-%d:%02d", hour_offset, min_remainder);
    103 
    104   // TODO(jungshik): When coming up with a better list of timezones, we also
    105   // have to come up with better 'display' names. One possibility is to list
    106   // multiple cities (e.g. "Los Angeles, Vancouver .." in the order of
    107   // the population of a country the city belongs to.).
    108   // We can also think of using LONG_GENERIC or LOCATION once we upgrade
    109   // to ICU 4.6.
    110   // In the meantime, we use "LONG" name with "Exemplar City" to distinguish
    111   // multiple timezones with the same "LONG" name but with different
    112   // rules (e.g. US Mountain Time in Denver vs Phoenix).
    113   icu::UnicodeString name;
    114   timezone.getDisplayName(dst_offset != 0, icu::TimeZone::LONG, name);
    115   string16 result(l10n_util::GetStringFUTF16(
    116       IDS_OPTIONS_SETTINGS_TIMEZONE_DISPLAY_TEMPLATE, ASCIIToUTF16(offset_str),
    117       string16(name.getBuffer(), name.length()), GetExemplarCity(timezone)));
    118   base::i18n::AdjustStringForLocaleDirection(&result);
    119   return result;
    120 }
    121 
    122 }  // namespace
    123 
    124 namespace options {
    125 
    126 // Creates a list of pairs of each timezone's ID and name.
    127 scoped_ptr<base::ListValue> GetTimezoneList() {
    128   const std::vector<icu::TimeZone*> &timezones =
    129       chromeos::system::TimezoneSettings::GetInstance()->GetTimezoneList();
    130   scoped_ptr<base::ListValue> timezoneList(new base::ListValue());
    131   for (std::vector<icu::TimeZone*>::const_iterator iter = timezones.begin();
    132        iter != timezones.end(); ++iter) {
    133     const icu::TimeZone* timezone = *iter;
    134     base::ListValue* option = new base::ListValue();
    135     option->Append(new base::StringValue(
    136         chromeos::system::TimezoneSettings::GetTimezoneID(*timezone)));
    137     option->Append(new base::StringValue(GetTimezoneName(*timezone)));
    138     timezoneList->Append(option);
    139   }
    140   return timezoneList.Pass();
    141 }
    142 
    143 }  // namespace options
    144