Skip to content

Commit eaf9c66

Browse files
author
Daniel Erat
committed
base: Make TimeDurationFormat* report failures.
Update base::TimeDurationFormat() and base::TimeDurationFormatWithSeconds() to return boolean "success" values and use out-params to return formatted strings. ICU errors were formerly swallowed, which makes it impossible for the UI to handle them gracefully or for developers to investigate their causes. BUG=698802,677043 Review-Url: https://codereview.chromium.org/2734883003 Cr-Commit-Position: refs/heads/master@{#455144} (cherry picked from commit efcd30c) Review-Url: https://codereview.chromium.org/2736363002 . Cr-Commit-Position: refs/branch-heads/3029@{#73} Cr-Branched-From: 939b32e-refs/heads/master@{#454471}
1 parent 498c3d6 commit eaf9c66

File tree

6 files changed

+116
-48
lines changed

6 files changed

+116
-48
lines changed

ash/common/system/chromeos/power/battery_notification.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,11 @@ std::unique_ptr<Notification> CreateNotification(
6262
} else if (PowerStatus::ShouldDisplayBatteryTime(time) &&
6363
!status.IsBatteryDischargingOnLinePower()) {
6464
if (status.IsBatteryCharging()) {
65+
base::string16 duration;
66+
if (!TimeDurationFormat(time, base::DURATION_WIDTH_NARROW, &duration))
67+
LOG(ERROR) << "Failed to format duration " << time.ToInternalValue();
6568
time_message = l10n_util::GetStringFUTF16(
66-
IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL,
67-
TimeDurationFormat(time, base::DURATION_WIDTH_NARROW));
69+
IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL, duration);
6870
} else {
6971
// This is a low battery warning prompting the user in minutes.
7072
time_message = ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING,

ash/common/system/chromeos/power/power_status_view.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,15 @@ void PowerStatusView::UpdateText() {
124124
: status.GetBatteryTimeToEmpty();
125125
if (PowerStatus::ShouldDisplayBatteryTime(time) &&
126126
!status.IsBatteryDischargingOnLinePower()) {
127+
base::string16 duration;
128+
if (!base::TimeDurationFormat(time, base::DURATION_WIDTH_NUMERIC,
129+
&duration))
130+
LOG(ERROR) << "Failed to format duration " << time.ToInternalValue();
127131
battery_time_status = l10n_util::GetStringFUTF16(
128132
status.IsBatteryCharging()
129133
? IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL_SHORT
130134
: IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_SHORT,
131-
TimeDurationFormat(time, base::DURATION_WIDTH_NUMERIC));
135+
duration);
132136
}
133137
}
134138
}

base/i18n/time_formatting.cc

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "base/logging.h"
1212
#include "base/strings/utf_string_conversions.h"
1313
#include "base/time/time.h"
14+
#include "third_party/icu/source/common/unicode/utypes.h"
1415
#include "third_party/icu/source/i18n/unicode/datefmt.h"
1516
#include "third_party/icu/source/i18n/unicode/dtitvfmt.h"
1617
#include "third_party/icu/source/i18n/unicode/dtptngen.h"
@@ -176,26 +177,51 @@ string16 TimeFormatWithPattern(const Time& time, const char* pattern) {
176177
return TimeFormat(&formatter, time);
177178
}
178179

