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 "grit/ui_strings.h" 14 #include "third_party/icu/source/common/unicode/unistr.h" 15 #include "ui/base/l10n/formatter.h" 16 #include "ui/base/l10n/l10n_util.h" 17 #include "ui/base/ui_base_export.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