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. 4 5 #include "base/i18n/time_formatting.h" 6 7 #include "base/logging.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "base/time/time.h" 11 #include "third_party/icu/source/i18n/unicode/datefmt.h" 12 #include "third_party/icu/source/i18n/unicode/dtptngen.h" 13 #include "third_party/icu/source/i18n/unicode/smpdtfmt.h" 14 15 using base::Time; 16 17 namespace { 18 19 string16 TimeFormat(const icu::DateFormat* formatter, 20 const Time& time) { 21 DCHECK(formatter); 22 icu::UnicodeString date_string; 23 24 formatter->format(static_cast<UDate>(time.ToDoubleT() * 1000), date_string); 25 return string16(date_string.getBuffer(), 26 static_cast<size_t>(date_string.length())); 27 } 28 29 string16 TimeFormatWithoutAmPm(const icu::DateFormat* formatter, 30 const Time& time) { 31 DCHECK(formatter); 32 icu::UnicodeString time_string; 33 34 icu::FieldPosition ampm_field(icu::DateFormat::kAmPmField); 35 formatter->format( 36 static_cast<UDate>(time.ToDoubleT() * 1000), time_string, ampm_field); 37 int ampm_length = ampm_field.getEndIndex() - ampm_field.getBeginIndex(); 38 if (ampm_length) { 39 int begin = ampm_field.getBeginIndex(); 40 // Doesn't include any spacing before the field. 41 if (begin) 42 begin--; 43 time_string.removeBetween(begin, ampm_field.getEndIndex()); 44 } 45 return string16(time_string.getBuffer(), 46 static_cast<size_t>(time_string.length())); 47 } 48 49 } // namespace 50 51 namespace base { 52 53 string16 TimeFormatTimeOfDay(const Time& time) { 54 // We can omit the locale parameter because the default should match 55 // Chrome's application locale. 56 scoped_ptr<icu::DateFormat> formatter( 57 icu::DateFormat::createTimeInstance(icu::DateFormat::kShort)); 58 return TimeFormat(formatter.get(), time); 59 } 60 61 string16 TimeFormatTimeOfDayWithHourClockType(const Time& time, 62 HourClockType type, 63 AmPmClockType ampm) { 64 // Just redirect to the normal function if the default type matches the 65 // given type. 66 HourClockType default_type = GetHourClockType(); 67 if (default_type == type && (type == k24HourClock || ampm == kKeepAmPm)) { 68 return TimeFormatTimeOfDay(time); 69 } 70 71 // Generate a locale-dependent format pattern. The generator will take 72 // care of locale-dependent formatting issues like which separator to 73 // use (some locales use '.' instead of ':'), and where to put the am/pm 74 // marker. 75 UErrorCode status = U_ZERO_ERROR; 76 scoped_ptr<icu::DateTimePatternGenerator> generator( 77 icu::DateTimePatternGenerator::createInstance(status)); 78 DCHECK(U_SUCCESS(status)); 79 const char* base_pattern = (type == k12HourClock ? "ahm" : "Hm"); 80 icu::UnicodeString generated_pattern = 81 generator->getBestPattern(icu::UnicodeString(base_pattern), status); 82 DCHECK(U_SUCCESS(status)); 83 84 // Then, format the time using the generated pattern. 85 icu::SimpleDateFormat formatter(generated_pattern, status); 86 DCHECK(U_SUCCESS(status)); 87 if (ampm == kKeepAmPm) { 88 return TimeFormat(&formatter, time); 89 } else { 90 return TimeFormatWithoutAmPm(&formatter, time); 91 } 92 } 93 94 string16 TimeFormatShortDate(const Time& time) { 95 scoped_ptr<icu::DateFormat> formatter( 96 icu::DateFormat::createDateInstance(icu::DateFormat::kMedium)); 97 return TimeFormat(formatter.get(), time); 98 } 99 100 string16 TimeFormatShortDateNumeric(const Time& time) { 101 scoped_ptr<icu::DateFormat> formatter( 102 icu::DateFormat::createDateInstance(icu::DateFormat::kShort)); 103 return TimeFormat(formatter.get(), time); 104 } 105 106 string16 TimeFormatShortDateAndTime(const Time& time) { 107 scoped_ptr<icu::DateFormat> formatter( 108 icu::DateFormat::createDateTimeInstance(icu::DateFormat::kShort)); 109 return TimeFormat(formatter.get(), time); 110 } 111 112 string16 TimeFormatFriendlyDateAndTime(const Time& time) { 113 scoped_ptr<icu::DateFormat> formatter( 114 icu::DateFormat::createDateTimeInstance(icu::DateFormat::kFull)); 115 return TimeFormat(formatter.get(), time); 116 } 117 118 string16 TimeFormatFriendlyDate(const Time& time) { 119 scoped_ptr<icu::DateFormat> formatter(icu::DateFormat::createDateInstance( 120 icu::DateFormat::kFull)); 121 return TimeFormat(formatter.get(), time); 122 } 123 124 HourClockType GetHourClockType() { 125 // TODO(satorux,jshin): Rework this with ures_getByKeyWithFallback() 126 // once it becomes public. The short time format can be found at 127 // "calendar/gregorian/DateTimePatterns/3" in the resources. 128 scoped_ptr<icu::SimpleDateFormat> formatter( 129 static_cast<icu::SimpleDateFormat*>( 130 icu::DateFormat::createTimeInstance(icu::DateFormat::kShort))); 131 // Retrieve the short time format. 132 icu::UnicodeString pattern_unicode; 133 formatter->toPattern(pattern_unicode); 134 135 // Determine what hour clock type the current locale uses, by checking 136 // "a" (am/pm marker) in the short time format. This is reliable as "a" 137 // is used by all of 12-hour clock formats, but not any of 24-hour clock 138 // formats, as shown below. 139 // 140 // % grep -A4 DateTimePatterns third_party/icu/source/data/locales/*.txt | 141 // grep -B1 -- -- |grep -v -- '--' | 142 // perl -nle 'print $1 if /^\S+\s+"(.*)"/' |sort -u 143 // 144 // H.mm 145 // H:mm 146 // HH.mm 147 // HH:mm 148 // a h:mm 149 // ah:mm 150 // ahh:mm 151 // h-mm a 152 // h:mm a 153 // hh:mm a 154 // 155 // See http://userguide.icu-project.org/formatparse/datetime for details 156 // about the date/time format syntax. 157 if (pattern_unicode.indexOf('a') == -1) { 158 return k24HourClock; 159 } else { 160 return k12HourClock; 161 } 162 } 163 164 } // namespace base 165