Bug 1954138 - Part 2: Set Gregorian change date when constructing formatters. r=dminor
Always reset the Gregorian change date, because all uses either require the Gregorian change date to be set to `-8.64e15` or don't care about dates far into the past. The slow `DateIntervalFormat::TryFormatCalendar` is now only used when `MOZ_SYSTEM_ICU` is defined. And also resets the Gregorian change date for "buddhist", "japanese", and "roc" to get consistent results when compared to the ICU4X-based Temporal code. Differential Revision: https://phabricator.services.mozilla.com/D241644
This commit is contained in:
@@ -574,9 +574,6 @@ TEST(IntlDateTimeFormat, SetStartTimeIfGregorian)
|
|||||||
|
|
||||||
auto timeZone = Some(MakeStringSpan(u"UTC"));
|
auto timeZone = Some(MakeStringSpan(u"UTC"));
|
||||||
|
|
||||||
// Beginning of ECMAScript time.
|
|
||||||
constexpr double StartOfTime = -8.64e15;
|
|
||||||
|
|
||||||
// Gregorian change date defaults to October 15, 1582 in ICU. Test with a date
|
// Gregorian change date defaults to October 15, 1582 in ICU. Test with a date
|
||||||
// before the default change date, in this case January 1, 1582.
|
// before the default change date, in this case January 1, 1582.
|
||||||
constexpr double FirstJanuary1582 = -12244089600000.0;
|
constexpr double FirstJanuary1582 = -12244089600000.0;
|
||||||
@@ -597,38 +594,25 @@ TEST(IntlDateTimeFormat, SetStartTimeIfGregorian)
|
|||||||
MakeStringSpan(locale), style, gen.get(), timeZone)
|
MakeStringSpan(locale), style, gen.get(), timeZone)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
const char* Dec22_1581;
|
|
||||||
const char* Jan01_1582;
|
const char* Jan01_1582;
|
||||||
const char* Jan01_1583;
|
const char* Jan01_1583;
|
||||||
if (locale == "en-US-u-ca-iso8601"sv) {
|
if (locale == "en-US-u-ca-iso8601"sv) {
|
||||||
Dec22_1581 = "1581 December 22";
|
|
||||||
Jan01_1582 = "1582 January 1";
|
Jan01_1582 = "1582 January 1";
|
||||||
Jan01_1583 = "1583 January 1";
|
Jan01_1583 = "1583 January 1";
|
||||||
} else {
|
} else {
|
||||||
Dec22_1581 = "December 22, 1581";
|
|
||||||
Jan01_1582 = "January 1, 1582";
|
Jan01_1582 = "January 1, 1582";
|
||||||
Jan01_1583 = "January 1, 1583";
|
Jan01_1583 = "January 1, 1583";
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBuffer<char> buffer;
|
TestBuffer<char> buffer;
|
||||||
|
|
||||||
// Before the default Gregorian change date, so interpreted in the Julian
|
// Before the default Gregorian change date, but not interpreted in the
|
||||||
// calendar, which is December 22, 1581.
|
// Julian calendar, which is December 22, 1581. Instead interpreted in
|
||||||
dtFormat->TryFormat(FirstJanuary1582, buffer).unwrap();
|
// proleptic Gregorian calendar at January 1, 1582.
|
||||||
ASSERT_TRUE(buffer.verboseMatches(Dec22_1581));
|
|
||||||
|
|
||||||
// After default Gregorian change date, so January 1, 1583.
|
|
||||||
dtFormat->TryFormat(FirstJanuary1582 + oneYear, buffer).unwrap();
|
|
||||||
ASSERT_TRUE(buffer.verboseMatches(Jan01_1583));
|
|
||||||
|
|
||||||
// Adjust the start time to use a proleptic Gregorian calendar.
|
|
||||||
dtFormat->SetStartTimeIfGregorian(StartOfTime);
|
|
||||||
|
|
||||||
// Now interpreted in proleptic Gregorian calendar at January 1, 1582.
|
|
||||||
dtFormat->TryFormat(FirstJanuary1582, buffer).unwrap();
|
dtFormat->TryFormat(FirstJanuary1582, buffer).unwrap();
|
||||||
ASSERT_TRUE(buffer.verboseMatches(Jan01_1582));
|
ASSERT_TRUE(buffer.verboseMatches(Jan01_1582));
|
||||||
|
|
||||||
// Still January 1, 1583.
|
// After default Gregorian change date, so January 1, 1583.
|
||||||
dtFormat->TryFormat(FirstJanuary1582 + oneYear, buffer).unwrap();
|
dtFormat->TryFormat(FirstJanuary1582 + oneYear, buffer).unwrap();
|
||||||
ASSERT_TRUE(buffer.verboseMatches(Jan01_1583));
|
ASSERT_TRUE(buffer.verboseMatches(Jan01_1583));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,18 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "DateTimeFormat.h" // for DATE_TIME_FORMAT_REPLACE_SPECIAL_SPACES
|
|
||||||
#include "DateTimeFormatUtils.h"
|
#include "DateTimeFormatUtils.h"
|
||||||
#include "ScopedICUObject.h"
|
#include "ScopedICUObject.h"
|
||||||
|
|
||||||
#include "mozilla/intl/Calendar.h"
|
#include "mozilla/intl/Calendar.h"
|
||||||
#include "mozilla/intl/DateIntervalFormat.h"
|
#include "mozilla/intl/DateIntervalFormat.h"
|
||||||
|
#include "mozilla/intl/DateTimeFormat.h"
|
||||||
|
|
||||||
|
#if !MOZ_SYSTEM_ICU
|
||||||
|
# include "unicode/calendar.h"
|
||||||
|
# include "unicode/datefmt.h"
|
||||||
|
# include "unicode/dtitvfmt.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace mozilla::intl {
|
namespace mozilla::intl {
|
||||||
|
|
||||||
@@ -63,7 +69,23 @@ Result<UniquePtr<DateIntervalFormat>, ICUError> DateIntervalFormat::TryCreate(
|
|||||||
return Err(ToICUError(status));
|
return Err(ToICUError(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
return UniquePtr<DateIntervalFormat>(new DateIntervalFormat(dif));
|
auto result = UniquePtr<DateIntervalFormat>(new DateIntervalFormat(dif));
|
||||||
|
|
||||||
|
#if !MOZ_SYSTEM_ICU
|
||||||
|
auto* dtif = reinterpret_cast<icu::DateIntervalFormat*>(dif);
|
||||||
|
const icu::Calendar* calendar = dtif->getDateFormat()->getCalendar();
|
||||||
|
|
||||||
|
auto replacement = CreateCalendarOverride(calendar);
|
||||||
|
if (replacement.isErr()) {
|
||||||
|
return replacement.propagateErr();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto newCalendar = replacement.unwrap()) {
|
||||||
|
dtif->adoptCalendar(newCalendar.release());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DateIntervalFormat::~DateIntervalFormat() {
|
DateIntervalFormat::~DateIntervalFormat() {
|
||||||
@@ -133,6 +155,50 @@ ICUResult DateIntervalFormat::TryFormatDateTime(
|
|||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ICUResult DateIntervalFormat::TryFormatDateTime(
|
||||||
|
double aStart, double aEnd, const DateTimeFormat* aDateTimeFormat,
|
||||||
|
AutoFormattedDateInterval& aFormatted, bool* aPracticallyEqual) const {
|
||||||
|
#if MOZ_SYSTEM_ICU
|
||||||
|
// We can't access the calendar used by UDateIntervalFormat to change it to a
|
||||||
|
// proleptic Gregorian calendar. Instead we need to call a different formatter
|
||||||
|
// function which accepts UCalendar instead of UDate.
|
||||||
|
// But creating new UCalendar objects for each call is slow, so when we can
|
||||||
|
// ensure that the input dates are later than the Gregorian change date,
|
||||||
|
// directly call the formatter functions taking UDate.
|
||||||
|
|
||||||
|
constexpr int32_t msPerDay = 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
|
// The Gregorian change date "1582-10-15T00:00:00.000Z".
|
||||||
|
constexpr double GregorianChangeDate = -12219292800000.0;
|
||||||
|
|
||||||
|
// Add a full day to account for time zone offsets.
|
||||||
|
constexpr double GregorianChangeDatePlusOneDay =
|
||||||
|
GregorianChangeDate + msPerDay;
|
||||||
|
|
||||||
|
if (aStart < GregorianChangeDatePlusOneDay ||
|
||||||
|
aEnd < GregorianChangeDatePlusOneDay) {
|
||||||
|
// Create calendar objects for the start and end date by cloning the date
|
||||||
|
// formatter calendar. The date formatter calendar already has the correct
|
||||||
|
// time zone set and was changed to use a proleptic Gregorian calendar.
|
||||||
|
auto startCal = aDateTimeFormat->CloneCalendar(aStart);
|
||||||
|
if (startCal.isErr()) {
|
||||||
|
return startCal.propagateErr();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto endCal = aDateTimeFormat->CloneCalendar(aEnd);
|
||||||
|
if (endCal.isErr()) {
|
||||||
|
return endCal.propagateErr();
|
||||||
|
}
|
||||||
|
|
||||||
|
return TryFormatCalendar(*startCal.unwrap(), *endCal.unwrap(), aFormatted,
|
||||||
|
aPracticallyEqual);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// The common fast path which doesn't require creating calendar objects.
|
||||||
|
return TryFormatDateTime(aStart, aEnd, aFormatted, aPracticallyEqual);
|
||||||
|
}
|
||||||
|
|
||||||
ICUResult DateIntervalFormat::TryFormattedToParts(
|
ICUResult DateIntervalFormat::TryFormattedToParts(
|
||||||
const AutoFormattedDateInterval& aFormatted,
|
const AutoFormattedDateInterval& aFormatted,
|
||||||
DateTimePartVector& aParts) const {
|
DateTimePartVector& aParts) const {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
namespace mozilla::intl {
|
namespace mozilla::intl {
|
||||||
class Calendar;
|
class Calendar;
|
||||||
|
class DateTimeFormat;
|
||||||
|
|
||||||
using AutoFormattedDateInterval =
|
using AutoFormattedDateInterval =
|
||||||
AutoFormattedResult<UFormattedDateInterval, udtitvfmt_openResult,
|
AutoFormattedResult<UFormattedDateInterval, udtitvfmt_openResult,
|
||||||
@@ -79,6 +80,21 @@ class DateIntervalFormat final {
|
|||||||
AutoFormattedDateInterval& aFormatted,
|
AutoFormattedDateInterval& aFormatted,
|
||||||
bool* aPracticallyEqual) const;
|
bool* aPracticallyEqual) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a date-time range between two Unix epoch times in milliseconds.
|
||||||
|
*
|
||||||
|
* The result will be stored in aFormatted, caller can use
|
||||||
|
* AutoFormattedDateInterval::ToSpan() to get the formatted string, or pass
|
||||||
|
* the aFormatted to TryFormattedToParts to get the parts vector.
|
||||||
|
*
|
||||||
|
* aPracticallyEqual will be set to true if the date times of the two
|
||||||
|
* Unix epoch times are equal.
|
||||||
|
*/
|
||||||
|
ICUResult TryFormatDateTime(double aStart, double aEnd,
|
||||||
|
const DateTimeFormat* aDateTimeFormat,
|
||||||
|
AutoFormattedDateInterval& aFormatted,
|
||||||
|
bool* aPracticallyEqual) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the formatted DateIntervalFormat into several parts.
|
* Convert the formatted DateIntervalFormat into several parts.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -279,6 +279,8 @@ Result<UniquePtr<DateTimeFormat>, ICUError> DateTimeFormat::TryCreateFromStyle(
|
|||||||
return Err(ToICUError(status));
|
return Err(ToICUError(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MOZ_TRY(ApplyCalendarOverride(dateFormat));
|
||||||
|
|
||||||
auto df = UniquePtr<DateTimeFormat>(new DateTimeFormat(dateFormat));
|
auto df = UniquePtr<DateTimeFormat>(new DateTimeFormat(dateFormat));
|
||||||
|
|
||||||
if (aStyleBag.time && (aStyleBag.hour12 || aStyleBag.hourCycle)) {
|
if (aStyleBag.time && (aStyleBag.hour12 || aStyleBag.hourCycle)) {
|
||||||
@@ -564,11 +566,12 @@ DateTimeFormat::TryCreateFromPattern(
|
|||||||
UDateFormat* dateFormat = udat_open(
|
UDateFormat* dateFormat = udat_open(
|
||||||
UDAT_PATTERN, UDAT_PATTERN, IcuLocale(aLocale), tzID, tzIDLength,
|
UDAT_PATTERN, UDAT_PATTERN, IcuLocale(aLocale), tzID, tzIDLength,
|
||||||
aPattern.data(), static_cast<int32_t>(aPattern.size()), &status);
|
aPattern.data(), static_cast<int32_t>(aPattern.size()), &status);
|
||||||
|
|
||||||
if (U_FAILURE(status)) {
|
if (U_FAILURE(status)) {
|
||||||
return Err(ToICUError(status));
|
return Err(ToICUError(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MOZ_TRY(ApplyCalendarOverride(dateFormat));
|
||||||
|
|
||||||
// The DateTimeFormat wrapper will control the life cycle of the ICU
|
// The DateTimeFormat wrapper will control the life cycle of the ICU
|
||||||
// dateFormat object.
|
// dateFormat object.
|
||||||
return UniquePtr<DateTimeFormat>(new DateTimeFormat(dateFormat));
|
return UniquePtr<DateTimeFormat>(new DateTimeFormat(dateFormat));
|
||||||
@@ -612,13 +615,6 @@ ICUResult DateTimeFormat::CacheSkeleton(Span<const char16_t> aSkeleton) {
|
|||||||
return Err(ICUError::OutOfMemory);
|
return Err(ICUError::OutOfMemory);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DateTimeFormat::SetStartTimeIfGregorian(double aTime) {
|
|
||||||
UErrorCode status = U_ZERO_ERROR;
|
|
||||||
UCalendar* cal = const_cast<UCalendar*>(udat_getCalendar(mDateFormat));
|
|
||||||
ucal_setGregorianChange(cal, aTime, &status);
|
|
||||||
// An error here means the calendar is not Gregorian, and can be ignored.
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
Result<UniquePtr<Calendar>, ICUError> DateTimeFormat::CloneCalendar(
|
Result<UniquePtr<Calendar>, ICUError> DateTimeFormat::CloneCalendar(
|
||||||
double aUnixEpoch) const {
|
double aUnixEpoch) const {
|
||||||
|
|||||||
@@ -477,12 +477,6 @@ class DateTimeFormat final {
|
|||||||
}
|
}
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Set the start time of the Gregorian calendar. This is useful for
|
|
||||||
* ensuring the consistent use of a proleptic Gregorian calendar for ECMA-402.
|
|
||||||
* https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
|
|
||||||
*/
|
|
||||||
void SetStartTimeIfGregorian(double aTime);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines the resolved components for the current DateTimeFormat.
|
* Determines the resolved components for the current DateTimeFormat.
|
||||||
|
|||||||
@@ -3,8 +3,17 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
|
#include "mozilla/Try.h"
|
||||||
|
|
||||||
#include "DateTimeFormatUtils.h"
|
#include "DateTimeFormatUtils.h"
|
||||||
|
#include "mozilla/intl/ICU4CGlue.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#if !MOZ_SYSTEM_ICU
|
||||||
|
# include "unicode/datefmt.h"
|
||||||
|
# include "unicode/gregocal.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace mozilla::intl {
|
namespace mozilla::intl {
|
||||||
|
|
||||||
@@ -101,4 +110,74 @@ DateTimePartType ConvertUFormatFieldToPartType(UDateFormatField fieldName) {
|
|||||||
return DateTimePartType::Unknown;
|
return DateTimePartType::Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start of ECMAScript time.
|
||||||
|
static constexpr double StartOfTime = -8.64e15;
|
||||||
|
|
||||||
|
#if !MOZ_SYSTEM_ICU
|
||||||
|
static bool IsGregorianLikeCalendar(const char* type) {
|
||||||
|
return std::strcmp(type, "gregorian") == 0 ||
|
||||||
|
std::strcmp(type, "iso8601") == 0 ||
|
||||||
|
std::strcmp(type, "buddhist") == 0 ||
|
||||||
|
std::strcmp(type, "japanese") == 0 || std::strcmp(type, "roc") == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the start time of the Gregorian calendar. This is useful for
|
||||||
|
* ensuring the consistent use of a proleptic Gregorian calendar for ECMA-402.
|
||||||
|
* https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
|
||||||
|
*/
|
||||||
|
static Result<Ok, ICUError> SetGregorianChangeDate(
|
||||||
|
icu::GregorianCalendar* gregorian) {
|
||||||
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
gregorian->setGregorianChange(StartOfTime, status);
|
||||||
|
if (U_FAILURE(status)) {
|
||||||
|
return Err(ToICUError(status));
|
||||||
|
}
|
||||||
|
return Ok{};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Result<Ok, ICUError> ApplyCalendarOverride(UDateFormat* aDateFormat) {
|
||||||
|
#if !MOZ_SYSTEM_ICU
|
||||||
|
icu::DateFormat* df = reinterpret_cast<icu::DateFormat*>(aDateFormat);
|
||||||
|
const icu::Calendar* calendar = df->getCalendar();
|
||||||
|
|
||||||
|
const char* type = calendar->getType();
|
||||||
|
|
||||||
|
if (IsGregorianLikeCalendar(type)) {
|
||||||
|
auto* gregorian = static_cast<const icu::GregorianCalendar*>(calendar);
|
||||||
|
MOZ_TRY(
|
||||||
|
SetGregorianChangeDate(const_cast<icu::GregorianCalendar*>(gregorian)));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
|
UCalendar* cal = const_cast<UCalendar*>(udat_getCalendar(aDateFormat));
|
||||||
|
ucal_setGregorianChange(cal, StartOfTime, &status);
|
||||||
|
// An error here means the calendar is not Gregorian, and can be ignored.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return Ok{};
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !MOZ_SYSTEM_ICU
|
||||||
|
Result<UniquePtr<icu::Calendar>, ICUError> CreateCalendarOverride(
|
||||||
|
const icu::Calendar* calendar) {
|
||||||
|
const char* type = calendar->getType();
|
||||||
|
|
||||||
|
if (IsGregorianLikeCalendar(type)) {
|
||||||
|
UniquePtr<icu::GregorianCalendar> gregorian(
|
||||||
|
static_cast<const icu::GregorianCalendar*>(calendar)->clone());
|
||||||
|
if (!gregorian) {
|
||||||
|
return Err(ICUError::OutOfMemory);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_TRY(SetGregorianChangeDate(gregorian.get()));
|
||||||
|
|
||||||
|
return UniquePtr<icu::Calendar>{gregorian.release()};
|
||||||
|
}
|
||||||
|
|
||||||
|
return UniquePtr<icu::Calendar>{};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace mozilla::intl
|
} // namespace mozilla::intl
|
||||||
|
|||||||
@@ -5,10 +5,24 @@
|
|||||||
#define intl_components_DateTimeFormatUtils_h_
|
#define intl_components_DateTimeFormatUtils_h_
|
||||||
#include "unicode/udat.h"
|
#include "unicode/udat.h"
|
||||||
|
|
||||||
|
#if !MOZ_SYSTEM_ICU
|
||||||
|
# include "unicode/calendar.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "mozilla/Result.h"
|
||||||
|
#include "mozilla/UniquePtr.h"
|
||||||
#include "mozilla/intl/DateTimePart.h"
|
#include "mozilla/intl/DateTimePart.h"
|
||||||
|
#include "mozilla/intl/ICUError.h"
|
||||||
|
|
||||||
namespace mozilla::intl {
|
namespace mozilla::intl {
|
||||||
DateTimePartType ConvertUFormatFieldToPartType(UDateFormatField fieldName);
|
DateTimePartType ConvertUFormatFieldToPartType(UDateFormatField fieldName);
|
||||||
|
|
||||||
|
Result<Ok, ICUError> ApplyCalendarOverride(UDateFormat* aDateFormat);
|
||||||
|
|
||||||
|
#if !MOZ_SYSTEM_ICU
|
||||||
|
Result<UniquePtr<icu::Calendar>, ICUError> CreateCalendarOverride(
|
||||||
|
const icu::Calendar* calendar);
|
||||||
|
#endif
|
||||||
} // namespace mozilla::intl
|
} // namespace mozilla::intl
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1638,10 +1638,6 @@ static mozilla::intl::DateTimeFormat* NewDateTimeFormat(
|
|||||||
df = dfResult.unwrap();
|
df = dfResult.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ECMAScript requires the Gregorian calendar to be used from the beginning
|
|
||||||
// of ECMAScript time.
|
|
||||||
df->SetStartTimeIfGregorian(StartOfTime);
|
|
||||||
|
|
||||||
return df.release();
|
return df.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2578,51 +2574,12 @@ static bool PartitionDateTimeRangePattern(
|
|||||||
MOZ_ASSERT(x.isValid());
|
MOZ_ASSERT(x.isValid());
|
||||||
MOZ_ASSERT(y.isValid());
|
MOZ_ASSERT(y.isValid());
|
||||||
|
|
||||||
// We can't access the calendar used by UDateIntervalFormat to change it to a
|
auto result =
|
||||||
// proleptic Gregorian calendar. Instead we need to call a different formatter
|
dif->TryFormatDateTime(x.toDouble(), y.toDouble(), df, formatted, equal);
|
||||||
// function which accepts UCalendar instead of UDate.
|
|
||||||
// But creating new UCalendar objects for each call is slow, so when we can
|
|
||||||
// ensure that the input dates are later than the Gregorian change date,
|
|
||||||
// directly call the formatter functions taking UDate.
|
|
||||||
|
|
||||||
// The Gregorian change date "1582-10-15T00:00:00.000Z".
|
|
||||||
constexpr double GregorianChangeDate = -12219292800000.0;
|
|
||||||
|
|
||||||
// Add a full day to account for time zone offsets.
|
|
||||||
constexpr double GregorianChangeDatePlusOneDay =
|
|
||||||
GregorianChangeDate + msPerDay;
|
|
||||||
|
|
||||||
mozilla::intl::ICUResult result = Ok();
|
|
||||||
if (x.toDouble() < GregorianChangeDatePlusOneDay ||
|
|
||||||
y.toDouble() < GregorianChangeDatePlusOneDay) {
|
|
||||||
// Create calendar objects for the start and end date by cloning the date
|
|
||||||
// formatter calendar. The date formatter calendar already has the correct
|
|
||||||
// time zone set and was changed to use a proleptic Gregorian calendar.
|
|
||||||
auto startCal = df->CloneCalendar(x.toDouble());
|
|
||||||
if (startCal.isErr()) {
|
|
||||||
intl::ReportInternalError(cx, startCal.unwrapErr());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto endCal = df->CloneCalendar(y.toDouble());
|
|
||||||
if (endCal.isErr()) {
|
|
||||||
intl::ReportInternalError(cx, endCal.unwrapErr());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = dif->TryFormatCalendar(*startCal.unwrap(), *endCal.unwrap(),
|
|
||||||
formatted, equal);
|
|
||||||
} else {
|
|
||||||
// The common fast path which doesn't require creating calendar objects.
|
|
||||||
result =
|
|
||||||
dif->TryFormatDateTime(x.toDouble(), y.toDouble(), formatted, equal);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.isErr()) {
|
if (result.isErr()) {
|
||||||
intl::ReportInternalError(cx, result.unwrapErr());
|
intl::ReportInternalError(cx, result.unwrapErr());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user