summaryrefslogtreecommitdiffstats
path: root/ui/base/l10n/time_format.cc
blob: 1f03599ebfa8773df5b4a41b644b0154c06a8bf5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/base/l10n/time_format.h"

#include <limits>

#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "third_party/icu/source/common/unicode/unistr.h"
#include "ui/base/l10n/formatter.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_export.h"
#include "ui/strings/grit/ui_strings.h"

using base::Time;
using base::TimeDelta;
using ui::TimeFormat;

namespace ui {

UI_BASE_EXPORT base::LazyInstance<FormatterContainer> g_container =
    LAZY_INSTANCE_INITIALIZER;

// static
base::string16 TimeFormat::Simple(TimeFormat::Format format,
                                  TimeFormat::Length length,
                                  const base::TimeDelta& delta) {
  return Detailed(format, length, 0, delta);
}

// static
base::string16 TimeFormat::Detailed(TimeFormat::Format format,
                                    TimeFormat::Length length,
                                    int cutoff,
                                    const base::TimeDelta& delta) {
  if (delta < TimeDelta::FromSeconds(0)) {
    NOTREACHED() << "Negative duration";
    return base::string16();
  }

  // Negative cutoff: always use two-value format.
  if (cutoff < 0)
    cutoff = std::numeric_limits<int>::max();

  const TimeDelta one_minute(TimeDelta::FromMinutes(1));
  const TimeDelta one_hour(TimeDelta::FromHours(1));
  const TimeDelta one_day(TimeDelta::FromDays(1));
  const TimeDelta half_second(TimeDelta::FromMilliseconds(500));
  const TimeDelta half_minute(TimeDelta::FromSeconds(30));
  const TimeDelta half_hour(TimeDelta::FromMinutes(30));
  const TimeDelta half_day(TimeDelta::FromHours(12));

  // Rationale: Start by determining major (first) unit, then add minor (second)
  // unit if mandated by |cutoff|.
  icu::UnicodeString time_string;
  const Formatter* formatter = g_container.Get().Get(format, length);
  if (delta < one_minute - half_second) {
    // Anything up to 59.500 seconds is formatted as seconds.
    const int seconds = static_cast<int>((delta + half_second).InSeconds());
    formatter->Format(Formatter::UNIT_SEC, seconds, &time_string);

  } else if (delta < one_hour - (cutoff < 60 ? half_minute : half_second)) {
    // Anything up to 59.5 minutes (respectively 59:59.500 when |cutoff| permits
    // two-value output) is formatted as minutes (respectively minutes and
    // seconds).
    if (delta >= cutoff * one_minute - half_second) {
      const int minutes = (delta + half_minute).InMinutes();
      formatter->Format(Formatter::UNIT_MIN, minutes, &time_string);
    } else {
      const int minutes = (delta + half_second).InMinutes();
      const int seconds = static_cast<int>(
          (delta + half_second).InSeconds() % 60);
      formatter->Format(Formatter::TWO_UNITS_MIN_SEC,
                        minutes, seconds, &time_string);
    }

  } else if (delta < one_day - (cutoff < 24 ? half_hour : half_minute)) {
    // Anything up to 23.5 hours (respectively 23:59:30.000 when |cutoff|
    // permits two-value output) is formatted as hours (respectively hours and
    // minutes).
    if (delta >= cutoff * one_hour - half_minute) {
      const int hours = (delta + half_hour).InHours();
      formatter->Format(Formatter::UNIT_HOUR, hours, &time_string);
    } else {
      const int hours = (delta + half_minute).InHours();
      const int minutes = (delta + half_minute).InMinutes() % 60;
      formatter->Format(Formatter::TWO_UNITS_HOUR_MIN,
                        hours, minutes, &time_string);
    }

  } else {
    // Anything bigger is formatted as days (respectively days and hours).
    if (delta >= cutoff * one_day - half_hour) {
      const int days = (delta + half_day).InDays();
      formatter->Format(Formatter::UNIT_DAY, days, &time_string);
    } else {
      const int days = (delta + half_hour).InDays();
      const int hours = (delta + half_hour).InHours() % 24;
      formatter->Format(Formatter::TWO_UNITS_DAY_HOUR,
                        days, hours, &time_string);
    }
  }

  const int capacity = time_string.length() + 1;
  DCHECK_GT(capacity, 1);
  base::string16 result;
  UErrorCode error = U_ZERO_ERROR;
  time_string.extract(static_cast<UChar*>(base::WriteInto(&result, capacity)),
                      capacity, error);
  DCHECK(U_SUCCESS(error));
  return result;
}

// static
base::string16 TimeFormat::RelativeDate(
    const Time& time,
    const Time* optional_midnight_today) {
  Time midnight_today = optional_midnight_today ? *optional_midnight_today :
      Time::Now().LocalMidnight();
  TimeDelta day = TimeDelta::FromMicroseconds(Time::kMicrosecondsPerDay);
  Time tomorrow = midnight_today + day;
  Time yesterday = midnight_today - day;
  if (time >= tomorrow)
    return base::string16();
  else if (time >= midnight_today)
    return l10n_util::GetStringUTF16(IDS_PAST_TIME_TODAY);
  else if (time >= yesterday)
    return l10n_util::GetStringUTF16(IDS_PAST_TIME_YESTERDAY);
  return base::string16();
}

}  // namespace ui