Refactor management of overview window copy lifetime into a separate class.
[chromium-blink-merge.git] / content / renderer / date_time_formatter.cc
bloba0ce77b91cf84928a093d127208c573580389ec5
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.
5 #include "content/renderer/date_time_formatter.h"
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"
14 namespace content {
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] = "";
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";
31 // Returns true if icu_value parses as a valid for the specified date/time
32 // pattern. The date/time pattern given is for icu::SimpleDateFormat.
33 static bool TryPattern(const char* pattern,
34 const icu::UnicodeString& icu_value) {
35 icu::UnicodeString time_pattern = pattern;
36 UErrorCode success = U_ZERO_ERROR;
37 icu::SimpleDateFormat formatter(time_pattern, success);
38 formatter.parse(icu_value, success);
39 return success == U_ZERO_ERROR;
42 // For a time value represented as a string find the longest time
43 // pattern which matches it. A valid time can have hours and minutes
44 // or hours, minutes and seconds or hour, minutes, seconds and upto 3
45 // digits of fractional seconds. Specify step in milliseconds, it is 1000
46 // times the value specified as "step" in the "<input type=time step=...>
47 // HTML fragment. A value of 60000 or more indicates that seconds
48 // are not expected and a value of 1000 or more indicates that fractional
49 // seconds are not expected.
50 static const char* FindLongestTimePatternWhichMatches(const std::string& value,
51 double step) {
52 const char* pattern = "HH:mm";
53 if (step >= 60000)
54 return pattern;
56 icu::UnicodeString icu_value = icu::UnicodeString::fromUTF8(
57 icu::StringPiece(value.data(), value.size()));
58 const char* last_pattern = pattern;
59 pattern = "HH:mm:ss";
60 if (!TryPattern(pattern, icu_value))
61 return last_pattern;
62 if (step >= 1000)
63 return pattern;
64 last_pattern = pattern;
65 pattern = "HH:mm:ss.S";
66 if (!TryPattern(pattern, icu_value))
67 return last_pattern;
68 last_pattern = pattern;
69 pattern = "HH:mm:ss.SS";
70 if (!TryPattern(pattern, icu_value))
71 return last_pattern;
72 last_pattern = pattern;
73 pattern = "HH:mm:ss.SSS";
74 if (!TryPattern(pattern, icu_value))
75 return last_pattern;
76 return pattern;
79 DateTimeFormatter::DateTimeFormatter(
80 const WebKit::WebDateTimeChooserParams& source)
81 : formatted_string_(source.currentValue.utf8()) {
82 CreatePatternMap();
83 if (source.type == WebKit::WebDateTimeInputTypeTime)
84 time_pattern_ =
85 FindLongestTimePatternWhichMatches(formatted_string_, source.step);
86 ExtractType(source);
87 if (!ParseValues()) {
88 type_ = ui::TEXT_INPUT_TYPE_NONE;
89 ClearAll();
90 LOG(WARNING) << "Problems parsing input <" << formatted_string_ << ">";
94 DateTimeFormatter::DateTimeFormatter(ui::TextInputType type,
95 int year,
96 int month,
97 int day,
98 int hour,
99 int minute,
100 int second,
101 int milli,
102 int week_year,
103 int week)
104 : type_(type),
105 year_(year),
106 month_(month),
107 day_(day),
108 hour_(hour),
109 minute_(minute),
110 second_(second),
111 milli_(milli),
112 week_year_(week_year),
113 week_(week) {
114 CreatePatternMap();
115 if (type_ == ui::TEXT_INPUT_TYPE_TIME && (second != 0 || milli != 0)) {
116 if (milli == 0)
117 time_pattern_ = "HH:mm:ss";
118 else if (milli % 100 == 0)
119 time_pattern_ = "HH:mm:ss.S";
120 else if (milli % 10 == 0)
121 time_pattern_ = "HH:mm:ss.SS";
122 else
123 time_pattern_ = "HH:mm:ss.SSS";
124 pattern_ = &time_pattern_;
125 } else {
126 pattern_ = type_ > 0 && type_ <= ui::TEXT_INPUT_TYPE_MAX ?
127 &patterns_[type_] : &patterns_[ui::TEXT_INPUT_TYPE_NONE];
130 formatted_string_ = FormatString();
133 DateTimeFormatter::~DateTimeFormatter() {
136 int DateTimeFormatter::GetYear() const {
137 return year_;
140 int DateTimeFormatter::GetMonth() const {
141 return month_;
144 int DateTimeFormatter::GetDay() const {
145 return day_;
148 int DateTimeFormatter::GetHour() const {
149 return hour_;
152 int DateTimeFormatter::GetMinute() const {
153 return minute_;
156 int DateTimeFormatter::GetSecond() const {
157 return second_;
160 int DateTimeFormatter::GetMilli() const { return milli_; }
162 int DateTimeFormatter::GetWeekYear() const { return week_year_; }
164 int DateTimeFormatter::GetWeek() const {
165 return week_;
168 ui::TextInputType DateTimeFormatter::GetType() const {
169 return type_;
172 const std::string& DateTimeFormatter::GetFormattedValue() const {
173 return formatted_string_;
176 const std::string DateTimeFormatter::FormatString() const {
177 UErrorCode success = U_ZERO_ERROR;
178 if (year_ == 0 && month_ == 0 && day_ == 0 && hour_ == 0 && minute_ == 0 &&
179 second_ == 0 && milli_ == 0 && week_year_ == 0 && week_ == 0) {
180 return std::string();
183 std::string result;
184 icu::GregorianCalendar calendar(success);
185 if (success <= U_ZERO_ERROR) {
186 if (type_ == ui::TEXT_INPUT_TYPE_WEEK) {
187 // An ISO week starts with Monday.
188 calendar.setFirstDayOfWeek(UCAL_MONDAY);
189 // ISO 8601 defines that the week with the year's first Thursday is the
190 // first week.
191 calendar.setMinimalDaysInFirstWeek(4);
192 calendar.set(UCAL_YEAR_WOY, week_year_);
193 calendar.set(UCAL_WEEK_OF_YEAR, week_);
194 } else {
195 calendar.set(UCAL_YEAR, year_);
196 calendar.set(UCAL_MONTH, month_);
197 calendar.set(UCAL_DATE, day_);
198 calendar.set(UCAL_HOUR_OF_DAY, hour_);
199 calendar.set(UCAL_MINUTE, minute_);
200 calendar.set(UCAL_SECOND, second_);
201 calendar.set(UCAL_MILLISECOND, milli_);
203 icu::SimpleDateFormat formatter(*pattern_, success);
204 icu::UnicodeString formatted_time;
205 formatter.format(calendar, formatted_time, NULL, success);
206 UTF16ToUTF8(formatted_time.getBuffer(),
207 static_cast<size_t>(formatted_time.length()),
208 &result);
209 if (success <= U_ZERO_ERROR)
210 return result;
212 LOG(WARNING) << "Calendar not created: error " << success;
213 return std::string();
216 void DateTimeFormatter::ExtractType(
217 const WebKit::WebDateTimeChooserParams& source) {
218 switch (source.type) {
219 case WebKit::WebDateTimeInputTypeDate:
220 type_ = ui::TEXT_INPUT_TYPE_DATE;
221 break;
222 case WebKit::WebDateTimeInputTypeDateTime:
223 type_ = ui::TEXT_INPUT_TYPE_DATE_TIME;
224 break;
225 case WebKit::WebDateTimeInputTypeDateTimeLocal:
226 type_ = ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL;
227 break;
228 case WebKit::WebDateTimeInputTypeMonth:
229 type_ = ui::TEXT_INPUT_TYPE_MONTH;
230 break;
231 case WebKit::WebDateTimeInputTypeTime:
232 type_ = ui::TEXT_INPUT_TYPE_TIME;
233 break;
234 case WebKit::WebDateTimeInputTypeWeek:
235 type_ = ui::TEXT_INPUT_TYPE_WEEK;
236 break;
237 case WebKit::WebDateTimeInputTypeNone:
238 default:
239 type_ = ui::TEXT_INPUT_TYPE_NONE;
243 // Not all fields are defined in all configurations and ICU might store
244 // garbage if success <= U_ZERO_ERROR so the output is sanitized here.
245 int DateTimeFormatter::ExtractValue(
246 const icu::Calendar* calendar, UCalendarDateFields value) const {
247 UErrorCode success = U_ZERO_ERROR;
248 int result = calendar->get(value, success);
249 return (success <= U_ZERO_ERROR) ? result : 0;
252 bool DateTimeFormatter::ParseValues() {
253 if (type_ == ui::TEXT_INPUT_TYPE_NONE) {
254 ClearAll();
255 return false;
258 if (formatted_string_.empty()) {
259 ClearAll();
260 return true;
263 UErrorCode success = U_ZERO_ERROR;
264 icu::UnicodeString icu_value = icu::UnicodeString::fromUTF8(
265 icu::StringPiece(formatted_string_.data(), formatted_string_.size()));
266 if (type_ > 0 && type_ <= ui::TEXT_INPUT_TYPE_MAX) {
267 const icu::UnicodeString pattern =
268 type_ == ui::TEXT_INPUT_TYPE_TIME ? time_pattern_ : patterns_[type_];
269 icu::SimpleDateFormat formatter(pattern, success);
270 formatter.parse(icu_value, success);
271 if (success <= U_ZERO_ERROR) {
272 const icu::Calendar* cal = formatter.getCalendar();
273 year_ = ExtractValue(cal, UCAL_YEAR);
274 month_ = ExtractValue(cal, UCAL_MONTH);
275 day_ = ExtractValue(cal, UCAL_DATE);
276 hour_ = ExtractValue(cal, UCAL_HOUR_OF_DAY); // 24h format
277 minute_ = ExtractValue(cal, UCAL_MINUTE);
278 second_ = ExtractValue(cal, UCAL_SECOND);
279 milli_ = ExtractValue(cal, UCAL_MILLISECOND);
280 week_year_ = ExtractValue(cal, UCAL_YEAR_WOY);
281 week_ = ExtractValue(cal, UCAL_WEEK_OF_YEAR);
285 return (success <= U_ZERO_ERROR);
288 void DateTimeFormatter::ClearAll() {
289 year_ = 0;
290 month_ = 0;
291 day_ = 0;
292 hour_ = 0;
293 minute_ = 0;
294 second_ = 0;
295 milli_ = 0;
296 week_year_ = 0;
297 week_ = 0;
300 } // namespace content