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