xref: /haiku/src/kits/locale/RelativeDateTimeFormat.cpp (revision d284f7cc43cc0d1106c3b0c40e62c58107648573)
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 static const URelativeDateTimeUnit kTimeUnitToRelativeDateTime[] = {
29 	UDAT_REL_UNIT_YEAR,
30 	UDAT_REL_UNIT_MONTH,
31 	UDAT_REL_UNIT_WEEK,
32 	UDAT_REL_UNIT_DAY,
33 	UDAT_REL_UNIT_HOUR,
34 	UDAT_REL_UNIT_MINUTE,
35 	UDAT_REL_UNIT_SECOND,
36 };
37 
38 
39 static const UCalendarDateFields kTimeUnitToICUDateField[] = {
40 	UCAL_YEAR,
41 	UCAL_MONTH,
42 	UCAL_WEEK_OF_MONTH,
43 	UCAL_DAY_OF_WEEK,
44 	UCAL_HOUR_OF_DAY,
45 	UCAL_MINUTE,
46 	UCAL_SECOND,
47 };
48 
49 
50 BRelativeDateTimeFormat::BRelativeDateTimeFormat()
51 	: Inherited()
52 {
53 	Locale icuLocale(fLanguage.Code());
54 	UErrorCode icuStatus = U_ZERO_ERROR;
55 
56 	fFormatter = new RelativeDateTimeFormatter(icuLocale, icuStatus);
57 	if (fFormatter == NULL) {
58 		fInitStatus = B_NO_MEMORY;
59 		return;
60 	}
61 
62 	fCalendar = new GregorianCalendar(icuStatus);
63 	if (fCalendar == NULL) {
64 		fInitStatus = B_NO_MEMORY;
65 		return;
66 	}
67 
68 	if (!U_SUCCESS(icuStatus))
69 		fInitStatus = B_ERROR;
70 }
71 
72 
73 BRelativeDateTimeFormat::BRelativeDateTimeFormat(const BLanguage& language,
74 	const BFormattingConventions& conventions)
75 	: Inherited(language, conventions)
76 {
77 	Locale icuLocale(fLanguage.Code());
78 	UErrorCode icuStatus = U_ZERO_ERROR;
79 
80 	fFormatter = new RelativeDateTimeFormatter(icuLocale, icuStatus);
81 	if (fFormatter == NULL) {
82 		fInitStatus = B_NO_MEMORY;
83 		return;
84 	}
85 
86 	fCalendar = new GregorianCalendar(icuStatus);
87 	if (fCalendar == NULL) {
88 		fInitStatus = B_NO_MEMORY;
89 		return;
90 	}
91 
92 	if (!U_SUCCESS(icuStatus))
93 		fInitStatus = B_ERROR;
94 }
95 
96 
97 BRelativeDateTimeFormat::BRelativeDateTimeFormat(const BRelativeDateTimeFormat& other)
98 	: Inherited(other),
99 	fFormatter(other.fFormatter != NULL
100 		? new RelativeDateTimeFormatter(*other.fFormatter) : NULL),
101 	fCalendar(other.fCalendar != NULL
102 		? new GregorianCalendar(*other.fCalendar) : NULL)
103 {
104 	if ((fFormatter == NULL && other.fFormatter != NULL)
105 		|| (fCalendar == NULL && other.fCalendar != NULL))
106 		fInitStatus = B_NO_MEMORY;
107 }
108 
109 
110 BRelativeDateTimeFormat::~BRelativeDateTimeFormat()
111 {
112 	delete fFormatter;
113 	delete fCalendar;
114 }
115 
116 
117 status_t
118 BRelativeDateTimeFormat::Format(BString& string,
119 	const time_t timeValue) const
120 {
121 	if (fFormatter == NULL)
122 		return B_NO_INIT;
123 
124 	time_t currentTime = time(NULL);
125 
126 	UErrorCode icuStatus = U_ZERO_ERROR;
127 	fCalendar->setTime((UDate)currentTime * 1000, icuStatus);
128 	if (!U_SUCCESS(icuStatus))
129 		return B_ERROR;
130 
131 	UDate UTimeValue = (UDate)timeValue * 1000;
132 
133 	int delta = 0;
134 	int offset = 1;
135 	URelativeDateTimeUnit unit = UDAT_REL_UNIT_SECOND;
136 
137 	for (int timeUnit = 0; timeUnit <= B_TIME_UNIT_LAST; ++timeUnit) {
138 		delta = fCalendar->fieldDifference(UTimeValue,
139 			kTimeUnitToICUDateField[timeUnit], icuStatus);
140 
141 		if (!U_SUCCESS(icuStatus))
142 			return B_ERROR;
143 
144 		if (abs(delta) >= offset) {
145 			unit = kTimeUnitToRelativeDateTime[timeUnit];
146 			break;
147 		}
148 	}
149 
150 	UnicodeString unicodeResult;
151 
152 	// Note: icu::RelativeDateTimeFormatter::formatNumeric() is a part of ICU Draft API
153 	// and may be changed in the future versions and was introduced in ICU 57.
154 	fFormatter->formatNumeric(delta, unit, unicodeResult, icuStatus);
155 
156 	if (!U_SUCCESS(icuStatus))
157 		return B_ERROR;
158 
159 	BStringByteSink byteSink(&string);
160 	unicodeResult.toUTF8(byteSink);
161 
162 	return B_OK;
163 }
164