15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/time_formatting.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h" 9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 10eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h" 11ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "third_party/icu/source/i18n/unicode/datefmt.h" 12ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "third_party/icu/source/i18n/unicode/dtptngen.h" 13ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "third_party/icu/source/i18n/unicode/smpdtfmt.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)namespace base { 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 TimeFormat(const icu::DateFormat* formatter, 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const Time& time) { 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(formatter); 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::UnicodeString date_string; 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) formatter->format(static_cast<UDate>(time.ToDoubleT() * 1000), date_string); 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return string16(date_string.getBuffer(), 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static_cast<size_t>(date_string.length())); 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 TimeFormatWithoutAmPm(const icu::DateFormat* formatter, 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const Time& time) { 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(formatter); 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::UnicodeString time_string; 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::FieldPosition ampm_field(icu::DateFormat::kAmPmField); 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) formatter->format( 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static_cast<UDate>(time.ToDoubleT() * 1000), time_string, ampm_field); 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int ampm_length = ampm_field.getEndIndex() - ampm_field.getBeginIndex(); 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (ampm_length) { 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int begin = ampm_field.getBeginIndex(); 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Doesn't include any spacing before the field. 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (begin) 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) begin--; 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time_string.removeBetween(begin, ampm_field.getEndIndex()); 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return string16(time_string.getBuffer(), 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static_cast<size_t>(time_string.length())); 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 TimeFormatTimeOfDay(const Time& time) { 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We can omit the locale parameter because the default should match 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Chrome's application locale. 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<icu::DateFormat> formatter( 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::DateFormat::createTimeInstance(icu::DateFormat::kShort)); 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TimeFormat(formatter.get(), time); 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 TimeFormatTimeOfDayWithHourClockType(const Time& time, 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HourClockType type, 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AmPmClockType ampm) { 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Just redirect to the normal function if the default type matches the 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // given type. 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HourClockType default_type = GetHourClockType(); 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (default_type == type && (type == k24HourClock || ampm == kKeepAmPm)) { 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TimeFormatTimeOfDay(time); 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Generate a locale-dependent format pattern. The generator will take 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // care of locale-dependent formatting issues like which separator to 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // use (some locales use '.' instead of ':'), and where to put the am/pm 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // marker. 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UErrorCode status = U_ZERO_ERROR; 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<icu::DateTimePatternGenerator> generator( 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::DateTimePatternGenerator::createInstance(status)); 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(U_SUCCESS(status)); 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const char* base_pattern = (type == k12HourClock ? "ahm" : "Hm"); 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::UnicodeString generated_pattern = 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) generator->getBestPattern(icu::UnicodeString(base_pattern), status); 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(U_SUCCESS(status)); 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Then, format the time using the generated pattern. 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::SimpleDateFormat formatter(generated_pattern, status); 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(U_SUCCESS(status)); 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (ampm == kKeepAmPm) { 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TimeFormat(&formatter, time); 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TimeFormatWithoutAmPm(&formatter, time); 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 TimeFormatShortDate(const Time& time) { 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<icu::DateFormat> formatter( 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::DateFormat::createDateInstance(icu::DateFormat::kMedium)); 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TimeFormat(formatter.get(), time); 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 TimeFormatShortDateNumeric(const Time& time) { 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<icu::DateFormat> formatter( 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::DateFormat::createDateInstance(icu::DateFormat::kShort)); 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TimeFormat(formatter.get(), time); 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 TimeFormatShortDateAndTime(const Time& time) { 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<icu::DateFormat> formatter( 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::DateFormat::createDateTimeInstance(icu::DateFormat::kShort)); 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TimeFormat(formatter.get(), time); 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 TimeFormatFriendlyDateAndTime(const Time& time) { 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<icu::DateFormat> formatter( 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::DateFormat::createDateTimeInstance(icu::DateFormat::kFull)); 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TimeFormat(formatter.get(), time); 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 TimeFormatFriendlyDate(const Time& time) { 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<icu::DateFormat> formatter(icu::DateFormat::createDateInstance( 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::DateFormat::kFull)); 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TimeFormat(formatter.get(), time); 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HourClockType GetHourClockType() { 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(satorux,jshin): Rework this with ures_getByKeyWithFallback() 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // once it becomes public. The short time format can be found at 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // "calendar/gregorian/DateTimePatterns/3" in the resources. 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<icu::SimpleDateFormat> formatter( 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static_cast<icu::SimpleDateFormat*>( 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::DateFormat::createTimeInstance(icu::DateFormat::kShort))); 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Retrieve the short time format. 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::UnicodeString pattern_unicode; 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) formatter->toPattern(pattern_unicode); 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Determine what hour clock type the current locale uses, by checking 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // "a" (am/pm marker) in the short time format. This is reliable as "a" 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // is used by all of 12-hour clock formats, but not any of 24-hour clock 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // formats, as shown below. 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // % grep -A4 DateTimePatterns third_party/icu/source/data/locales/*.txt | 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // grep -B1 -- -- |grep -v -- '--' | 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // perl -nle 'print $1 if /^\S+\s+"(.*)"/' |sort -u 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // H.mm 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // H:mm 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // HH.mm 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // HH:mm 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // a h:mm 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // ah:mm 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // ahh:mm 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // h-mm a 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // h:mm a 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // hh:mm a 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // See http://userguide.icu-project.org/formatparse/datetime for details 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // about the date/time format syntax. 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (pattern_unicode.indexOf('a') == -1) { 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return k24HourClock; 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return k12HourClock; 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace base 162