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) 5bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch#include "ui/base/l10n/time_format.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <limits> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/lazy_instance.h" 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/strings/string_util.h" 12eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h" 13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "third_party/icu/source/common/unicode/unistr.h" 14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/base/l10n/formatter.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h" 16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/base/ui_base_export.h" 171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "ui/strings/grit/ui_strings.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::Time; 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::TimeDelta; 21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)using ui::TimeFormat; 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace ui { 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)UI_BASE_EXPORT base::LazyInstance<FormatterContainer> g_container = 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LAZY_INSTANCE_INITIALIZER; 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// static 29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)base::string16 TimeFormat::Simple(TimeFormat::Format format, 30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) TimeFormat::Length length, 31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const base::TimeDelta& delta) { 32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return Detailed(format, length, 0, delta); 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// static 36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)base::string16 TimeFormat::Detailed(TimeFormat::Format format, 37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) TimeFormat::Length length, 38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) int cutoff, 39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const base::TimeDelta& delta) { 405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (delta < TimeDelta::FromSeconds(0)) { 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED() << "Negative duration"; 42a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return base::string16(); 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Negative cutoff: always use two-value format. 46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (cutoff < 0) 47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) cutoff = std::numeric_limits<int>::max(); 485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const TimeDelta one_minute(TimeDelta::FromMinutes(1)); 505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const TimeDelta one_hour(TimeDelta::FromHours(1)); 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const TimeDelta one_day(TimeDelta::FromDays(1)); 525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const TimeDelta half_second(TimeDelta::FromMilliseconds(500)); 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const TimeDelta half_minute(TimeDelta::FromSeconds(30)); 545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const TimeDelta half_hour(TimeDelta::FromMinutes(30)); 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const TimeDelta half_day(TimeDelta::FromHours(12)); 565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Rationale: Start by determining major (first) unit, then add minor (second) 58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // unit if mandated by |cutoff|. 59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) icu::UnicodeString time_string; 60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const Formatter* formatter = g_container.Get().Get(format, length); 615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (delta < one_minute - half_second) { 62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Anything up to 59.500 seconds is formatted as seconds. 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const int seconds = static_cast<int>((delta + half_second).InSeconds()); 64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) formatter->Format(Formatter::UNIT_SEC, seconds, time_string); 65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else if (delta < one_hour - (cutoff < 60 ? half_minute : half_second)) { 67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Anything up to 59.5 minutes (respectively 59:59.500 when |cutoff| permits 68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // two-value output) is formatted as minutes (respectively minutes and 69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // seconds). 70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (delta >= cutoff * one_minute - half_second) { 71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const int minutes = (delta + half_minute).InMinutes(); 72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) formatter->Format(Formatter::UNIT_MIN, minutes, time_string); 73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else { 74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const int minutes = (delta + half_second).InMinutes(); 75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const int seconds = static_cast<int>( 76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) (delta + half_second).InSeconds() % 60); 77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) formatter->Format(Formatter::TWO_UNITS_MIN_SEC, 78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) minutes, seconds, time_string); 79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else if (delta < one_day - (cutoff < 24 ? half_hour : half_minute)) { 82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Anything up to 23.5 hours (respectively 23:59:30.000 when |cutoff| 83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // permits two-value output) is formatted as hours (respectively hours and 84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // minutes). 85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (delta >= cutoff * one_hour - half_minute) { 86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const int hours = (delta + half_hour).InHours(); 87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) formatter->Format(Formatter::UNIT_HOUR, hours, time_string); 88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else { 89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const int hours = (delta + half_minute).InHours(); 90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const int minutes = (delta + half_minute).InMinutes() % 60; 91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) formatter->Format(Formatter::TWO_UNITS_HOUR_MIN, 92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) hours, minutes, time_string); 93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Anything bigger is formatted as days (respectively days and hours). 97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (delta >= cutoff * one_day - half_hour) { 98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const int days = (delta + half_day).InDays(); 99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) formatter->Format(Formatter::UNIT_DAY, days, time_string); 100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else { 101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const int days = (delta + half_hour).InDays(); 102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const int hours = (delta + half_hour).InHours() % 24; 103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) formatter->Format(Formatter::TWO_UNITS_DAY_HOUR, 104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) days, hours, time_string); 105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const int capacity = time_string.length() + 1; 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_GT(capacity, 1); 110a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::string16 result; 111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) UErrorCode error = U_ZERO_ERROR; 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time_string.extract(static_cast<UChar*>(WriteInto(&result, capacity)), 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) capacity, error); 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(U_SUCCESS(error)); 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result; 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static 119a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)base::string16 TimeFormat::RelativeDate( 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const Time& time, 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const Time* optional_midnight_today) { 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Time midnight_today = optional_midnight_today ? *optional_midnight_today : 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Time::Now().LocalMidnight(); 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) TimeDelta day = TimeDelta::FromMicroseconds(Time::kMicrosecondsPerDay); 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Time tomorrow = midnight_today + day; 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Time yesterday = midnight_today - day; 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time >= tomorrow) 128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return base::string16(); 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else if (time >= midnight_today) 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return l10n_util::GetStringUTF16(IDS_PAST_TIME_TODAY); 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else if (time >= yesterday) 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return l10n_util::GetStringUTF16(IDS_PAST_TIME_YESTERDAY); 133a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return base::string16(); 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 135bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch 136bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch} // namespace ui 137