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"
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
20 for (int i
= 0 ; i
<= ui::TEXT_INPUT_TYPE_MAX
; ++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
,
52 const char* pattern
= "HH:mm";
56 icu::UnicodeString icu_value
= icu::UnicodeString::fromUTF8(
57 icu::StringPiece(value
.data(), value
.size()));
58 const char* last_pattern
= pattern
;
60 if (!TryPattern(pattern
, icu_value
))
64 last_pattern
= pattern
;
65 pattern
= "HH:mm:ss.S";
66 if (!TryPattern(pattern
, icu_value
))
68 last_pattern
= pattern
;
69 pattern
= "HH:mm:ss.SS";
70 if (!TryPattern(pattern
, icu_value
))
72 last_pattern
= pattern
;
73 pattern
= "HH:mm:ss.SSS";
74 if (!TryPattern(pattern
, icu_value
))
79 DateTimeFormatter::DateTimeFormatter(
80 const WebKit::WebDateTimeChooserParams
& source
)
81 : formatted_string_(source
.currentValue
.utf8()) {
83 if (source
.type
== WebKit::WebDateTimeInputTypeTime
)
85 FindLongestTimePatternWhichMatches(formatted_string_
, source
.step
);
88 type_
= ui::TEXT_INPUT_TYPE_NONE
;
90 LOG(WARNING
) << "Problems parsing input <" << formatted_string_
<< ">";
94 DateTimeFormatter::DateTimeFormatter(ui::TextInputType type
,
112 week_year_(week_year
),
115 if (type_
== ui::TEXT_INPUT_TYPE_TIME
&& (second
!= 0 || 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";
123 time_pattern_
= "HH:mm:ss.SSS";
124 pattern_
= &time_pattern_
;
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 {
140 int DateTimeFormatter::GetMonth() const {
144 int DateTimeFormatter::GetDay() const {
148 int DateTimeFormatter::GetHour() const {
152 int DateTimeFormatter::GetMinute() const {
156 int DateTimeFormatter::GetSecond() const {
160 int DateTimeFormatter::GetMilli() const { return milli_
; }
162 int DateTimeFormatter::GetWeekYear() const { return week_year_
; }
164 int DateTimeFormatter::GetWeek() const {
168 ui::TextInputType
DateTimeFormatter::GetType() const {
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();
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
191 calendar
.setMinimalDaysInFirstWeek(4);
192 calendar
.set(UCAL_YEAR_WOY
, week_year_
);
193 calendar
.set(UCAL_WEEK_OF_YEAR
, week_
);
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()),
209 if (success
<= U_ZERO_ERROR
)
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
;
222 case WebKit::WebDateTimeInputTypeDateTime
:
223 type_
= ui::TEXT_INPUT_TYPE_DATE_TIME
;
225 case WebKit::WebDateTimeInputTypeDateTimeLocal
:
226 type_
= ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL
;
228 case WebKit::WebDateTimeInputTypeMonth
:
229 type_
= ui::TEXT_INPUT_TYPE_MONTH
;
231 case WebKit::WebDateTimeInputTypeTime
:
232 type_
= ui::TEXT_INPUT_TYPE_TIME
;
234 case WebKit::WebDateTimeInputTypeWeek
:
235 type_
= ui::TEXT_INPUT_TYPE_WEEK
;
237 case WebKit::WebDateTimeInputTypeNone
:
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
) {
258 if (formatted_string_
.empty()) {
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() {
300 } // namespace content