xref: /aosp_15_r20/external/cronet/base/i18n/time_formatting.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2011 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker #include "base/i18n/time_formatting.h"
6*6777b538SAndroid Build Coastguard Worker 
7*6777b538SAndroid Build Coastguard Worker #include <stddef.h>
8*6777b538SAndroid Build Coastguard Worker 
9*6777b538SAndroid Build Coastguard Worker #include <memory>
10*6777b538SAndroid Build Coastguard Worker #include <string>
11*6777b538SAndroid Build Coastguard Worker #include <string_view>
12*6777b538SAndroid Build Coastguard Worker 
13*6777b538SAndroid Build Coastguard Worker #include "base/i18n/unicodestring.h"
14*6777b538SAndroid Build Coastguard Worker #include "base/logging.h"
15*6777b538SAndroid Build Coastguard Worker #include "base/notreached.h"
16*6777b538SAndroid Build Coastguard Worker #include "base/numerics/safe_conversions.h"
17*6777b538SAndroid Build Coastguard Worker #include "base/strings/utf_string_conversions.h"
18*6777b538SAndroid Build Coastguard Worker #include "base/time/time.h"
19*6777b538SAndroid Build Coastguard Worker #include "build/chromeos_buildflags.h"
20*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/common/unicode/locid.h"
21*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/common/unicode/utypes.h"
22*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/i18n/unicode/datefmt.h"
23*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/i18n/unicode/dtitvfmt.h"
24*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/i18n/unicode/dtptngen.h"
25*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/i18n/unicode/fmtable.h"
26*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/i18n/unicode/measfmt.h"
27*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
28*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/i18n/unicode/timezone.h"
29*6777b538SAndroid Build Coastguard Worker 
30*6777b538SAndroid Build Coastguard Worker namespace base {
31*6777b538SAndroid Build Coastguard Worker namespace {
32*6777b538SAndroid Build Coastguard Worker 
ToUDate(const Time & time)33*6777b538SAndroid Build Coastguard Worker UDate ToUDate(const Time& time) {
34*6777b538SAndroid Build Coastguard Worker   // TODO(crbug.com/1392437): Consider using the `...IgnoringNull` variant and
35*6777b538SAndroid Build Coastguard Worker   // adding a `CHECK(!time.is_null())`; trying to format a null Time as a string
36*6777b538SAndroid Build Coastguard Worker   // is almost certainly an indication that the caller has made a mistake.
37*6777b538SAndroid Build Coastguard Worker   return time.InMillisecondsFSinceUnixEpoch();
38*6777b538SAndroid Build Coastguard Worker }
39*6777b538SAndroid Build Coastguard Worker 
TimeFormat(const icu::DateFormat & formatter,const Time & time)40*6777b538SAndroid Build Coastguard Worker std::u16string TimeFormat(const icu::DateFormat& formatter, const Time& time) {
41*6777b538SAndroid Build Coastguard Worker   icu::UnicodeString date_string;
42*6777b538SAndroid Build Coastguard Worker 
43*6777b538SAndroid Build Coastguard Worker   formatter.format(ToUDate(time), date_string);
44*6777b538SAndroid Build Coastguard Worker   return i18n::UnicodeStringToString16(date_string);
45*6777b538SAndroid Build Coastguard Worker }
46*6777b538SAndroid Build Coastguard Worker 
TimeFormatWithoutAmPm(const icu::DateFormat * formatter,const Time & time)47*6777b538SAndroid Build Coastguard Worker std::u16string TimeFormatWithoutAmPm(const icu::DateFormat* formatter,
48*6777b538SAndroid Build Coastguard Worker                                      const Time& time) {
49*6777b538SAndroid Build Coastguard Worker   DCHECK(formatter);
50*6777b538SAndroid Build Coastguard Worker   icu::UnicodeString time_string;
51*6777b538SAndroid Build Coastguard Worker 
52*6777b538SAndroid Build Coastguard Worker   icu::FieldPosition ampm_field(icu::DateFormat::kAmPmField);
53*6777b538SAndroid Build Coastguard Worker   formatter->format(ToUDate(time), time_string, ampm_field);
54*6777b538SAndroid Build Coastguard Worker   int ampm_length = ampm_field.getEndIndex() - ampm_field.getBeginIndex();
55*6777b538SAndroid Build Coastguard Worker   if (ampm_length) {
56*6777b538SAndroid Build Coastguard Worker     int begin = ampm_field.getBeginIndex();
57*6777b538SAndroid Build Coastguard Worker     // Doesn't include any spacing before the field.
58*6777b538SAndroid Build Coastguard Worker     if (begin)
59*6777b538SAndroid Build Coastguard Worker       begin--;
60*6777b538SAndroid Build Coastguard Worker     time_string.removeBetween(begin, ampm_field.getEndIndex());
61*6777b538SAndroid Build Coastguard Worker   }
62*6777b538SAndroid Build Coastguard Worker   return i18n::UnicodeStringToString16(time_string);
63*6777b538SAndroid Build Coastguard Worker }
64*6777b538SAndroid Build Coastguard Worker 
CreateSimpleDateFormatter(std::string_view pattern,bool generate_pattern=true,const icu::Locale & locale=icu::Locale::getDefault ())65*6777b538SAndroid Build Coastguard Worker icu::SimpleDateFormat CreateSimpleDateFormatter(
66*6777b538SAndroid Build Coastguard Worker     std::string_view pattern,
67*6777b538SAndroid Build Coastguard Worker     bool generate_pattern = true,
68*6777b538SAndroid Build Coastguard Worker     const icu::Locale& locale = icu::Locale::getDefault()) {
69*6777b538SAndroid Build Coastguard Worker   UErrorCode status = U_ZERO_ERROR;
70*6777b538SAndroid Build Coastguard Worker   icu::UnicodeString generated_pattern(pattern.data(), pattern.length());
71*6777b538SAndroid Build Coastguard Worker 
72*6777b538SAndroid Build Coastguard Worker   if (generate_pattern) {
73*6777b538SAndroid Build Coastguard Worker     // Generate a locale-dependent format pattern. The generator will take
74*6777b538SAndroid Build Coastguard Worker     // care of locale-dependent formatting issues like which separator to
75*6777b538SAndroid Build Coastguard Worker     // use (some locales use '.' instead of ':'), and where to put the am/pm
76*6777b538SAndroid Build Coastguard Worker     // marker.
77*6777b538SAndroid Build Coastguard Worker     std::unique_ptr<icu::DateTimePatternGenerator> generator(
78*6777b538SAndroid Build Coastguard Worker         icu::DateTimePatternGenerator::createInstance(status));
79*6777b538SAndroid Build Coastguard Worker     DCHECK(U_SUCCESS(status));
80*6777b538SAndroid Build Coastguard Worker     generated_pattern = generator->getBestPattern(generated_pattern, status);
81*6777b538SAndroid Build Coastguard Worker     DCHECK(U_SUCCESS(status));
82*6777b538SAndroid Build Coastguard Worker   }
83*6777b538SAndroid Build Coastguard Worker 
84*6777b538SAndroid Build Coastguard Worker   // Then, format the time using the desired pattern.
85*6777b538SAndroid Build Coastguard Worker   icu::SimpleDateFormat formatter(generated_pattern, locale, status);
86*6777b538SAndroid Build Coastguard Worker   DCHECK(U_SUCCESS(status));
87*6777b538SAndroid Build Coastguard Worker 
88*6777b538SAndroid Build Coastguard Worker   return formatter;
89*6777b538SAndroid Build Coastguard Worker }
90*6777b538SAndroid Build Coastguard Worker 
DurationWidthToMeasureWidth(DurationFormatWidth width)91*6777b538SAndroid Build Coastguard Worker UMeasureFormatWidth DurationWidthToMeasureWidth(DurationFormatWidth width) {
92*6777b538SAndroid Build Coastguard Worker   switch (width) {
93*6777b538SAndroid Build Coastguard Worker     case DURATION_WIDTH_WIDE: return UMEASFMT_WIDTH_WIDE;
94*6777b538SAndroid Build Coastguard Worker     case DURATION_WIDTH_SHORT: return UMEASFMT_WIDTH_SHORT;
95*6777b538SAndroid Build Coastguard Worker     case DURATION_WIDTH_NARROW: return UMEASFMT_WIDTH_NARROW;
96*6777b538SAndroid Build Coastguard Worker     case DURATION_WIDTH_NUMERIC: return UMEASFMT_WIDTH_NUMERIC;
97*6777b538SAndroid Build Coastguard Worker   }
98*6777b538SAndroid Build Coastguard Worker   NOTREACHED();
99*6777b538SAndroid Build Coastguard Worker   return UMEASFMT_WIDTH_COUNT;
100*6777b538SAndroid Build Coastguard Worker }
101*6777b538SAndroid Build Coastguard Worker 
DateFormatToString(DateFormat format)102*6777b538SAndroid Build Coastguard Worker const char* DateFormatToString(DateFormat format) {
103*6777b538SAndroid Build Coastguard Worker   switch (format) {
104*6777b538SAndroid Build Coastguard Worker     case DATE_FORMAT_YEAR_MONTH:
105*6777b538SAndroid Build Coastguard Worker       return UDAT_YEAR_MONTH;
106*6777b538SAndroid Build Coastguard Worker     case DATE_FORMAT_MONTH_WEEKDAY_DAY:
107*6777b538SAndroid Build Coastguard Worker       return UDAT_MONTH_WEEKDAY_DAY;
108*6777b538SAndroid Build Coastguard Worker   }
109*6777b538SAndroid Build Coastguard Worker   NOTREACHED();
110*6777b538SAndroid Build Coastguard Worker   return UDAT_YEAR_MONTH_DAY;
111*6777b538SAndroid Build Coastguard Worker }
112*6777b538SAndroid Build Coastguard Worker 
113*6777b538SAndroid Build Coastguard Worker }  // namespace
114*6777b538SAndroid Build Coastguard Worker 
TimeFormatTimeOfDay(const Time & time)115*6777b538SAndroid Build Coastguard Worker std::u16string TimeFormatTimeOfDay(const Time& time) {
116*6777b538SAndroid Build Coastguard Worker   // We can omit the locale parameter because the default should match
117*6777b538SAndroid Build Coastguard Worker   // Chrome's application locale.
118*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<icu::DateFormat> formatter(
119*6777b538SAndroid Build Coastguard Worker       icu::DateFormat::createTimeInstance(icu::DateFormat::kShort));
120*6777b538SAndroid Build Coastguard Worker   return TimeFormat(*formatter, time);
121*6777b538SAndroid Build Coastguard Worker }
122*6777b538SAndroid Build Coastguard Worker 
TimeFormatTimeOfDayWithMilliseconds(const Time & time)123*6777b538SAndroid Build Coastguard Worker std::u16string TimeFormatTimeOfDayWithMilliseconds(const Time& time) {
124*6777b538SAndroid Build Coastguard Worker   icu::SimpleDateFormat formatter = CreateSimpleDateFormatter("HmsSSS");
125*6777b538SAndroid Build Coastguard Worker   return TimeFormatWithoutAmPm(&formatter, time);
126*6777b538SAndroid Build Coastguard Worker }
127*6777b538SAndroid Build Coastguard Worker 
TimeFormatTimeOfDayWithHourClockType(const Time & time,HourClockType type,AmPmClockType ampm)128*6777b538SAndroid Build Coastguard Worker std::u16string TimeFormatTimeOfDayWithHourClockType(const Time& time,
129*6777b538SAndroid Build Coastguard Worker                                                     HourClockType type,
130*6777b538SAndroid Build Coastguard Worker                                                     AmPmClockType ampm) {
131*6777b538SAndroid Build Coastguard Worker   // Just redirect to the normal function if the default type matches the
132*6777b538SAndroid Build Coastguard Worker   // given type.
133*6777b538SAndroid Build Coastguard Worker   HourClockType default_type = GetHourClockType();
134*6777b538SAndroid Build Coastguard Worker   if (default_type == type && (type == k24HourClock || ampm == kKeepAmPm)) {
135*6777b538SAndroid Build Coastguard Worker     return TimeFormatTimeOfDay(time);
136*6777b538SAndroid Build Coastguard Worker   }
137*6777b538SAndroid Build Coastguard Worker 
138*6777b538SAndroid Build Coastguard Worker   const char* base_pattern = (type == k12HourClock ? "ahm" : "Hm");
139*6777b538SAndroid Build Coastguard Worker   icu::SimpleDateFormat formatter = CreateSimpleDateFormatter(base_pattern);
140*6777b538SAndroid Build Coastguard Worker 
141*6777b538SAndroid Build Coastguard Worker   return (ampm == kKeepAmPm) ? TimeFormat(formatter, time)
142*6777b538SAndroid Build Coastguard Worker                              : TimeFormatWithoutAmPm(&formatter, time);
143*6777b538SAndroid Build Coastguard Worker }
144*6777b538SAndroid Build Coastguard Worker 
TimeFormatShortDate(const Time & time)145*6777b538SAndroid Build Coastguard Worker std::u16string TimeFormatShortDate(const Time& time) {
146*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<icu::DateFormat> formatter(
147*6777b538SAndroid Build Coastguard Worker       icu::DateFormat::createDateInstance(icu::DateFormat::kMedium));
148*6777b538SAndroid Build Coastguard Worker   return TimeFormat(*formatter, time);
149*6777b538SAndroid Build Coastguard Worker }
150*6777b538SAndroid Build Coastguard Worker 
TimeFormatShortDateNumeric(const Time & time)151*6777b538SAndroid Build Coastguard Worker std::u16string TimeFormatShortDateNumeric(const Time& time) {
152*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<icu::DateFormat> formatter(
153*6777b538SAndroid Build Coastguard Worker       icu::DateFormat::createDateInstance(icu::DateFormat::kShort));
154*6777b538SAndroid Build Coastguard Worker   return TimeFormat(*formatter, time);
155*6777b538SAndroid Build Coastguard Worker }
156*6777b538SAndroid Build Coastguard Worker 
TimeFormatShortDateAndTime(const Time & time)157*6777b538SAndroid Build Coastguard Worker std::u16string TimeFormatShortDateAndTime(const Time& time) {
158*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<icu::DateFormat> formatter(
159*6777b538SAndroid Build Coastguard Worker       icu::DateFormat::createDateTimeInstance(icu::DateFormat::kShort));
160*6777b538SAndroid Build Coastguard Worker   return TimeFormat(*formatter, time);
161*6777b538SAndroid Build Coastguard Worker }
162*6777b538SAndroid Build Coastguard Worker 
TimeFormatShortDateAndTimeWithTimeZone(const Time & time)163*6777b538SAndroid Build Coastguard Worker std::u16string TimeFormatShortDateAndTimeWithTimeZone(const Time& time) {
164*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<icu::DateFormat> formatter(
165*6777b538SAndroid Build Coastguard Worker       icu::DateFormat::createDateTimeInstance(icu::DateFormat::kShort,
166*6777b538SAndroid Build Coastguard Worker                                               icu::DateFormat::kLong));
167*6777b538SAndroid Build Coastguard Worker   return TimeFormat(*formatter, time);
168*6777b538SAndroid Build Coastguard Worker }
169*6777b538SAndroid Build Coastguard Worker 
170*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_CHROMEOS_ASH)
TimeFormatMonthAndYearForTimeZone(const Time & time,const icu::TimeZone * time_zone)171*6777b538SAndroid Build Coastguard Worker std::u16string TimeFormatMonthAndYearForTimeZone(
172*6777b538SAndroid Build Coastguard Worker     const Time& time,
173*6777b538SAndroid Build Coastguard Worker     const icu::TimeZone* time_zone) {
174*6777b538SAndroid Build Coastguard Worker   icu::SimpleDateFormat formatter =
175*6777b538SAndroid Build Coastguard Worker       CreateSimpleDateFormatter(DateFormatToString(DATE_FORMAT_YEAR_MONTH));
176*6777b538SAndroid Build Coastguard Worker   formatter.setTimeZone(*time_zone);
177*6777b538SAndroid Build Coastguard Worker   return TimeFormat(formatter, time);
178*6777b538SAndroid Build Coastguard Worker }
179*6777b538SAndroid Build Coastguard Worker #endif
180*6777b538SAndroid Build Coastguard Worker 
TimeFormatMonthAndYear(const Time & time)181*6777b538SAndroid Build Coastguard Worker std::u16string TimeFormatMonthAndYear(const Time& time) {
182*6777b538SAndroid Build Coastguard Worker   return TimeFormat(
183*6777b538SAndroid Build Coastguard Worker       CreateSimpleDateFormatter(DateFormatToString(DATE_FORMAT_YEAR_MONTH)),
184*6777b538SAndroid Build Coastguard Worker       time);
185*6777b538SAndroid Build Coastguard Worker }
186*6777b538SAndroid Build Coastguard Worker 
TimeFormatFriendlyDateAndTime(const Time & time)187*6777b538SAndroid Build Coastguard Worker std::u16string TimeFormatFriendlyDateAndTime(const Time& time) {
188*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<icu::DateFormat> formatter(
189*6777b538SAndroid Build Coastguard Worker       icu::DateFormat::createDateTimeInstance(icu::DateFormat::kFull));
190*6777b538SAndroid Build Coastguard Worker   return TimeFormat(*formatter, time);
191*6777b538SAndroid Build Coastguard Worker }
192*6777b538SAndroid Build Coastguard Worker 
TimeFormatFriendlyDate(const Time & time)193*6777b538SAndroid Build Coastguard Worker std::u16string TimeFormatFriendlyDate(const Time& time) {
194*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<icu::DateFormat> formatter(
195*6777b538SAndroid Build Coastguard Worker       icu::DateFormat::createDateInstance(icu::DateFormat::kFull));
196*6777b538SAndroid Build Coastguard Worker   return TimeFormat(*formatter, time);
197*6777b538SAndroid Build Coastguard Worker }
198*6777b538SAndroid Build Coastguard Worker 
LocalizedTimeFormatWithPattern(const Time & time,std::string_view pattern)199*6777b538SAndroid Build Coastguard Worker std::u16string LocalizedTimeFormatWithPattern(const Time& time,
200*6777b538SAndroid Build Coastguard Worker                                               std::string_view pattern) {
201*6777b538SAndroid Build Coastguard Worker   return TimeFormat(CreateSimpleDateFormatter(std::move(pattern)), time);
202*6777b538SAndroid Build Coastguard Worker }
203*6777b538SAndroid Build Coastguard Worker 
UnlocalizedTimeFormatWithPattern(const Time & time,std::string_view pattern,const icu::TimeZone * time_zone)204*6777b538SAndroid Build Coastguard Worker std::string UnlocalizedTimeFormatWithPattern(const Time& time,
205*6777b538SAndroid Build Coastguard Worker                                              std::string_view pattern,
206*6777b538SAndroid Build Coastguard Worker                                              const icu::TimeZone* time_zone) {
207*6777b538SAndroid Build Coastguard Worker   icu::SimpleDateFormat formatter =
208*6777b538SAndroid Build Coastguard Worker       CreateSimpleDateFormatter({}, false, icu::Locale("en_US"));
209*6777b538SAndroid Build Coastguard Worker   if (time_zone) {
210*6777b538SAndroid Build Coastguard Worker     formatter.setTimeZone(*time_zone);
211*6777b538SAndroid Build Coastguard Worker   }
212*6777b538SAndroid Build Coastguard Worker 
213*6777b538SAndroid Build Coastguard Worker   // Formats `time` according to `pattern`.
214*6777b538SAndroid Build Coastguard Worker   const auto format_time = [&formatter](const Time& time,
215*6777b538SAndroid Build Coastguard Worker                                         std::string_view pattern) {
216*6777b538SAndroid Build Coastguard Worker     formatter.applyPattern(
217*6777b538SAndroid Build Coastguard Worker         icu::UnicodeString(pattern.data(), pattern.length()));
218*6777b538SAndroid Build Coastguard Worker     return base::UTF16ToUTF8(TimeFormat(formatter, time));
219*6777b538SAndroid Build Coastguard Worker   };
220*6777b538SAndroid Build Coastguard Worker 
221*6777b538SAndroid Build Coastguard Worker   // If `time` has nonzero microseconds, check if the caller requested
222*6777b538SAndroid Build Coastguard Worker   // microsecond-precision output; this must be handled internally since
223*6777b538SAndroid Build Coastguard Worker   // `SimpleDateFormat` won't do it.
224*6777b538SAndroid Build Coastguard Worker   std::string output;
225*6777b538SAndroid Build Coastguard Worker   if (const int64_t microseconds =
226*6777b538SAndroid Build Coastguard Worker           time.ToDeltaSinceWindowsEpoch().InMicroseconds() %
227*6777b538SAndroid Build Coastguard Worker           Time::kMicrosecondsPerMillisecond) {
228*6777b538SAndroid Build Coastguard Worker     // Adds digits to `output` for each 'S' at the start of `pattern`.
229*6777b538SAndroid Build Coastguard Worker     const auto format_microseconds = [&output](int64_t mutable_micros,
230*6777b538SAndroid Build Coastguard Worker                                                std::string_view pattern) {
231*6777b538SAndroid Build Coastguard Worker       size_t i = 0;
232*6777b538SAndroid Build Coastguard Worker       for (; i < pattern.length() && pattern[i] == 'S'; ++i) {
233*6777b538SAndroid Build Coastguard Worker         output += static_cast<char>('0' + mutable_micros / 100);
234*6777b538SAndroid Build Coastguard Worker         mutable_micros = (mutable_micros % 100) * 10;
235*6777b538SAndroid Build Coastguard Worker       }
236*6777b538SAndroid Build Coastguard Worker       return i;
237*6777b538SAndroid Build Coastguard Worker     };
238*6777b538SAndroid Build Coastguard Worker 
239*6777b538SAndroid Build Coastguard Worker     // Look for fractional seconds patterns with greater-than-millisecond
240*6777b538SAndroid Build Coastguard Worker     // precision.
241*6777b538SAndroid Build Coastguard Worker     bool in_quotes = false;
242*6777b538SAndroid Build Coastguard Worker     for (size_t i = 0; i < pattern.length();) {
243*6777b538SAndroid Build Coastguard Worker       if (pattern[i] == '\'') {
244*6777b538SAndroid Build Coastguard Worker         in_quotes = !in_quotes;
245*6777b538SAndroid Build Coastguard Worker       } else if (!in_quotes && !pattern.compare(i, 4, "SSSS")) {
246*6777b538SAndroid Build Coastguard Worker         // Let ICU format everything up through milliseconds.
247*6777b538SAndroid Build Coastguard Worker         const size_t fourth_s = i + 3;
248*6777b538SAndroid Build Coastguard Worker         if (i != 0) {
249*6777b538SAndroid Build Coastguard Worker           output += format_time(time, pattern.substr(0, fourth_s));
250*6777b538SAndroid Build Coastguard Worker         }
251*6777b538SAndroid Build Coastguard Worker 
252*6777b538SAndroid Build Coastguard Worker         // Add microseconds digits, then truncate to the remaining pattern.
253*6777b538SAndroid Build Coastguard Worker         pattern = pattern.substr(
254*6777b538SAndroid Build Coastguard Worker             fourth_s +
255*6777b538SAndroid Build Coastguard Worker             format_microseconds(microseconds, pattern.substr(fourth_s)));
256*6777b538SAndroid Build Coastguard Worker         i = 0;
257*6777b538SAndroid Build Coastguard Worker         continue;
258*6777b538SAndroid Build Coastguard Worker       }
259*6777b538SAndroid Build Coastguard Worker       ++i;
260*6777b538SAndroid Build Coastguard Worker     }
261*6777b538SAndroid Build Coastguard Worker   }
262*6777b538SAndroid Build Coastguard Worker 
263*6777b538SAndroid Build Coastguard Worker   // Format any remaining pattern.
264*6777b538SAndroid Build Coastguard Worker   if (!pattern.empty()) {
265*6777b538SAndroid Build Coastguard Worker     output += format_time(time, std::move(pattern));
266*6777b538SAndroid Build Coastguard Worker   }
267*6777b538SAndroid Build Coastguard Worker   return output;
268*6777b538SAndroid Build Coastguard Worker }
269*6777b538SAndroid Build Coastguard Worker 
TimeFormatAsIso8601(const Time & time)270*6777b538SAndroid Build Coastguard Worker std::string TimeFormatAsIso8601(const Time& time) {
271*6777b538SAndroid Build Coastguard Worker   return UnlocalizedTimeFormatWithPattern(time, "yyyy-MM-dd'T'HH:mm:ss.SSSX",
272*6777b538SAndroid Build Coastguard Worker                                           icu::TimeZone::getGMT());
273*6777b538SAndroid Build Coastguard Worker }
274*6777b538SAndroid Build Coastguard Worker 
TimeFormatHTTP(const Time & time)275*6777b538SAndroid Build Coastguard Worker std::string TimeFormatHTTP(const Time& time) {
276*6777b538SAndroid Build Coastguard Worker   return UnlocalizedTimeFormatWithPattern(time, "E, dd MMM yyyy HH:mm:ss O",
277*6777b538SAndroid Build Coastguard Worker                                           icu::TimeZone::getGMT());
278*6777b538SAndroid Build Coastguard Worker }
279*6777b538SAndroid Build Coastguard Worker 
TimeDurationFormat(TimeDelta time,DurationFormatWidth width,std::u16string * out)280*6777b538SAndroid Build Coastguard Worker bool TimeDurationFormat(TimeDelta time,
281*6777b538SAndroid Build Coastguard Worker                         DurationFormatWidth width,
282*6777b538SAndroid Build Coastguard Worker                         std::u16string* out) {
283*6777b538SAndroid Build Coastguard Worker   DCHECK(out);
284*6777b538SAndroid Build Coastguard Worker   UErrorCode status = U_ZERO_ERROR;
285*6777b538SAndroid Build Coastguard Worker   const int total_minutes = ClampRound(time / base::Minutes(1));
286*6777b538SAndroid Build Coastguard Worker   const int hours = total_minutes / 60;
287*6777b538SAndroid Build Coastguard Worker   const int minutes = total_minutes % 60;
288*6777b538SAndroid Build Coastguard Worker   UMeasureFormatWidth u_width = DurationWidthToMeasureWidth(width);
289*6777b538SAndroid Build Coastguard Worker 
290*6777b538SAndroid Build Coastguard Worker   // TODO(derat): Delete the |status| checks and LOG(ERROR) calls throughout
291*6777b538SAndroid Build Coastguard Worker   // this function once the cause of http://crbug.com/677043 is tracked down.
292*6777b538SAndroid Build Coastguard Worker   const icu::Measure measures[] = {
293*6777b538SAndroid Build Coastguard Worker       icu::Measure(hours, icu::MeasureUnit::createHour(status), status),
294*6777b538SAndroid Build Coastguard Worker       icu::Measure(minutes, icu::MeasureUnit::createMinute(status), status)};
295*6777b538SAndroid Build Coastguard Worker   if (U_FAILURE(status)) {
296*6777b538SAndroid Build Coastguard Worker     LOG(ERROR) << "Creating MeasureUnit or Measure for " << hours << "h"
297*6777b538SAndroid Build Coastguard Worker                << minutes << "m failed: " << u_errorName(status);
298*6777b538SAndroid Build Coastguard Worker     return false;
299*6777b538SAndroid Build Coastguard Worker   }
300*6777b538SAndroid Build Coastguard Worker 
301*6777b538SAndroid Build Coastguard Worker   icu::MeasureFormat measure_format(icu::Locale::getDefault(), u_width, status);
302*6777b538SAndroid Build Coastguard Worker   if (U_FAILURE(status)) {
303*6777b538SAndroid Build Coastguard Worker     LOG(ERROR) << "Creating MeasureFormat for "
304*6777b538SAndroid Build Coastguard Worker                << icu::Locale::getDefault().getName()
305*6777b538SAndroid Build Coastguard Worker                << " failed: " << u_errorName(status);
306*6777b538SAndroid Build Coastguard Worker     return false;
307*6777b538SAndroid Build Coastguard Worker   }
308*6777b538SAndroid Build Coastguard Worker 
309*6777b538SAndroid Build Coastguard Worker   icu::UnicodeString formatted;
310*6777b538SAndroid Build Coastguard Worker   icu::FieldPosition ignore(icu::FieldPosition::DONT_CARE);
311*6777b538SAndroid Build Coastguard Worker   measure_format.formatMeasures(measures, 2, formatted, ignore, status);
312*6777b538SAndroid Build Coastguard Worker   if (U_FAILURE(status)) {
313*6777b538SAndroid Build Coastguard Worker     LOG(ERROR) << "formatMeasures failed: " << u_errorName(status);
314*6777b538SAndroid Build Coastguard Worker     return false;
315*6777b538SAndroid Build Coastguard Worker   }
316*6777b538SAndroid Build Coastguard Worker 
317*6777b538SAndroid Build Coastguard Worker   *out = i18n::UnicodeStringToString16(formatted);
318*6777b538SAndroid Build Coastguard Worker   return true;
319*6777b538SAndroid Build Coastguard Worker }
320*6777b538SAndroid Build Coastguard Worker 
TimeDurationFormatWithSeconds(TimeDelta time,DurationFormatWidth width,std::u16string * out)321*6777b538SAndroid Build Coastguard Worker bool TimeDurationFormatWithSeconds(TimeDelta time,
322*6777b538SAndroid Build Coastguard Worker                                    DurationFormatWidth width,
323*6777b538SAndroid Build Coastguard Worker                                    std::u16string* out) {
324*6777b538SAndroid Build Coastguard Worker   DCHECK(out);
325*6777b538SAndroid Build Coastguard Worker   UErrorCode status = U_ZERO_ERROR;
326*6777b538SAndroid Build Coastguard Worker   const int64_t total_seconds = ClampRound<int64_t>(time.InSecondsF());
327*6777b538SAndroid Build Coastguard Worker   const int64_t hours = total_seconds / base::Time::kSecondsPerHour;
328*6777b538SAndroid Build Coastguard Worker   const int64_t minutes =
329*6777b538SAndroid Build Coastguard Worker       (total_seconds - hours * base::Time::kSecondsPerHour) /
330*6777b538SAndroid Build Coastguard Worker       base::Time::kSecondsPerMinute;
331*6777b538SAndroid Build Coastguard Worker   const int64_t seconds = total_seconds % base::Time::kSecondsPerMinute;
332*6777b538SAndroid Build Coastguard Worker   UMeasureFormatWidth u_width = DurationWidthToMeasureWidth(width);
333*6777b538SAndroid Build Coastguard Worker 
334*6777b538SAndroid Build Coastguard Worker   const icu::Measure measures[] = {
335*6777b538SAndroid Build Coastguard Worker       icu::Measure(hours, icu::MeasureUnit::createHour(status), status),
336*6777b538SAndroid Build Coastguard Worker       icu::Measure(minutes, icu::MeasureUnit::createMinute(status), status),
337*6777b538SAndroid Build Coastguard Worker       icu::Measure(seconds, icu::MeasureUnit::createSecond(status), status)};
338*6777b538SAndroid Build Coastguard Worker   icu::MeasureFormat measure_format(icu::Locale::getDefault(), u_width, status);
339*6777b538SAndroid Build Coastguard Worker   icu::UnicodeString formatted;
340*6777b538SAndroid Build Coastguard Worker   icu::FieldPosition ignore(icu::FieldPosition::DONT_CARE);
341*6777b538SAndroid Build Coastguard Worker   measure_format.formatMeasures(measures, 3, formatted, ignore, status);
342*6777b538SAndroid Build Coastguard Worker   *out = i18n::UnicodeStringToString16(formatted);
343*6777b538SAndroid Build Coastguard Worker   return U_SUCCESS(status);
344*6777b538SAndroid Build Coastguard Worker }
345*6777b538SAndroid Build Coastguard Worker 
DateIntervalFormat(const Time & begin_time,const Time & end_time,DateFormat format)346*6777b538SAndroid Build Coastguard Worker std::u16string DateIntervalFormat(const Time& begin_time,
347*6777b538SAndroid Build Coastguard Worker                                   const Time& end_time,
348*6777b538SAndroid Build Coastguard Worker                                   DateFormat format) {
349*6777b538SAndroid Build Coastguard Worker   UErrorCode status = U_ZERO_ERROR;
350*6777b538SAndroid Build Coastguard Worker 
351*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<icu::DateIntervalFormat> formatter(
352*6777b538SAndroid Build Coastguard Worker       icu::DateIntervalFormat::createInstance(DateFormatToString(format),
353*6777b538SAndroid Build Coastguard Worker                                               status));
354*6777b538SAndroid Build Coastguard Worker 
355*6777b538SAndroid Build Coastguard Worker   icu::FieldPosition pos = 0;
356*6777b538SAndroid Build Coastguard Worker   UDate start_date = ToUDate(begin_time);
357*6777b538SAndroid Build Coastguard Worker   UDate end_date = ToUDate(end_time);
358*6777b538SAndroid Build Coastguard Worker   icu::DateInterval interval(start_date, end_date);
359*6777b538SAndroid Build Coastguard Worker   icu::UnicodeString formatted;
360*6777b538SAndroid Build Coastguard Worker   formatter->format(&interval, formatted, pos, status);
361*6777b538SAndroid Build Coastguard Worker   return i18n::UnicodeStringToString16(formatted);
362*6777b538SAndroid Build Coastguard Worker }
363*6777b538SAndroid Build Coastguard Worker 
GetHourClockType()364*6777b538SAndroid Build Coastguard Worker HourClockType GetHourClockType() {
365*6777b538SAndroid Build Coastguard Worker   // TODO(satorux,jshin): Rework this with ures_getByKeyWithFallback()
366*6777b538SAndroid Build Coastguard Worker   // once it becomes public. The short time format can be found at
367*6777b538SAndroid Build Coastguard Worker   // "calendar/gregorian/DateTimePatterns/3" in the resources.
368*6777b538SAndroid Build Coastguard Worker   std::unique_ptr<icu::SimpleDateFormat> formatter(
369*6777b538SAndroid Build Coastguard Worker       static_cast<icu::SimpleDateFormat*>(
370*6777b538SAndroid Build Coastguard Worker           icu::DateFormat::createTimeInstance(icu::DateFormat::kShort)));
371*6777b538SAndroid Build Coastguard Worker   // Retrieve the short time format.
372*6777b538SAndroid Build Coastguard Worker   icu::UnicodeString pattern_unicode;
373*6777b538SAndroid Build Coastguard Worker   formatter->toPattern(pattern_unicode);
374*6777b538SAndroid Build Coastguard Worker 
375*6777b538SAndroid Build Coastguard Worker   // Determine what hour clock type the current locale uses, by checking
376*6777b538SAndroid Build Coastguard Worker   // "a" (am/pm marker) in the short time format. This is reliable as "a"
377*6777b538SAndroid Build Coastguard Worker   // is used by all of 12-hour clock formats, but not any of 24-hour clock
378*6777b538SAndroid Build Coastguard Worker   // formats, as shown below.
379*6777b538SAndroid Build Coastguard Worker   //
380*6777b538SAndroid Build Coastguard Worker   // % grep -A4 DateTimePatterns third_party/icu/source/data/locales/*.txt |
381*6777b538SAndroid Build Coastguard Worker   //   grep -B1 -- -- |grep -v -- '--' |
382*6777b538SAndroid Build Coastguard Worker   //   perl -nle 'print $1 if /^\S+\s+"(.*)"/' |sort -u
383*6777b538SAndroid Build Coastguard Worker   //
384*6777b538SAndroid Build Coastguard Worker   // H.mm
385*6777b538SAndroid Build Coastguard Worker   // H:mm
386*6777b538SAndroid Build Coastguard Worker   // HH.mm
387*6777b538SAndroid Build Coastguard Worker   // HH:mm
388*6777b538SAndroid Build Coastguard Worker   // a h:mm
389*6777b538SAndroid Build Coastguard Worker   // ah:mm
390*6777b538SAndroid Build Coastguard Worker   // ahh:mm
391*6777b538SAndroid Build Coastguard Worker   // h-mm a
392*6777b538SAndroid Build Coastguard Worker   // h:mm a
393*6777b538SAndroid Build Coastguard Worker   // hh:mm a
394*6777b538SAndroid Build Coastguard Worker   //
395*6777b538SAndroid Build Coastguard Worker   // See http://userguide.icu-project.org/formatparse/datetime for details
396*6777b538SAndroid Build Coastguard Worker   // about the date/time format syntax.
397*6777b538SAndroid Build Coastguard Worker   return pattern_unicode.indexOf('a') == -1 ? k24HourClock : k12HourClock;
398*6777b538SAndroid Build Coastguard Worker }
399*6777b538SAndroid Build Coastguard Worker 
400*6777b538SAndroid Build Coastguard Worker }  // namespace base
401