From: Tomasz Bocheński Date: Fri, 23 Mar 2018 13:05:47 +0000 (+0100) Subject: [base-utils][ACR-1203][DateIntervalFormat] Module implementation X-Git-Tag: accepted/tizen/unified/20180509.073059^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=d980c49d65bb9e7e4b9b97620999e716b86542ae;p=platform%2Fcore%2Fapi%2Fbase-utils.git [base-utils][ACR-1203][DateIntervalFormat] Module implementation Change-Id: I12ee75d1fe165d3ff078c0c026df2990f7a1c038 Signed-off-by: Tomasz Bocheński --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5e9cae8..d6a2494 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -44,6 +44,7 @@ SET(BASEUTILS_SRCS utils_i18n_plural_format.cpp utils_i18n_immutable_idx.cpp utils_i18n_date_interval.cpp + utils_i18n_date_interval_format.cpp ) ADD_LIBRARY(${target_name} SHARED ${BASEUTILS_SRCS} @@ -95,5 +96,6 @@ INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${INC_DIR}/utils_i18n_plural_rules.h D INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${INC_DIR}/utils_i18n_plural_format.h DESTINATION ${INCLUDE_INSTALL_DIR}/base) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${INC_DIR}/utils_i18n_immutable_idx.h DESTINATION ${INCLUDE_INSTALL_DIR}/base) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${INC_DIR}/utils_i18n_date_interval.h DESTINATION ${INCLUDE_INSTALL_DIR}/base) +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${INC_DIR}/utils_i18n_date_interval_format.h DESTINATION ${INCLUDE_INSTALL_DIR}/base) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${INC_DIR}/utils_i18n.h DESTINATION ${INCLUDE_INSTALL_DIR}/base) INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${pc_name}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) diff --git a/src/include/utils_i18n.h b/src/include/utils_i18n.h index 28ca174..048090d 100644 --- a/src/include/utils_i18n.h +++ b/src/include/utils_i18n.h @@ -49,6 +49,7 @@ #include #include #include +#include /** * @file utils_i18n.h @@ -91,6 +92,7 @@ extern "C" { * - plural format * - immutable_idx * - date interval + * - date interval format * * This module provides flexible generation of number or date format patterns and helps you format and parse dates/number for any locale. * The i18n module provides various features based on data from ICU. The following table shows the version of ICU used in each Tizen platform. @@ -241,6 +243,10 @@ extern "C" { * @ref CAPI_BASE_UTILS_I18N_DATE_INTERVAL_MODULE * Date interval represents a date interval. * + * + * @ref CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE + * Date interval format module formats date intervals in a language-independent manner. + * * * * @section CAPI_BASE_UTILS_I18N_MODULE_MAPPING_TABLE Mapping Table @@ -3952,6 +3958,56 @@ extern "C" { * #i18n_date_interval_equals * operator== * + * + * @ref CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE + * #i18n_date_interval_fmt_create + * createInstance + * + * + * @ref CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE + * #i18n_date_interval_fmt_create_from_locale + * createInstance + * + * + * @ref CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE + * #i18n_date_interval_fmt_clone + * clone + * + * + * @ref CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE + * #i18n_date_interval_fmt_destroy + * ~DateIntervalFormat + * + * + * @ref CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE + * #i18n_date_interval_fmt_equals + * operator== + * + * + * @ref CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE + * #i18n_date_interval_fmt_set_time_zone + * setTimeZone + * + * + * @ref CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE + * #i18n_date_interval_fmt_get_time_zone + * getTimeZone + * + * + * @ref CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE + * #i18n_date_interval_fmt_adopt_time_zone + * adoptTimeZone + * + * + * @ref CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE + * #i18n_date_interval_fmt_format_with_date_interval + * format + * + * + * @ref CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE + * #i18n_date_interval_fmt_format_with_calendar + * format + * * */ diff --git a/src/include/utils_i18n_date_interval_format.h b/src/include/utils_i18n_date_interval_format.h new file mode 100644 index 0000000..cc164c7 --- /dev/null +++ b/src/include/utils_i18n_date_interval_format.h @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __UTILS_I18N_DATE_INTERVAL_FORMAT_H__ +#define __UTILS_I18N_DATE_INTERVAL_FORMAT_H__ + +#include + +/** + * @file utils_i18n_date_interval_format.h + * @version 0.1 + * @brief utils_i18n_date_interval_format + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup CAPI_BASE_UTILS_I18N_MODULE + * @defgroup CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE DateIntervalFormat + * @brief Date Interval Format module formats date intervals in a language-independent manner. + * @section CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE_HEADER Required Header + * \#include + * + * @section CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE_OVERVIEW Overview + * @details Date Interval Format module formats a Date Interval into text as compactly as possible. + * For example, the date interval format from "Jan 11, 2008" to "Jan 18,. 2008" is + * "Jan 11-18, 2008" for English. + * + * There is no structural information in date time patterns. For any punctuations and + * string literals inside a date time pattern, we do not know whether it is just a separator, + * or a prefix, or a suffix. Without such information, so, it is difficult to generate + * a sub-pattern (or super-pattern) by algorithm. So, formatting a Date Interval is pattern-driven. + * + * A skeleton + * * only keeps the field pattern letter and ignores all other parts in a pattern, such as space, + * punctuations, and string literals. + * * hides the order of fields. + * * might hide a field's pattern letter length. + * + * For those non-digit calendar fields, the pattern letter length is important, such + * as MMM, MMMM, and MMMMM; EEE and EEEE, and the field's pattern letter length is honored. + * + * The calendar fields we support for interval formatting are: year, month, date, day-of-week, + * am-pm, hour, hour-of-day, minute, and second (though we do not currently have specific + * interval format date for skeletons with seconds). Those calendar fields can be defined in + * the following order: year > month > date > hour (in day) > minute > second. + * + * The largest different calendar fields between 2 calendars is the first different calendar + * field in above order. + * + * For example: the largest different calendar fields between "Jan 10, 2007" and "Feb 20, + * 2008" is year. + * + * For other calendar fields, the compact interval formatting is not supported. And the interval + * format will be fall back to fall-back patterns, which is mostly "{date0} - {date1}". + * + * There is a set of pre-defined static skeleton strings. There are pre-defined interval patterns + * for those pre-defined skeletons in locales' resource files. For example, for a skeleton + * #I18N_UDAT_YEAR_ABBR_MONTH_DAY, which is "yMMMd", in en_US, if the largest different calendar + * field between date1 and date2 is "year", the date interval pattern is "MMM d, yyyy - MMM + * d, yyyy", such as "Jan 10, 2007 - Jan 10, 2008". If the largest different calendar field between + * date1 and date2 is "month", the date interval pattern is "MMM d - MMM d, yyyy", such as + * "Jan 10 - Feb 10, 2007". If the largest different calendar field between date1 and date2 is "day", + * the date interval pattern is "MMM d-d, yyyy", such as "Jan 10-20, 2007". + * + * For date skeleton, the interval patterns when year, or month, or date is different are defined + * in resource files. For time skeleton, the interval patterns when am/pm, or hour, or minute + * is different are defined in resource files. + */ + +/** + * @addtogroup CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE + * @{ + */ + +/** + * @brief Creates a date interval format object with a non-specified locale. + * @since_tizen 5.0 + * @remarks The created object should be released by the caller with the + * i18n_date_interval_fmt_destroy() function. + * + * @param[in] skeleton The skeleton on which interval format based + * @param[out] date_interval_fmt The created date interval format object + * + * @return @c 0 on success, otherwise a negative error value + * @retval #I18N_ERROR_NONE Successful + * @retval #I18N_ERROR_INVALID_PARAMETER Invalid function parameter + * @retval #I18N_ERROR_OUT_OF_MEMORY Out of memory + */ +int i18n_date_interval_fmt_create(const char *skeleton, i18n_date_interval_fmt_h *date_interval_fmt); + +/** + * @brief Creates a date interval format object for the given locale. + * @since_tizen 5.0 + * @remarks The created object should be released by the caller with the + * i18n_date_interval_fmt_destroy() function. + * + * @param[in] skeleton The skeleton on which interval format based + * @param[in] locale The locale value + * @param[out] date_interval_fmt The created date interval format object + * + * @return @c 0 on success, otherwise a negative error value + * @retval #I18N_ERROR_NONE Successful + * @retval #I18N_ERROR_INVALID_PARAMETER Invalid function parameter + * @retval #I18N_ERROR_OUT_OF_MEMORY Out of memory + */ +int i18n_date_interval_fmt_create_from_locale(const char *skeleton, const char *locale, + i18n_date_interval_fmt_h *date_interval_fmt); + +/** + * @brief Creates a clone of the given @a date_interval_fmt object. + * @since_tizen 5.0 + * @remarks The @a clone object should be released by the caller with the + * i18n_date_interval_fmt_destroy() function. + * + * @param[in] date_interval_fmt The date interval format object to be cloned + * @param[out] clone The created date interval format object + * + * @return @c 0 on success, otherwise a negative error value + * @retval #I18N_ERROR_NONE Successful + * @retval #I18N_ERROR_INVALID_PARAMETER Invalid function parameter + * @retval #I18N_ERROR_OUT_OF_MEMORY Out of memory + */ +int i18n_date_interval_fmt_clone(i18n_date_interval_fmt_h date_interval_fmt, + i18n_date_interval_fmt_h *clone); + +/** + * @brief Destroys the date interval format object. + * @since_tizen 5.0 + * + * @param[in] date_interval_fmt The date interval format object to destroy + * + * @return @c 0 on success, otherwise a negative error value + * @retval #I18N_ERROR_NONE Successful + * @retval #I18N_ERROR_INVALID_PARAMETER Invalid function parameter + */ +int i18n_date_interval_fmt_destroy(i18n_date_interval_fmt_h date_interval_fmt); + +/** + * @brief Checks whether two date interval format objects are equal. + * @since_tizen 5.0 + * + * @param[in] date_interval_fmt_1 The first date interval format object + * @param[in] date_interval_fmt_2 The second date interval format object + * @param[out] equals @c true if @a date_interval_fmt1 is equal + * to @c date_interval_fmt2, @c false otherwise + * + * @return @c 0 on success, otherwise a negative error value + * @retval #I18N_ERROR_NONE Successful + * @retval #I18N_ERROR_INVALID_PARAMETER Invalid function parameter + */ +int i18n_date_interval_fmt_equals(i18n_date_interval_fmt_h date_interval_fmt_1, + i18n_date_interval_fmt_h date_interval_fmt_2, + bool *equals); + +/** + * @brief Sets the time zone for the calendar used by the #i18n_date_interval_fmt_h object. + * @since_tizen 5.0 + * + * @param[in] date_interval_fmt The date interval format object + * @param[in] timezone The new time zone + * + * @return @c 0 on success, otherwise a negative error value + * @retval #I18N_ERROR_NONE Successful + * @retval #I18N_ERROR_INVALID_PARAMETER Invalid function parameter + */ +int i18n_date_interval_fmt_set_time_zone(i18n_date_interval_fmt_h date_interval_fmt, + i18n_timezone_h timezone); + +/** + * @brief Gets the time zone used by the #i18n_date_interval_fmt_h object. + * @since_tizen 5.0 + * @remarks The obtained timezone should be released by the caller with the + * i18n_timezone_destroy() function. + * + * @param[in] date_interval_fmt The date interval format object + * @param[out] timezone The time zone used by @a date_interval_fmt object + * + * @return @c 0 on success, otherwise a negative error value + * @retval #I18N_ERROR_NONE Successful + * @retval #I18N_ERROR_INVALID_PARAMETER Invalid function parameter + */ +int i18n_date_interval_fmt_get_time_zone(i18n_date_interval_fmt_h date_interval_fmt, + i18n_timezone_h *timezone); + +/** + * @brief Adopts the time zone for the calendar used by the #i18n_date_interval_fmt_h object. + * @details The caller no longer owns the timezone object and should not delete it + * after this call. + * @since_tizen 5.0 + * + * @param[in] date_interval_fmt The date interval format object + * @param[out] timezone The time zone used by @a date_interval_fmt object + * + * @return @c 0 on success, otherwise a negative error value + * @retval #I18N_ERROR_NONE Successful + * @retval #I18N_ERROR_INVALID_PARAMETER Invalid function parameter + */ +int i18n_date_interval_fmt_adopt_time_zone(i18n_date_interval_fmt_h date_interval_fmt, + i18n_timezone_h timezone); + +/** + * @brief Formats a date interval to produce a string. + * @since_tizen 5.0 + * + * @param[in] date_interval_fmt The date interval format object + * @param[in] date_interval Date interval to be formatted + * @param[in,out] append_to Input/output parameter to receive the result. + * The result is appended to the existing contents. + * @param[in] field_position The #i18n_field_position_h object + * + * @return @c 0 on success, otherwise a negative error value + * @retval #I18N_ERROR_NONE Successful + * @retval #I18N_ERROR_INVALID_PARAMETER Invalid function parameter + * @retval #I18N_ERROR_BUFFER_OVERFLOW Buffer overflow + */ +int i18n_date_interval_fmt_format_with_date_interval(i18n_date_interval_fmt_h date_interval_fmt, + i18n_date_interval_h date_interval, char **append_to, + i18n_field_position_h field_position); + +/** + * @brief Formats two calendars to produce a string. + * @since_tizen 5.0 + * + * @param[in] date_interval_fmt The date interval format object + * @param[in] from_cal Calendar set to the 'from date' in date interval to be formatted + * into date interval string + * @param[in] to_cal Calendar set to the 'to date' in date interval to be formatted into + * date interval string + * @param[in,out] append_to Input/output parameter to receive the result. + * The result is appended to the existing contents. + * @param[in] field_position The #i18n_field_position_h object + * + * @return @c 0 on success, otherwise a negative error value + * @retval #I18N_ERROR_NONE Successful + * @retval #I18N_ERROR_INVALID_PARAMETER Invalid function parameter + * @retval #I18N_ERROR_BUFFER_OVERFLOW Buffer overflow + */ +int i18n_date_interval_fmt_format_with_calendar(i18n_date_interval_fmt_h date_interval_fmt, + i18n_ucalendar_h from_cal, i18n_ucalendar_h to_cal, + char **append_to, i18n_field_position_h field_position); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __UTILS_I18N_DATE_INTERVAL_FORMAT_H__ */ diff --git a/src/include/utils_i18n_types.h b/src/include/utils_i18n_types.h index c8ace19..177aba6 100644 --- a/src/include/utils_i18n_types.h +++ b/src/include/utils_i18n_types.h @@ -4418,13 +4418,20 @@ typedef void *i18n_date_interval_h; */ /** - * @brief This value is intended for sentinel values for APIs that (take or) return - * single code points (#i18n_uchar32). - * @details It is outside of the Unicode code point range 0..0x10ffff. For example, - * a "done" or "error" value in a new API could be indicated with #I18N_SENTINEL. - * @since_tizen 4.0 +* @addtogroup CAPI_BASE_UTILS_I18N_DATE_INTERVAL_FORMAT_MODULE +* @{ +*/ + +/** + * @brief An #i18n_date_interval_fmt_h handle. + * @details Use i18n_date_interval_fmt_* functions to operate on #i18n_date_interval_fmt_h objects. + * @since_tizen 5.0 + */ +typedef void *i18n_date_interval_fmt_h; + +/** + * @} */ -#define I18N_SENTINEL (-1) #ifdef __cplusplus } diff --git a/src/utils_i18n_date_interval_format.cpp b/src/utils_i18n_date_interval_format.cpp new file mode 100644 index 0000000..a665546 --- /dev/null +++ b/src/utils_i18n_date_interval_format.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include + +int i18n_date_interval_fmt_create(const char *skeleton, i18n_date_interval_fmt_h *date_interval_fmt) +{ + retv_if(date_interval_fmt == NULL, I18N_ERROR_INVALID_PARAMETER); + + UErrorCode status = U_ZERO_ERROR; + UnicodeString unicode_skeleton(skeleton); + *date_interval_fmt = DateIntervalFormat::createInstance(unicode_skeleton, status); + + retv_if(*date_interval_fmt == NULL, I18N_ERROR_OUT_OF_MEMORY); + return _i18n_error_mapping(status); +} + +int i18n_date_interval_fmt_create_from_locale(const char *skeleton, const char *locale, + i18n_date_interval_fmt_h *date_interval_fmt) +{ + retv_if(date_interval_fmt == NULL, I18N_ERROR_INVALID_PARAMETER); + + UErrorCode status = U_ZERO_ERROR; + UnicodeString unicode_skeleton(skeleton); + Locale loc(locale, 0, 0, 0); + *date_interval_fmt = DateIntervalFormat::createInstance(unicode_skeleton, loc, status); + + retv_if(*date_interval_fmt == NULL, I18N_ERROR_OUT_OF_MEMORY); + return _i18n_error_mapping(status); +} + +int i18n_date_interval_fmt_clone(i18n_date_interval_fmt_h date_interval_fmt, + i18n_date_interval_fmt_h *clone) +{ + retv_if(date_interval_fmt == NULL, I18N_ERROR_INVALID_PARAMETER); + retv_if(clone == NULL, I18N_ERROR_INVALID_PARAMETER); + + *clone = ((DateIntervalFormat *) date_interval_fmt)->clone(); + retv_if(clone == NULL, I18N_ERROR_OUT_OF_MEMORY); + + return I18N_ERROR_NONE; +} + +int i18n_date_interval_fmt_destroy(i18n_date_interval_fmt_h date_interval_fmt) +{ + retv_if(date_interval_fmt == NULL, I18N_ERROR_INVALID_PARAMETER); + + delete((DateIntervalFormat *) date_interval_fmt); + + return I18N_ERROR_NONE; +} + +int i18n_date_interval_fmt_equals(i18n_date_interval_fmt_h date_interval_fmt_1, + i18n_date_interval_fmt_h date_interval_fmt_2, + bool *equals) +{ + retv_if(date_interval_fmt_1 == NULL, I18N_ERROR_INVALID_PARAMETER); + retv_if(date_interval_fmt_2 == NULL, I18N_ERROR_INVALID_PARAMETER); + retv_if(equals == NULL, I18N_ERROR_INVALID_PARAMETER); + + *equals = (*((DateIntervalFormat *) date_interval_fmt_1)) == + (*((DateIntervalFormat *) date_interval_fmt_2)); + + return I18N_ERROR_NONE; +} + +int i18n_date_interval_fmt_set_time_zone(i18n_date_interval_fmt_h date_interval_fmt, + i18n_timezone_h timezone) +{ + retv_if(date_interval_fmt == NULL, I18N_ERROR_INVALID_PARAMETER); + retv_if(timezone == NULL, I18N_ERROR_INVALID_PARAMETER); + + ((DateIntervalFormat *) date_interval_fmt)->setTimeZone(*((TimeZone *) timezone)); + + return I18N_ERROR_NONE; +} + +int i18n_date_interval_fmt_get_time_zone(i18n_date_interval_fmt_h date_interval_fmt, + i18n_timezone_h *timezone) +{ + retv_if(date_interval_fmt == NULL, I18N_ERROR_INVALID_PARAMETER); + retv_if(timezone == NULL, I18N_ERROR_INVALID_PARAMETER); + + *timezone = (((DateIntervalFormat *) date_interval_fmt)->getTimeZone()).clone(); + retv_if(*timezone == NULL, I18N_ERROR_OUT_OF_MEMORY); + + return I18N_ERROR_NONE; +} + +int i18n_date_interval_fmt_adopt_time_zone(i18n_date_interval_fmt_h date_interval_fmt, + i18n_timezone_h timezone) +{ + retv_if(date_interval_fmt == NULL, I18N_ERROR_INVALID_PARAMETER); + retv_if(timezone == NULL, I18N_ERROR_INVALID_PARAMETER); + + ((DateIntervalFormat *) date_interval_fmt)->adoptTimeZone((TimeZone *) timezone); + + return I18N_ERROR_NONE; +} + +int i18n_date_interval_fmt_format_with_date_interval(i18n_date_interval_fmt_h date_interval_fmt, + i18n_date_interval_h date_interval, char **append_to, + i18n_field_position_h field_position) +{ + retv_if(date_interval_fmt == NULL, I18N_ERROR_INVALID_PARAMETER); + retv_if(date_interval == NULL, I18N_ERROR_INVALID_PARAMETER); + retv_if(append_to == NULL, I18N_ERROR_INVALID_PARAMETER); + retv_if(field_position == NULL, I18N_ERROR_INVALID_PARAMETER); + + UErrorCode status = U_ZERO_ERROR; + + UnicodeString unicode_append_to(*append_to); + + UnicodeString &result = + ((DateIntervalFormat *) date_interval_fmt)->format(((DateInterval *) date_interval), + unicode_append_to, + *((FieldPosition *) field_position), + status); + const i18n_uchar *uchar_result = (i18n_uchar *)result.getTerminatedBuffer(); + + retv_if(uchar_result == NULL, I18N_ERROR_INVALID_PARAMETER); + int32_t ulen = i18n_ustring_get_length(uchar_result); + + retv_if(ulen <= 0, I18N_ERROR_INVALID_PARAMETER); + + /* + * UTF-16 uses at least two bytes, growing up to four bytes as necessary, + * this is why we multiply UChar by 4. + */ + int32_t char_len = 4*ulen+4; + char _append_to[char_len]; + memset(_append_to, 0x0, char_len); + i18n_ustring_copy_au(_append_to, uchar_result); + + int32_t len = strlen(_append_to); + free(*append_to); + *append_to = (char *) malloc(len+1); + + retv_if(*append_to == NULL, I18N_ERROR_OUT_OF_MEMORY); + + memset(*append_to, 0x0, len+1); + + COPY_STR(*append_to, _append_to, len+1); + + return _i18n_error_mapping(status); +} + +int i18n_date_interval_fmt_format_with_calendar(i18n_date_interval_fmt_h date_interval_fmt, + i18n_ucalendar_h from_cal, i18n_ucalendar_h to_cal, + char **append_to, i18n_field_position_h field_position) +{ + retv_if(date_interval_fmt == NULL, I18N_ERROR_INVALID_PARAMETER); + retv_if(from_cal == NULL, I18N_ERROR_INVALID_PARAMETER); + retv_if(to_cal == NULL, I18N_ERROR_INVALID_PARAMETER); + retv_if(append_to == NULL, I18N_ERROR_INVALID_PARAMETER); + retv_if(field_position == NULL, I18N_ERROR_INVALID_PARAMETER); + + UErrorCode status = U_ZERO_ERROR; + + UnicodeString unicode_append_to(*append_to); + + UnicodeString &result = + ((DateIntervalFormat *) date_interval_fmt)->format(*((Calendar *) from_cal), *((Calendar *) to_cal), + unicode_append_to, + *((FieldPosition *) field_position), + status); + const i18n_uchar *uchar_result = (i18n_uchar *)result.getTerminatedBuffer(); + + retv_if(uchar_result == NULL, I18N_ERROR_INVALID_PARAMETER); + int32_t ulen = i18n_ustring_get_length(uchar_result); + + retv_if(ulen <= 0, I18N_ERROR_INVALID_PARAMETER); + + /* + * UTF-16 uses at least two bytes, growing up to four bytes as necessary, + * this is why we multiply UChar by 4. + */ + int32_t char_len = 4*ulen+4; + char _append_to[char_len]; + memset(_append_to, 0x0, char_len); + i18n_ustring_copy_au(_append_to, uchar_result); + + int32_t len = strlen(_append_to); + free(*append_to); + *append_to = (char *) malloc(len+1); + + retv_if(*append_to == NULL, I18N_ERROR_OUT_OF_MEMORY); + + memset(*append_to, 0x0, len+1); + + COPY_STR(*append_to, _append_to, len+1); + + return _i18n_error_mapping(status); +}