179-
string16 TimeDurationFormat(const TimeDelta time,
180-
const DurationFormatWidth width) {
180+
bool TimeDurationFormat(const TimeDelta time,
181+
const DurationFormatWidth width,
182+
string16* out) {
183+
DCHECK(out);
181184
UErrorCode status = U_ZERO_ERROR;
182185
const int total_minutes = static_cast<int>(time.InSecondsF() / 60 + 0.5);
183186
const int hours = total_minutes / 60;
184187
const int minutes = total_minutes % 60;
185188
UMeasureFormatWidth u_width = DurationWidthToMeasureWidth(width);
186189

190+
// TODO(derat): Delete the |status| checks and LOG(ERROR) calls throughout
191+
// this function once the cause of http://crbug.com/677043 is tracked down.
187192
const icu::Measure measures[] = {
188193
icu::Measure(hours, icu::MeasureUnit::createHour(status), status),
189194
icu::Measure(minutes, icu::MeasureUnit::createMinute(status), status)};
195+
if (U_FAILURE(status)) {
196+
LOG(ERROR) << "Creating MeasureUnit or Measure for " << hours << "h"
197+
<< minutes << "m failed: " << u_errorName(status);
198+
return false;
199+
}
200+
190201
icu::MeasureFormat measure_format(icu::Locale::getDefault(), u_width, status);
202+
if (U_FAILURE(status)) {
203+
LOG(ERROR) << "Creating MeasureFormat for "
204+
<< icu::Locale::getDefault().getName()
205+
<< " failed: " << u_errorName(status);
206+
return false;
207+
}
208+
191209
icu::UnicodeString formatted;
192210
icu::FieldPosition ignore(icu::FieldPosition::DONT_CARE);
193211
measure_format.formatMeasures(measures, 2, formatted, ignore, status);
194-
return base::string16(formatted.getBuffer(), formatted.length());
212+
if (U_FAILURE(status)) {
213+
LOG(ERROR) << "formatMeasures failed: " << u_errorName(status);
214+
return false;
215+
}
216+
217+
*out = base::string16(formatted.getBuffer(), formatted.length());
218+
return true;
195219
}
196220

197-
string16 TimeDurationFormatWithSeconds(const TimeDelta time,
198-
const DurationFormatWidth width) {
221+
bool TimeDurationFormatWithSeconds(const TimeDelta time,
222+
const DurationFormatWidth width,
223+
string16* out) {
224+
DCHECK(out);
199225
UErrorCode status = U_ZERO_ERROR;
200226
const int64_t total_seconds = static_cast<int>(time.InSecondsF() + 0.5);
201227
const int hours = total_seconds / 3600;
@@ -211,7 +237,8 @@ string16 TimeDurationFormatWithSeconds(const TimeDelta time,
211237
icu::UnicodeString formatted;
212238
icu::FieldPosition ignore(icu::FieldPosition::DONT_CARE);
213239
measure_format.formatMeasures(measures, 3, formatted, ignore, status);
214-
return base::string16(formatted.getBuffer(), formatted.length());
240+
*out = base::string16(formatted.getBuffer(), formatted.length());
241+
return U_SUCCESS(status) == TRUE;
215242
}
216243

217244
string16 DateIntervalFormat(const Time& begin_time,

base/i18n/time_formatting.h

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#ifndef BASE_I18N_TIME_FORMATTING_H_
99
#define BASE_I18N_TIME_FORMATTING_H_
1010

11+
#include "base/compiler_specific.h"
1112
#include "base/i18n/base_i18n_export.h"
1213
#include "base/strings/string16.h"
1314

@@ -46,6 +47,10 @@ enum DateFormat {
4647
DATE_FORMAT_MONTH_WEEKDAY_DAY,
4748
};
4849

50+
// TODO([email protected]): Update all of these functions to return boolean
51+
// "success" values and use out-params for formatted strings:
52+
// http://crbug.com/698802
53+
4954
// Returns the time of day, e.g., "3:07 PM".
5055
BASE_I18N_EXPORT string16 TimeFormatTimeOfDay(const Time& time);
5156

@@ -95,28 +100,31 @@ BASE_I18N_EXPORT string16 TimeFormatWithPattern(const Time& time,
95100
const char* pattern);
96101

97102
// Formats a time duration of hours and minutes into various formats, e.g.,
98-
// "3:07" or "3 hours, 7 minutes". See DurationFormatWidth for details.
103+
// "3:07" or "3 hours, 7 minutes", and returns true on success. See
104+
// DurationFormatWidth for details.
99105
//
100106
// Please don't use width = DURATION_WIDTH_NUMERIC when the time duration
101107
// can possibly be larger than 24h, as the hour value will be cut below 24
102108
// after formatting.
103109
// TODO(chengx): fix function output when width = DURATION_WIDTH_NUMERIC
104110
// (http://crbug.com/675791)
105-
BASE_I18N_EXPORT string16 TimeDurationFormat(const TimeDelta time,
106-
const DurationFormatWidth width);
111+
BASE_I18N_EXPORT bool TimeDurationFormat(const TimeDelta time,
112+
const DurationFormatWidth width,
113+
string16* out) WARN_UNUSED_RESULT;
107114

108115
// Formats a time duration of hours, minutes and seconds into various formats,
109-
// e.g., "3:07:30" or "3 hours, 7 minutes, 30 seconds". See DurationFormatWidth
110-
// for details.
116+
// e.g., "3:07:30" or "3 hours, 7 minutes, 30 seconds", and returns true on
117+
// success. See DurationFormatWidth for details.
111118
//
112119
// Please don't use width = DURATION_WIDTH_NUMERIC when the time duration
113120
// can possibly be larger than 24h, as the hour value will be cut below 24
114121
// after formatting.
115122
// TODO(chengx): fix function output when width = DURATION_WIDTH_NUMERIC
116123
// (http://crbug.com/675791)
117-
BASE_I18N_EXPORT string16
118-
TimeDurationFormatWithSeconds(const TimeDelta time,
119-
const DurationFormatWidth width);
124+
BASE_I18N_EXPORT bool TimeDurationFormatWithSeconds(
125+
const TimeDelta time,
126+
const DurationFormatWidth width,
127+
string16* out) WARN_UNUSED_RESULT;
120128

121129
// Formats a date interval into various formats, e.g. "2 December - 4 December"
122130
// or "March 2016 - December 2016". See DateFormat for details.

base/i18n/time_formatting_unittest.cc

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const Time::Exploded kTestDateTimeExploded = {
2727
// Returns difference between the local time and GMT formatted as string.
2828
// This function gets |time| because the difference depends on time,
2929
// see https://en.wikipedia.org/wiki/Daylight_saving_time for details.
30-
base::string16 GetShortTimeZone(const Time& time) {
30+
string16 GetShortTimeZone(const Time& time) {
3131
UErrorCode status = U_ZERO_ERROR;
3232
std::unique_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
3333
std::unique_ptr<icu::TimeZoneFormat> zone_formatter(
@@ -37,7 +37,30 @@ base::string16 GetShortTimeZone(const Time& time) {
3737
zone_formatter->format(UTZFMT_STYLE_SPECIFIC_SHORT, *zone,
3838
static_cast<UDate>(time.ToDoubleT() * 1000),
3939
name, nullptr);
40-
return base::string16(name.getBuffer(), name.length());
40+
return string16(name.getBuffer(), name.length());
41+
}
42+
43+
// Calls TimeDurationFormat() with |delta| and |width| and returns the resulting
44+
// string. On failure, adds a failed expectation and returns an empty string.
45+
string16 TimeDurationFormatString(const TimeDelta& delta,
46+
DurationFormatWidth width) {
47+
string16 str;
48+
EXPECT_TRUE(TimeDurationFormat(delta, width, &str))
49+
<< "Failed to format " << delta.ToInternalValue() << " with width "
50+
<< width;
51+
return str;
52+
}
53+
54+
// Calls TimeDurationFormatWithSeconds() with |delta| and |width| and returns
55+
// the resulting string. On failure, adds a failed expectation and returns an
56+
// empty string.
57+
string16 TimeDurationFormatWithSecondsString(const TimeDelta& delta,
58+
DurationFormatWidth width) {
59+
string16 str;
60+
EXPECT_TRUE(TimeDurationFormatWithSeconds(delta, width, &str))
61+
<< "Failed to format " << delta.ToInternalValue() << " with width "
62+
<< width;
63+
return str;
4164
}
4265

4366
#if defined(OS_ANDROID)
@@ -254,24 +277,24 @@ TEST(TimeFormattingTest, TimeDurationFormat) {
254277
// US English.
255278
i18n::SetICUDefaultLocale("en_US");
256279
EXPECT_EQ(ASCIIToUTF16("15 hours, 42 minutes"),
257-
TimeDurationFormat(delta, DURATION_WIDTH_WIDE));
280+
TimeDurationFormatString(delta, DURATION_WIDTH_WIDE));
258281
EXPECT_EQ(ASCIIToUTF16("15 hr, 42 min"),
259-
TimeDurationFormat(delta, DURATION_WIDTH_SHORT));
282+
TimeDurationFormatString(delta, DURATION_WIDTH_SHORT));
260283
EXPECT_EQ(ASCIIToUTF16("15h 42m"),
261-
TimeDurationFormat(delta, DURATION_WIDTH_NARROW));
284+
TimeDurationFormatString(delta, DURATION_WIDTH_NARROW));
262285
EXPECT_EQ(ASCIIToUTF16("15:42"),
263-
TimeDurationFormat(delta, DURATION_WIDTH_NUMERIC));
286+
TimeDurationFormatString(delta, DURATION_WIDTH_NUMERIC));
264287

265288
// Danish, with Latin alphabet but different abbreviations and punctuation.
266289
i18n::SetICUDefaultLocale("da");
267290
EXPECT_EQ(ASCIIToUTF16("15 timer og 42 minutter"),
268-
TimeDurationFormat(delta, DURATION_WIDTH_WIDE));
291+
TimeDurationFormatString(delta, DURATION_WIDTH_WIDE));
269292
EXPECT_EQ(ASCIIToUTF16("15 t og 42 min."),
270-
TimeDurationFormat(delta, DURATION_WIDTH_SHORT));
293+
TimeDurationFormatString(delta, DURATION_WIDTH_SHORT));
271294
EXPECT_EQ(ASCIIToUTF16("15 t og 42 min"),
272-
TimeDurationFormat(delta, DURATION_WIDTH_NARROW));
295+
TimeDurationFormatString(delta, DURATION_WIDTH_NARROW));
273296
EXPECT_EQ(ASCIIToUTF16("15.42"),
274-
TimeDurationFormat(delta, DURATION_WIDTH_NUMERIC));
297+
TimeDurationFormatString(delta, DURATION_WIDTH_NUMERIC));
275298

276299
// Persian, with non-Arabic numbers.
277300
i18n::SetICUDefaultLocale("fa");
@@ -285,10 +308,11 @@ TEST(TimeFormattingTest, TimeDurationFormat) {
285308
L"\x6f1\x6f5\x20\x633\x627\x639\x62a\x20\x6f4\x6f2\x20\x62f\x642\x6cc"
286309
L"\x642\x647");
287310
string16 fa_numeric = WideToUTF16(L"\x6f1\x6f5\x3a\x6f4\x6f2");
288-
EXPECT_EQ(fa_wide, TimeDurationFormat(delta, DURATION_WIDTH_WIDE));
289-
EXPECT_EQ(fa_short, TimeDurationFormat(delta, DURATION_WIDTH_SHORT));
290-
EXPECT_EQ(fa_narrow, TimeDurationFormat(delta, DURATION_WIDTH_NARROW));
291-
EXPECT_EQ(fa_numeric, TimeDurationFormat(delta, DURATION_WIDTH_NUMERIC));
311+
EXPECT_EQ(fa_wide, TimeDurationFormatString(delta, DURATION_WIDTH_WIDE));
312+
EXPECT_EQ(fa_short, TimeDurationFormatString(delta, DURATION_WIDTH_SHORT));
313+
EXPECT_EQ(fa_narrow, TimeDurationFormatString(delta, DURATION_WIDTH_NARROW));
314+
EXPECT_EQ(fa_numeric,
315+
TimeDurationFormatString(delta, DURATION_WIDTH_NUMERIC));
292316
}
293317

