Home | History | Annotate | Download | only in i18n
      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