Merge lp:~nick-dedekind/ubuntu-ui-toolkit/lp1378821.time-translation into lp:ubuntu-ui-toolkit

Proposed by Nick Dedekind
Status: Superseded
Proposed branch: lp:~nick-dedekind/ubuntu-ui-toolkit/lp1378821.time-translation
Merge into: lp:ubuntu-ui-toolkit
Diff against target: 905 lines (+718/-24)
10 files modified
debian/changelog (+7/-0)
modules/Ubuntu/Components/plugin/plugin.cpp (+2/-2)
modules/Ubuntu/Components/plugin/plugin.pro (+5/-2)
modules/Ubuntu/Components/plugin/timeformatter.cpp (+199/-0)
modules/Ubuntu/Components/plugin/timeformatter.h (+61/-0)
modules/Ubuntu/Components/plugin/timeformatter_p.h (+177/-0)
po/ubuntu-ui-toolkit.pot (+133/-19)
tests/unit/tst_components/dateExt.js (+67/-0)
tests/unit/tst_components/tst_components.pro (+2/-1)
tests/unit/tst_components/tst_timeformatter.qml (+65/-0)
To merge this branch: bzr merge lp:~nick-dedekind/ubuntu-ui-toolkit/lp1378821.time-translation
Reviewer Review Type Date Requested Status
Cris Dywan Needs Fixing
Zoltan Balogh Needs Fixing
PS Jenkins bot continuous-integration Needs Fixing
Review via email: mp+246191@code.launchpad.net

Commit message

Move the time formatter to SDK.

Description of the change

Move the time formatter to SDK.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

You need to update the .api file here.

Revision history for this message
Zoltan Balogh (bzoltan) wrote :

Please re-target the MR to the rtm or the staging branch.

review: Needs Fixing
Revision history for this message
Cris Dywan (kalikiana) wrote :

First and foremost, let's have an API proposal for this. Adding new API to the UITK means it will stay for a few years and if we just drop code like this we get in trouble (thumbnailer anyone?).

Second, we already have i18n for localization features. It seems to me like we'd want to extend that instead of a whole new component.

Third, how urgent is this? Who's using this aside from Unity? API should only be added to UITK if it's useful for several apps, otherwise there's little benefit and we still pay the cost of maintenance.

review: Needs Fixing
Revision history for this message
Albert Astals Cid (aacid) wrote :

Are you telling me you can't really see the use case for this in multiple apps? Phone? Calendar? Email?

Revision history for this message
Michał Sawicz (saviq) wrote :

FWIW, see the linked spec for use cases for this:

https://blueprints.launchpad.net/ubuntu-ui-toolkit/+spec/time-formatter

Revision history for this message
Cris Dywan (kalikiana) wrote :

Albert, when I asked in IRC I was told Unity was the only currently known use case. It's not about whether I can envision use cases but we need to know who will use the API so that we know if it actually solves the problem.

Thanks for the blueprint!
The point "Timestamps displayed differently depending on the available space" needs clarification. Is this explicitly to be done by the application or part of the formatter?

Please extend API proposal 1 https://docs.google.com/a/canonical.com/document/d/1qDcfbu9aAj7uU9qzjXCOJn8zGexBnXwZCgO8pLDsO5M/edit# to incorporate the relative time formatter. This makes more sense to Tim and I than an entierely new component which only has one feature. I imagine it could be as simple as i18n.relativeTime(timestamp) and i18n.absoluteTime(timestamp) - the unit test don't currently cover the latter but the spec lists it as a use case.
A short brainstorming meeting might be helpful, if you're interested we should arrange one in IRC.

1120. By Nick Dedekind

live timer

1121. By Nick Dedekind

fixed time translation

1122. By Nick Dedekind

updated potfile

1123. By Nick Dedekind

LiveTimer & i18n.relativeDateTime tests

1124. By Nick Dedekind

version bumped

1125. By Nick Dedekind

merged with trunk

1126. By Nick Dedekind

updated potfile

1127. By Nick Dedekind

removed old file

1128. By Nick Dedekind

version bump

1129. By Nick Dedekind

updated api

1130. By Nick Dedekind

reverted old changelogs

1131. By Nick Dedekind

better frequency handling

1132. By Nick Dedekind

whitespace

1133. By Nick Dedekind

unsubscribe live timer on destruction

Unmerged revisions

1133. By Nick Dedekind

unsubscribe live timer on destruction

1132. By Nick Dedekind

whitespace

1131. By Nick Dedekind

better frequency handling

1130. By Nick Dedekind

reverted old changelogs

1129. By Nick Dedekind

updated api

1128. By Nick Dedekind

version bump

1127. By Nick Dedekind

removed old file

1126. By Nick Dedekind

updated potfile

1125. By Nick Dedekind

merged with trunk

1124. By Nick Dedekind

