Home | History | Annotate | Download | only in renderer
      1 // Copyright (c) 2013 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 "content/renderer/date_time_formatter.h"
      6 
      7 #include "base/strings/string_util.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "third_party/WebKit/public/platform/WebCString.h"
     10 #include "third_party/WebKit/public/web/WebDateTimeChooserParams.h"
     11 #include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
     12 
     13 
     14 namespace content {
     15 
     16 void DateTimeFormatter::CreatePatternMap() {
     17   // Initialize all the UI elements with empty patterns,
     18   // then fill in the ones that are actually date/time inputs and
     19   // are implemented.
     20   for (int i = 0 ; i <= ui::TEXT_INPUT_TYPE_MAX; ++i) {
     21     patterns_[i] = "";
     22   }
     23   patterns_[ui::TEXT_INPUT_TYPE_DATE] = "yyyy-MM-dd";
     24   patterns_[ui::TEXT_INPUT_TYPE_DATE_TIME] =  "yyyy-MM-dd'T'HH:mm'Z'";
     25   patterns_[ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL] =  "yyyy-MM-dd'T'HH:mm";
     26   patterns_[ui::TEXT_INPUT_TYPE_MONTH] = "yyyy-MM";
     27   patterns_[ui::TEXT_INPUT_TYPE_TIME] = "HH:mm";
     28   patterns_[ui::TEXT_INPUT_TYPE_WEEK] = "Y-'W'ww";
     29 }
     30 
     31 DateTimeFormatter::DateTimeFormatter(
     32     const WebKit::WebDateTimeChooserParams& source)
     33   : formatted_string_(source.currentValue.utf8()) {
     34   CreatePatternMap();
     35   ExtractType(source);
     36   if (!ParseValues()) {
     37     type_ = ui::TEXT_INPUT_TYPE_NONE;
     38     ClearAll();
     39     LOG(WARNING) << "Problems parsing input <" << formatted_string_ << ">";
     40   }
     41 }
     42 
     43 DateTimeFormatter::DateTimeFormatter(
     44     ui::TextInputType type,
     45     int year, int month, int day, int hour, int minute, int second,
     46     int week_year, int week)
     47   : type_(type),
     48     year_(year),
     49     month_(month),
     50     day_(day),
     51     hour_(hour),
     52     minute_(minute),
     53     second_(second),
     54     week_year_(week_year),
     55     week_(week) {
     56   CreatePatternMap();
     57   pattern_ = type_ > 0 && type_ <= ui::TEXT_INPUT_TYPE_MAX ?
     58       &patterns_[type_] : &patterns_[ui::TEXT_INPUT_TYPE_NONE];
     59 
     60   formatted_string_ = FormatString();
     61 }
     62 
     63 DateTimeFormatter::~DateTimeFormatter() {
     64 }
     65 
     66 int DateTimeFormatter::GetYear() const {
     67   return year_;
     68 }
     69 
     70 int DateTimeFormatter::GetMonth() const {
     71   return month_;
     72 }
     73 
     74 int DateTimeFormatter::GetDay() const {
     75   return day_;
     76 }
     77 
     78 int DateTimeFormatter::GetHour() const {
     79   return hour_;
     80 }
     81 
     82 int DateTimeFormatter::GetMinute() const {
     83   return minute_;
     84 }
     85 
     86 int DateTimeFormatter::GetSecond() const {
     87   return second_;
     88 }
     89 
     90 int DateTimeFormatter::GetWeekYear() const {
     91   return week_year_;
     92 }
     93 
     94 int DateTimeFormatter::GetWeek() const {
     95   return week_;
     96 }
     97 
     98 ui::TextInputType DateTimeFormatter::GetType() const {
     99   return type_;
    100 }
    101 
    102 const std::string& DateTimeFormatter::GetFormattedValue() const {
    103   return formatted_string_;
    104 }
    105 
    106 const std::string DateTimeFormatter::FormatString() const {
    107   UErrorCode success = U_ZERO_ERROR;
    108   if (year_ == 0 && month_ == 0 && day_ == 0 &&
    109       hour_ == 0 && minute_ == 0 && second_ == 0 &&
    110       week_year_ == 0 && week_ == 0) {
    111     return std::string();
    112   }
    113 
    114   std::string result;
    115   icu::GregorianCalendar calendar(success);
    116   if (success <= U_ZERO_ERROR) {
    117     if (type_ == ui::TEXT_INPUT_TYPE_WEEK) {
    118       // An ISO week starts with Monday.
    119       calendar.setFirstDayOfWeek(UCAL_MONDAY);
    120       // ISO 8601 defines that the week with the year's first Thursday is the
    121       // first week.
    122       calendar.setMinimalDaysInFirstWeek(4);
    123       calendar.set(UCAL_YEAR_WOY, week_year_);
    124       calendar.set(UCAL_WEEK_OF_YEAR, week_);
    125     } else {
    126       calendar.set(UCAL_YEAR, year_);
    127       calendar.set(UCAL_MONTH, month_);
    128       calendar.set(UCAL_DATE, day_);
    129       calendar.set(UCAL_HOUR_OF_DAY, hour_);
    130       calendar.set(UCAL_MINUTE, minute_);
    131       calendar.set(UCAL_SECOND, second_);
    132     }
    133     icu::SimpleDateFormat formatter(*pattern_, success);
    134     icu::UnicodeString formatted_time;
    135     formatter.format(calendar, formatted_time, NULL, success);
    136     UTF16ToUTF8(formatted_time.getBuffer(),
    137                 static_cast<size_t>(formatted_time.length()),
    138                 &result);
    139     if (success <= U_ZERO_ERROR)
    140       return result;
    141   }
    142   LOG(WARNING) << "Calendar not created: error " <<  success;
    143   return std::string();
    144 }
    145 
    146 void DateTimeFormatter::ExtractType(
    147     const WebKit::WebDateTimeChooserParams& source) {
    148   switch (source.type) {
    149     case WebKit::WebDateTimeInputTypeDate:
    150       type_ = ui::TEXT_INPUT_TYPE_DATE;
    151       break;
    152     case WebKit::WebDateTimeInputTypeDateTime:
    153       type_ = ui::TEXT_INPUT_TYPE_DATE_TIME;
    154       break;
    155     case WebKit::WebDateTimeInputTypeDateTimeLocal:
    156       type_ = ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL;
    157       break;
    158     case WebKit::WebDateTimeInputTypeMonth:
    159       type_ = ui::TEXT_INPUT_TYPE_MONTH;
    160       break;
    161     case WebKit::WebDateTimeInputTypeTime:
    162       type_ = ui::TEXT_INPUT_TYPE_TIME;
    163       break;
    164     case WebKit::WebDateTimeInputTypeWeek:
    165       type_ = ui::TEXT_INPUT_TYPE_WEEK;
    166       break;
    167     case WebKit::WebDateTimeInputTypeNone:
    168     default:
    169       type_ = ui::TEXT_INPUT_TYPE_NONE;
    170   }
    171 }
    172 
    173 // Not all fields are defined in all configurations and ICU might store
    174 // garbage if success <= U_ZERO_ERROR so the output is sanitized here.
    175 int DateTimeFormatter::ExtractValue(
    176     const icu::Calendar* calendar, UCalendarDateFields value) const {
    177   UErrorCode success = U_ZERO_ERROR;
    178   int result = calendar->get(value, success);
    179   return (success <= U_ZERO_ERROR) ? result : 0;
    180 }
    181 
    182 bool DateTimeFormatter::ParseValues() {
    183   if (type_ == ui::TEXT_INPUT_TYPE_NONE) {
    184     ClearAll();
    185     return false;
    186   }
    187 
    188   if (formatted_string_.empty()) {
    189     ClearAll();
    190     return true;
    191   }
    192 
    193   UErrorCode success = U_ZERO_ERROR;
    194   icu::UnicodeString icu_value = icu::UnicodeString::fromUTF8(
    195       icu::StringPiece(formatted_string_.data(), formatted_string_.size()));
    196   if (type_ > 0 && type_ <= ui::TEXT_INPUT_TYPE_MAX) {
    197     const icu::UnicodeString pattern = patterns_[type_];
    198     icu::SimpleDateFormat formatter(pattern, success);
    199     formatter.parse(icu_value, success);
    200     if (success <= U_ZERO_ERROR) {
    201       const icu::Calendar* cal = formatter.getCalendar();
    202       year_ = ExtractValue(cal, UCAL_YEAR);
    203       month_ = ExtractValue(cal, UCAL_MONTH);
    204       day_ = ExtractValue(cal, UCAL_DATE);
    205       hour_ = ExtractValue(cal, UCAL_HOUR_OF_DAY);  // 24h format
    206       minute_ = ExtractValue(cal, UCAL_MINUTE);
    207       second_ = ExtractValue(cal, UCAL_SECOND);
    208       week_year_ = ExtractValue(cal, UCAL_YEAR_WOY);
    209       week_ = ExtractValue(cal, UCAL_WEEK_OF_YEAR);
    210     }
    211   }
    212 
    213   return (success <= U_ZERO_ERROR);
    214 }
    215 
    216 void DateTimeFormatter::ClearAll() {
    217   year_ = 0;
    218   month_ = 0;
    219   day_ = 0;
    220   hour_ = 0;
    221   minute_ = 0;
    222   second_ = 0;
    223   week_year_ = 0;
    224   week_ = 0;
    225 }
    226 
    227 }  // namespace content
    228