xref: /haiku/src/kits/locale/RelativeDateTimeFormat.cpp (revision 1bad1ff34a3e132a8816d2ac7ba9722d3a5249a9)
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 
28 U_NAMESPACE_USE
29 
30 
31 static 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 
42 static 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 
BRelativeDateTimeFormat()53 BRelativeDateTimeFormat::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 
BRelativeDateTimeFormat(const BLanguage & language,const BFormattingConventions & conventions)76 BRelativeDateTimeFormat::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 
BRelativeDateTimeFormat(const BRelativeDateTimeFormat & other)100 BRelativeDateTimeFormat::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 
~BRelativeDateTimeFormat()113 BRelativeDateTimeFormat::~BRelativeDateTimeFormat()
114 {
115 	delete fFormatter;
116 	delete fCalendar;
117 }
118 
119 
120 status_t
Format(BString & string,const time_t timeValue) const121 BRelativeDateTimeFormat::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