1 // Copyright (c) 2011 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 "ui/base/l10n/time_format.h"
9 #include "base/lazy_instance.h"
10 #include "base/logging.h"
11 #include "base/strings/string_util.h"
12 #include "base/time/time.h"
13 #include "third_party/icu/source/common/unicode/unistr.h"
14 #include "ui/base/l10n/formatter.h"
15 #include "ui/base/l10n/l10n_util.h"
16 #include "ui/base/ui_base_export.h"
17 #include "ui/strings/grit/ui_strings.h"
20 using base::TimeDelta
;
25 UI_BASE_EXPORT
base::LazyInstance
<FormatterContainer
> g_container
=
26 LAZY_INSTANCE_INITIALIZER
;
29 base::string16
TimeFormat::Simple(TimeFormat::Format format
,
30 TimeFormat::Length length
,
31 const base::TimeDelta
& delta
) {
32 return Detailed(format
, length
, 0, delta
);
36 base::string16
TimeFormat::Detailed(TimeFormat::Format format
,
37 TimeFormat::Length length
,
39 const base::TimeDelta
& delta
) {
40 if (delta
< TimeDelta::FromSeconds(0)) {
41 NOTREACHED() << "Negative duration";
42 return base::string16();
45 // Negative cutoff: always use two-value format.
47 cutoff
= std::numeric_limits
<int>::max();
49 const TimeDelta
one_minute(TimeDelta::FromMinutes(1));
50 const TimeDelta
one_hour(TimeDelta::FromHours(1));
51 const TimeDelta
one_day(TimeDelta::FromDays(1));
52 const TimeDelta
half_second(TimeDelta::FromMilliseconds(500));
53 const TimeDelta
half_minute(TimeDelta::FromSeconds(30));
54 const TimeDelta
half_hour(TimeDelta::FromMinutes(30));
55 const TimeDelta
half_day(TimeDelta::FromHours(12));
57 // Rationale: Start by determining major (first) unit, then add minor (second)
58 // unit if mandated by |cutoff|.
59 icu::UnicodeString time_string
;
60 const Formatter
* formatter
= g_container
.Get().Get(format
, length
);
61 if (delta
< one_minute
- half_second
) {
62 // Anything up to 59.500 seconds is formatted as seconds.
63 const int seconds
= static_cast<int>((delta
+ half_second
).InSeconds());
64 formatter
->Format(Formatter::UNIT_SEC
, seconds
, &time_string
);
66 } else if (delta
< one_hour
- (cutoff
< 60 ? half_minute
: half_second
)) {
67 // Anything up to 59.5 minutes (respectively 59:59.500 when |cutoff| permits
68 // two-value output) is formatted as minutes (respectively minutes and
70 if (delta
>= cutoff
* one_minute
- half_second
) {
71 const int minutes
= (delta
+ half_minute
).InMinutes();
72 formatter
->Format(Formatter::UNIT_MIN
, minutes
, &time_string
);
74 const int minutes
= (delta
+ half_second
).InMinutes();
75 const int seconds
= static_cast<int>(
76 (delta
+ half_second
).InSeconds() % 60);
77 formatter
->Format(Formatter::TWO_UNITS_MIN_SEC
,
78 minutes
, seconds
, &time_string
);
81 } else if (delta
< one_day
- (cutoff
< 24 ? half_hour
: half_minute
)) {
82 // Anything up to 23.5 hours (respectively 23:59:30.000 when |cutoff|
83 // permits two-value output) is formatted as hours (respectively hours and
85 if (delta
>= cutoff
* one_hour
- half_minute
) {
86 const int hours
= (delta
+ half_hour
).InHours();
87 formatter
->Format(Formatter::UNIT_HOUR
, hours
, &time_string
);
89 const int hours
= (delta
+ half_minute
).InHours();
90 const int minutes
= (delta
+ half_minute
).InMinutes() % 60;
91 formatter
->Format(Formatter::TWO_UNITS_HOUR_MIN
,
92 hours
, minutes
, &time_string
);
96 // Anything bigger is formatted as days (respectively days and hours).
97 if (delta
>= cutoff
* one_day
- half_hour
) {
98 const int days
= (delta
+ half_day
).InDays();
99 formatter
->Format(Formatter::UNIT_DAY
, days
, &time_string
);
101 const int days
= (delta
+ half_hour
).InDays();
102 const int hours
= (delta
+ half_hour
).InHours() % 24;
103 formatter
->Format(Formatter::TWO_UNITS_DAY_HOUR
,
104 days
, hours
, &time_string
);
108 const int capacity
= time_string
.length() + 1;
109 DCHECK_GT(capacity
, 1);
110 base::string16 result
;
111 UErrorCode error
= U_ZERO_ERROR
;
112 time_string
.extract(static_cast<UChar
*>(base::WriteInto(&result
, capacity
)),
114 DCHECK(U_SUCCESS(error
));
119 base::string16
TimeFormat::RelativeDate(
121 const Time
* optional_midnight_today
) {
122 Time midnight_today
= optional_midnight_today
? *optional_midnight_today
:
123 Time::Now().LocalMidnight();
124 TimeDelta day
= TimeDelta::FromMicroseconds(Time::kMicrosecondsPerDay
);
125 Time tomorrow
= midnight_today
+ day
;
126 Time yesterday
= midnight_today
- day
;
127 if (time
>= tomorrow
)
128 return base::string16();
129 else if (time
>= midnight_today
)
130 return l10n_util::GetStringUTF16(IDS_PAST_TIME_TODAY
);
131 else if (time
>= yesterday
)
132 return l10n_util::GetStringUTF16(IDS_PAST_TIME_YESTERDAY
);
133 return base::string16();