version bumped

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2014-12-17 09:52:56 +0000
3+++ debian/changelog 2015-01-12 17:27:26 +0000
4@@ -348,6 +348,13 @@
5
6 -- Zoltán Balogh <zoltan@bakter.hu> Fri, 10 Oct 2014 11:19:10 +0300
7
8+ubuntu-ui-toolkit (1.1.1279.2-0ubuntu1) UNRELEASED; urgency=medium
9+
10+ [ Nick Dedekind ]
11+ * Added time formatter
12+
13+ -- Nick Dedekind <nick.dedekind@canonical.com> Mon, 15 Dec 2014 09:57:12 +0000
14+
15 ubuntu-ui-toolkit (1.1.1279.1+14.10.20141014-0ubuntu1) 14.09; urgency=low
16
17 [ Ubuntu daily release ]
18
19=== modified file 'modules/Ubuntu/Components/plugin/plugin.cpp'
20--- modules/Ubuntu/Components/plugin/plugin.cpp 2014-12-03 14:43:27 +0000
21+++ modules/Ubuntu/Components/plugin/plugin.cpp 2015-01-12 17:27:26 +0000
22@@ -35,6 +35,7 @@
23 #include "inversemouseareatype.h"
24 #include "qquickclipboard.h"
25 #include "qquickmimedata.h"
26+#include "timeformatter.h"
27 #include "ucubuntuanimation.h"
28 #include "ucfontutils.h"
29 #include "ucarguments.h"
30@@ -166,11 +167,10 @@
31 qmlRegisterType<QSortFilterProxyModelQML>(uri, 1, 1, "SortFilterModel");
32 qmlRegisterUncreatableType<FilterBehavior>(uri, 1, 1, "FilterBehavior", "Not instantiable");
33 qmlRegisterUncreatableType<SortBehavior>(uri, 1, 1, "SortBehavior", "Not instantiable");
34-
35- // ListItem and related types, released to 1.2
36 qmlRegisterType<UCListItem, 2>(uri, 1, 2, "ListItem");
37 qmlRegisterType<UCListItemDivider>();
38 qmlRegisterType<UCListItemActions, 2>(uri, 1, 2, "ListItemActions");
39+ qmlRegisterType<TimeFormatter>(uri, 1, 1, "TimeFormatter");
40 }
41
42 void UbuntuComponentsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
43
44=== modified file 'modules/Ubuntu/Components/plugin/plugin.pro'
45--- modules/Ubuntu/Components/plugin/plugin.pro 2014-12-03 14:43:27 +0000
46+++ modules/Ubuntu/Components/plugin/plugin.pro 2015-01-12 17:27:26 +0000
47@@ -72,7 +72,9 @@
48 uclistitemactions.h \
49 uclistitemactions_p.h \
50 propertychange_p.h \
51- uclistitemstyle.h
52+ uclistitemstyle.h \
53+ timeformatter.h \
54+ timeformatter_p.h
55
56 SOURCES += plugin.cpp \
57 uctheme.cpp \
58@@ -114,7 +116,8 @@
59 uclistitemactionsattached.cpp \
60 uclistitemattached.cpp \
61 propertychange_p.cpp \
62- uclistitemstyle.cpp
63+ uclistitemstyle.cpp \
64+ timeformatter.cpp
65
66 # adapters
67 SOURCES += adapters/alarmsadapter_organizer.cpp
68
69=== added file 'modules/Ubuntu/Components/plugin/timeformatter.cpp'
70--- modules/Ubuntu/Components/plugin/timeformatter.cpp 1970-01-01 00:00:00 +0000
71+++ modules/Ubuntu/Components/plugin/timeformatter.cpp 2015-01-12 17:27:26 +0000
72@@ -0,0 +1,199 @@
73+/*
74+ * Copyright 2014 Canonical Ltd.
75+ *
76+ * This program is free software; you can redistribute it and/or modify
77+ * it under the terms of the GNU General Public License as published by
78+ * the Free Software Foundation; version 3.
79+ *
80+ * This program is distributed in the hope that it will be useful,
81+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
82+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
83+ * GNU General Public License for more details.
84+ *
85+ * You should have received a copy of the GNU General Public License
86+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
87+ */
88+
89+#include "timeformatter.h"
90+#include "timeformatter_p.h"
91+
92+TimeFormatterShared* TimeFormatterShared::s_timeformatter = NULL;
93+
94+TimeFormatterShared::TimeFormatterShared()
95+{
96+ QDBusConnection::systemBus().connect("org.freedesktop.timedate1",
97+ "/org/freedesktop/timedate1",
98+ "org.freedesktop.DBus.Properties",
99+ "PropertiesChanged",
100+ this,
101+ SLOT(timedate1PropertiesChanged(QString, QVariantMap, QStringList)));
102+}
103+
104+QString TimeFormatterPrivate::relativeTime(const QDateTime &datetime)
105+{
106+ QDateTime now = m_timeSource ? m_timeSource->property("time").toDateTime() : QDateTime::currentDateTime();
107+ const date_proximity_t prox = getDateProximity(now, datetime);
108+
109+ switch (prox) {
110+ case DATE_PROXIMITY_NOW:
111+ m_proximityTimer.setInterval(1000);
112+ if (!m_proximityTimer.isActive()) m_proximityTimer.start();
113+
114+ /* TRANSLATORS: Time based "this is happening/happened now" */
115+ return UbuntuI18n::instance().tr("Now");
116+
117+ case DATE_PROXIMITY_HOUR:
118+ {
119+ m_proximityTimer.setInterval(1000);
120+ if (!m_proximityTimer.isActive()) m_proximityTimer.start();
121+
122+ qint64 diff = datetime.toMSecsSinceEpoch() - now.toMSecsSinceEpoch();
123+ qint64 minutes = qRound(float(diff) / 60000);
124+ if (minutes < 0) {
125+ if (minutes == -1) return UbuntuI18n::instance().tr("%1 minute ago").arg(qAbs(minutes));
126+ return UbuntuI18n::instance().tr("%1 minutes ago").arg(qAbs(minutes));
127+ }
128+ if (minutes == 1) return UbuntuI18n::instance().tr("%1 minute").arg(qAbs(minutes));
129+ return UbuntuI18n::instance().tr("%1 minutes").arg(qAbs(minutes));
130+ }
131+
132+ case DATE_PROXIMITY_TODAY:
133+ /* en_US example: "1:00 PM" */
134+ m_proximityTimer.stop();
135+ /* TRANSLATORS: Please translated these to your locale datetime format using the format specified by
136+ https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2 */
137+ return datetime.toString(isLocale12h() ? UbuntuI18n::instance().tr("h:mm ap"):
138+ /* TRANSLATORS: Please translated these to your locale datetime format using the format specified by
139+ https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2 */
140+ UbuntuI18n::instance().tr("HH:mm"));
141+
142+ case DATE_PROXIMITY_YESTERDAY:
143+ /* en_US example: "Yesterday 13:00" */
144+ m_proximityTimer.stop();
145+ /* TRANSLATORS: Please translated these to your locale datetime format using the format specified by
146+ https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2 */
147+ return datetime.toString(isLocale12h() ? UbuntuI18n::instance().tr("'Yesterday\u2003'h:mm ap") :
148+ /* TRANSLATORS: Please translated these to your locale datetime format using the format specified by
149+ https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2 */
150+ UbuntuI18n::instance().tr("'Yesterday\u2003'HH:mm"));
151+
152+ case DATE_PROXIMITY_TOMORROW:
153+ /* en_US example: "Tomorrow 1:00 PM" */
154+ /* TRANSLATORS: Please translated these to your locale datetime format using the format specified by
155+ https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2 */
156+ return datetime.toString(isLocale12h() ? UbuntuI18n::instance().tr("'Tomorrow\u2003'h:mm ap") :
157+ /* TRANSLATORS: Please translated these to your locale datetime format using the format specified by
158+ https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2 */
159+ UbuntuI18n::instance().tr("'Tomorrow\u2003'HH:mm"));
160+
161+ case DATE_PROXIMITY_LAST_WEEK:
162+ case DATE_PROXIMITY_NEXT_WEEK:
163+ /* en_US example: "Fri 1:00 PM" */
164+ m_proximityTimer.stop();
165+ /* TRANSLATORS: Please translated these to your locale datetime format using the format specified by
166+ https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2 */
167+ return datetime.toString(isLocale12h() ? UbuntuI18n::instance().tr("ddd'\u2003'h:mm ap") :
168+ /* TRANSLATORS: Please translated these to your locale datetime format using the format specified by
169+ https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2 */
170+ UbuntuI18n::instance().tr("ddd'\u2003'HH:mm"));
171+
172+ case DATE_PROXIMITY_FAR:
173+ default:
174+ m_proximityTimer.stop();
175+ /* TRANSLATORS: Please translated these to your locale datetime format using the format specified by
176+ https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2 */
177+ return datetime.toString(isLocale12h() ? UbuntuI18n::instance().tr("ddd d MMM'\u2003'h:mm ap") :
178+ /* TRANSLATORS: Please translated these to your locale datetime format using the format specified by
179+ https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2 */
180+ UbuntuI18n::instance().tr("ddd d MMM'\u2003'HH:mm"));
181+ }
182+ m_proximityTimer.stop();
183+ return datetime.toString(Qt::DefaultLocaleShortDate);
184+}
185+
186+TimeFormatter::TimeFormatter(QObject *parent)
187+ : QObject(parent)
188+ , d(new TimeFormatterPrivate)
189+{
190+ connect(TimeFormatterShared::s_timeformatter, &TimeFormatterShared::updated, this, &TimeFormatter::update);
191+ connect(&d->m_proximityTimer, &QTimer::timeout, this, &TimeFormatter::update);
192+}
193+
194+TimeFormatter::~TimeFormatter()
195+{
196+}
197+
198+QString TimeFormatter::format() const
199+{
200+ return d->m_format;
201+}
202+
203+QString TimeFormatter::timeString() const
204+{
205+ return d->m_timeString;
206+}
207+
208+qint64 TimeFormatter::millisecondsSinceEpoc() const
209+{
210+ return d->m_millisecondsSinceEpoc;
211+}
212+
213+bool TimeFormatter::relative() const
214+{
215+ return d->m_relative;
216+}
217+
218+QObject* TimeFormatter::timeSource() const
219+{
220+ return d->m_timeSource;
221+}
222+
223+void TimeFormatter::setFormat(const QString &format)
224+{
225+ if (d->m_format != format) {
226+ d->m_format = format;
227+ Q_EMIT formatChanged(d->m_format);
228+ update();
229+ }
230+}
231+
232+void TimeFormatter::setMilliSeccondsSinceEpoc(qint64 millisecondsSinceEpoc)
233+{
234+ if (d->m_millisecondsSinceEpoc != millisecondsSinceEpoc) {
235+ d->m_millisecondsSinceEpoc = millisecondsSinceEpoc;
236+ Q_EMIT millisecondsSinceEpocChanged(d->m_millisecondsSinceEpoc);
237+ update();
238+ }
239+}
240+
241+void TimeFormatter::setRelative(bool relative)
242+{
243+ if (d->m_relative != relative) {
244+ if (!relative) d->m_proximityTimer.stop();
245+ d->m_relative = relative;
246+ Q_EMIT relativeChanged(d->m_relative);
247+ update();
248+ }
249+}
250+
251+void TimeFormatter::setTimeSource(QObject* timeSource)
252+{
253+ if (timeSource != d->m_timeSource) {
254+ d->m_timeSource = timeSource;
255+ Q_EMIT timeSourceChanged(d->m_timeSource);
256+ update();
257+ }
258+}
259+
260+void TimeFormatter::update()
261+{
262+ if (format().isEmpty() && !d->m_relative) return;
263+
264+ QDateTime datetime(QDateTime::fromMSecsSinceEpoch(millisecondsSinceEpoc()));
265+ QString timeString = d->m_relative ? d->relativeTime(datetime) : datetime.toString(format());
266+
267+ if (d->m_timeString != timeString) {
268+ d->m_timeString = timeString;
269+ Q_EMIT timeStringChanged(d->m_timeString);
270+ }
271+}
272
273=== added file 'modules/Ubuntu/Components/plugin/timeformatter.h'
274--- modules/Ubuntu/Components/plugin/timeformatter.h 1970-01-01 00:00:00 +0000
275+++ modules/Ubuntu/Components/plugin/timeformatter.h 2015-01-12 17:27:26 +0000
276@@ -0,0 +1,61 @@
277+/*
278+ * Copyright 2014 Canonical Ltd.
279+ *
280+ * This program is free software; you can redistribute it and/or modify
281+ * it under the terms of the GNU General Public License as published by
282+ * the Free Software Foundation; version 3.
283+ *
284+ * This program is distributed in the hope that it will be useful,
285+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
286+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
287+ * GNU General Public License for more details.
288+ *
289+ * You should have received a copy of the GNU General Public License
290+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
291+ */
292+
293+#ifndef TIME_FORMATTER_H
294+#define TIME_FORMATTER_H
295+
296+#include <QObject>
297+
298+// TODO - bug #1260728
299+class TimeFormatter : public QObject
300+{
301+ Q_OBJECT
302+ Q_PROPERTY(QString format READ format WRITE setFormat NOTIFY formatChanged)
303+ Q_PROPERTY(QString timeString READ timeString NOTIFY timeStringChanged)
304+ Q_PROPERTY(qint64 millisecondsSinceEpoc READ millisecondsSinceEpoc WRITE setMilliSeccondsSinceEpoc NOTIFY millisecondsSinceEpocChanged)
305+ Q_PROPERTY(bool relative READ relative WRITE setRelative NOTIFY relativeChanged)
306+
307+ /* for testing */
308+ Q_PROPERTY(QObject* timeSource READ timeSource WRITE setTimeSource NOTIFY timeSourceChanged)
309+public:
310+ TimeFormatter(QObject *parent = 0);
311+ virtual ~TimeFormatter();
312+
313+ virtual QString format() const;
314+ QString timeString() const;
315+ qint64 millisecondsSinceEpoc() const;
316+ bool relative() const;
317+ QObject* timeSource() const;
318+
319+ void setFormat(const QString &format);
320+ void setMilliSeccondsSinceEpoc(qint64 millisecondsSinceEpoc);
321+ void setRelative(bool relative);
322+ void setTimeSource(QObject* timeSource);
323+
324+ void update();
325+
326+Q_SIGNALS:
327+ void formatChanged(const QString &format);
328+ void timeStringChanged(const QString &timeString);
329+ void millisecondsSinceEpocChanged(qint64 millisecondsSinceEpoc);
330+ void relativeChanged(bool relative);
331+ void timeSourceChanged(QObject* timeSource);
332+
333+private:
334+ class TimeFormatterPrivate *d;
335+};
336+
337+#endif
338
339=== added file 'modules/Ubuntu/Components/plugin/timeformatter_p.h'
340--- modules/Ubuntu/Components/plugin/timeformatter_p.h 1970-01-01 00:00:00 +0000
341+++ modules/Ubuntu/Components/plugin/timeformatter_p.h 2015-01-12 17:27:26 +0000
342@@ -0,0 +1,177 @@
343+/*
344+ * Copyright 2014 Canonical Ltd.
345+ *
346+ * This program is free software; you can redistribute it and/or modify
347+ * it under the terms of the GNU General Public License as published by
348+ * the Free Software Foundation; version 3.
349+ *
350+ * This program is distributed in the hope that it will be useful,
351+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
352+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
353+ * GNU General Public License for more details.
354+ *
355+ * You should have received a copy of the GNU General Public License
356+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
357+ */
358+
359+#ifndef TIMEFORMATTER_P_H
360+#define TIMEFORMATTER_P_H
361+
362+#include "i18n.h"
363+
364+#include <QDateTime>
365+#include <QDBusConnection>
366+#include <QLocale>
367+#include <QObject>
368+#include <QTimer>
369+
370+/* Check the system locale setting to see if the format is 24-hour
371+ time or 12-hour time */
372+inline bool isLocale12h(void)
373+{
374+ QString strTimeFormat = QLocale::system().timeFormat();
375+ QStringList includes; includes << "AP"; includes << "ap";
376+ QStringList excludes; excludes << "H"; excludes << "HH";
377+
378+ Q_FOREACH(const QString& exclude, excludes) {
379+ if (strTimeFormat.contains(exclude)) {
380+ return false;
381+ }
382+ }
383+
384+ Q_FOREACH(const QString& include, includes) {
385+ if (strTimeFormat.contains(include)) {
386+ return true;
387+ }
388+ }
389+
390+ return false;
391+}
392+
393+typedef enum
394+{
395+ DATE_PROXIMITY_NOW,
396+ DATE_PROXIMITY_HOUR,
397+ DATE_PROXIMITY_TODAY,
398+ DATE_PROXIMITY_YESTERDAY,
399+ DATE_PROXIMITY_TOMORROW,
400+ DATE_PROXIMITY_LAST_WEEK,
401+ DATE_PROXIMITY_NEXT_WEEK,
402+ DATE_PROXIMITY_FAR
403+} date_proximity_t;
404+
405+inline date_proximity_t getDateProximity(const QDateTime& now, const QDateTime& time)
406+{
407+ int now_day = now.date().day();
408+ int now_month = now.date().month();
409+ int now_year = now.date().year();
410+
411+ int time_day = time.date().day();
412+ int time_month = time.date().month();
413+ int time_year = time.date().year();
414+
415+ qint64 diff = time.toMSecsSinceEpoch() - now.toMSecsSinceEpoch();
416+ if (qAbs(diff) < 30000) return DATE_PROXIMITY_NOW;
417+ else if (qAbs(diff) < 3600000) return DATE_PROXIMITY_HOUR;
418+
419+ // does it happen today?
420+ if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day)) {
421+ return DATE_PROXIMITY_TODAY;
422+ }
423+
424+ // did it happen yesterday?
425+ QDateTime yesterday(now.addDays(-1));
426+ int yesterday_day = yesterday.date().day();
427+ int yesterday_month = yesterday.date().month();
428+ int yesterday_year = yesterday.date().year();
429+ if ((yesterday_year == time_year) && (yesterday_month == time_month) && (yesterday_day == time_day)) {
430+ return DATE_PROXIMITY_YESTERDAY;
431+ }
432+
433+ // does it happen tomorrow?
434+ QDateTime tomorrow(now.addDays(1));
435+ int tomorrow_day = tomorrow.date().day();
436+ int tomorrow_month = tomorrow.date().month();
437+ int tomorrow_year = tomorrow.date().year();
438+ if ((tomorrow_year == time_year) && (tomorrow_month == time_month) && (tomorrow_day == time_day)) {
439+ return DATE_PROXIMITY_TOMORROW;
440+ }
441+
442+ if (time < now) {
443+ QDateTime lastWeek(now.addDays(-6));
444+ QDateTime lastWeekBound(lastWeek.date(), QTime(0, 0, 0, 0));
445+
446+ // does it happen last week?
447+ if (time >= lastWeekBound) {
448+ return DATE_PROXIMITY_LAST_WEEK;
449+ }
450+ } else {
451+
452+ QDateTime nextWeek(now.addDays(6));
453+ QDateTime nextWeekBound(nextWeek.date(), QTime(23, 59, 59, 999));
454+
455+ // does it happen this week?
456+ if (time <= nextWeekBound) {
457+ return DATE_PROXIMITY_NEXT_WEEK;
458+ }
459+ }
460+
461+ return DATE_PROXIMITY_FAR;
462+}
463+
464+class TimeFormatterShared : public QObject
465+{
466+ Q_OBJECT
467+public:
468+ TimeFormatterShared();
469+
470+ ~TimeFormatterShared()
471+ {
472+ s_timeformatter = NULL;
473+ }
474+
475+public Q_SLOTS:
476+ void timedate1PropertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList&)
477+ {
478+ if (interface != "org.freedesktop.timedate1") return;
479+ if (changed.contains("Timezone")) {
480+ Q_EMIT updated();
481+ }
482+ }
483+
484+Q_SIGNALS:
485+ void updated();
486+
487+public:
488+ static TimeFormatterShared* s_timeformatter;
489+};
490+
491+class TimeFormatterPrivate
492+{
493+public:
494+ TimeFormatterPrivate()
495+ : m_format(QLocale::system().dateTimeFormat())
496+ , m_millisecondsSinceEpoc(0)
497+ , m_relative(false)
498+ , m_timeSource(NULL)
499+ {
500+ if (!TimeFormatterShared::s_timeformatter) { TimeFormatterShared::s_timeformatter = new TimeFormatterShared(); }
501+ m_shared.reset(TimeFormatterShared::s_timeformatter);
502+
503+ m_proximityTimer.setSingleShot(false);
504+ }
505+
506+ QString relativeTime(const QDateTime &datetime);
507+
508+ QString m_format;
509+ QString m_timeString;
510+ qint64 m_millisecondsSinceEpoc;
511+ bool m_relative;
512+ date_proximity_t m_lastProximity;
513+ QObject* m_timeSource;
514+ QTimer m_proximityTimer;
515+
516+ QSharedPointer<TimeFormatterShared> m_shared;
517+};
518+
519+#endif // TIMEFORMATTER_P_H
520
521=== modified file 'po/ubuntu-ui-toolkit.pot'
522--- po/ubuntu-ui-toolkit.pot 2014-11-06 15:50:17 +0000
523+++ po/ubuntu-ui-toolkit.pot 2015-01-12 17:27:26 +0000
524@@ -8,19 +8,28 @@
525 msgstr ""
526 "Project-Id-Version: ubuntu-ui-toolkit\n"
527 "Report-Msgid-Bugs-To: \n"
528-"POT-Creation-Date: 2014-11-05 14:29+0100\n"
529+"POT-Creation-Date: 2014-12-17 16:18+0000\n"
530 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
531 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
532 "Language-Team: LANGUAGE <LL@li.org>\n"
533 "Language: \n"
534 "MIME-Version: 1.0\n"
535-"Content-Type: text/plain; charset=CHARSET\n"
536+"Content-Type: text/plain; charset=UTF-8\n"
537 "Content-Transfer-Encoding: 8bit\n"
538
539-#: Ubuntu/Components/ListItems/Empty.qml:413
540+#: Ubuntu/Components/ListItems/Empty.qml:397
541 msgid "Delete"
542 msgstr ""
543
544+#: Ubuntu/Components/plugin/adapters/dbuspropertywatcher_p.cpp:51
545+msgid "No service/path specified"
546+msgstr ""
547+
548+#: Ubuntu/Components/plugin/adapters/dbuspropertywatcher_p.cpp:69
549+#, qt-format
550+msgid "Invalid bus type: %1."
551+msgstr ""
552+
553 #: Ubuntu/Components/plugin/statesaverbackend_p.cpp:176
554 #, qt-format
555 msgid ""
556@@ -33,13 +42,98 @@
557 msgid "property \"%1\" does not exist or is not writable for object %2"
558 msgstr ""
559
560-#: Ubuntu/Components/plugin/ucalarm.cpp:42
561-#: Ubuntu/Components/plugin/ucalarm.cpp:136
562+#. TRANSLATORS: Time based "this is happening/happened now"
563+#: Ubuntu/Components/plugin/timeformatter.cpp:43
564+msgid "Now"
565+msgstr ""
566+
567+#: Ubuntu/Components/plugin/timeformatter.cpp:53
568+#, qt-format
569+msgid "%1 minute ago"
570+msgstr ""
571+
572+#: Ubuntu/Components/plugin/timeformatter.cpp:54
573+#, qt-format
574+msgid "%1 minutes ago"
575+msgstr ""
576+
577+#: Ubuntu/Components/plugin/timeformatter.cpp:56
578+#, qt-format
579+msgid "%1 minute"
580+msgstr ""
581+
582+#: Ubuntu/Components/plugin/timeformatter.cpp:57
583+#, qt-format
584+msgid "%1 minutes"
585+msgstr ""
586+
587+#. TRANSLATORS: Please translated these to your locale datetime format using the format specified by
588+#. https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2
589+#: Ubuntu/Components/plugin/timeformatter.cpp:65
590+msgid "h:mm ap"
591+msgstr ""
592+
593+#. TRANSLATORS: Please translated these to your locale datetime format using the format specified by
594+#. https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2
595+#: Ubuntu/Components/plugin/timeformatter.cpp:68
596+msgid "HH:mm"
597+msgstr ""
598+
599+#. TRANSLATORS: Please translated these to your locale datetime format using the format specified by
600+#. https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2
601+#: Ubuntu/Components/plugin/timeformatter.cpp:75
602+msgid "'Yesterday 'h:mm ap"
603+msgstr ""
604+
605+#. TRANSLATORS: Please translated these to your locale datetime format using the format specified by
606+#. https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2
607+#: Ubuntu/Components/plugin/timeformatter.cpp:78
608+msgid "'Yesterday 'HH:mm"
609+msgstr ""
610+
611+#. TRANSLATORS: Please translated these to your locale datetime format using the format specified by
612+#. https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2
613+#: Ubuntu/Components/plugin/timeformatter.cpp:84
614+msgid "'Tomorrow 'h:mm ap"
615+msgstr ""
616+
617+#. TRANSLATORS: Please translated these to your locale datetime format using the format specified by
618+#. https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2
619+#: Ubuntu/Components/plugin/timeformatter.cpp:87
620+msgid "'Tomorrow 'HH:mm"
621+msgstr ""
622+
623+#. TRANSLATORS: Please translated these to your locale datetime format using the format specified by
624+#. https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2
625+#: Ubuntu/Components/plugin/timeformatter.cpp:95
626+msgid "ddd' 'h:mm ap"
627+msgstr ""
628+
629+#. TRANSLATORS: Please translated these to your locale datetime format using the format specified by
630+#. https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2
631+#: Ubuntu/Components/plugin/timeformatter.cpp:98
632+msgid "ddd' 'HH:mm"
633+msgstr ""
634+
635+#. TRANSLATORS: Please translated these to your locale datetime format using the format specified by
636+#. https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2
637+#: Ubuntu/Components/plugin/timeformatter.cpp:105
638+msgid "ddd d MMM' 'h:mm ap"
639+msgstr ""
640+
641+#. TRANSLATORS: Please translated these to your locale datetime format using the format specified by
642+#. https://qt-project.org/doc/qt-5-snapshot/qdatetime.html#fromString-2
643+#: Ubuntu/Components/plugin/timeformatter.cpp:108
644+msgid "ddd d MMM' 'HH:mm"
645+msgstr ""
646+
647+#: Ubuntu/Components/plugin/ucalarm.cpp:41
648+#: Ubuntu/Components/plugin/ucalarm.cpp:641
649 msgid "Alarm"
650 msgstr ""
651
652-#: Ubuntu/Components/plugin/ucalarm.cpp:684
653-#: Ubuntu/Components/plugin/ucalarm.cpp:716
654+#: Ubuntu/Components/plugin/ucalarm.cpp:633
655+#: Ubuntu/Components/plugin/ucalarm.cpp:665
656 msgid "Alarm has a pending operation."
657 msgstr ""
658
659@@ -66,10 +160,29 @@
660 msgid "%1 is expecting additional arguments: %2"
661 msgstr ""
662
663-#: Ubuntu/Components/plugin/ucmousefilters.cpp:1064
664+#: Ubuntu/Components/plugin/uclistitemactions.cpp:155
665+msgid "actionsDelegate not set!"
666+msgstr ""
667+
668+#: Ubuntu/Components/plugin/uclistitem.cpp:1040
669+#: Ubuntu/Components/plugin/uclistitem.cpp:1074
670+msgid "leadingActions and trailingActions cannot share the same object!"
671+msgstr ""
672+
673+#: Ubuntu/Components/plugin/ucmousefilters.cpp:1065
674 msgid "Ignoring AfterItem priority for InverseMouse filters."
675 msgstr ""
676
677+#: Ubuntu/Components/plugin/ucserviceproperties.cpp:62
678+msgid "Changing connection parameters forbidden."
679+msgstr ""
680+
681+#: Ubuntu/Components/plugin/ucserviceproperties.cpp:145
682+#, qt-format
683+msgid ""
684+"Binding detected on property '%1' will be removed by the service updates."
685+msgstr ""
686+
687 #: Ubuntu/Components/plugin/ucstatesaver.cpp:46
688 msgid "Warning: attachee must have an ID. State will not be saved."
689 msgstr ""
690@@ -87,23 +200,24 @@
691 "State saving disabled for %1, class %2"
692 msgstr ""
693
694-#: Ubuntu/Components/plugin/uctheme.cpp:233
695-msgid "Theme not found: "
696+#: Ubuntu/Components/plugin/uctheme.cpp:240
697+#, qt-format
698+msgid "Theme not found: \"%1\""
699 msgstr ""
700
701-#: Ubuntu/Components/Popups/ComposerSheet.qml:80
702+#: Ubuntu/Components/Popups/ComposerSheet.qml:78
703 msgid "Cancel"
704 msgstr ""
705
706-#: Ubuntu/Components/Popups/ComposerSheet.qml:90
707+#: Ubuntu/Components/Popups/ComposerSheet.qml:88
708 msgid "Confirm"
709 msgstr ""
710
711-#: Ubuntu/Components/Popups/DefaultSheet.qml:89
712+#: Ubuntu/Components/Popups/DefaultSheet.qml:85
713 msgid "Close"
714 msgstr ""
715
716-#: Ubuntu/Components/Popups/DefaultSheet.qml:99
717+#: Ubuntu/Components/Popups/DefaultSheet.qml:95
718 msgid "Done"
719 msgstr ""
720
721@@ -111,19 +225,19 @@
722 msgid "Select All"
723 msgstr ""
724
725-#: Ubuntu/Components/TextInputPopover.qml:33
726+#: Ubuntu/Components/TextInputPopover.qml:34
727 msgid "Cut"
728 msgstr ""
729
730-#: Ubuntu/Components/TextInputPopover.qml:42
731+#: Ubuntu/Components/TextInputPopover.qml:46
732 msgid "Copy"
733 msgstr ""
734
735-#: Ubuntu/Components/TextInputPopover.qml:49
736+#: Ubuntu/Components/TextInputPopover.qml:56
737 msgid "Paste"
738 msgstr ""
739
740-#: Ubuntu/Components/Themes/Ambiance/ProgressBarStyle.qml:57
741+#: Ubuntu/Components/Themes/Ambiance/ProgressBarStyle.qml:62
742 msgid "In Progress"
743 msgstr ""
744
745@@ -135,6 +249,6 @@
746 msgid "Pull to refresh..."
747 msgstr ""
748
749-#: Ubuntu/Components/ToolbarItems.qml:142
750+#: Ubuntu/Components/ToolbarItems.qml:145
751 msgid "Back"
752 msgstr ""
753
754=== added file 'tests/unit/tst_components/dateExt.js'
755--- tests/unit/tst_components/dateExt.js 1970-01-01 00:00:00 +0000
756+++ tests/unit/tst_components/dateExt.js 2015-01-12 17:27:26 +0000
757@@ -0,0 +1,67 @@
758+/*
759+ * Copyright 2014 Canonical Ltd.
760+ *
761+ * This program is free software; you can redistribute it and/or modify
762+ * it under the terms of the GNU Lesser General Public License as published by
763+ * the Free Software Foundation; version 3.
764+ *
765+ * This program is distributed in the hope that it will be useful,
766+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
767+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
768+ * GNU Lesser General Public License for more details.
769+ *
770+ * You should have received a copy of the GNU Lesser General Public License
771+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
772+ */
773+
774+.pragma library
775+
776+Date.msPerMinute = 60e3
777+Date.msPerHour = 3600e3
778+Date.msPerDay = 86400e3
779+Date.msPerWeek = Date.msPerDay * 7
780+
781+Date.leapYear = function(year) {
782+ return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
783+}
784+
785+Date.prototype.addMinutes = function(minutes) {
786+ var date = new Date(this)
787+ date.setTime(date.getTime() + Date.msPerMinute * minutes)
788+ return date
789+}
790+
791+Date.prototype.addHours = function(hours) {
792+ var date = new Date(this)
793+ date.setTime(date.getTime() + Date.msPerHour * hours)
794+ return date
795+}
796+
797+Date.prototype.addDays = function(days) {
798+ var date = new Date(this)
799+ date.setTime(date.getTime() + Date.msPerDay * days)
800+ return date
801+}
802+
803+Date.prototype.addMonths = function(months) {
804+ var date = new Date(this)
805+ date.setMonth(date.getMonth() + months)
806+ return date
807+}
808+
809+Date.prototype.weekStart = function(weekStartDay) {
810+ var date = this.midnight()
811+ var day = date.getDay(), n = 0
812+ while (day != weekStartDay) {
813+ if (day == 0) day = 6
814+ else day = day - 1
815+ n = n + 1
816+ }
817+ return date.addDays(-n)
818+}
819+
820+Date.prototype.midday = function() {
821+ var date = new Date(this)
822+ date.setHours(12,0,0,0);
823+ return date
824+}
825
826=== modified file 'tests/unit/tst_components/tst_components.pro'
827--- tests/unit/tst_components/tst_components.pro 2012-12-04 08:11:38 +0000
828+++ tests/unit/tst_components/tst_components.pro 2015-01-12 17:27:26 +0000
829@@ -6,4 +6,5 @@
830
831 SOURCES += tst_components.cpp
832
833-OTHER_FILES += $$system(ls *.qml)
834+OTHER_FILES += $$system(ls *.qml) \
835+ dateExt.js
836
837=== added file 'tests/unit/tst_components/tst_timeformatter.qml'
838--- tests/unit/tst_components/tst_timeformatter.qml 1970-01-01 00:00:00 +0000
839+++ tests/unit/tst_components/tst_timeformatter.qml 2015-01-12 17:27:26 +0000
840@@ -0,0 +1,65 @@
841+/*
842+ * Copyright 2014 Canonical Ltd.
843+ *
844+ * This program is free software; you can redistribute it and/or modify
845+ * it under the terms of the GNU Lesser General Public License as published by
846+ * the Free Software Foundation; version 3.
847+ *
848+ * This program is distributed in the hope that it will be useful,
849+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
850+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
851+ * GNU Lesser General Public License for more details.
852+ *
853+ * You should have received a copy of the GNU Lesser General Public License
854+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
855+ */
856+
857+import QtQuick 2.0
858+import QtTest 1.0
859+import Ubuntu.Components 1.1
860+import "dateExt.js" as DateExt
861+
862+TestCase {
863+ name: "TimeFormatter"
864+
865+ function init() {
866+ timeFormatter.relative = false;
867+ }
868+
869+ property date testTime: (new Date(2014, 10, 27, 14, 48, 0, 0))
870+
871+ function test_relative_data() {
872+ return [
873+ { tag: "now", datetime: testTime, expected: "Now" },
874+ { tag: "last minute", datetime: testTime.addMinutes(-1), expected: "1 minute ago" },
875+ { tag: "next minute", datetime: testTime.addMinutes(1), expected: "1 minute" },
876+ { tag: "last hour", datetime: testTime.addMinutes(-50), expected: "50 minutes ago" },
877+ { tag: "next hour", datetime: testTime.addMinutes(50), expected: "50 minutes" },
878+ { tag: "today-before", datetime: testTime.addHours(-4), expected: "10:48" },
879+ { tag: "today-after", datetime: testTime.addHours(4), expected: "18:48" },
880+ { tag: "yesterday", datetime: testTime.addDays(-1), expected: "Yesterday\u200314:48" },
881+ { tag: "tomorrow", datetime: testTime.addDays(1), expected: "Tomorrow\u200314:48" },
882+ { tag: "last-week", datetime: testTime.addDays(-6), expected: "Fri\u200314:48" },
883+ { tag: "next-week", datetime: testTime.addDays(6), expected: "Wed\u200314:48" },
884+ { tag: "far-past", datetime: testTime.addDays(-7), expected: "Thu 20 Nov\u200314:48" },
885+ { tag: "far-future", datetime: testTime.addDays(7), expected: "Thu 4 Dec\u200314:48" }
886+ ];
887+ }
888+
889+ function test_relative(data) {
890+ timeFormatter.relative = true;
891+ timeFormatter.millisecondsSinceEpoc = data.datetime.getTime();
892+
893+ if (data.expected !== undefined) {
894+ compare(timeFormatter.timeString, data.expected);
895+ }
896+ }
897+
898+ TimeFormatter {
899+ id: timeFormatter
900+
901+ timeSource: QtObject {
902+ property date time: testTime
903+ }
904+ }
905+}

Subscribers

People subscribed via source and target branches

to status/vote changes: