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