Implement a formatter for relative dates.

Signed-off-by: Adrien Destugues <[email protected]>

It can give results such as "in 2 hours", "2 days ago", etc.
This is different from BDurationFormat which will just say "1 hour, 2
minute and 36 seconds"
This commit is contained in:
Akshay Agarwal
2017-08-25 21:14:27 +02:00
committed by Adrien Destugues
parent 31e6a56fb3
commit 1e4ab34cd7
3 changed files with 208 additions and 0 deletions
@@ -0,0 +1,44 @@
/*
* Copyright 2017, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Akshay Agarwal <[email protected]>
*/
#ifndef _B_RELATIVE_DATE_TIME_FORMAT_H_
#define _B_RELATIVE_DATE_TIME_FORMAT_H_
#include <Format.h>
#include <SupportDefs.h>
class BString;
#ifndef U_ICU_NAMESPACE
#define U_ICU_NAMESPACE icu
#endif
namespace U_ICU_NAMESPACE {
class GregorianCalendar;
class RelativeDateTimeFormatter;
}
class BRelativeDateTimeFormat: public BFormat {
typedef BFormat Inherited;
public:
BRelativeDateTimeFormat();
BRelativeDateTimeFormat(const BLanguage& language,
const BFormattingConventions& conventions);
BRelativeDateTimeFormat(const BRelativeDateTimeFormat& other);
virtual ~BRelativeDateTimeFormat();
status_t Format(BString& string, const time_t timeValue) const;
private:
U_ICU_NAMESPACE::RelativeDateTimeFormatter* fFormatter;
U_ICU_NAMESPACE::GregorianCalendar* fCalendar;
};
#endif // _B_RELATIVE_DATE_TIME_FORMAT_H_
+1
View File
@@ -34,6 +34,7 @@ local sources =
TimeUnitFormat.cpp TimeUnitFormat.cpp
Format.cpp # Used by some of the above. Format.cpp # Used by some of the above.
UnicodeChar.cpp # Already used in FirstBootPrompt. UnicodeChar.cpp # Already used in FirstBootPrompt.
RelativeDateTimeFormat.cpp
; ;
local architectureObject ; local architectureObject ;
+163
View File
@@ -0,0 +1,163 @@
/*
* Copyright 2017, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Akshay Agarwal <[email protected]>
*/
#include <unicode/uversion.h>
#include <RelativeDateTimeFormat.h>
#include <stdlib.h>
#include <time.h>
#include <unicode/gregocal.h>
#include <unicode/reldatefmt.h>
#include <unicode/utypes.h>
#include <ICUWrapper.h>
#include <Language.h>
#include <Locale.h>
#include <LocaleRoster.h>
#include <TimeUnitFormat.h>
static const URelativeDateTimeUnit kTimeUnitToRelativeDateTime[] = {
UDAT_REL_UNIT_YEAR,
UDAT_REL_UNIT_MONTH,
UDAT_REL_UNIT_WEEK,
UDAT_REL_UNIT_DAY,
UDAT_REL_UNIT_HOUR,
UDAT_REL_UNIT_MINUTE,
UDAT_REL_UNIT_SECOND,
};
static const UCalendarDateFields kTimeUnitToICUDateField[] = {
UCAL_YEAR,
UCAL_MONTH,
UCAL_WEEK_OF_MONTH,
UCAL_DAY_OF_WEEK,
UCAL_HOUR_OF_DAY,
UCAL_MINUTE,
UCAL_SECOND,
};
BRelativeDateTimeFormat::BRelativeDateTimeFormat()
: Inherited()
{
Locale icuLocale(fLanguage.Code());
UErrorCode icuStatus = U_ZERO_ERROR;
fFormatter = new RelativeDateTimeFormatter(icuLocale, icuStatus);
if (fFormatter == NULL) {
fInitStatus = B_NO_MEMORY;
return;
}
fCalendar = new GregorianCalendar(icuStatus);
if (fCalendar == NULL) {
fInitStatus = B_NO_MEMORY;
return;
}
if (!U_SUCCESS(icuStatus))
fInitStatus = B_ERROR;
}
BRelativeDateTimeFormat::BRelativeDateTimeFormat(const BLanguage& language,
const BFormattingConventions& conventions)
: Inherited(language, conventions)
{
Locale icuLocale(fLanguage.Code());
UErrorCode icuStatus = U_ZERO_ERROR;
fFormatter = new RelativeDateTimeFormatter(icuLocale, icuStatus);
if (fFormatter == NULL) {
fInitStatus = B_NO_MEMORY;
return;
}
fCalendar = new GregorianCalendar(icuStatus);
if (fCalendar == NULL) {
fInitStatus = B_NO_MEMORY;
return;
}
if (!U_SUCCESS(icuStatus))
fInitStatus = B_ERROR;
}
BRelativeDateTimeFormat::BRelativeDateTimeFormat(const BRelativeDateTimeFormat& other)
: Inherited(other),
fFormatter(other.fFormatter != NULL
? new RelativeDateTimeFormatter(*other.fFormatter) : NULL),
fCalendar(other.fCalendar != NULL
? new GregorianCalendar(*other.fCalendar) : NULL)
{
if ((fFormatter == NULL && other.fFormatter != NULL)
|| (fCalendar == NULL && other.fCalendar != NULL))
fInitStatus = B_NO_MEMORY;
}
BRelativeDateTimeFormat::~BRelativeDateTimeFormat()
{
delete fFormatter;
delete fCalendar;
}
status_t
BRelativeDateTimeFormat::Format(BString& string,
const time_t timeValue) const
{
if (fFormatter == NULL)
return B_NO_INIT;
time_t currentTime = time(NULL);
UErrorCode icuStatus = U_ZERO_ERROR;
fCalendar->setTime((UDate)currentTime * 1000, icuStatus);
if (!U_SUCCESS(icuStatus))
return B_ERROR;
UDate UTimeValue = (UDate)timeValue * 1000;
int delta = 0;
int offset = 1;
URelativeDateTimeUnit unit = UDAT_REL_UNIT_SECOND;
for (int timeUnit = 0; timeUnit <= B_TIME_UNIT_LAST; ++timeUnit) {
delta = fCalendar->fieldDifference(UTimeValue,
kTimeUnitToICUDateField[timeUnit], icuStatus);
if (!U_SUCCESS(icuStatus))
return B_ERROR;
if (abs(delta) >= offset) {
unit = kTimeUnitToRelativeDateTime[timeUnit];
break;
}
}
UnicodeString unicodeResult;
// Note: icu::RelativeDateTimeFormatter::formatNumeric() is a part of ICU Draft API
// and may be changed in the future versions and was introduced in ICU 57.
fFormatter->formatNumeric(delta, unit, unicodeResult, icuStatus);
if (!U_SUCCESS(icuStatus))
return B_ERROR;
BStringByteSink byteSink(&string);
unicodeResult.toUTF8(byteSink);
return B_OK;
}