294318
TEST(TimeFormattingTest, TimeDurationFormatWithSeconds) {
@@ -300,44 +324,44 @@ TEST(TimeFormattingTest, TimeDurationFormatWithSeconds) {
300324
// Test different formats.
301325
TimeDelta delta = TimeDelta::FromSeconds(15 * 3600 + 42 * 60 + 30);
302326
EXPECT_EQ(ASCIIToUTF16("15 hours, 42 minutes, 30 seconds"),
303-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_WIDE));
327+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_WIDE));
304328
EXPECT_EQ(ASCIIToUTF16("15 hr, 42 min, 30 sec"),
305-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_SHORT));
329+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_SHORT));
306330
EXPECT_EQ(ASCIIToUTF16("15h 42m 30s"),
307-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_NARROW));
331+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NARROW));
308332
EXPECT_EQ(ASCIIToUTF16("15:42:30"),
309-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_NUMERIC));
333+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NUMERIC));
310334

311335
// Test edge case when hour >= 100.
312336
delta = TimeDelta::FromSeconds(125 * 3600 + 42 * 60 + 30);
313337
EXPECT_EQ(ASCIIToUTF16("125 hours, 42 minutes, 30 seconds"),
314-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_WIDE));
338+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_WIDE));
315339
EXPECT_EQ(ASCIIToUTF16("125 hr, 42 min, 30 sec"),
316-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_SHORT));
340+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_SHORT));
317341
EXPECT_EQ(ASCIIToUTF16("125h 42m 30s"),
318-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_NARROW));
342+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NARROW));
319343

