Home | History | Annotate | Download | only in l10n
      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 "ui/base/l10n/time_format.h"
      6 
      7 #include <limits>
      8 
      9 #include "base/lazy_instance.h"
     10 #include "base/logging.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/time/time.h"
     13 #include "third_party/icu/source/common/unicode/unistr.h"
     14 #include "ui/base/l10n/formatter.h"
     15 #include "ui/base/l10n/l10n_util.h"
     16 #include "ui/base/ui_base_export.h"
     17 #include "ui/strings/grit/ui_strings.h"
     18 
     19 using base::Time;
     20 using base::TimeDelta;
     21 using ui::TimeFormat;
     22 
     23 namespace ui {
     24 
     25 UI_BASE_EXPORT base::LazyInstance<FormatterContainer> g_container =
     26     LAZY_INSTANCE_INITIALIZER;
     27 
     28 // static
     29 base::string16 TimeFormat::Simple(TimeFormat::Format format,
     30                                   TimeFormat::Length length,
     31                                   const base::TimeDelta& delta) {
     32   return Detailed(format, length, 0, delta);
     33 }
     34 
     35 // static
     36 base::string16 TimeFormat::Detailed(TimeFormat::Format format,
     37                                     TimeFormat::Length length,
     38                                     int cutoff,
     39                                     const base::TimeDelta& delta) {
     40   if (delta < TimeDelta::FromSeconds(0)) {
     41     NOTREACHED() << "Negative duration";
     42     return base::string16();
     43   }
     44 
     45   // Negative cutoff: always use two-value format.
     46   if (cutoff < 0)
     47     cutoff = std::numeric_limits<int>::max();
     48 
     49   const TimeDelta one_minute(TimeDelta::FromMinutes(1));
     50   const TimeDelta one_hour(TimeDelta::FromHours(1));
     51   const TimeDelta one_day(TimeDelta::FromDays(1));
     52   const TimeDelta half_second(TimeDelta::FromMilliseconds(500));
     53   const TimeDelta half_minute(TimeDelta::FromSeconds(30));
     54   const TimeDelta half_hour(TimeDelta::FromMinutes(30));
     55   const TimeDelta half_day(TimeDelta::FromHours(12));
     56 
     57   // Rationale: Start by determining major (first) unit, then add minor (second)
     58   // unit if mandated by |cutoff|.
     59   icu::UnicodeString time_string;
     60   const Formatter* formatter = g_container.Get().Get(format, length);
     61   if (delta < one_minute - half_second) {
     62     // Anything up to 59.500 seconds is formatted as seconds.
     63     const int seconds = static_cast<int>((delta + half_second).InSeconds());
     64     formatter->Format(Formatter::UNIT_SEC, seconds, time_string);
     65 
     66   } else if (delta < one_hour - (cutoff < 60 ? half_minute : half_second)) {
     67     // Anything up to 59.5 minutes (respectively 59:59.500 when |cutoff| permits
     68     // two-value output) is formatted as minutes (respectively minutes and
     69     // seconds).
     70     if (delta >= cutoff * one_minute - half_second) {
     71       const int minutes = (delta + half_minute).InMinutes();
     72       formatter->Format(Formatter::UNIT_MIN, minutes, time_string);
     73     } else {
     74       const int minutes = (delta + half_second).InMinutes();
     75       const int seconds = static_cast<int>(
     76           (delta + half_second).InSeconds() % 60);
     77       formatter->Format(Formatter::TWO_UNITS_MIN_SEC,
     78                         minutes, seconds, time_string);
     79     }
     80 
     81   } else if (delta < one_day - (cutoff < 24 ? half_hour : half_minute)) {
     82     // Anything up to 23.5 hours (respectively 23:59:30.000 when |cutoff|
     83     // permits two-value output) is formatted as hours (respectively hours and
     84     // minutes).
     85     if (delta >= cutoff * one_hour - half_minute) {
     86       const int hours = (delta + half_hour).InHours();
     87       formatter->Format(Formatter::UNIT_HOUR, hours, time_string);
     88     } else {
     89       const int hours = (delta + half_minute).InHours();
     90       const int minutes = (delta + half_minute).InMinutes() % 60;
     91       formatter->Format(Formatter::TWO_UNITS_HOUR_MIN,
     92                         hours, minutes, time_string);
     93     }
     94 
     95   } else {
     96     // Anything bigger is formatted as days (respectively days and hours).
     97     if (delta >= cutoff * one_day - half_hour) {
     98       const int days = (delta + half_day).InDays();
     99       formatter->Format(Formatter::UNIT_DAY, days, time_string);
    100     } else {
    101       const int days = (delta + half_hour).InDays();
    102       const int hours = (delta + half_hour).InHours() % 24;
    103       formatter->Format(Formatter::TWO_UNITS_DAY_HOUR,
    104                         days, hours, time_string);
    105     }
    106   }
    107 
    108   const int capacity = time_string.length() + 1;
    109   DCHECK_GT(capacity, 1);
    110   base::string16 result;
    111   UErrorCode error = U_ZERO_ERROR;
    112   time_string.extract(static_cast<UChar*>(WriteInto(&result, capacity)),
    113                       capacity, error);
    114   DCHECK(U_SUCCESS(error));
    115   return result;
    116 }
    117 
    118 // static
    119 base::string16 TimeFormat::RelativeDate(
    120     const Time& time,
    121     const Time* optional_midnight_today) {
    122   Time midnight_today = optional_midnight_today ? *optional_midnight_today :
    123       Time::Now().LocalMidnight();
    124   TimeDelta day = TimeDelta::FromMicroseconds(Time::kMicrosecondsPerDay);
    125   Time tomorrow = midnight_today + day;
    126   Time yesterday = midnight_today - day;
    127   if (time >= tomorrow)
    128     return base::string16();
    129   else if (time >= midnight_today)
    130     return l10n_util::GetStringUTF16(IDS_PAST_TIME_TODAY);
    131   else if (time >= yesterday)
    132     return l10n_util::GetStringUTF16(IDS_PAST_TIME_YESTERDAY);
    133   return base::string16();
    134 }
    135 
    136 }  // namespace ui
    137