1/*
2 * Copyright 2017, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Akshay Agarwal <agarwal.akshay.akshay8@gmail.com>
7 */
8
9
10#include <unicode/uversion.h>
11#include <RelativeDateTimeFormat.h>
12
13#include <stdlib.h>
14#include <time.h>
15
16#include <unicode/gregocal.h>
17#include <unicode/reldatefmt.h>
18#include <unicode/utypes.h>
19
20#include <ICUWrapper.h>
21
22#include <Language.h>
23#include <Locale.h>
24#include <LocaleRoster.h>
25#include <TimeUnitFormat.h>
26
27
28U_NAMESPACE_USE
29
30
31static const URelativeDateTimeUnit kTimeUnitToRelativeDateTime[] = {
32	UDAT_REL_UNIT_YEAR,
33	UDAT_REL_UNIT_MONTH,
34	UDAT_REL_UNIT_WEEK,
35	UDAT_REL_UNIT_DAY,
36	UDAT_REL_UNIT_HOUR,
37	UDAT_REL_UNIT_MINUTE,
38	UDAT_REL_UNIT_SECOND,
39};
40
41
42static const UCalendarDateFields kTimeUnitToICUDateField[] = {
43	UCAL_YEAR,
44	UCAL_MONTH,
45	UCAL_WEEK_OF_MONTH,
46	UCAL_DAY_OF_WEEK,
47	UCAL_HOUR_OF_DAY,
48	UCAL_MINUTE,
49	UCAL_SECOND,
50};
51
52
53BRelativeDateTimeFormat::BRelativeDateTimeFormat()
54	: Inherited()
55{
56	Locale icuLocale(fLanguage.Code());
57	UErrorCode icuStatus = U_ZERO_ERROR;
58
59	fFormatter = new RelativeDateTimeFormatter(icuLocale, icuStatus);
60	if (fFormatter == NULL) {
61		fInitStatus = B_NO_MEMORY;
62		return;
63	}
64
65	fCalendar = new GregorianCalendar(icuStatus);
66	if (fCalendar == NULL) {
67		fInitStatus = B_NO_MEMORY;
68		return;
69	}
70
71	if (!U_SUCCESS(icuStatus))
72		fInitStatus = B_ERROR;
73}
74
75
76BRelativeDateTimeFormat::BRelativeDateTimeFormat(const BLanguage& language,
77	const BFormattingConventions& conventions)
78	: Inherited(language, conventions)
79{
80	Locale icuLocale(fLanguage.Code());
81	UErrorCode icuStatus = U_ZERO_ERROR;
82
83	fFormatter = new RelativeDateTimeFormatter(icuLocale, icuStatus);
84	if (fFormatter == NULL) {
85		fInitStatus = B_NO_MEMORY;
86		return;
87	}
88
89	fCalendar = new GregorianCalendar(icuStatus);
90	if (fCalendar == NULL) {
91		fInitStatus = B_NO_MEMORY;
92		return;
93	}
94
95	if (!U_SUCCESS(icuStatus))
96		fInitStatus = B_ERROR;
97}
98
99
100BRelativeDateTimeFormat::BRelativeDateTimeFormat(const BRelativeDateTimeFormat& other)
101	: Inherited(other),
102	fFormatter(other.fFormatter != NULL
103		? new RelativeDateTimeFormatter(*other.fFormatter) : NULL),
104	fCalendar(other.fCalendar != NULL
105		? new GregorianCalendar(*other.fCalendar) : NULL)
106{
107	if ((fFormatter == NULL && other.fFormatter != NULL)
108		|| (fCalendar == NULL && other.fCalendar != NULL))
109		fInitStatus = B_NO_MEMORY;
110}
111
112
113BRelativeDateTimeFormat::~BRelativeDateTimeFormat()
114{
115	delete fFormatter;
116	delete fCalendar;
117}
118
119
120status_t
121BRelativeDateTimeFormat::Format(BString& string,
122	const time_t timeValue) const
123{
124	if (fFormatter == NULL)
125		return B_NO_INIT;
126
127	time_t currentTime = time(NULL);
128
129	UErrorCode icuStatus = U_ZERO_ERROR;
130	fCalendar->setTime((UDate)currentTime * 1000, icuStatus);
131	if (!U_SUCCESS(icuStatus))
132		return B_ERROR;
133
134	UDate UTimeValue = (UDate)timeValue * 1000;
135
136	int delta = 0;
137	int offset = 1;
138	URelativeDateTimeUnit unit = UDAT_REL_UNIT_SECOND;
139
140	for (int timeUnit = 0; timeUnit <= B_TIME_UNIT_LAST; ++timeUnit) {
141		delta = fCalendar->fieldDifference(UTimeValue,
142			kTimeUnitToICUDateField[timeUnit], icuStatus);
143
144		if (!U_SUCCESS(icuStatus))
145			return B_ERROR;
146
147		if (abs(delta) >= offset) {
148			unit = kTimeUnitToRelativeDateTime[timeUnit];
149			break;
150		}
151	}
152
153	UnicodeString unicodeResult;
154
155	// Note: icu::RelativeDateTimeFormatter::formatNumeric() is a part of ICU Draft API
156	// and may be changed in the future versions and was introduced in ICU 57.
157	fFormatter->formatNumeric(delta, unit, unicodeResult, icuStatus);
158
159	if (!U_SUCCESS(icuStatus))
160		return B_ERROR;
161
162	BStringByteSink byteSink(&string);
163	unicodeResult.toUTF8(byteSink);
164
165	return B_OK;
166}
167