320344
// Test edge case when minute = 0.
321345
delta = TimeDelta::FromSeconds(15 * 3600 + 0 * 60 + 30);
322346
EXPECT_EQ(ASCIIToUTF16("15 hours, 0 minutes, 30 seconds"),
323-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_WIDE));
347+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_WIDE));
324348
EXPECT_EQ(ASCIIToUTF16("15 hr, 0 min, 30 sec"),
325-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_SHORT));
349+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_SHORT));
326350
EXPECT_EQ(ASCIIToUTF16("15h 0m 30s"),
327-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_NARROW));
351+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NARROW));
328352
EXPECT_EQ(ASCIIToUTF16("15:00:30"),
329-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_NUMERIC));
353+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NUMERIC));
330354

331355
// Test edge case when second = 0.
332356
delta = TimeDelta::FromSeconds(15 * 3600 + 42 * 60 + 0);
333357
EXPECT_EQ(ASCIIToUTF16("15 hours, 42 minutes, 0 seconds"),
334-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_WIDE));
358+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_WIDE));
335359
EXPECT_EQ(ASCIIToUTF16("15 hr, 42 min, 0 sec"),
336-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_SHORT));
360+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_SHORT));
337361
EXPECT_EQ(ASCIIToUTF16("15h 42m 0s"),
338-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_NARROW));
362+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NARROW));
339363
EXPECT_EQ(ASCIIToUTF16("15:42:00"),
340-
TimeDurationFormatWithSeconds(delta, DURATION_WIDTH_NUMERIC));
364+
TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NUMERIC));
341365
}
342366

343367
TEST(TimeFormattingTest, TimeIntervalFormat) {

chrome/browser/ui/task_manager/task_manager_table_model.cc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,11 @@ class TaskManagerValuesStringifier {
141141
if (cpu_time.is_zero())
142142
return n_a_string_;
143143

144-
return base::TimeDurationFormatWithSeconds(cpu_time,
145-
base::DURATION_WIDTH_NARROW);
144+
base::string16 duration;
145+
return base::TimeDurationFormatWithSeconds(
146+
cpu_time, base::DURATION_WIDTH_NARROW, &duration)
147+
? duration
148+
: n_a_string_;
146149
}
147150

148151
base::string16 GetMemoryUsageText(int64_t memory_usage, bool has_duplicates) {

0 commit comments

Comments
 (0)