mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-14 09:21:03 +00:00
Tests pass with new DateInfo struct
This commit is contained in:
parent
f8c67ece8b
commit
c42fe17a3a
5 changed files with 98 additions and 36 deletions
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "unicode/messageformat2_formattable.h"
|
||||
#include "unicode/smpdtfmt.h"
|
||||
#include "messageformat2_allocation.h"
|
||||
#include "messageformat2_macros.h"
|
||||
|
||||
#include "limits.h"
|
||||
|
@ -227,19 +228,50 @@ namespace message2 {
|
|||
return df.orphan();
|
||||
}
|
||||
|
||||
void formatDateWithDefaults(const Locale& locale, const GregorianCalendar& cal, UnicodeString& result, UErrorCode& errorCode) {
|
||||
GregorianCalendar* DateInfo::createGregorianCalendar(UErrorCode& errorCode) const {
|
||||
NULL_ON_ERROR(errorCode);
|
||||
|
||||
LocalPointer<GregorianCalendar> cal(new GregorianCalendar(errorCode));
|
||||
if (!cal.isValid()) {
|
||||
errorCode = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
// Copy info from this
|
||||
// Non-Gregorian calendars not implemented yet
|
||||
U_ASSERT(calendarName.length() == 0);
|
||||
cal->setTime(date, errorCode);
|
||||
// If time zone is present...
|
||||
if (zoneName.length() > 0) {
|
||||
if (zoneName == UnicodeString("UTC")) {
|
||||
cal->setTimeZone(*TimeZone::getGMT());
|
||||
} else {
|
||||
LocalPointer<TimeZone> tz(TimeZone::createTimeZone(zoneName));
|
||||
if (!tz.isValid()) {
|
||||
errorCode = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
cal->setTimeZone(*tz);
|
||||
}
|
||||
}
|
||||
return cal.orphan();
|
||||
}
|
||||
|
||||
void formatDateWithDefaults(const Locale& locale,
|
||||
const DateInfo& dateInfo,
|
||||
UnicodeString& result,
|
||||
UErrorCode& errorCode) {
|
||||
CHECK_ERROR(errorCode);
|
||||
|
||||
LocalPointer<DateFormat> df(defaultDateTimeInstance(locale, errorCode));
|
||||
CHECK_ERROR(errorCode);
|
||||
// We have to copy the `const` reference that was passed in,
|
||||
// because DateFormat::format() takes a non-const reference.
|
||||
LocalPointer<GregorianCalendar> copied(new GregorianCalendar(cal));
|
||||
if (!copied.isValid()) {
|
||||
errorCode = U_MEMORY_ALLOCATION_ERROR;
|
||||
return;
|
||||
}
|
||||
df->format(*copied, result, nullptr, errorCode);
|
||||
|
||||
// Non-Gregorian calendars not supported yet
|
||||
U_ASSERT(dateInfo.calendarName.length() == 0);
|
||||
LocalPointer<GregorianCalendar> cal(dateInfo.createGregorianCalendar(errorCode));
|
||||
CHECK_ERROR(errorCode);
|
||||
df->format(*cal, result, nullptr, errorCode);
|
||||
}
|
||||
|
||||
// Called when output is required and the contents are an unevaluated `Formattable`;
|
||||
|
@ -270,9 +302,9 @@ namespace message2 {
|
|||
switch (type) {
|
||||
case UFMT_DATE: {
|
||||
UnicodeString result;
|
||||
const GregorianCalendar* cal = toFormat.getDate(status);
|
||||
const DateInfo* dateInfo = toFormat.getDate(status);
|
||||
U_ASSERT(U_SUCCESS(status));
|
||||
formatDateWithDefaults(locale, *cal, result, status);
|
||||
formatDateWithDefaults(locale, *dateInfo, result, status);
|
||||
return FormattedPlaceholder(input, FormattedValue(std::move(result)));
|
||||
}
|
||||
case UFMT_DOUBLE: {
|
||||
|
|
|
@ -1067,7 +1067,10 @@ SimpleTimeZone* createTimeZonePart(const UnicodeString& offsetStr, UErrorCode& e
|
|||
return nullptr;
|
||||
}
|
||||
int32_t offset = sign * (static_cast<int32_t>(tzHour) * 60 + static_cast<int32_t>(tzMin)) * 60 * 1000;
|
||||
LocalPointer<SimpleTimeZone> tz(new SimpleTimeZone(offset, UnicodeString("offset")));
|
||||
LocalPointer<SimpleTimeZone> tz(new SimpleTimeZone(offset, UnicodeString("GMT") + offsetStr));
|
||||
/*
|
||||
Use tz.getIanaID() to get the time zone name
|
||||
*/
|
||||
if (!tz.isValid()) {
|
||||
errorCode = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
|
@ -1084,6 +1087,8 @@ static bool hasTzOffset(const UnicodeString& sourceStr) {
|
|||
&& sourceStr[len - 3] == COLON);
|
||||
}
|
||||
|
||||
// Note: `calendar` option to :datetime not implemented yet;
|
||||
// Gregorian calendar is assumed
|
||||
static GregorianCalendar* createCalendarFromDateString(const UnicodeString& sourceStr, UErrorCode& errorCode) {
|
||||
NULL_ON_ERROR(errorCode);
|
||||
|
||||
|
@ -1327,7 +1332,6 @@ FormattedPlaceholder StandardFunctions::DateTime::format(FormattedPlaceholder&&
|
|||
U_ASSERT(U_SUCCESS(errorCode));
|
||||
|
||||
LocalPointer<GregorianCalendar> cal(createCalendarFromDateString(sourceStr, errorCode));
|
||||
|
||||
if (U_FAILURE(errorCode)) {
|
||||
errorCode = U_MF_OPERAND_MISMATCH_ERROR;
|
||||
return {};
|
||||
|
@ -1337,20 +1341,32 @@ FormattedPlaceholder StandardFunctions::DateTime::format(FormattedPlaceholder&&
|
|||
// in the returned FormattedPlaceholder; this is necessary
|
||||
// so the date can be re-formatted
|
||||
df->format(*cal, result, 0, errorCode);
|
||||
toFormat = FormattedPlaceholder(message2::Formattable(std::move(*cal)),
|
||||
|
||||
// Construct DateInfo from Date
|
||||
UDate absoluteDate = cal->getTime(errorCode);
|
||||
const TimeZone& tz = cal->getTimeZone();
|
||||
UnicodeString timeZoneId;
|
||||
tz.getID(timeZoneId);
|
||||
UnicodeString timeZoneName;
|
||||
// This doesn't work for offsets -- TODO
|
||||
// tz.getIanaID(timeZoneId, timeZoneName, errorCode);
|
||||
// Empty string for Gregorian calendar (default);
|
||||
// `:datetime` `calendar` option not implemented yet,
|
||||
// so other calendars aren't implemented
|
||||
DateInfo dateInfo = {absoluteDate, timeZoneId, {}}; // Should be timeZoneName, but getIanaID() doesn't work for strings like GMT+03:30
|
||||
toFormat = FormattedPlaceholder(message2::Formattable(std::move(dateInfo)),
|
||||
toFormat.getFallback());
|
||||
break;
|
||||
}
|
||||
case UFMT_DATE: {
|
||||
const GregorianCalendar* cal = source.getDate(errorCode);
|
||||
const DateInfo* dateInfo = source.getDate(errorCode);
|
||||
U_ASSERT(U_SUCCESS(errorCode));
|
||||
U_ASSERT(cal != nullptr);
|
||||
LocalPointer<GregorianCalendar> copied(new GregorianCalendar(*cal));
|
||||
if (!copied.isValid()) {
|
||||
errorCode = U_MEMORY_ALLOCATION_ERROR;
|
||||
|
||||
LocalPointer<GregorianCalendar> cal(dateInfo->createGregorianCalendar(errorCode));
|
||||
if (U_FAILURE(errorCode)) {
|
||||
return {};
|
||||
}
|
||||
df->format(*copied, result, 0, errorCode);
|
||||
df->format(*cal, result, 0, errorCode);
|
||||
if (U_FAILURE(errorCode)) {
|
||||
if (errorCode == U_ILLEGAL_ARGUMENT_ERROR) {
|
||||
errorCode = U_MF_OPERAND_MISMATCH_ERROR;
|
||||
|
|
|
@ -68,6 +68,7 @@ namespace message2 {
|
|||
virtual ~FormattableObject();
|
||||
}; // class FormattableObject
|
||||
|
||||
struct DateInfo;
|
||||
class Formattable;
|
||||
} // namespace message2
|
||||
|
||||
|
@ -86,7 +87,7 @@ template class U_I18N_API std::_Variant_storage_<false,
|
|||
int64_t,
|
||||
icu::UnicodeString,
|
||||
icu::Formattable,
|
||||
icu::GregorianCalendar,
|
||||
icu::message2::DateInfo;
|
||||
const icu::message2::FormattableObject *,
|
||||
std::pair<const icu::message2::Formattable *,int32_t>>;
|
||||
#endif
|
||||
|
@ -95,7 +96,7 @@ template class U_I18N_API std::variant<double,
|
|||
int64_t,
|
||||
icu::UnicodeString,
|
||||
icu::Formattable,
|
||||
icu::GregorianCalendar,
|
||||
icu::message2::DateInfo,
|
||||
const icu::message2::FormattableObject*,
|
||||
P>;
|
||||
#endif
|
||||
|
@ -104,6 +105,19 @@ template class U_I18N_API std::variant<double,
|
|||
U_NAMESPACE_BEGIN
|
||||
|
||||
namespace message2 {
|
||||
|
||||
// TODO: doc comments
|
||||
struct U_I18N_API DateInfo {
|
||||
// Milliseconds since Unix epoch
|
||||
UDate date;
|
||||
// IANA time zone name; "UTC" if UTC; empty string if value is floating
|
||||
UnicodeString zoneName;
|
||||
// Empty if not specified; proleptic Gregorian (8601) calendar is default
|
||||
UnicodeString calendarName;
|
||||
|
||||
GregorianCalendar* createGregorianCalendar(UErrorCode&) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* The `Formattable` class represents a typed value that can be formatted,
|
||||
* originating either from a message argument or a literal in the code.
|
||||
|
@ -232,20 +246,20 @@ namespace message2 {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the calendar representing the date value of this object.
|
||||
* Gets the struct representing the date value of this object.
|
||||
* If this object is not of type kDate then the result is
|
||||
* undefined and the error code is set.
|
||||
*
|
||||
* @param status Input/output error code.
|
||||
* @return A non-owned pointer to a GregorianCalendar object
|
||||
* @return A non-owned pointer to a DateInfo object
|
||||
* representing the underlying date of this object.
|
||||
* @internal ICU 75 technology preview
|
||||
* @deprecated This API is for technology preview only.
|
||||
*/
|
||||
const GregorianCalendar* getDate(UErrorCode& status) const {
|
||||
const DateInfo* getDate(UErrorCode& status) const {
|
||||
if (U_SUCCESS(status)) {
|
||||
if (isDate()) {
|
||||
return std::get_if<GregorianCalendar>(&contents);
|
||||
return std::get_if<DateInfo>(&contents);
|
||||
}
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
}
|
||||
|
@ -360,13 +374,13 @@ namespace message2 {
|
|||
/**
|
||||
* Date constructor.
|
||||
*
|
||||
* @param c A GregorianCalendar value representing a date,
|
||||
* @param d A DateInfo struct representing a date,
|
||||
* to wrap as a Formattable.
|
||||
* Passed by move
|
||||
* @internal ICU 75 technology preview
|
||||
* @deprecated This API is for technology preview only.
|
||||
*/
|
||||
Formattable(GregorianCalendar&& c) : contents(std::move(c)) {}
|
||||
Formattable(DateInfo&& d) : contents(std::move(d)) {}
|
||||
/**
|
||||
* Creates a Formattable object of an appropriate numeric type from a
|
||||
* a decimal number in string form. The Formattable will retain the
|
||||
|
@ -426,7 +440,7 @@ namespace message2 {
|
|||
int64_t,
|
||||
UnicodeString,
|
||||
icu::Formattable, // represents a Decimal
|
||||
GregorianCalendar,
|
||||
DateInfo,
|
||||
const FormattableObject*,
|
||||
std::pair<const Formattable*, int32_t>> contents;
|
||||
UnicodeString bogusString; // :((((
|
||||
|
@ -435,7 +449,7 @@ namespace message2 {
|
|||
return std::holds_alternative<icu::Formattable>(contents);
|
||||
}
|
||||
UBool isDate() const {
|
||||
return std::holds_alternative<GregorianCalendar>(contents);
|
||||
return std::holds_alternative<DateInfo>(contents);
|
||||
}
|
||||
}; // class Formattable
|
||||
|
||||
|
|
|
@ -163,7 +163,10 @@ void TestMessageFormat2::testAPISimple() {
|
|||
cal.set(2136, Calendar::OCTOBER, 28, 8, 39, 12);
|
||||
|
||||
argsBuilder.clear();
|
||||
argsBuilder["today"] = message2::Formattable(std::move(cal));
|
||||
DateInfo dateInfo = { cal.getTime(errorCode),
|
||||
"Pacific Standard Time",
|
||||
"" };
|
||||
argsBuilder["today"] = message2::Formattable(std::move(dateInfo));
|
||||
args = MessageArguments(argsBuilder, errorCode);
|
||||
result = mf.formatToString(args, errorCode);
|
||||
assertEquals("testAPI", "Today is Sunday, October 28, 2136.", result);
|
||||
|
@ -210,7 +213,7 @@ void TestMessageFormat2::testAPI() {
|
|||
|
||||
test = testBuilder.setName("testAPI")
|
||||
.setPattern("Today is {$today :date style=full}.")
|
||||
.setDateArgument("today", date, errorCode)
|
||||
.setDateArgument("today", date)
|
||||
.setExpected("Today is Sunday, October 28, 2136.")
|
||||
.setLocale("en_US")
|
||||
.build();
|
||||
|
|
|
@ -112,16 +112,13 @@ class TestCase : public UMemory {
|
|||
arguments[k] = Formattable(val);
|
||||
return *this;
|
||||
}
|
||||
Builder& setDateArgument(const UnicodeString& k, UDate date, UErrorCode& errorCode) {
|
||||
Builder& setDateArgument(const UnicodeString& k, UDate date) {
|
||||
// This ignores time zones; the data-driven tests represent date/time values
|
||||
// as a datestamp, so this code suffices to handle those.
|
||||
// Date/time literal strings would be handled using `setArgument()` with a string
|
||||
// argument.
|
||||
GregorianCalendar cal(errorCode);
|
||||
THIS_ON_ERROR(errorCode);
|
||||
cal.setTime(date, errorCode);
|
||||
THIS_ON_ERROR(errorCode);
|
||||
arguments[k] = Formattable(std::move(cal));
|
||||
DateInfo dateInfo = { date, {}, {} }; // No time zone or calendar name
|
||||
arguments[k] = Formattable(std::move(dateInfo));
|
||||
return *this;
|
||||
}
|
||||
Builder& setDecimalArgument(const UnicodeString& k, std::string_view decimal, UErrorCode& errorCode) {
|
||||
|
|
Loading…
Add table
Reference in a new issue