1
0
Fork 0
libreoffice/svl/qa/unit/svl.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

2042 lines
86 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* 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/.
*/
#include <sal/types.h>
#include <cppunit/TestAssert.h>
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/plugin/TestPlugIn.h>
#include <sal/config.h>
#include <cppuhelper/bootstrap.hxx>
#include <comphelper/processfactory.hxx>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <i18nlangtag/lang.h>
#include <math.h>
#include <o3tl/nonstaticstring.hxx>
#include <svl/numformat.hxx>
#include <svl/zforlist.hxx>
#include <svl/zformat.hxx>
#include <svl/sharedstringpool.hxx>
#include <svl/sharedstring.hxx>
#include <tools/color.hxx>
#include <unotools/syslocale.hxx>
#include <memory>
#include <optional>
#include <unicode/timezone.h>
using namespace ::com::sun::star;
using namespace svl;
namespace svl
{
static std::ostream& operator<<(std::ostream& rStrm, const SharedString& string )
{
return rStrm << "(" << static_cast<const void*>(string.getData()) << ")" << string.getString();
}
}
namespace {
class Test : public CppUnit::TestFixture {
public:
Test();
virtual ~Test() override;
virtual void tearDown() override;
void testNumberFormat();
void testSharedString();
void testSharedStringPool();
void testSharedStringPoolPurge();
void testSharedStringPoolPurgeBug1();
void testSharedStringPoolEmptyString();
void testFdo60915();
void testI116701();
void testTdf103060();
void testDateInput();
void testIsNumberFormat();
void testIsNumberFormatSpecific();
void testUserDefinedNumberFormats();
void testNfEnglishKeywordsIntegrity();
void testStandardColorIntegrity();
void testColorNamesConversion();
void testExcelExportFormats();
CPPUNIT_TEST_SUITE(Test);
CPPUNIT_TEST(testNumberFormat);
CPPUNIT_TEST(testSharedString);
CPPUNIT_TEST(testSharedStringPool);
CPPUNIT_TEST(testSharedStringPoolPurge);
CPPUNIT_TEST(testSharedStringPoolPurgeBug1);
CPPUNIT_TEST(testSharedStringPoolEmptyString);
CPPUNIT_TEST(testFdo60915);
CPPUNIT_TEST(testI116701);
CPPUNIT_TEST(testTdf103060);
CPPUNIT_TEST(testDateInput);
CPPUNIT_TEST(testIsNumberFormat);
CPPUNIT_TEST(testIsNumberFormatSpecific);
CPPUNIT_TEST(testUserDefinedNumberFormats);
CPPUNIT_TEST(testNfEnglishKeywordsIntegrity);
CPPUNIT_TEST(testStandardColorIntegrity);
CPPUNIT_TEST(testColorNamesConversion);
CPPUNIT_TEST(testExcelExportFormats);
CPPUNIT_TEST_SUITE_END();
protected:
uno::Reference< uno::XComponentContext > m_xContext;
void checkPreviewString(SvNumberFormatter& aFormatter,
const OUString& sCode,
double fPreviewNumber,
LanguageType eLang,
OUString const & sExpected);
void checkDateInput( SvNumberFormatter& rFormatter, const char* pTimezone, const char* pIsoDate );
std::unique_ptr<icu::TimeZone> m_pDefaultTimeZone;
};
Test::Test() : m_xContext(cppu::defaultBootstrap_InitialComponentContext())
{
uno::Reference<lang::XMultiComponentFactory> xFactory(m_xContext->getServiceManager());
uno::Reference<lang::XMultiServiceFactory> xSM(xFactory, uno::UNO_QUERY_THROW);
//Without this we're crashing because callees are using
//getProcessServiceFactory. In general those should be removed in favour
//of retaining references to the root ServiceFactory as it's passed around
comphelper::setProcessServiceFactory(xSM);
m_pDefaultTimeZone.reset(icu::TimeZone::createDefault());
}
void Test::tearDown()
{
icu::TimeZone::setDefault(*m_pDefaultTimeZone);
}
Test::~Test()
{
uno::Reference< lang::XComponent >(m_xContext, uno::UNO_QUERY_THROW)->dispose();
}
void Test::testNumberFormat()
{
LanguageType eLang = LANGUAGE_ENGLISH_US;
const char* pNumber[] = {
"General",
"0",
"0.00",
"#,##0",
"#,##0.00",
"#,###.00",
nullptr
};
const char* pScientific[] = {
"0.00E+000",
"0.00E+00",
nullptr
};
const char* pPercent[] = {
"0%",
"0.00%",
nullptr
};
const char* pFraction[] = {
"# \?/\?",
"# \?\?/\?\?",
nullptr
};
// Following aren't in range of NF_FRACTION_START and NF_FRACTION_END
// see enum NfIndexTableOffset in svl/inc/svl/zforlist.hxx
const char* pFractionExt[] = {
"# \?\?\?/\?\?\?",
"# \?/2",
"# \?/4",
"# \?/8",
"# \?\?/16",
"# \?\?/10",
"# \?\?/100",
nullptr
};
const char* pCurrency[] = {
"$#,##0;-$#,##0",
"$#,##0.00;-$#,##0.00",
"$#,##0;[RED]-$#,##0",
"$#,##0.00;[RED]-$#,##0.00",
"#,##0.00 CCC",
"$#,##0.--;[RED]-$#,##0.--",
nullptr
};
const char* pDate[] = {
"M/D/YY",
"NNNNMMMM D, YYYY",
"MM/DD/YY",
"MM/DD/YYYY",
"MMM D, YY",
"MMM D, YYYY",
"D. MMM. YYYY",
"MMMM D, YYYY",
"D. MMMM YYYY",
"NN, MMM D, YY",
"NN DD/MMM YY",
"NN, MMMM D, YYYY",
"NNNNMMMM D, YYYY",
"MM-DD",
"YY-MM-DD",
"YYYY-MM-DD",
"MM/YY",
"MMM DD",
"MMMM",
"QQ YY",
"WW",
nullptr
};
const char* pTime[] = {
"HH:MM",
"HH:MM:SS",
"HH:MM AM/PM",
"HH:MM:SS AM/PM",
"[HH]:MM:SS",
"MM:SS.00",
"[HH]:MM:SS.00",
nullptr
};
const char* pDateTime[] = {
"MM/DD/YY HH:MM AM/PM",
"MM/DD/YYYY HH:MM:SS",
nullptr
};
// Following aren't in range of NF_DATETIME_START and NF_DATETIME_END
// see enum NfIndexTableOffset in svl/inc/svl/zforlist.hxx
const char* pDateTimeExt1[] = {
"MM/DD/YYYY HH:MM AM/PM",
nullptr
};
const char* pDateTimeExt2[] = {
"YYYY-MM-DD HH:MM:SS",
"YYYY-MM-DD HH:MM:SS.000",
"YYYY-MM-DD\"T\"HH:MM:SS",
"YYYY-MM-DD\"T\"HH:MM:SS.000",
nullptr
};
const char* pBoolean[] = {
"BOOLEAN",
nullptr
};
const char* pText[] = {
"@",
nullptr
};
struct {
NfIndexTableOffset eStart;
NfIndexTableOffset eEnd;
size_t nSize;
const char** pCodes;
} aTests[] = {
{ NF_NUMBER_START, NF_NUMBER_END, 6, pNumber },
{ NF_SCIENTIFIC_START, NF_SCIENTIFIC_END, 2, pScientific },
{ NF_PERCENT_START, NF_PERCENT_END, 2, pPercent },
{ NF_FRACTION_START, NF_FRACTION_END, 2, pFraction },
{ NF_CURRENCY_START, NF_CURRENCY_END, 6, pCurrency },
{ NF_DATE_START, NF_DATE_END, 21, pDate },
{ NF_TIME_START, NF_TIME_END, 7, pTime },
{ NF_DATETIME_START, NF_DATETIME_END, 2, pDateTime },
{ NF_BOOLEAN, NF_BOOLEAN, 1, pBoolean },
{ NF_TEXT, NF_TEXT, 1, pText },
{ NF_DATETIME_SYS_DDMMYYYY_HHMM, NF_DATETIME_SYS_DDMMYYYY_HHMM, 1, pDateTimeExt1 },
{ NF_FRACTION_3D, NF_FRACTION_100, 7, pFractionExt },
{ NF_DATETIME_ISO_YYYYMMDD_HHMMSS, NF_DATETIME_ISO_YYYYMMDDTHHMMSS000, 4, pDateTimeExt2 }
};
SvNumberFormatter aFormatter(m_xContext, eLang);
for (auto const[eStart, eEnd, nSize, pCodes] : aTests)
{
size_t nStart = eStart;
size_t nEnd = eEnd;
CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected number of formats for this category.", nSize,
(nEnd - nStart + 1));
for (size_t j = nStart; j <= nEnd; ++j)
{
sal_uInt32 nIndex =
aFormatter.GetFormatIndex(static_cast<NfIndexTableOffset>(j));
const SvNumberformat* p = aFormatter.GetEntry(nIndex);
CPPUNIT_ASSERT_MESSAGE("Number format entry is expected, but doesn't exist.", p);
OUString aCode = p->GetFormatstring();
CPPUNIT_ASSERT_EQUAL(OString( pCodes[j-nStart]), aCode.toUtf8());
}
}
sal_Int32 nPos;
SvNumFormatType nType = SvNumFormatType::DEFINED;
sal_uInt32 nKey;
OUString aCode;
// Thai date format (implicit locale).
aCode = "[$-1070000]d/mm/yyyy;@";
if (!aFormatter.PutEntry(aCode, nPos, nType, nKey))
{
CPPUNIT_ASSERT_MESSAGE("failed to insert format code '[$-1070000]d/mm/yyyy;@'", false);
}
// Thai date format (explicit locale)
aCode = "[$-107041E]d/mm/yyyy;@";
if (!aFormatter.PutEntry(aCode, nPos, nType, nKey))
{
CPPUNIT_ASSERT_MESSAGE("failed to insert format code '[$-107041E]d/mm/yyyy;@'", false);
}
// Thai date format (using buddhist calendar type).
aCode = "[~buddhist]D MMMM YYYY";
if (!aFormatter.PutEntry(aCode, nPos, nType, nKey))
{
CPPUNIT_ASSERT_MESSAGE("failed to insert format code '[~buddhist]D MMMM YYYY'", false);
}
}
void Test::testSharedString()
{
// Use shared string as normal, non-shared string, which is allowed.
SharedString aSS1(u"Test"_ustr), aSS2(u"Test"_ustr);
CPPUNIT_ASSERT_MESSAGE("Equality check should return true.", bool(aSS1 == aSS2));
SharedString aSS3(u"test"_ustr);
CPPUNIT_ASSERT_MESSAGE("Equality check is case sensitive.", aSS1 != aSS3);
}
void Test::testSharedStringPool()
{
SvtSysLocale aSysLocale;
svl::SharedStringPool aPool(aSysLocale.GetCharClass());
svl::SharedString p1, p2;
p1 = aPool.intern(u"Andy"_ustr);
p2 = aPool.intern(u"Andy"_ustr);
CPPUNIT_ASSERT_EQUAL(p1.getData(), p2.getData());
p2 = aPool.intern(u"Bruce"_ustr);
CPPUNIT_ASSERT_MESSAGE("They must differ.", p1.getData() != p2.getData());
OUString aAndy(u"Andy"_ustr);
p1 = aPool.intern(u"Andy"_ustr);
p2 = aPool.intern(aAndy);
CPPUNIT_ASSERT_MESSAGE("Identifier shouldn't be NULL.", p1.getData());
CPPUNIT_ASSERT_MESSAGE("Identifier shouldn't be NULL.", p2.getData());
CPPUNIT_ASSERT_EQUAL(p1.getData(), p2.getData());
// Test case insensitive string ID's.
p1 = aPool.intern(aAndy);
p2 = aPool.intern(u"andy"_ustr);
CPPUNIT_ASSERT_MESSAGE("Failed to intern strings.", p1.getData());
CPPUNIT_ASSERT_MESSAGE("Failed to intern strings.", p2.getData());
CPPUNIT_ASSERT_MESSAGE("These two ID's should differ.", p1.getData() != p2.getData());
CPPUNIT_ASSERT_EQUAL_MESSAGE("These two ID's should be equal.", p2.getDataIgnoreCase(), p1.getDataIgnoreCase());
p2 = aPool.intern(u"ANDY"_ustr);
CPPUNIT_ASSERT_MESSAGE("Failed to intern string.", p2.getData());
CPPUNIT_ASSERT_MESSAGE("These two ID's should differ.", p1.getData() != p2.getData());
CPPUNIT_ASSERT_EQUAL_MESSAGE("These two ID's should be equal.", p2.getDataIgnoreCase(), p1.getDataIgnoreCase());
}
void Test::testSharedStringPoolPurge()
{
SvtSysLocale aSysLocale;
svl::SharedStringPool aPool(aSysLocale.GetCharClass());
size_t extraCount = aPool.getCount(); // internal items such as SharedString::getEmptyString()
size_t extraCountIgnoreCase = aPool.getCountIgnoreCase();
aPool.intern(o3tl::nonStaticString(u"Andy"));
aPool.intern(o3tl::nonStaticString(u"andy"));
aPool.intern(o3tl::nonStaticString(u"ANDY"));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong string count.", 3+extraCount, aPool.getCount());
CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong case insensitive string count.", 1+extraCountIgnoreCase, aPool.getCountIgnoreCase());
// Since no string objects referencing the pooled strings exist, purging
// the pool should empty it (except for internal items).
aPool.purge();
CPPUNIT_ASSERT_EQUAL(extraCount, aPool.getCount());
CPPUNIT_ASSERT_EQUAL(extraCountIgnoreCase, aPool.getCountIgnoreCase());
// Now, create string objects using optional so we can clear them
std::optional<svl::SharedString> pStr1 = aPool.intern(o3tl::nonStaticString(u"Andy"));
std::optional<svl::SharedString> pStr2 = aPool.intern(o3tl::nonStaticString(u"andy"));
std::optional<svl::SharedString> pStr3 = aPool.intern(o3tl::nonStaticString(u"ANDY"));
std::optional<svl::SharedString> pStr4 = aPool.intern(o3tl::nonStaticString(u"Bruce"));
CPPUNIT_ASSERT_EQUAL(5+extraCount, aPool.getCount());
CPPUNIT_ASSERT_EQUAL(2+extraCountIgnoreCase, aPool.getCountIgnoreCase());
// This shouldn't purge anything.
aPool.purge();
CPPUNIT_ASSERT_EQUAL(5+extraCount, aPool.getCount());
CPPUNIT_ASSERT_EQUAL(2+extraCountIgnoreCase, aPool.getCountIgnoreCase());
// Delete one heap string object, and purge. That should purge one string.
pStr1.reset();
aPool.purge();
CPPUNIT_ASSERT_EQUAL(4+extraCount, aPool.getCount());
CPPUNIT_ASSERT_EQUAL(2+extraCountIgnoreCase, aPool.getCountIgnoreCase());
// Nothing changes, because the upper-string is still in the map
pStr3.reset();
aPool.purge();
CPPUNIT_ASSERT_EQUAL(4+extraCount, aPool.getCount());
CPPUNIT_ASSERT_EQUAL(2+extraCountIgnoreCase, aPool.getCountIgnoreCase());
// Again.
pStr2.reset();
aPool.purge();
CPPUNIT_ASSERT_EQUAL(2+extraCount, aPool.getCount());
CPPUNIT_ASSERT_EQUAL(1+extraCountIgnoreCase, aPool.getCountIgnoreCase());
// Delete 'Bruce' and purge.
pStr4.reset();
aPool.purge();
CPPUNIT_ASSERT_EQUAL(extraCount, aPool.getCount());
CPPUNIT_ASSERT_EQUAL(extraCountIgnoreCase, aPool.getCountIgnoreCase());
}
void Test::testSharedStringPoolPurgeBug1()
{
// We had a bug where, if we had two strings that mapped to the same uppercase string,
// purge() would de-reference a dangling pointer and consequently cause an ASAN failure.
SvtSysLocale aSysLocale;
svl::SharedStringPool aPool(aSysLocale.GetCharClass());
size_t extraCount = aPool.getCount(); // internal items such as SharedString::getEmptyString()
size_t extraCountIgnoreCase = aPool.getCountIgnoreCase();
aPool.intern(o3tl::nonStaticString(u"Andy"));
aPool.intern(o3tl::nonStaticString(u"andy"));
aPool.purge();
CPPUNIT_ASSERT_EQUAL(extraCount, aPool.getCount());
CPPUNIT_ASSERT_EQUAL(extraCountIgnoreCase, aPool.getCountIgnoreCase());
}
void Test::testSharedStringPoolEmptyString()
{
// Make sure SharedString::getEmptyString() is in the pool and matches empty strings.
SvtSysLocale aSysLocale;
svl::SharedStringPool aPool(aSysLocale.GetCharClass());
aPool.intern(u""_ustr);
CPPUNIT_ASSERT_EQUAL(SharedString::getEmptyString(), aPool.intern(u""_ustr));
CPPUNIT_ASSERT_EQUAL(SharedString::getEmptyString(), aPool.intern(SharedString::EMPTY_STRING));
// And it should still work even after purging.
aPool.purge();
CPPUNIT_ASSERT_EQUAL(SharedString::getEmptyString(), aPool.intern(SharedString::EMPTY_STRING));
}
void Test::checkPreviewString(SvNumberFormatter& aFormatter,
const OUString& sCode,
double fPreviewNumber,
LanguageType eLang,
OUString const & sExpected)
{
OUString sStr;
const Color* pColor = nullptr;
if (!aFormatter.GetPreviewString(sCode, fPreviewNumber, sStr, &pColor, eLang))
{
OString aMessage = "GetPreviewString( \"" +
OUStringToOString( sCode, RTL_TEXTENCODING_ASCII_US ) +
"\", " +
OString::number( fPreviewNumber ) +
", sStr, ppColor, ";
aMessage += OString::number( static_cast<sal_uInt16>(eLang) ) +
" ) failed";
CPPUNIT_FAIL( aMessage.getStr() );
}
CPPUNIT_ASSERT_EQUAL(sExpected, sStr);
}
void Test::testFdo60915()
{
LanguageType eLang = LANGUAGE_THAI;
OUString sCode, sExpected;
double fPreviewNumber = 36486; // equals 1999-11-22 (2542 B.E.)
SvNumberFormatter aFormatter(m_xContext, eLang);
{
sCode = "[~buddhist]D/MM/YYYY";
sExpected = "22/11/2542";
checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
}
{
sCode = "[~buddhist]D/MM/YY";
sExpected = "22/11/42";
checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
}
{
sCode = "[NatNum1][$-41E][~buddhist]D/MM/YYYY";
sal_Unicode sTemp[] =
{
0x0E52, 0x0E52, 0x002F,
0x0E51, 0x0E51, 0x002F,
0x0E52, 0x0E55, 0x0E54, 0x0E52
};
sExpected = OUString(sTemp, SAL_N_ELEMENTS(sTemp));
checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
}
{
sCode = "[NatNum1][$-41E][~buddhist]D/MM/YY";
sal_Unicode sTemp[] =
{
0x0E52, 0x0E52, 0x002F,
0x0E51, 0x0E51, 0x002F,
0x0E54, 0x0E52
};
sExpected = OUString(sTemp, SAL_N_ELEMENTS(sTemp));
checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
}
}
// https://bz.apache.org/ooo/show_bug.cgi?id=116701
void Test::testI116701()
{
LanguageType eLang = LANGUAGE_CHINESE_TRADITIONAL;
OUString sCode, sExpected;
double fPreviewNumber = 40573; // equals 30/01/2011
SvNumberFormatter aFormatter(m_xContext, eLang);
// DateFormatskey25 in i18npool/source/localedata/data/zh_TW.xml
sal_Unicode CODE1[] =
{
0x0047, 0x0047, 0x0047, 0x0045, 0x0045, // GGGEE
0x0022, 0x5E74, 0x0022,
0x004D, // M
0x0022, 0x6708, 0x0022,
0x0044, // D
0x0022, 0x65E5, 0x0022
};
sCode = OUString(CODE1, SAL_N_ELEMENTS(CODE1));
sal_Unicode EXPECTED[] =
{
0x4E2D, 0x83EF, 0x6C11, 0x570B,
0x0031, 0x0030, 0x0030, // 100
0x5E74,
0x0031, // 1
0x6708,
0x0033, 0x0030, // 30
0x65E5
};
sExpected = OUString(EXPECTED, SAL_N_ELEMENTS(EXPECTED));
checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
sal_Unicode CODE2[] =
{
0x0047, 0x0047, 0x0047, 0x0045, // GGGE
0x0022, 0x5E74, 0x0022,
0x004D, // M
0x0022, 0x6708, 0x0022,
0x0044, // D
0x0022, 0x65E5, 0x0022
};
sCode = OUString(CODE2, SAL_N_ELEMENTS(CODE2));
checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
}
void Test::testTdf103060()
{
LanguageType eLang = LANGUAGE_JAPANESE;
OUString sCode, sExpected;
double fPreviewNumber = 42655; // equals 2016-10-12
SvNumberFormatter aFormatter(m_xContext, eLang);
sCode = "G";
sExpected = "H"; // Heisei era
checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
sCode = "GG";
sExpected = u"\u5E73"_ustr;
checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
sCode = "GGG";
sExpected = u"\u5E73\u6210"_ustr;
checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
}
void Test::testDateInput()
{
const char* aData[][2] = {
{ "Europe/Paris", "1938-10-07" }, // i#76623
{ "Europe/Moscow", "1919-07-01" }, // i#86094
{ "America/St_Johns", "1935-03-30" }, // i#86094 i#90627
{ "Europe/Tallinn", "1790-03-01" }, // i#105864
{ "Australia/Perth", "2004-04-11" }, // i#17222
{ "America/Sao_Paulo", "1902-04-22" }, // tdf#44286
{ "Europe/Berlin", "1790-07-27" },
{ "US/Mountain", "1790-07-26" },
{ "Asia/Tehran", "1999-03-22" },
// Data from https://bugs.documentfoundation.org/show_bug.cgi?id=63230
// https://bugs.documentfoundation.org/attachment.cgi?id=79051
// https://bugs.documentfoundation.org/show_bug.cgi?id=79663
{ "Africa/Accra", "1800-01-01" },
{ "Africa/Accra", "1800-04-10" },
{ "Africa/Addis_Ababa", "1870-01-01" },
{ "Africa/Addis_Ababa", "1936-05-05" },
{ "Africa/Algiers", "1956-01-29" },
{ "Africa/Algiers", "1981-05-01" },
{ "Africa/Asmara", "1936-05-05" },
{ "Africa/Asmera", "1936-05-05" },
{ "Africa/Bujumbura", "1890-01-01" },
{ "Africa/Casablanca", "1984-03-16" },
{ "Africa/Ceuta", "1984-03-16" },
{ "Africa/Dar_es_Salaam", "1931-01-01" },
{ "Africa/Dar_es_Salaam", "1961-01-01" },
{ "Africa/Djibouti", "1911-07-01" },
{ "Africa/Douala", "1912-01-01" },
{ "Africa/El_Aaiun", "1934-01-01" },
{ "Africa/Freetown", "1913-06-01" },
{ "Africa/Gaborone", "1885-01-01" },
{ "Africa/Johannesburg", "1903-03-01" },
{ "Africa/Kampala", "1928-07-01" },
{ "Africa/Kampala", "1948-01-01" },
{ "Africa/Kampala", "1957-01-01" },
{ "Africa/Lagos", "1919-09-01" },
{ "Africa/Libreville", "1912-01-01" },
{ "Africa/Luanda", "1911-05-26" },
{ "Africa/Lubumbashi", "1897-11-09" },
{ "Africa/Lusaka", "1903-03-01" },
{ "Africa/Malabo", "1963-12-15" },
{ "Africa/Maseru", "1903-03-01" },
{ "Africa/Mogadishu", "1957-01-01" },
{ "Africa/Monrovia", "1919-03-01" },
{ "Africa/Nairobi", "1928-07-01" },
{ "Africa/Nairobi", "1940-01-01" },
{ "Africa/Nairobi", "1960-01-01" },
{ "Africa/Niamey", "1960-01-01" },
{ "Africa/Porto-Novo", "1934-02-26" },
{ "Africa/Tripoli", "1920-01-01" },
{ "Africa/Tripoli", "1959-01-01" },
{ "Africa/Tripoli", "1990-05-04" },
{ "Africa/Tunis", "1911-03-11" },
{ "Africa/Windhoek", "1892-02-08" },
{ "Africa/Windhoek", "1903-03-01" },
{ "America/Antigua", "1912-03-02" },
{ "America/Argentina/Buenos_Aires", "1894-10-31" },
{ "America/Argentina/Catamarca", "1991-10-20" },
{ "America/Argentina/Catamarca", "2004-06-01" },
{ "America/Argentina/ComodRivadavia", "1991-10-20" },
{ "America/Argentina/ComodRivadavia", "2004-06-01" },
{ "America/Argentina/Cordoba", "1991-10-20" },
{ "America/Argentina/Jujuy", "1991-10-06" },
{ "America/Argentina/La_Rioja", "2004-06-01" },
{ "America/Argentina/Mendoza", "1992-10-18" },
{ "America/Argentina/Mendoza", "2004-05-23" },
{ "America/Argentina/Rio_Gallegos", "2004-06-01" },
{ "America/Argentina/Salta", "1991-10-20" },
{ "America/Argentina/San_Juan", "2004-05-31" },
{ "America/Argentina/San_Luis", "2004-05-31" },
{ "America/Argentina/San_Luis", "2008-01-21" },
{ "America/Argentina/Tucuman", "1991-10-20" },
{ "America/Argentina/Tucuman", "2004-06-01" },
{ "America/Argentina/Ushuaia", "2004-05-30" },
{ "America/Asuncion", "1931-10-10" },
{ "America/Asuncion", "1974-04-01" },
{ "America/Bahia", "1914-01-01" },
{ "America/Bahia_Banderas", "1930-11-15" },
{ "America/Bahia_Banderas", "1931-10-01" },
{ "America/Bahia_Banderas", "1942-04-24" },
{ "America/Bahia_Banderas", "1949-01-14" },
{ "America/Barbados", "1932-01-01" },
{ "America/Belize", "1912-04-01" },
{ "America/Blanc-Sablon", "1884-01-01" },
{ "America/Bogota", "1914-11-23" },
{ "America/Buenos_Aires", "1894-10-31" },
{ "America/Cambridge_Bay", "2000-11-05" },
{ "America/Campo_Grande", "1914-01-01" },
{ "America/Caracas", "1912-02-12" },
{ "America/Catamarca", "1991-10-20" },
{ "America/Catamarca", "2004-06-01" },
{ "America/Cayenne", "1911-07-01" },
{ "America/Chihuahua", "1930-11-15" },
{ "America/Chihuahua", "1931-10-01" },
{ "America/Cordoba", "1991-10-20" },
{ "America/Costa_Rica", "1921-01-15" },
{ "America/Cuiaba", "1914-01-01" },
{ "America/Danmarkshavn", "1916-07-28" },
{ "America/Detroit", "1905-01-01" },
{ "America/Eirunepe", "1914-01-01" },
{ "America/El_Salvador", "1921-01-01" },
{ "America/Ensenada", "1924-01-01" },
{ "America/Ensenada", "1930-11-15" },
{ "America/Fortaleza", "1914-01-01" },
{ "America/Glace_Bay", "1902-06-15" },
{ "America/Grand_Turk", "1890-01-01" },
{ "America/Guyana", "1991-01-01" },
{ "America/Havana", "1890-01-01" },
{ "America/Hermosillo", "1930-11-15" },
{ "America/Hermosillo", "1931-10-01" },
{ "America/Hermosillo", "1942-04-24" },
{ "America/Hermosillo", "1949-01-14" },
{ "America/Jujuy", "1991-10-06" },
{ "America/Lima", "1890-01-01" },
{ "America/Maceio", "1914-01-01" },
{ "America/Managua", "1890-01-01" },
{ "America/Managua", "1934-06-23" },
{ "America/Managua", "1975-02-16" },
{ "America/Managua", "1992-09-24" },
{ "America/Managua", "1997-01-01" },
{ "America/Mazatlan", "1930-11-15" },
{ "America/Mazatlan", "1931-10-01" },
{ "America/Mazatlan", "1942-04-24" },
{ "America/Mazatlan", "1949-01-14" },
{ "America/Mendoza", "1992-10-18" },
{ "America/Mendoza", "2004-05-23" },
{ "America/Merida", "1982-12-02" },
{ "America/Mexico_City", "1930-11-15" },
{ "America/Mexico_City", "1931-10-01" },
{ "America/Miquelon", "1911-05-15" },
{ "America/Moncton", "1883-12-09" },
{ "America/Montevideo", "1942-12-14" },
{ "America/Montevideo", "1974-12-22" },
{ "America/Montreal", "1884-01-01" },
{ "America/Ojinaga", "1930-11-15" },
{ "America/Ojinaga", "1931-10-01" },
{ "America/Panama", "1890-01-01" },
{ "America/Paramaribo", "1911-01-01" },
{ "America/Porto_Acre", "1914-01-01" },
{ "America/Recife", "1914-01-01" },
{ "America/Regina", "1905-09-01" },
{ "America/Rio_Branco", "1914-01-01" },
{ "America/Rosario", "1991-10-20" },
{ "America/Santa_Isabel", "1924-01-01" },
{ "America/Santa_Isabel", "1930-11-15" },
{ "America/Santarem", "1914-01-01" },
{ "America/Santiago", "1910-01-01" },
{ "America/Santiago", "1919-07-01" },
{ "America/Santo_Domingo", "1890-01-01" },
{ "America/Scoresbysund", "1916-07-28" },
{ "America/Scoresbysund", "1981-03-29" },
{ "America/Tegucigalpa", "1921-04-01" },
{ "America/Thunder_Bay", "1895-01-01" },
{ "America/Tijuana", "1924-01-01" },
{ "America/Tijuana", "1930-11-15" },
{ "Antarctica/Casey", "1969-01-01" },
{ "Antarctica/Casey", "2009-10-18" },
{ "Antarctica/Davis", "1957-01-13" },
{ "Antarctica/Davis", "1969-02-01" },
{ "Antarctica/Davis", "2010-03-11" },
{ "Antarctica/DumontDUrville", "1947-01-01" },
{ "Antarctica/DumontDUrville", "1956-11-01" },
{ "Antarctica/Macquarie", "1911-01-01" },
{ "Antarctica/Mawson", "1954-02-13" },
{ "Antarctica/McMurdo", "1956-01-01" },
{ "Antarctica/Palmer", "1982-05-01" },
{ "Antarctica/South_Pole", "1956-01-01" },
{ "Antarctica/Syowa", "1957-01-29" },
{ "Antarctica/Vostok", "1957-12-16" },
{ "Arctic/Longyearbyen", "1895-01-01" },
{ "Asia/Almaty", "1930-06-21" },
{ "Asia/Anadyr", "1924-05-02" },
{ "Asia/Anadyr", "1930-06-21" },
{ "Asia/Anadyr", "1992-01-19" },
{ "Asia/Anadyr", "2011-03-27" },
{ "Asia/Aqtau", "1924-05-02" },
{ "Asia/Aqtau", "1930-06-21" },
{ "Asia/Aqtau", "1981-10-01" },
{ "Asia/Aqtau", "2005-03-15" },
{ "Asia/Aqtobe", "1924-05-02" },
{ "Asia/Aqtobe", "1930-06-21" },
{ "Asia/Ashgabat", "1924-05-02" },
{ "Asia/Ashgabat", "1930-06-21" },
{ "Asia/Ashgabat", "1992-01-19" },
{ "Asia/Ashkhabad", "1924-05-02" },
{ "Asia/Ashkhabad", "1930-06-21" },
{ "Asia/Ashkhabad", "1992-01-19" },
{ "Asia/Baghdad", "1918-01-01" },
{ "Asia/Bahrain", "1920-01-01" },
{ "Asia/Baku", "1957-03-01" },
{ "Asia/Bangkok", "1920-04-01" },
{ "Asia/Bishkek", "1924-05-02" },
{ "Asia/Bishkek", "1930-06-21" },
{ "Asia/Brunei", "1933-01-01" },
{ "Asia/Calcutta", "1941-10-01" },
{ "Asia/Choibalsan", "1978-01-01" },
{ "Asia/Chongqing", "1980-05-01" },
{ "Asia/Chungking", "1980-05-01" },
{ "Asia/Colombo", "1880-01-01" },
{ "Asia/Colombo", "1906-01-01" },
{ "Asia/Colombo", "1942-09-01" },
{ "Asia/Colombo", "1996-05-25" },
{ "Asia/Dacca", "1941-10-01" },
{ "Asia/Dacca", "1942-09-01" },
{ "Asia/Dhaka", "1941-10-01" },
{ "Asia/Dhaka", "1942-09-01" },
{ "Asia/Dili", "2000-09-17" },
{ "Asia/Dubai", "1920-01-01" },
{ "Asia/Dushanbe", "1924-05-02" },
{ "Asia/Dushanbe", "1930-06-21" },
{ "Asia/Harbin", "1928-01-01" },
{ "Asia/Harbin", "1940-01-01" },
{ "Asia/Ho_Chi_Minh", "1912-05-01" },
{ "Asia/Hong_Kong", "1904-10-30" },
{ "Asia/Hong_Kong", "1941-12-25" },
{ "Asia/Hovd", "1978-01-01" },
{ "Asia/Irkutsk", "1920-01-25" },
{ "Asia/Irkutsk", "1930-06-21" },
{ "Asia/Irkutsk", "1992-01-19" },
{ "Asia/Irkutsk", "2011-03-27" },
{ "Asia/Istanbul", "1880-01-01" },
{ "Asia/Istanbul", "1910-10-01" },
{ "Asia/Istanbul", "1978-10-15" },
{ "Asia/Jakarta", "1932-11-01" },
{ "Asia/Jakarta", "1942-03-23" },
{ "Asia/Jakarta", "1948-05-01" },
{ "Asia/Jayapura", "1944-09-01" },
{ "Asia/Kabul", "1945-01-01" },
{ "Asia/Kamchatka", "1922-11-10" },
{ "Asia/Kamchatka", "1930-06-21" },
{ "Asia/Kamchatka", "1992-01-19" },
{ "Asia/Kamchatka", "2011-03-27" },
{ "Asia/Karachi", "1907-01-01" },
{ "Asia/Kashgar", "1928-01-01" },
{ "Asia/Kashgar", "1980-05-01" },
{ "Asia/Kathmandu", "1986-01-01" },
{ "Asia/Katmandu", "1986-01-01" },
{ "Asia/Kolkata", "1941-10-01" },
{ "Asia/Krasnoyarsk", "1930-06-21" },
{ "Asia/Krasnoyarsk", "1992-01-19" },
{ "Asia/Krasnoyarsk", "2011-03-27" },
{ "Asia/Kuala_Lumpur", "1901-01-01" },
{ "Asia/Kuala_Lumpur", "1905-06-01" },
{ "Asia/Kuala_Lumpur", "1941-09-01" },
{ "Asia/Kuala_Lumpur", "1942-02-16" },
{ "Asia/Kuala_Lumpur", "1982-01-01" },
{ "Asia/Kuching", "1926-03-01" },
{ "Asia/Kuching", "1933-01-01" },
{ "Asia/Kuching", "1942-02-16" },
{ "Asia/Macao", "1912-01-01" },
{ "Asia/Macau", "1912-01-01" },
{ "Asia/Magadan", "1930-06-21" },
{ "Asia/Magadan", "1992-01-19" },
{ "Asia/Magadan", "2011-03-27" },
{ "Asia/Makassar", "1932-11-01" },
{ "Asia/Makassar", "1942-02-09" },
{ "Asia/Manila", "1942-05-01" },
{ "Asia/Muscat", "1920-01-01" },
{ "Asia/Novokuznetsk", "1920-01-06" },
{ "Asia/Novokuznetsk", "1930-06-21" },
{ "Asia/Novokuznetsk", "1992-01-19" },
{ "Asia/Novokuznetsk", "2011-03-27" },
{ "Asia/Novosibirsk", "1930-06-21" },
{ "Asia/Novosibirsk", "1992-01-19" },
{ "Asia/Novosibirsk", "2011-03-27" },
{ "Asia/Omsk", "1919-11-14" },
{ "Asia/Omsk", "1930-06-21" },
{ "Asia/Omsk", "1992-01-19" },
{ "Asia/Omsk", "2011-03-27" },
{ "Asia/Oral", "1924-05-02" },
{ "Asia/Oral", "1930-06-21" },
{ "Asia/Oral", "2005-03-15" },
{ "Asia/Phnom_Penh", "1906-06-09" },
{ "Asia/Phnom_Penh", "1912-05-01" },
{ "Asia/Pontianak", "1932-11-01" },
{ "Asia/Pontianak", "1942-01-29" },
{ "Asia/Pontianak", "1948-05-01" },
{ "Asia/Pontianak", "1964-01-01" },
{ "Asia/Pyongyang", "1890-01-01" },
{ "Asia/Pyongyang", "1904-12-01" },
{ "Asia/Pyongyang", "1932-01-01" },
{ "Asia/Pyongyang", "1961-08-10" },
{ "Asia/Qatar", "1920-01-01" },
{ "Asia/Qyzylorda", "1930-06-21" },
{ "Asia/Qyzylorda", "1992-01-19" },
{ "Asia/Rangoon", "1920-01-01" },
{ "Asia/Rangoon", "1942-05-01" },
{ "Asia/Saigon", "1912-05-01" },
{ "Asia/Sakhalin", "1945-08-25" },
{ "Asia/Sakhalin", "1992-01-19" },
{ "Asia/Sakhalin", "2011-03-27" },
{ "Asia/Samarkand", "1930-06-21" },
{ "Asia/Seoul", "1890-01-01" },
{ "Asia/Seoul", "1904-12-01" },
{ "Asia/Seoul", "1932-01-01" },
{ "Asia/Seoul", "1961-08-10" },
{ "Asia/Seoul", "1968-10-01" },
{ "Asia/Singapore", "1905-06-01" },
{ "Asia/Singapore", "1941-09-01" },
{ "Asia/Singapore", "1942-02-16" },
{ "Asia/Singapore", "1982-01-01" },
{ "Asia/Tashkent", "1924-05-02" },
{ "Asia/Tashkent", "1930-06-21" },
{ "Asia/Tbilisi", "1924-05-02" },
{ "Asia/Tbilisi", "1957-03-01" },
{ "Asia/Tbilisi", "2005-03-27" },
{ "Asia/Tehran", "1946-01-01" },
{ "Asia/Tehran", "1977-11-01" },
{ "Asia/Thimbu", "1987-10-01" },
{ "Asia/Thimphu", "1987-10-01" },
{ "Asia/Ujung_Pandang", "1932-11-01" },
{ "Asia/Ujung_Pandang", "1942-02-09" },
{ "Asia/Ulaanbaatar", "1978-01-01" },
{ "Asia/Ulan_Bator", "1978-01-01" },
{ "Asia/Urumqi", "1928-01-01" },
{ "Asia/Urumqi", "1980-05-01" },
{ "Asia/Vientiane", "1906-06-09" },
{ "Asia/Vientiane", "1912-05-01" },
{ "Asia/Vladivostok", "1922-11-15" },
{ "Asia/Vladivostok", "1930-06-21" },
{ "Asia/Vladivostok", "1992-01-19" },
{ "Asia/Vladivostok", "2011-03-27" },
{ "Asia/Yakutsk", "1930-06-21" },
{ "Asia/Yakutsk", "1992-01-19" },
{ "Asia/Yakutsk", "2011-03-27" },
{ "Asia/Yekaterinburg", "1930-06-21" },
{ "Asia/Yekaterinburg", "1992-01-19" },
{ "Asia/Yekaterinburg", "2011-03-27" },
{ "Asia/Yerevan", "1924-05-02" },
{ "Asia/Yerevan", "1957-03-01" },
{ "Atlantic/Azores", "1884-01-01" },
{ "Atlantic/Azores", "1911-05-24" },
{ "Atlantic/Azores", "1942-04-25" },
{ "Atlantic/Azores", "1943-04-17" },
{ "Atlantic/Azores", "1944-04-22" },
{ "Atlantic/Azores", "1945-04-21" },
{ "Atlantic/Cape_Verde", "1907-01-01" },
{ "Atlantic/Jan_Mayen", "1895-01-01" },
{ "Atlantic/Madeira", "1942-04-25" },
{ "Atlantic/Madeira", "1943-04-17" },
{ "Atlantic/Madeira", "1944-04-22" },
{ "Atlantic/Madeira", "1945-04-21" },
{ "Atlantic/Reykjavik", "1837-01-01" },
{ "Atlantic/Stanley", "1912-03-12" },
{ "Australia/Adelaide", "1899-05-01" },
{ "Australia/Broken_Hill", "1895-02-01" },
{ "Australia/Broken_Hill", "1899-05-01" },
{ "Australia/Currie", "1895-09-01" },
{ "Australia/Darwin", "1895-02-01" },
{ "Australia/Darwin", "1899-05-01" },
{ "Australia/Eucla", "1895-12-01" },
{ "Australia/Hobart", "1895-09-01" },
{ "Australia/LHI", "1981-03-01" },
{ "Australia/Lindeman", "1895-01-01" },
{ "Australia/Lord_Howe", "1981-03-01" },
{ "Australia/Melbourne", "1895-02-01" },
{ "Australia/North", "1895-02-01" },
{ "Australia/North", "1899-05-01" },
{ "Australia/Perth", "1895-12-01" },
{ "Australia/South", "1899-05-01" },
{ "Australia/Tasmania", "1895-09-01" },
{ "Australia/Victoria", "1895-02-01" },
{ "Australia/West", "1895-12-01" },
{ "Australia/Yancowinna", "1895-02-01" },
{ "Australia/Yancowinna", "1899-05-01" },
{ "Brazil/Acre", "1914-01-01" },
{ "Canada/East-Saskatchewan", "1905-09-01" },
{ "Canada/Saskatchewan", "1905-09-01" },
{ "Chile/Continental", "1910-01-01" },
{ "Chile/Continental", "1919-07-01" },
{ "Chile/EasterIsland", "1932-09-01" },
{ "Cuba", "1890-01-01" },
{ "Eire", "1880-08-02" },
{ "Europe/Amsterdam", "1937-07-01" },
{ "Europe/Andorra", "1946-09-30" },
{ "Europe/Athens", "1916-07-28" },
{ "Europe/Athens", "1944-04-04" },
{ "Europe/Berlin", "1893-04-01" },
{ "Europe/Bratislava", "1891-10-01" },
{ "Europe/Brussels", "1914-11-08" },
{ "Europe/Bucharest", "1931-07-24" },
{ "Europe/Chisinau", "1931-07-24" },
{ "Europe/Copenhagen", "1894-01-01" },
{ "Europe/Dublin", "1880-08-02" },
{ "Europe/Gibraltar", "1941-05-04" },
{ "Europe/Gibraltar", "1942-04-05" },
{ "Europe/Gibraltar", "1943-04-04" },
{ "Europe/Gibraltar", "1944-04-02" },
{ "Europe/Gibraltar", "1945-04-02" },
{ "Europe/Gibraltar", "1947-04-13" },
{ "Europe/Helsinki", "1921-05-01" },
{ "Europe/Istanbul", "1880-01-01" },
{ "Europe/Istanbul", "1910-10-01" },
{ "Europe/Istanbul", "1978-10-15" },
{ "Europe/Kaliningrad", "1945-01-01" },
{ "Europe/Kaliningrad", "1946-01-01" },
{ "Europe/Kaliningrad", "2011-03-27" },
{ "Europe/Kiev", "1930-06-21" },
{ "Europe/Kiev", "1943-11-06" },
{ "Europe/Luxembourg", "1904-06-01" },
{ "Europe/Madrid", "1942-05-02" },
{ "Europe/Madrid", "1943-04-17" },
{ "Europe/Madrid", "1944-04-15" },
{ "Europe/Madrid", "1945-04-14" },
{ "Europe/Madrid", "1946-04-13" },
{ "Europe/Malta", "1893-11-02" },
{ "Europe/Mariehamn", "1921-05-01" },
{ "Europe/Minsk", "1924-05-02" },
{ "Europe/Minsk", "1930-06-21" },
{ "Europe/Minsk", "2011-03-27" },
{ "Europe/Monaco", "1941-05-05" },
{ "Europe/Monaco", "1942-03-09" },
{ "Europe/Monaco", "1943-03-29" },
{ "Europe/Monaco", "1944-04-03" },
{ "Europe/Monaco", "1945-04-02" },
{ "Europe/Moscow", "1916-07-03" },
{ "Europe/Moscow", "1919-05-31" },
{ "Europe/Moscow", "1930-06-21" },
{ "Europe/Moscow", "1992-01-19" },
{ "Europe/Moscow", "2011-03-27" },
{ "Europe/Oslo", "1895-01-01" },
{ "Europe/Paris", "1945-04-02" },
{ "Europe/Prague", "1891-10-01" },
{ "Europe/Riga", "1926-05-11" },
{ "Europe/Riga", "1940-08-05" },
{ "Europe/Riga", "1944-10-13" },
{ "Europe/Rome", "1893-11-01" },
{ "Europe/Samara", "1930-06-21" },
{ "Europe/Samara", "1991-10-20" },
{ "Europe/Samara", "2011-03-27" },
{ "Europe/San_Marino", "1893-11-01" },
{ "Europe/Simferopol", "1930-06-21" },
{ "Europe/Simferopol", "1994-05-01" },
{ "Europe/Sofia", "1880-01-01" },
{ "Europe/Sofia", "1894-11-30" },
{ "Europe/Tallinn", "1919-07-01" },
{ "Europe/Tallinn", "1921-05-01" },
{ "Europe/Tallinn", "1940-08-06" },
{ "Europe/Tiraspol", "1931-07-24" },
{ "Europe/Uzhgorod", "1945-06-29" },
{ "Europe/Vaduz", "1894-06-01" },
{ "Europe/Vatican", "1893-11-01" },
{ "Europe/Vilnius", "1917-01-01" },
{ "Europe/Vilnius", "1920-07-12" },
{ "Europe/Vilnius", "1940-08-03" },
{ "Europe/Volgograd", "1920-01-03" },
{ "Europe/Volgograd", "1930-06-21" },
{ "Europe/Volgograd", "1991-03-31" },
{ "Europe/Volgograd", "2011-03-27" },
{ "Europe/Zaporozhye", "1930-06-21" },
{ "Europe/Zaporozhye", "1943-10-25" },
{ "Europe/Zurich", "1894-06-01" },
{ "Hongkong", "1904-10-30" },
{ "Hongkong", "1941-12-25" },
{ "Iceland", "1837-01-01" },
{ "Indian/Chagos", "1907-01-01" },
{ "Indian/Chagos", "1996-01-01" },
{ "Indian/Cocos", "1900-01-01" },
{ "Indian/Comoro", "1911-07-01" },
{ "Indian/Kerguelen", "1950-01-01" },
{ "Indian/Mahe", "1906-06-01" },
{ "Indian/Maldives", "1960-01-01" },
{ "Indian/Mauritius", "1907-01-01" },
{ "Indian/Reunion", "1911-06-01" },
{ "Iran", "1946-01-01" },
{ "Iran", "1977-11-01" },
{ "Libya", "1920-01-01" },
{ "Libya", "1959-01-01" },
{ "Libya", "1990-05-04" },
{ "Mexico/BajaNorte", "1924-01-01" },
{ "Mexico/BajaNorte", "1930-11-15" },
{ "Mexico/BajaSur", "1930-11-15" },
{ "Mexico/BajaSur", "1931-10-01" },
{ "Mexico/BajaSur", "1942-04-24" },
{ "Mexico/BajaSur", "1949-01-14" },
{ "Mexico/General", "1930-11-15" },
{ "Mexico/General", "1931-10-01" },
{ "NZ-CHAT", "1957-01-01" },
{ "Pacific/Apia", "1911-01-01" },
{ "Pacific/Apia", "2011-12-30" },
{ "Pacific/Chatham", "1957-01-01" },
{ "Pacific/Easter", "1932-09-01" },
{ "Pacific/Enderbury", "1901-01-01" },
{ "Pacific/Enderbury", "1995-01-01" },
{ "Pacific/Fakaofo", "2011-12-30" },
{ "Pacific/Fiji", "1915-10-26" },
{ "Pacific/Funafuti", "1901-01-01" },
{ "Pacific/Galapagos", "1986-01-01" },
{ "Pacific/Gambier", "1912-10-01" },
{ "Pacific/Guadalcanal", "1912-10-01" },
{ "Pacific/Guam", "1901-01-01" },
{ "Pacific/Kiritimati", "1901-01-01" },
{ "Pacific/Kiritimati", "1995-01-01" },
{ "Pacific/Kosrae", "1901-01-01" },
{ "Pacific/Kosrae", "1969-10-01" },
{ "Pacific/Kwajalein", "1993-08-20" },
{ "Pacific/Majuro", "1969-10-01" },
{ "Pacific/Marquesas", "1912-10-01" },
{ "Pacific/Nauru", "1921-01-15" },
{ "Pacific/Nauru", "1944-08-15" },
{ "Pacific/Nauru", "1979-05-01" },
{ "Pacific/Niue", "1901-01-01" },
{ "Pacific/Niue", "1951-01-01" },
{ "Pacific/Norfolk", "1901-01-01" },
{ "Pacific/Norfolk", "1951-01-01" },
{ "Pacific/Pago_Pago", "1911-01-01" },
{ "Pacific/Palau", "1901-01-01" },
{ "Pacific/Pohnpei", "1901-01-01" },
{ "Pacific/Ponape", "1901-01-01" },
{ "Pacific/Port_Moresby", "1895-01-01" },
{ "Pacific/Rarotonga", "1978-11-12" },
{ "Pacific/Saipan", "1969-10-01" },
{ "Pacific/Samoa", "1911-01-01" },
{ "Pacific/Tahiti", "1912-10-01" },
{ "Pacific/Tarawa", "1901-01-01" },
{ "Pacific/Tongatapu", "1901-01-01" },
{ "Pacific/Tongatapu", "1941-01-01" },
{ "Pacific/Wake", "1901-01-01" },
{ "ROK", "1890-01-01" },
{ "ROK", "1904-12-01" },
{ "ROK", "1932-01-01" },
{ "ROK", "1961-08-10" },
{ "ROK", "1968-10-01" },
{ "Singapore", "1905-06-01" },
{ "Singapore", "1941-09-01" },
{ "Singapore", "1942-02-16" },
{ "Singapore", "1982-01-01" },
{ "Turkey", "1880-01-01" },
{ "Turkey", "1910-10-01" },
{ "Turkey", "1978-10-15" },
{ "US/Michigan", "1905-01-01" },
{ "US/Samoa", "1911-01-01" },
{ "W-SU", "1916-07-03" },
{ "W-SU", "1930-06-21" },
{ "W-SU", "1992-01-19" },
{ "W-SU", "2011-03-27" }
};
LanguageType eLang = LANGUAGE_ENGLISH_US;
SvNumberFormatter aFormatter(m_xContext, eLang);
for (auto const& aEntry : aData)
{
checkDateInput(aFormatter, aEntry[0], aEntry[1]);
}
}
void Test::checkDateInput( SvNumberFormatter& rFormatter, const char* pTimezone, const char* pIsoDate )
{
icu::TimeZone::adoptDefault( icu::TimeZone::createTimeZone( pTimezone));
OUString aDate( OUString::createFromAscii(pIsoDate));
sal_uInt32 nIndex = 0;
double fVal = 0.0;
bool bVal = rFormatter.IsNumberFormat( aDate, nIndex, fVal);
CPPUNIT_ASSERT_MESSAGE( OString(OString::Concat("Date not recognized: ") +
pTimezone + " " + pIsoDate).getStr(), bVal);
CPPUNIT_ASSERT_MESSAGE("Format parsed is not date.",
(rFormatter.GetType(nIndex) & SvNumFormatType::DATE));
OUString aOutString;
const Color *pColor;
rFormatter.GetOutputString( fVal, nIndex, aOutString, &pColor);
CPPUNIT_ASSERT_EQUAL( aDate, aOutString);
}
void Test::testIsNumberFormat()
{
LanguageType eLang = LANGUAGE_ENGLISH_US;
SvNumberFormatter aFormatter(m_xContext, eLang);
static struct NumberFormatData
{
const char* pFormat;
bool bIsNumber;
} const aTests[] = {
{ "20.3", true },
{ "2", true },
{ "test", false },
{ "$0.12", true }, // tdf#48706
{ "$.12", true }, // tdf#48706
{ "1 .", false }, // tdf#131562
{ "1 .2", false }, // tdf#131562
{ "1 . 2", false }, // tdf#131562
{ "1. 2", false }, // tdf#131562
{ " . 2", false }, // tdf#131562
{ ". 2", false }, // tdf#131562
{ " .2", true }, // tdf#131562
{ "Jan1", false }, // tdf#34724
{ "1Jan", false }, // tdf#34724
{ "Jan1 2000", true }, // tdf#91420
{ "Jan1, 2000", true }, // tdf#91420
{ "Jan 1", true },
{ "Sept 1", true }, //tdf#127363
{ "5/d", false }, //tdf#143165
{ "Jan 1 2000", true },
{ "5-12-14", true }, // tdf#164239
{ "005-12-14", true },
{ "15-10-30", true },
{ "2015-10-30", true },
{ "1999-11-23T12:34:56", true },
{ "1999-11-23 12:34:56", true },
{ "1999-11-23T12:34:56.789", true },
{ "1999-11-23T12:34:56,789", true }, // ISO 8601 defines both dot and comma as fractional separator
{ "1999-11-23 12:34:56.789", true },
{ "1999-11-23 12:34:56,789", false }, // comma not in en-US if 'T' separator is not present,
// debatable, 'T' "may be omitted by mutual consent of those
// interchanging data, if ambiguity can be avoided."
{ "1999-11-23T12:34:56/789", false },
{ "1000", true } // unicode minus
};
for (auto const[pFormat, bTestIsNumber] : aTests)
{
sal_uInt32 nIndex = 0;
double nNumber = 0;
OUString aString = OUString::fromUtf8(pFormat);
bool bIsNumber = aFormatter.IsNumberFormat(aString, nIndex, nNumber);
CPPUNIT_ASSERT_EQUAL_MESSAGE(pFormat, bTestIsNumber, bIsNumber);
}
}
struct FormatInputOutput
{
const char* mpInput;
const bool mbNumber;
const char* mpOutput;
const sal_uInt32 mnOutputIndex;
};
void checkSpecificNumberFormats( SvNumberFormatter& rFormatter,
const std::vector<FormatInputOutput>& rVec, const char* pName )
{
for (size_t i = 0; i < rVec.size(); ++i)
{
sal_uInt32 nIndex = 0;
double fNumber = 0;
OUString aString( OUString::fromUtf8( rVec[i].mpInput));
const bool bIsNumber = rFormatter.IsNumberFormat( aString, nIndex, fNumber);
CPPUNIT_ASSERT_EQUAL_MESSAGE( OString( pName + OString::Concat(" ") + OString::number(i) +
(rVec[i].mbNumber ? " not recognized: " : " should not be recognized: ") +
OUStringToOString( aString, RTL_TEXTENCODING_UTF8)).getStr(), rVec[i].mbNumber, bIsNumber);
if (bIsNumber)
{
if (rVec[i].mnOutputIndex)
nIndex = rVec[i].mnOutputIndex;
const Color* pColor;
rFormatter.GetOutputString( fNumber, nIndex, aString, &pColor);
CPPUNIT_ASSERT_EQUAL_MESSAGE( OString( pName + OString::Concat(" ") + OString::number(i) + " mismatch").getStr(),
OUString::fromUtf8( rVec[i].mpOutput), aString);
}
}
}
void Test::testIsNumberFormatSpecific()
{
{
// en-US uses M/D/Y format, test that without Y-M-D pattern an a-b-c
// input with a<=12 leads to ISO a-b-c date output.
SvNumberFormatter aFormatter(m_xContext, LANGUAGE_ENGLISH_US);
std::vector<FormatInputOutput> aIO = {
{ "005-12-14", true, "0005-12-14", 0 },
{ "5-12-14", true, "2005-12-14", 0 },
{ "32-12-14", true, "1932-12-14", 0 }
};
checkSpecificNumberFormats( aFormatter, aIO, "[en-US] date");
}
{
// de-DE uses D.M.Y format, test that without Y-M-D pattern an a-b-c
// input with a<=31 leads to ISO a-b-c date output.
SvNumberFormatter aFormatter(m_xContext, LANGUAGE_GERMAN);
std::vector<FormatInputOutput> aIO = {
{ "005-12-14", true, "0005-12-14", 0 },
{ "5-12-14", true, "2005-12-14", 0 },
{ "32-12-14", true, "1932-12-14", 0 }
};
checkSpecificNumberFormats( aFormatter, aIO, "[de-DE] date");
}
{
// nl-NL uses D-M-Y format, test that D-M-Y input leads to D-M-Y output
// and ISO Y-M-D input leads to Y-M-D output.
SvNumberFormatter aFormatter(m_xContext, LANGUAGE_DUTCH);
std::vector<FormatInputOutput> aIO = {
{ "001-2-11", true, "0001-02-11", 0 },
{ "22-11-1999", true, "22-11-99", 0 }, // if default YY changes to YYYY adapt this
{ "1999-11-22", true, "1999-11-22", 0 },
{ "1-2-11", true, "01-02-11", 0 }, // if default YY changes to YYYY adapt this
{ "99-2-11", true, "1999-02-11", 0 }
};
checkSpecificNumberFormats( aFormatter, aIO, "[nl-NL] date");
}
{
// en-ZA uses Y-M-D and Y/M/D format, test that either are accepted.
// The default format changed from YY/MM/DD to YYYY-MM-DD.
SvNumberFormatter aFormatter(m_xContext, LANGUAGE_ENGLISH_SAFRICA);
std::vector<FormatInputOutput> aIO = {
{ "1999/11/22", true, "1999-11-22", 0 },
{ "1999-11-22", true, "1999-11-22", 0 },
{ "11/2/1", true, "2011-02-01", 0 },
{ "99-2-11", true, "1999-02-11", 0 },
{ "22-2-11", true, "2022-02-11", 0 },
{ "02 Mar 2020",true, "2020-03-02", 0 }
};
checkSpecificNumberFormats( aFormatter, aIO, "[en-ZA] date");
}
{
// fr-FR uses D/M/Y format with additional D.M.Y and D-M-Y date
// acceptance patterns, test combinations.
SvNumberFormatter aFormatter(m_xContext, LANGUAGE_FRENCH);
std::vector<FormatInputOutput> aIO = {
{ "22/11/1999", true, "22/11/99", 0 }, // if default YY changes to YYYY adapt this
{ "1999-11-22", true, "1999-11-22", 0 },
{ "1/2/11", true, "01/02/11", 0 }, // if default YY changes to YYYY adapt this
{ "99-2-11", true, "1999-02-11", 0 },
{ "22-2-11", true, "22/02/11", 0 }, // if default YY changes to YYYY adapt this
{ "22.2.11", true, "22/02/11", 0 } // if default YY changes to YYYY adapt this
};
checkSpecificNumberFormats( aFormatter, aIO, "[fr-FR] date");
}
{
// Test Spanish "mar" short name ambiguity, day "martes" or month "marzo".
// Day of week names are only parsed away, not evaluated if they actually
// correspond to the date given.
SvNumberFormatter aFormatter(m_xContext, LANGUAGE_SPANISH);
const sal_uInt32 n = aFormatter.GetFormatIndex( NF_DATE_SYS_DDMMYYYY, LANGUAGE_SPANISH);
std::vector<FormatInputOutput> aIO = {
{ "22/11/1999", true, "22/11/1999", n },
{ "Lun 22/11/1999", true, "22/11/1999", n },
{ "Mar 22/11/1999", true, "22/11/1999", n },
{ "Abr 22/11/1999", false, "", n }, // month name AND numeric month don't go along
{ "Lun Mar 22/11/1999", false, "", n }, // month name AND numeric month don't go along
{ "Mar Mar 22/11/1999", false, "", n }, // month name AND numeric month don't go along
{ "Lun Mar 22 1999", true, "22/03/1999", n },
{ "Mar Mar 22 1999", true, "22/03/1999", n },
{ "Mar Lun 22 1999", false, "", n } // day name only at the beginning (could change?)
};
checkSpecificNumberFormats( aFormatter, aIO, "[es-ES] date");
}
{
// Test that de-DE accepts Januar and Jänner.
SvNumberFormatter aFormatter(m_xContext, LANGUAGE_GERMAN);
const sal_uInt32 n = aFormatter.GetFormatIndex( NF_DATE_SYS_DDMMYYYY, LANGUAGE_GERMAN);
std::vector<FormatInputOutput> aIO = {
{ "23. Januar 1999", true, "23.01.1999", n },
{ "23. J\xC3\xA4nner 1999", true, "23.01.1999", n },
{ "23. Jan. 1999", true, "23.01.1999", n },
{ "23. J\xC3\xA4n. 1999", true, "23.01.1999", n },
};
checkSpecificNumberFormats( aFormatter, aIO, "[de-DE] date January month names");
}
{
// tdf#143664
SvNumberFormatter aFormatter(m_xContext, LANGUAGE_GERMAN);
const sal_uInt32 n = aFormatter.GetFormatIndex( NF_DATE_SYS_DDMMYYYY, LANGUAGE_GERMAN);
std::vector<FormatInputOutput> aIO = {
{ "23. M\u00C4R 1999", true, "23.03.1999", n },
{ "23. M\u00C4RZ 1999", true, "23.03.1999", n },
{ "23. MRZ 1999", true, "23.03.1999", n },
};
checkSpecificNumberFormats( aFormatter, aIO, "[de-DE] date March month names");
}
{
// Test that de-AT accepts Januar and Jänner.
SvNumberFormatter aFormatter(m_xContext, LANGUAGE_GERMAN_AUSTRIAN);
const sal_uInt32 n = aFormatter.GetFormatIndex( NF_DATE_SYS_DDMMYYYY, LANGUAGE_GERMAN_AUSTRIAN);
std::vector<FormatInputOutput> aIO = {
{ "23. Januar 1999", true, "23.01.1999", n },
{ "23. J\xC3\xA4nner 1999", true, "23.01.1999", n },
{ "23. Jan. 1999", true, "23.01.1999", n },
{ "23. J\xC3\xA4n. 1999", true, "23.01.1999", n },
};
checkSpecificNumberFormats( aFormatter, aIO, "[de-AT] date January month names");
}
}
void Test::testUserDefinedNumberFormats()
{
LanguageType eLang = LANGUAGE_ENGLISH_US;
OUString sCode, sExpected;
SvNumberFormatter aFormatter(m_xContext, eLang);
// tdf#158890 replace '?' with figure blank (0x2007)
static constexpr OUString sBlankDigit = u""_ustr;
{ // tdf#97835: suppress decimal separator
sCode = "0.##\" m\"";
sExpected = "12 m";
checkPreviewString(aFormatter, sCode, 12.0, eLang, sExpected);
}
{ // tdf#61996: skip quoted text
sCode = "0.00\" ;\"";
sExpected = "-12.00 ;";
checkPreviewString(aFormatter, sCode, -12.0, eLang, sExpected);
}
{ // tdf#100755
sCode = "000\" \"000/000";
sExpected = "003 016/113";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
}
{ // tdf#100834
sCode = "#\" string \"?/???";
sExpected = "3 string 16/113";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
}
{ // tdf#129878
sCode = "[HH]";
sExpected = "#FMT";
checkPreviewString(aFormatter, sCode, 2E+306, eLang, sExpected);
}
{ // tdf#144697
sCode = "YYYY-MM-DD";
sExpected = "#FMT";
checkPreviewString(aFormatter, sCode, -12662108.0, eLang, sExpected);
}
{ // tdf#122991
sCode = "[HH]:MM:SS";
sExpected = "08:47:00";
checkPreviewString(aFormatter, sCode, 0.365972222222222, eLang, sExpected);
sCode = "HH:MM:SS";
checkPreviewString(aFormatter, sCode, 0.365972222222222, eLang, sExpected);
}
{ // tdf#100122
sCode = "?/?";
sExpected = "-1/2";
checkPreviewString(aFormatter, sCode, -0.5, eLang, sExpected);
}
{ // tdf#52510
sCode = "_($* #,##0.00_);_($* (#,##0.00);";
sExpected = "";
checkPreviewString(aFormatter, sCode, 0.0, eLang, sExpected);
}
{ // tdf#95339: detect SSMM as second minute
sCode = "SS:MM:HH DD/MM/YY"; // Month not detected by Excel, but we do not follow that.
sExpected = "53:23:03 02/01/00";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
}
{ // tdf#101147: detect SSMM as second month
sCode = "HH:MM:SS MM/DD";
sExpected = "03:23:53 01/02";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
}
{ // tdf#123748
sCode = "HH:MM:SS.000000";
sExpected = "12:54:00.000000";
checkPreviewString(aFormatter, sCode, 43521.5375, eLang, sExpected);
}
{ // tdf#101096: different detection of month/minute with Excel
sCode = "HH DD MM"; // month detected because of previous DD
sExpected = "03 02 01";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "HH:MM HH DD/MM"; // month detected because of previous DD
sExpected = "03:23 03 02/01";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "SS:DD-MM-YY SS:MM"; // 1st is month, because of previous DD; 2nd is minute as SS has not minute
sExpected = "53:02-01-00 53:23";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
}
{ // tdf#99996: better algorithm for fraction representation
sCode = "# ?/???";
sExpected = "-575 540/697";
checkPreviewString(aFormatter, sCode, -575.774749601315, eLang, sExpected);
}
{ // tdf#153887: integer value without integer part displayed
sCode = "#/?";
sExpected = "2/1";
checkPreviewString(aFormatter, sCode, 1.95, eLang, sExpected);
checkPreviewString(aFormatter, sCode, 2.00, eLang, sExpected);
checkPreviewString(aFormatter, sCode, 2.05, eLang, sExpected);
sCode = "0/8";
sExpected = "16/8";
checkPreviewString(aFormatter, sCode, 1.95, eLang, sExpected);
checkPreviewString(aFormatter, sCode, 2.00, eLang, sExpected);
checkPreviewString(aFormatter, sCode, 2.05, eLang, sExpected);
}
{ // tdf#102507: left alignment of denominator
sCode = "# ?/???";
sExpected = OUString::Concat( u"3 1/2"_ustr ) + sBlankDigit + sBlankDigit;
checkPreviewString(aFormatter, sCode, 3.5, eLang, sExpected);
}
{ // tdf#100594: forced denominator
sCode = "# ?/100";
sExpected = " 6/100";
checkPreviewString(aFormatter, sCode, 0.06, eLang, sExpected);
}
{ // tdf#100754: forced denominator with text after fraction
sCode = "# ?/16\" inch\"";
sExpected = "2 6/16 inch";
checkPreviewString(aFormatter, sCode, 2.379, eLang, sExpected);
}
{ // tdf#100842: text before/after fraction
sCode = "\"before \"?/?\" after\"";
sExpected = "before 11/9 after";
checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
sCode = "\"before \"# ?/?\" after\"";
sExpected = "before 1 2/9 after";
checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
sCode = "\"before \"0.0\"inside\"0E+0\"middle\"0\" after\"";
sExpected = "before 1.2inside3E+0middle4 after";
checkPreviewString(aFormatter, sCode, 12345.667, eLang, sExpected);
}
{ // tdf#106190: text after fraction bar
sCode = "?/ ?";
sExpected = "11/ 9";
checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
sCode = "?/ 12";
sExpected = "15/ 12";
checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
sCode = "# ?/\" divisor \"?";
sExpected = "1 2/ divisor 9";
checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
sCode = "# ?/\"divided by \"?";
sExpected = "1 2/divided by 9";
checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
sCode = "?/\" \"12";
sExpected = "15/ 12";
checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
sCode = "?/\\ 12";
sExpected = "15/ 12";
checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
sCode = "# ?/ ???";
sExpected = OUString::Concat( u"3 1/ 2"_ustr ) + sBlankDigit + sBlankDigit;
checkPreviewString(aFormatter, sCode, 3.5, eLang, sExpected);
}
{ // Display 1.96 as 2 and not 1 1/1
sCode = "# ?/?";
sExpected = OUString::Concat( u"2 "_ustr ) + sBlankDigit + u" "_ustr + sBlankDigit;
checkPreviewString(aFormatter, sCode, 1.96, eLang, sExpected);
sCode = "# ?/ ?";
sExpected = OUString::Concat( u"2 "_ustr ) + sBlankDigit + u" "_ustr + sBlankDigit;
checkPreviewString(aFormatter, sCode, 1.96, eLang, sExpected);
sCode = "# #/#";
sExpected = "2";
checkPreviewString(aFormatter, sCode, 1.96, eLang, sExpected);
}
{ // tdf#79399 tdf#101462 Native Number Formats
sCode = "[NatNum5][$-0404]General\\ ";
// Chinese upper case number characters for 120
sExpected = u"\u58F9\u4F70\u8CB3\u62FE "_ustr;
checkPreviewString(aFormatter, sCode, 120, eLang, sExpected);
sCode = "[DBNum2][$-0404]General\\ ";
checkPreviewString(aFormatter, sCode, 120, eLang, sExpected);
// tdf#115007 - cardinal/ordinal number names/indicators
sCode = "[NatNum12]0";
sExpected = "one hundred twenty-three";
checkPreviewString(aFormatter, sCode, 123, eLang, sExpected);
sCode = "[NatNum12]0.00";
sExpected = "one hundred twenty-three point four five";
checkPreviewString(aFormatter, sCode, 123.45, eLang, sExpected);
sCode = "[NatNum12 ordinal]0";
sExpected = "one hundred twenty-third";
checkPreviewString(aFormatter, sCode, 123, eLang, sExpected);
sCode = "[NatNum12 ordinal-number]0";
sExpected = "123rd";
checkPreviewString(aFormatter, sCode, 123, eLang, sExpected);
sCode = "[NatNum12 capitalize]0";
sExpected = "One hundred twenty-three";
checkPreviewString(aFormatter, sCode, 123, eLang, sExpected);
sCode = "[NatNum12 title ordinal]0";
sExpected = "One Thousand Two Hundred Thirty-Fourth";
checkPreviewString(aFormatter, sCode, 1234, eLang, sExpected);
sCode = "[NatNum12 upper ordinal-number]0";
sExpected = "12345TH";
checkPreviewString(aFormatter, sCode, 12345, eLang, sExpected);
sCode = "[NatNum12 D=ordinal-number]D\" of \"MMMM";
sExpected = "2nd of January";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "[NatNum12 D=ordinal-number,YYYY=year]D\" of \"MMMM\", \"YYYY";
sExpected = "2nd of January, nineteen hundred";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "[NatNum12 YYYY=title year, D=capitalize ordinal]D\" of \"MMMM\", \"YYYY";
sExpected = "Second of January, Nineteen Hundred";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "[NatNum12 MMMM=upper MMM=upper MMMMM=upper]MMMM MMM MMMMM";
sExpected = "JANUARY JAN J";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "[NatNum12 DDDD=upper DDD=upper]DDDD DDD";
sExpected = "TUESDAY TUE";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "[NatNum12 NNN=upper NN=upper]NNN NN";
sExpected = "TUESDAY TUE";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "[NatNum12 MMMM=lower MMM=lower MMMMM=lower]MMMM MMM MMMMM";
sExpected = "january jan j";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "[NatNum12 DDDD=lower DDD=lower]DDDD DDD";
sExpected = "tuesday tue";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "[NatNum12 NNN=lower NN=lower]NNN NN";
sExpected = "tuesday tue";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
}
{ // tdf#130193 tdf#130140 Native Number Formats mapping for Chinese (Traditional), Japanese, Korean
// -- Traditional Chinese: DBNum1 -> NatNum4, DBNum2 -> NatNum5, DBnum3 -> NatNum3
// DBNum1 -> NatNum4: Chinese lower case text for 123456789
// 一億二千三百四十五萬六千七百八十九
sExpected = u"\u4e00\u5104\u4e8c\u5343\u4e09\u767e\u56db\u5341\u4e94\u842c\u516d\u5343"
u"\u4e03\u767e\u516b\u5341\u4e5d "_ustr;
sCode = "[NatNum4][$-0404]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
sCode = "[DBNum1][$-0404]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
// DBNum2 -> NatNum5: Chinese upper case text
// 壹億貳仟參佰肆拾伍萬陸仟柒佰捌拾玖
sExpected = u"\u58f9\u5104\u8cb3\u4edf\u53c3\u4f70\u8086\u62fe\u4f0d\u842c\u9678\u4edf"
u"\u67d2\u4f70\u634c\u62fe\u7396 "_ustr;
sCode = "[NatNum5][$-0404]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
sCode = "[DBNum2][$-0404]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
// DBNum3 -> NatNum3: fullwidth text
//
sExpected = u"\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19 "_ustr;
sCode = "[NatNum3][$-0404]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
sCode = "[DBNum3][$-0404]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
// -- Japanese: DBNum1 -> NatNum4, DBNum2 -> NatNum5, DBnum3 -> NatNum3
// DBNum1 -> NatNum4: Japanese modern long Kanji text for 123456789
// 一億二千三百四十五万六千七百八十九
sExpected = u"\u4e00\u5104\u4e8c\u5343\u4e09\u767e\u56db\u5341\u4e94\u4e07\u516d\u5343"
u"\u4e03\u767e\u516b\u5341\u4e5d "_ustr;
sCode = "[NatNum4][$-0411]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
sCode = "[DBNum1][$-0411]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
// DBNum2 -> NatNum5: traditional long Kanji text
// 壱億弐阡参百四拾伍萬六阡七百八拾九
sExpected = u"\u58f1\u5104\u5f10\u9621\u53c2\u767e\u56db\u62fe\u4f0d\u842c\u516d\u9621"
u"\u4e03\u767e\u516b\u62fe\u4e5d "_ustr;
sCode = "[NatNum5][$-0411]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
sCode = "[DBNum2][$-0411]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
// DBNum3 -> NatNum3: fullwidth Arabic digits
//
sExpected = u"\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19 "_ustr;
sCode = "[NatNum3][$-0411]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
sCode = "[DBNum3][$-0411]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
// -- Korean: DBNum1 -> NatNum4, DBNum2 -> NatNum5, DBNum3 -> NatNum6, DBNum4 -> NatNum10
// DBNum1 -> NatNum4: Korean lower case characters
// 一億二千三百四十五万六千七百八十九
sExpected = u"\u4e00\u5104\u4e8c\u5343\u4e09\u767e\u56db\u5341\u4e94\u4e07\u516d\u5343\u4e03\u767e\u516b\u5341\u4e5d "_ustr;
sCode = "[NatNum4][$-0412]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
sCode = "[DBNum1][$-0412]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
// DBNum2 -> NatNum5: Korean upper case characters
// 壹億貳阡參佰四拾伍萬六阡七佰八拾九
sExpected = u"\u58f9\u5104\u8cb3\u9621\u53c3\u4f70\u56db\u62fe\u4f0d\u842c\u516d\u9621\u4e03\u4f70\u516b\u62fe\u4e5d "_ustr;
sCode = "[NatNum5][$-0412]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
sCode = "[DBNum2][$-0412]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
// DBNum3 -> NatNum6: fullwidth Arabic digits
// 1억2천3백4십5만6천7백8십9
sExpected = u"\uff11\uc5b5\uff12\ucc9c\uff13\ubc31\uff14\uc2ed\uff15\ub9cc\uff16\ucc9c\uff17\ubc31\uff18\uc2ed\uff19 "_ustr;
sCode = "[NatNum6][$-0412]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
sCode = "[DBNum3][$-0412]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
// DBNum4 -> NatNum10: Hangul characters
// 일억이천삼백사십오만육천칠백팔십구
sExpected = u"\uc77c\uc5b5\uc774\ucc9c\uc0bc\ubc31\uc0ac\uc2ed\uc624\ub9cc\uc721\ucc9c\uce60\ubc31\ud314\uc2ed\uad6c "_ustr;
sCode = "[NatNum10][$-0412]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
sCode = "[DBNum4][$-0412]General\\ ";
checkPreviewString(aFormatter, sCode, 123456789, eLang, sExpected);
}
{ // tdf#105968 engineering format with value rounded up to next magnitude
sCode = "##0.00E+00";
sExpected = "100.00E+00";
checkPreviewString(aFormatter, sCode, 99.995, eLang, sExpected);
// test '1'=='1' assumption
checkPreviewString(aFormatter, sCode, 100.0, eLang, sExpected);
sExpected = "199.99E+00";
checkPreviewString(aFormatter, sCode, 199.99, eLang, sExpected);
sExpected = "1.00E+03";
checkPreviewString(aFormatter, sCode, 1000.0, eLang, sExpected);
// and another just "normally" rounded value
sExpected = "894.55E-06";
checkPreviewString(aFormatter, sCode, 0.000894549, eLang, sExpected);
// not expecting rounding into another magnitude
sExpected = "999.99E-06";
checkPreviewString(aFormatter, sCode, 0.000999991, eLang, sExpected);
// expecting rounding into another magnitude
sExpected = "1.00E-03";
checkPreviewString(aFormatter, sCode, 0.000999999, eLang, sExpected);
// Now the same all negative values.
sExpected = "-100.00E+00";
checkPreviewString(aFormatter, sCode, -99.995, eLang, sExpected);
checkPreviewString(aFormatter, sCode, -100.0, eLang, sExpected);
sExpected = "-199.99E+00";
checkPreviewString(aFormatter, sCode, -199.99, eLang, sExpected);
sExpected = "-1.00E+03";
checkPreviewString(aFormatter, sCode, -1000.0, eLang, sExpected);
sExpected = "-894.55E-06";
checkPreviewString(aFormatter, sCode, -0.000894549, eLang, sExpected);
sExpected = "-999.99E-06";
checkPreviewString(aFormatter, sCode, -0.000999991, eLang, sExpected);
sExpected = "-1.00E-03";
checkPreviewString(aFormatter, sCode, -0.000999999, eLang, sExpected);
}
{ // tdf#112933 one decimal seconds fraction
sCode = "MM:SS.0";
sExpected = "23:53.6";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
// Two decimals.
sCode = "MM:SS.00";
sExpected = "23:53.61";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
// Three decimals.
sCode = "MM:SS.000";
sExpected = "23:53.605";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
// Same with date+time.
sCode = "YYYY-MM-DD MM:SS.0";
sExpected = "1900-01-02 23:53.6";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "YYYY-MM-DD MM:SS.00";
sExpected = "1900-01-02 23:53.61";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "YYYY-MM-DD MM:SS.000";
sExpected = "1900-01-02 23:53.605";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
}
{ // tdf#150028 decimals of seconds fraction without truncate on overflow
sCode = "[SS]";
sExpected = "271434";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
// One decimal.
sCode = "[SS].0";
sExpected = "271433.6";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
// Two decimals.
sCode = "[SS].00";
sExpected = "271433.61";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
// Three decimals.
sCode = "[SS].000";
sExpected = "271433.605";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
}
{ // tdf#156449 Use '?' in exponent of scientific number
sCode = "0.00E+?0";
sExpected = OUString::Concat( u"3.14E+"_ustr ) + sBlankDigit + u"0"_ustr; // before change it was "3.14E+00"
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
// There should be at least one '0' in exponent
sCode = "0.00E+??";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
}
{ // tdf#33689 use English NfKeywords in non-English language
eLang = LANGUAGE_DUTCH;
sExpected = "Dutch: 1900/01/02 03:23:53";
sCode = "\"Dutch:\" JJJJ/MM/DD UU:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "\"Dutch: \"YYYY/MM/DD HH:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
eLang = LANGUAGE_GERMAN;
sExpected = "German: 1900/01/02 03:23:53";
sCode = "\"German: \"JJJJ/MM/TT HH:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "\"German: \"YYYY/MM/DD HH:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
eLang = LANGUAGE_FRENCH;
sExpected = "French: 1900/01/02 03:23:53";
sCode = "\"French: \"AAAA/MM/JJ HH:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "\"French: \"YYYY/MM/DD HH:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
eLang = LANGUAGE_ITALIAN;
sExpected = "Italian: 1900/01/02 03:23:53";
sCode = "\"Italian: \"AAAA/MM/GG HH:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "\"Italian: \"YYYY/MM/DD HH:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
eLang = LANGUAGE_PORTUGUESE;
sExpected = "Portuguese: 1900/01/02 03:23:53";
sCode = "\"Portuguese: \"AAAA/MM/DD HH:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "\"Portuguese: \"YYYY/MM/DD HH:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
eLang = LANGUAGE_SPANISH_MODERN;
sExpected = "Spanish: 1900/01/02 03:23:53";
sCode = "\"Spanish: \"AAAA/MM/DD HH:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "\"Spanish: \"YYYY/MM/DD HH:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
eLang = LANGUAGE_DANISH;
sExpected = "Danish: 1900/01/02 03:23:53";
sCode = "\"Danish: \"YYYY/MM/DD TT:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "\"Danish: \"YYYY/MM/DD HH:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
eLang = LANGUAGE_FINNISH;
sExpected = "Finnish: 1900/01/02 03:23:53";
sCode = "\"Finnish: \"VVVV/KK/PP TT:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
sCode = "\"Finnish: \"YYYY/MM/DD HH:MM:SS";
checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
}
{ // tdf#117819 wrong separator positions when displaying integers with
// more decimals than rtl::math::doubleToUString delivers.
sCode = "#,##0.00000000000000000000";
sExpected = "117,669,030,460,994.00000000000000000000";
checkPreviewString(aFormatter, sCode, 117669030460994.0, LANGUAGE_ENGLISH_US, sExpected);
}
{ // tdf#117575 treat thousand separator with '?' in integer part
sCode = "\"Value= \"?,??0.00";
sExpected = OUString::Concat( u"Value= "_ustr ) + sBlankDigit + u" "_ustr + sBlankDigit + sBlankDigit + u"3.14"_ustr;
checkPreviewString(aFormatter, sCode, M_PI, LANGUAGE_ENGLISH_US, sExpected);
sExpected = OUString::Concat( u"Value= "_ustr ) + sBlankDigit + u" "_ustr + sBlankDigit + u"12.00"_ustr;
checkPreviewString(aFormatter, sCode, 12, LANGUAGE_ENGLISH_US, sExpected);
sExpected = OUString::Concat( u"Value= "_ustr ) + sBlankDigit + u" 123.00"_ustr;
checkPreviewString(aFormatter, sCode, 123, LANGUAGE_ENGLISH_US, sExpected);
sExpected = "Value= 1,234.00";
checkPreviewString(aFormatter, sCode, 1234, LANGUAGE_ENGLISH_US, sExpected);
sExpected = "Value= 12,345.00";
checkPreviewString(aFormatter, sCode, 12345, LANGUAGE_ENGLISH_US, sExpected);
}
{ // tdf#159930 no digit in integer part
sCode = "+.000;-.000";
sExpected = "+3.142"; // without the patch is would display "3+.142"
checkPreviewString(aFormatter, sCode, M_PI, LANGUAGE_ENGLISH_US, sExpected);
}
}
void Test::testNfEnglishKeywordsIntegrity()
{
SvNumberFormatter aFormatter(m_xContext, LANGUAGE_ENGLISH_US);
const NfKeywordTable& rEnglishKeywords = SvNumberFormatter::GetEnglishKeywords();
const NfKeywordTable& sKeywords = aFormatter.GetKeywords(0);
CPPUNIT_ASSERT_EQUAL( size_t(NF_KEYWORD_ENTRIES_COUNT), rEnglishKeywords.size() );
for (size_t i = 0; i < size_t(NF_KEYWORD_ENTRIES_COUNT); ++i)
{
CPPUNIT_ASSERT_EQUAL( sKeywords[i], rEnglishKeywords[i] );
}
// Check the order of sEnglishKeyword
CPPUNIT_ASSERT_EQUAL( u"E"_ustr, rEnglishKeywords[NF_KEY_E] );
CPPUNIT_ASSERT_EQUAL( u"AM/PM"_ustr, rEnglishKeywords[NF_KEY_AMPM] );
CPPUNIT_ASSERT_EQUAL( u"A/P"_ustr, rEnglishKeywords[NF_KEY_AP] );
CPPUNIT_ASSERT_EQUAL( u"M"_ustr, rEnglishKeywords[NF_KEY_MI] );
CPPUNIT_ASSERT_EQUAL( u"MM"_ustr, rEnglishKeywords[NF_KEY_MMI] );
CPPUNIT_ASSERT_EQUAL( u"M"_ustr, rEnglishKeywords[NF_KEY_M] );
CPPUNIT_ASSERT_EQUAL( u"MM"_ustr, rEnglishKeywords[NF_KEY_MM] );
CPPUNIT_ASSERT_EQUAL( u"MMM"_ustr, rEnglishKeywords[NF_KEY_MMM] );
CPPUNIT_ASSERT_EQUAL( u"MMMM"_ustr, rEnglishKeywords[NF_KEY_MMMM] );
CPPUNIT_ASSERT_EQUAL( u"H"_ustr, rEnglishKeywords[NF_KEY_H] );
CPPUNIT_ASSERT_EQUAL( u"HH"_ustr, rEnglishKeywords[NF_KEY_HH] );
CPPUNIT_ASSERT_EQUAL( u"S"_ustr, rEnglishKeywords[NF_KEY_S] );
CPPUNIT_ASSERT_EQUAL( u"SS"_ustr, rEnglishKeywords[NF_KEY_SS] );
CPPUNIT_ASSERT_EQUAL( u"Q"_ustr, rEnglishKeywords[NF_KEY_Q] );
CPPUNIT_ASSERT_EQUAL( u"QQ"_ustr, rEnglishKeywords[NF_KEY_QQ] );
CPPUNIT_ASSERT_EQUAL( u"D"_ustr, rEnglishKeywords[NF_KEY_D] );
CPPUNIT_ASSERT_EQUAL( u"DD"_ustr, rEnglishKeywords[NF_KEY_DD] );
CPPUNIT_ASSERT_EQUAL( u"DDD"_ustr, rEnglishKeywords[NF_KEY_DDD] );
CPPUNIT_ASSERT_EQUAL( u"DDDD"_ustr, rEnglishKeywords[NF_KEY_DDDD] );
CPPUNIT_ASSERT_EQUAL( u"YY"_ustr, rEnglishKeywords[NF_KEY_YY] );
CPPUNIT_ASSERT_EQUAL( u"YYYY"_ustr, rEnglishKeywords[NF_KEY_YYYY] );
CPPUNIT_ASSERT_EQUAL( u"NN"_ustr, rEnglishKeywords[NF_KEY_NN] );
CPPUNIT_ASSERT_EQUAL( u"NNNN"_ustr, rEnglishKeywords[NF_KEY_NNNN] );
CPPUNIT_ASSERT_EQUAL( u"CCC"_ustr, rEnglishKeywords[NF_KEY_CCC] );
CPPUNIT_ASSERT_EQUAL( u"GENERAL"_ustr, rEnglishKeywords[NF_KEY_GENERAL] );
CPPUNIT_ASSERT_EQUAL( u"NNN"_ustr, rEnglishKeywords[NF_KEY_NNN] );
CPPUNIT_ASSERT_EQUAL( u"WW"_ustr, rEnglishKeywords[NF_KEY_WW] );
CPPUNIT_ASSERT_EQUAL( u"MMMMM"_ustr, rEnglishKeywords[NF_KEY_MMMMM] );
CPPUNIT_ASSERT_EQUAL( u"TRUE"_ustr, rEnglishKeywords[NF_KEY_TRUE] );
CPPUNIT_ASSERT_EQUAL( u"FALSE"_ustr, rEnglishKeywords[NF_KEY_FALSE] );
CPPUNIT_ASSERT_EQUAL( u"BOOLEAN"_ustr, rEnglishKeywords[NF_KEY_BOOLEAN] );
CPPUNIT_ASSERT_EQUAL( u"COLOR"_ustr, rEnglishKeywords[NF_KEY_COLOR] );
CPPUNIT_ASSERT_EQUAL( u"BLACK"_ustr, rEnglishKeywords[NF_KEY_BLACK] );
CPPUNIT_ASSERT_EQUAL( u"BLUE"_ustr, rEnglishKeywords[NF_KEY_BLUE] );
CPPUNIT_ASSERT_EQUAL( u"GREEN"_ustr, rEnglishKeywords[NF_KEY_GREEN] );
CPPUNIT_ASSERT_EQUAL( u"CYAN"_ustr, rEnglishKeywords[NF_KEY_CYAN] );
CPPUNIT_ASSERT_EQUAL( u"RED"_ustr, rEnglishKeywords[NF_KEY_RED] );
CPPUNIT_ASSERT_EQUAL( u"MAGENTA"_ustr, rEnglishKeywords[NF_KEY_MAGENTA] );
CPPUNIT_ASSERT_EQUAL( u"BROWN"_ustr, rEnglishKeywords[NF_KEY_BROWN] );
CPPUNIT_ASSERT_EQUAL( u"GREY"_ustr, rEnglishKeywords[NF_KEY_GREY] );
CPPUNIT_ASSERT_EQUAL( u"YELLOW"_ustr, rEnglishKeywords[NF_KEY_YELLOW] );
CPPUNIT_ASSERT_EQUAL( u"WHITE"_ustr, rEnglishKeywords[NF_KEY_WHITE] );
CPPUNIT_ASSERT_EQUAL( u"AAA"_ustr, rEnglishKeywords[NF_KEY_AAA]);
CPPUNIT_ASSERT_EQUAL( u"AAAA"_ustr, rEnglishKeywords[NF_KEY_AAAA] );
CPPUNIT_ASSERT_EQUAL( u"E"_ustr, rEnglishKeywords[NF_KEY_EC] );
CPPUNIT_ASSERT_EQUAL( u"EE"_ustr, rEnglishKeywords[NF_KEY_EEC] );
CPPUNIT_ASSERT_EQUAL( u"G"_ustr, rEnglishKeywords[NF_KEY_G] );
CPPUNIT_ASSERT_EQUAL( u"GG"_ustr, rEnglishKeywords[NF_KEY_GG] );
CPPUNIT_ASSERT_EQUAL( u"GGG"_ustr, rEnglishKeywords[NF_KEY_GGG] );
CPPUNIT_ASSERT_EQUAL( u"R"_ustr, rEnglishKeywords[NF_KEY_R] );
CPPUNIT_ASSERT_EQUAL( u"RR"_ustr, rEnglishKeywords[NF_KEY_RR] );
CPPUNIT_ASSERT_EQUAL( u"t"_ustr, rEnglishKeywords[NF_KEY_THAI_T] );
}
void Test::testStandardColorIntegrity()
{
SvNumberFormatter aFormatter(m_xContext, LANGUAGE_ENGLISH_US);
const ::std::vector<Color> & rStandardColors = SvNumberFormatter::GetStandardColors();
const size_t nMaxDefaultColors = SvNumberFormatter::GetMaxDefaultColors();
CPPUNIT_ASSERT_EQUAL( size_t(NF_KEY_LASTCOLOR) - size_t(NF_KEY_FIRSTCOLOR) + 1, nMaxDefaultColors );
CPPUNIT_ASSERT_EQUAL( nMaxDefaultColors, rStandardColors.size() );
// Colors must follow same order as in sEnglishKeyword
CPPUNIT_ASSERT_EQUAL( COL_BLACK, rStandardColors[0] );
CPPUNIT_ASSERT_EQUAL( COL_LIGHTBLUE, rStandardColors[1] );
CPPUNIT_ASSERT_EQUAL( COL_LIGHTGREEN, rStandardColors[2] );
CPPUNIT_ASSERT_EQUAL( COL_LIGHTCYAN, rStandardColors[3] );
CPPUNIT_ASSERT_EQUAL( COL_LIGHTRED, rStandardColors[4] );
CPPUNIT_ASSERT_EQUAL( COL_LIGHTMAGENTA, rStandardColors[5] );
CPPUNIT_ASSERT_EQUAL( COL_BROWN, rStandardColors[6] );
CPPUNIT_ASSERT_EQUAL( COL_GRAY, rStandardColors[7] );
CPPUNIT_ASSERT_EQUAL( COL_YELLOW, rStandardColors[8] );
CPPUNIT_ASSERT_EQUAL( COL_WHITE, rStandardColors[9] );
}
void Test::testColorNamesConversion()
{
SvNumberFormatter aFormatter(m_xContext, LANGUAGE_GERMAN);
const NfKeywordTable& rEnglishKeywords = SvNumberFormatter::GetEnglishKeywords();
const NfKeywordTable& rKeywords = aFormatter.GetKeywords(0);
// Holding a reference to the NfKeywordTable doesn't help if we switch
// locales internally, so copy the relevant parts in advance.
std::vector<OUString> aGermanKeywords(NF_KEYWORD_ENTRIES_COUNT);
for (size_t i = NF_KEY_COLOR; i <= NF_KEY_WHITE; ++i)
aGermanKeywords[i] = rKeywords[i];
// Check that we actually have German and English keywords.
CPPUNIT_ASSERT_EQUAL( u"FARBE"_ustr, aGermanKeywords[NF_KEY_COLOR]);
CPPUNIT_ASSERT_EQUAL( u"COLOR"_ustr, rEnglishKeywords[NF_KEY_COLOR]);
// Test each color conversion.
// [FARBE1] -> [COLOR1] can't be tested because we have no color table link
// set, so the scanner returns nCheckPos error.
sal_Int32 nCheckPos;
SvNumFormatType nType;
sal_uInt32 nKey;
OUString aFormatCode;
for (size_t i = NF_KEY_BLACK; i <= NF_KEY_WHITE; ++i)
{
aFormatCode = "[" + aGermanKeywords[i] + "]0";
aFormatter.PutandConvertEntry( aFormatCode, nCheckPos, nType, nKey, LANGUAGE_GERMAN, LANGUAGE_ENGLISH_US, false);
CPPUNIT_ASSERT_EQUAL_MESSAGE("CheckPos should be 0.", sal_Int32(0), nCheckPos);
CPPUNIT_ASSERT_EQUAL_MESSAGE("Type should be NUMBER.", SvNumFormatType::NUMBER, nType);
CPPUNIT_ASSERT_EQUAL( OUString("[" + rEnglishKeywords[i] + "]0"), aFormatCode);
}
}
void Test::testExcelExportFormats()
{
// Create a formatter with "system" locale other than the specific formats'
// locale, and different from the en-US export locale.
SvNumberFormatter aFormatter( m_xContext, LANGUAGE_ENGLISH_UK);
OUString aCode;
sal_Int32 nCheckPos;
SvNumFormatType eType;
sal_uInt32 nKey1, nKey2;
aCode = "00.00";
aFormatter.PutandConvertEntry( aCode, nCheckPos, eType, nKey1,
LANGUAGE_ENGLISH_US, LANGUAGE_ENGLISH_SAFRICA, false);
CPPUNIT_ASSERT_EQUAL_MESSAGE("CheckPos should be 0.", sal_Int32(0), nCheckPos);
CPPUNIT_ASSERT_MESSAGE("Key should be greater than system locale's keys.",
nKey1 > SV_COUNTRY_LANGUAGE_OFFSET);
aCode = "[$R-1C09] #,##0.0;[$R-1C09]-#,##0.0";
aFormatter.PutandConvertEntry( aCode, nCheckPos, eType, nKey2,
LANGUAGE_ENGLISH_US, LANGUAGE_ENGLISH_SAFRICA, false);
CPPUNIT_ASSERT_EQUAL_MESSAGE("CheckPos should be 0.", sal_Int32(0), nCheckPos);
CPPUNIT_ASSERT_MESSAGE("Key should be greater than system locale's keys.",
nKey2 > SV_COUNTRY_LANGUAGE_OFFSET);
// The export formatter.
SvNumberFormatter aTempFormatter( m_xContext, LANGUAGE_ENGLISH_US);
NfKeywordTable aKeywords;
aTempFormatter.FillKeywordTableForExcel( aKeywords);
aCode = aFormatter.GetFormatStringForExcel( nKey1, aKeywords, aTempFormatter);
// Test that LCID is prepended.
CPPUNIT_ASSERT_EQUAL( u"[$-1C09]00.00"_ustr, aCode);
aCode = aFormatter.GetFormatStringForExcel( nKey2, aKeywords, aTempFormatter);
// Test that LCID is not prepended. Note that literal characters are escaped.
CPPUNIT_ASSERT_EQUAL( u"[$R-1C09]\\ #,##0.0;[$R-1C09]\\-#,##0.0"_ustr, aCode);
}
CPPUNIT_TEST_FIXTURE(Test, testLanguageNone)
{
SvNumberFormatter aFormatter(m_xContext, LANGUAGE_ENGLISH_US);
NfKeywordTable keywords;
aFormatter.FillKeywordTableForExcel(keywords);
OUString code(u"TT.MM.JJJJ"_ustr);
sal_uInt32 nKey = aFormatter.GetEntryKey(code, LANGUAGE_GERMAN);
CPPUNIT_ASSERT(nKey != NUMBERFORMAT_ENTRY_NOT_FOUND);
SvNumberformat const*const pFormat = aFormatter.GetEntry(nKey);
LocaleDataWrapper ldw(m_xContext, LanguageTag(pFormat->GetLanguage()));
CPPUNIT_ASSERT_EQUAL(u"dd.mm.yyyy"_ustr, pFormat->GetMappedFormatstring(keywords, ldw));
}
CPPUNIT_TEST_FIXTURE(Test, testTdf160306)
{
// Check some cases, where the output of ROUND and of number formatter differed
SvNumberFormatter aFormatter(m_xContext, LANGUAGE_ENGLISH_US);
sal_uInt32 format = aFormatter.GetEntryKey(u"0.00", LANGUAGE_ENGLISH_US);
CPPUNIT_ASSERT(format != NUMBERFORMAT_ENTRY_NOT_FOUND);
OUString output;
const Color* color;
aFormatter.GetOutputString(2697.0649999999996, format, output, &color);
// Without the fix in place, this would fail with
// - Expected: 2697.07
// - Actual : 2697.06
CPPUNIT_ASSERT_EQUAL(u"2697.07"_ustr, output);
aFormatter.GetOutputString(57.374999999999993, format, output, &color);
// Without the fix in place, this would fail with
// - Expected: 57.38
// - Actual : 57.37
CPPUNIT_ASSERT_EQUAL(u"57.38"_ustr, output);
}
CPPUNIT_TEST_SUITE_REGISTRATION(Test);
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */