1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <sal/types.h>
11 #include <config_libnumbertext.h>
12 #include <cppunit/TestAssert.h>
13 #include <cppunit/TestFixture.h>
14 #include <cppunit/extensions/HelperMacros.h>
15 #include <cppunit/plugin/TestPlugIn.h>
16 
17 #include <sal/config.h>
18 
19 #include <cppuhelper/bootstrap.hxx>
20 #include <comphelper/processfactory.hxx>
21 
22 #include <com/sun/star/lang/XComponent.hpp>
23 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
24 #include <com/sun/star/lang/XMultiComponentFactory.hpp>
25 #include <com/sun/star/uno/XComponentContext.hpp>
26 
27 #include <i18nlangtag/lang.h>
28 
29 #include <math.h>
30 
31 #include <svl/zforlist.hxx>
32 #include <svl/zformat.hxx>
33 #include <svl/sharedstringpool.hxx>
34 #include <svl/sharedstring.hxx>
35 #include <tools/color.hxx>
36 #include <unotools/syslocale.hxx>
37 
38 #include <memory>
39 #include <unicode/timezone.h>
40 
41 using namespace ::com::sun::star;
42 using namespace svl;
43 
operator <<(std::ostream & rStrm,const Color & rColor)44 static std::ostream& operator<<(std::ostream& rStrm, const Color& rColor)
45 {
46     rStrm << "Color: R:" << static_cast<int>(rColor.GetRed()) << " G:" << static_cast<int>(rColor.GetGreen()) << " B: " << static_cast<int>(rColor.GetBlue());
47     return rStrm;
48 }
49 
50 namespace {
51 
52 class Test : public CppUnit::TestFixture {
53 public:
54     Test();
55     virtual ~Test() override;
56 
57     virtual void tearDown() override;
58 
59     void testNumberFormat();
60     void testSharedString();
61     void testSharedStringPool();
62     void testSharedStringPoolPurge();
63     void testFdo60915();
64     void testI116701();
65     void testTdf103060();
66     void testDateInput();
67     void testIsNumberFormat();
68     void testIsNumberFormatSpecific();
69     void testUserDefinedNumberFormats();
70     void testNfEnglishKeywordsIntegrity();
71     void testStandardColorIntegrity();
72     void testColorNamesConversion();
73     void testExcelExportFormats();
74 
75     CPPUNIT_TEST_SUITE(Test);
76     CPPUNIT_TEST(testNumberFormat);
77     CPPUNIT_TEST(testSharedString);
78     CPPUNIT_TEST(testSharedStringPool);
79     CPPUNIT_TEST(testSharedStringPoolPurge);
80     CPPUNIT_TEST(testFdo60915);
81     CPPUNIT_TEST(testI116701);
82     CPPUNIT_TEST(testTdf103060);
83     CPPUNIT_TEST(testDateInput);
84     CPPUNIT_TEST(testIsNumberFormat);
85     CPPUNIT_TEST(testIsNumberFormatSpecific);
86     CPPUNIT_TEST(testUserDefinedNumberFormats);
87     CPPUNIT_TEST(testNfEnglishKeywordsIntegrity);
88     CPPUNIT_TEST(testStandardColorIntegrity);
89     CPPUNIT_TEST(testColorNamesConversion);
90     CPPUNIT_TEST(testExcelExportFormats);
91     CPPUNIT_TEST_SUITE_END();
92 
93 private:
94     uno::Reference< uno::XComponentContext > m_xContext;
95     void checkPreviewString(SvNumberFormatter& aFormatter,
96                             const OUString& sCode,
97                             double fPreviewNumber,
98                             LanguageType eLang,
99                             OUString const & sExpected);
100     void checkDateInput( SvNumberFormatter& rFormatter, const char* pTimezone, const char* pIsoDate );
101     std::unique_ptr<icu::TimeZone> m_pDefaultTimeZone;
102 };
103 
Test()104 Test::Test()
105 {
106     m_xContext = cppu::defaultBootstrap_InitialComponentContext();
107 
108     uno::Reference<lang::XMultiComponentFactory> xFactory(m_xContext->getServiceManager());
109     uno::Reference<lang::XMultiServiceFactory> xSM(xFactory, uno::UNO_QUERY_THROW);
110 
111     //Without this we're crashing because callees are using
112     //getProcessServiceFactory.  In general those should be removed in favour
113     //of retaining references to the root ServiceFactory as it's passed around
114     comphelper::setProcessServiceFactory(xSM);
115     m_pDefaultTimeZone.reset(icu::TimeZone::createDefault());
116 }
117 
tearDown()118 void Test::tearDown()
119 {
120     icu::TimeZone::setDefault(*m_pDefaultTimeZone);
121 }
122 
~Test()123 Test::~Test()
124 {
125     uno::Reference< lang::XComponent >(m_xContext, uno::UNO_QUERY_THROW)->dispose();
126 }
127 
testNumberFormat()128 void Test::testNumberFormat()
129 {
130     LanguageType eLang = LANGUAGE_ENGLISH_US;
131 
132     const char* pNumber[] = {
133         "General",
134         "0",
135         "0.00",
136         "#,##0",
137         "#,##0.00",
138         "#,###.00",
139         nullptr
140     };
141 
142     const char* pScientific[] = {
143         "0.00E+000",
144         "0.00E+00",
145         nullptr
146     };
147 
148     const char* pPercent[] = {
149         "0%",
150         "0.00%",
151         nullptr
152     };
153 
154     const char* pFraction[] = {
155         "# \?/\?",
156         "# \?\?/\?\?",
157         nullptr
158     };
159 
160 // Following aren't in range of NF_FRACTION_START and NF_FRACTION_END
161 // see enum NfIndexTableOffset in svl/inc/svl/zforlist.hxx
162     const char* pFractionExt[] = {
163         "# \?\?\?/\?\?\?",
164         "# \?/2",
165         "# \?/4",
166         "# \?/8",
167         "# \?\?/16",
168         "# \?\?/10",
169         "# \?\?/100",
170         nullptr
171     };
172 
173     const char* pCurrency[] = {
174         "$#,##0;-$#,##0",
175         "$#,##0.00;-$#,##0.00",
176         "$#,##0;[RED]-$#,##0",
177         "$#,##0.00;[RED]-$#,##0.00",
178         "#,##0.00 CCC",
179         "$#,##0.--;[RED]-$#,##0.--",
180         nullptr
181     };
182 
183     const char* pDate[] = {
184         "M/D/YY",
185         "NNNNMMMM DD, YYYY",
186         "MM/DD/YY",
187         "MM/DD/YYYY",
188         "MMM D, YY",
189         "MMM D, YYYY",
190         "D. MMM. YYYY",
191         "MMMM D, YYYY",
192         "D. MMMM YYYY",
193         "NN, MMM D, YY",
194         "NN DD/MMM YY",
195         "NN, MMMM D, YYYY",
196         "NNNNMMMM D, YYYY",
197         "MM-DD",
198         "YY-MM-DD",
199         "YYYY-MM-DD",
200         "MM/YY",
201         "MMM DD",
202         "MMMM",
203         "QQ YY",
204         "WW",
205         nullptr
206     };
207 
208     const char* pTime[] = {
209         "HH:MM",
210         "HH:MM:SS",
211         "HH:MM AM/PM",
212         "HH:MM:SS AM/PM",
213         "[HH]:MM:SS",
214         "MM:SS.00",
215         "[HH]:MM:SS.00",
216         nullptr
217     };
218 
219     const char* pDateTime[] = {
220         "MM/DD/YY HH:MM AM/PM",
221         "MM/DD/YYYY HH:MM:SS",
222         nullptr
223     };
224 
225     const char* pBoolean[] = {
226         "BOOLEAN",
227         nullptr
228     };
229 
230     const char* pText[] = {
231         "@",
232         nullptr
233     };
234 
235     struct {
236         NfIndexTableOffset const eStart;
237         NfIndexTableOffset const eEnd;
238         size_t const nSize;
239         const char** pCodes;
240     } aTests[] = {
241         { NF_NUMBER_START, NF_NUMBER_END, 6, pNumber },
242         { NF_SCIENTIFIC_START, NF_SCIENTIFIC_END, 2, pScientific },
243         { NF_PERCENT_START, NF_PERCENT_END, 2, pPercent },
244         { NF_FRACTION_START, NF_FRACTION_END, 2, pFraction },
245         { NF_FRACTION_3D, NF_FRACTION_100, 7, pFractionExt },
246         { NF_CURRENCY_START, NF_CURRENCY_END, 6, pCurrency },
247         { NF_DATE_START, NF_DATE_END, 21, pDate },
248         { NF_TIME_START, NF_TIME_END, 7, pTime },
249         { NF_DATETIME_START, NF_DATETIME_END, 2, pDateTime },
250         { NF_BOOLEAN, NF_BOOLEAN, 1, pBoolean },
251         { NF_TEXT, NF_TEXT, 1, pText }
252     };
253 
254     SvNumberFormatter aFormatter(m_xContext, eLang);
255 
256     for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
257     {
258         size_t nStart = aTests[i].eStart;
259         size_t nEnd = aTests[i].eEnd;
260 
261         CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected number of formats for this category.",
262                                aTests[i].nSize, (nEnd - nStart + 1));
263 
264         for (size_t j = nStart; j <= nEnd; ++j)
265         {
266             sal_uInt32 nIndex =
267                 aFormatter.GetFormatIndex(static_cast<NfIndexTableOffset>(j));
268             const SvNumberformat* p = aFormatter.GetEntry(nIndex);
269 
270             CPPUNIT_ASSERT_MESSAGE("Number format entry is expected, but doesn't exist.", p);
271             OUString aCode = p->GetFormatstring();
272             CPPUNIT_ASSERT_EQUAL( aCode.toUtf8() , OString( aTests[i].pCodes[j-nStart] ) );
273         }
274     }
275 
276     sal_Int32 nPos;
277     SvNumFormatType nType = SvNumFormatType::DEFINED;
278     sal_uInt32 nKey;
279     OUString aCode;
280     // Thai date format (implicit locale).
281     aCode = "[$-1070000]d/mm/yyyy;@";
282     if (!aFormatter.PutEntry(aCode, nPos, nType, nKey))
283     {
284         CPPUNIT_ASSERT_MESSAGE("failed to insert format code '[$-1070000]d/mm/yyyy;@'", false);
285     }
286 
287     // Thai date format (explicit locale)
288     aCode = "[$-107041E]d/mm/yyyy;@";
289     if (!aFormatter.PutEntry(aCode, nPos, nType, nKey))
290     {
291         CPPUNIT_ASSERT_MESSAGE("failed to insert format code '[$-107041E]d/mm/yyyy;@'", false);
292     }
293 
294     // Thai date format (using buddhist calendar type).
295     aCode = "[~buddhist]D MMMM YYYY";
296     if (!aFormatter.PutEntry(aCode, nPos, nType, nKey))
297     {
298         CPPUNIT_ASSERT_MESSAGE("failed to insert format code '[~buddhist]D MMMM YYYY'", false);
299     }
300 }
301 
testSharedString()302 void Test::testSharedString()
303 {
304     // Use shared string as normal, non-shared string, which is allowed.
305     SharedString aSS1("Test"), aSS2("Test");
306     CPPUNIT_ASSERT_MESSAGE("Equality check should return true.", bool(aSS1 == aSS2));
307     SharedString aSS3("test");
308     CPPUNIT_ASSERT_MESSAGE("Equality check is case sensitive.", aSS1 != aSS3);
309 }
310 
testSharedStringPool()311 void Test::testSharedStringPool()
312 {
313     SvtSysLocale aSysLocale;
314     svl::SharedStringPool aPool(*aSysLocale.GetCharClassPtr());
315 
316     svl::SharedString p1, p2;
317     p1 = aPool.intern("Andy");
318     p2 = aPool.intern("Andy");
319     CPPUNIT_ASSERT_EQUAL(p1.getData(), p2.getData());
320 
321     p2 = aPool.intern("Bruce");
322     CPPUNIT_ASSERT_MESSAGE("They must differ.", p1.getData() != p2.getData());
323 
324     OUString aAndy("Andy");
325     p1 = aPool.intern("Andy");
326     p2 = aPool.intern(aAndy);
327     CPPUNIT_ASSERT_MESSAGE("Identifier shouldn't be NULL.", p1.getData());
328     CPPUNIT_ASSERT_MESSAGE("Identifier shouldn't be NULL.", p2.getData());
329     CPPUNIT_ASSERT_EQUAL(p1.getData(), p2.getData());
330 
331     // Test case insensitive string ID's.
332     OUString const aAndyLower("andy"), aAndyUpper("ANDY");
333     p1 = aPool.intern(aAndy);
334     p2 = aPool.intern(aAndyLower);
335     CPPUNIT_ASSERT_MESSAGE("Failed to intern strings.", p1.getData() && p2.getData());
336     CPPUNIT_ASSERT_MESSAGE("These two ID's should differ.", p1.getData() != p2.getData());
337     CPPUNIT_ASSERT_EQUAL_MESSAGE("These two ID's should be equal.", p2.getDataIgnoreCase(), p1.getDataIgnoreCase());
338     p2 = aPool.intern(aAndyUpper);
339     CPPUNIT_ASSERT_MESSAGE("Failed to intern string.", p2.getData());
340     CPPUNIT_ASSERT_MESSAGE("These two ID's should differ.", p1.getData() != p2.getData());
341     CPPUNIT_ASSERT_EQUAL_MESSAGE("These two ID's should be equal.", p2.getDataIgnoreCase(), p1.getDataIgnoreCase());
342 }
343 
testSharedStringPoolPurge()344 void Test::testSharedStringPoolPurge()
345 {
346     SvtSysLocale aSysLocale;
347     svl::SharedStringPool aPool(*aSysLocale.GetCharClassPtr());
348     aPool.intern("Andy");
349     aPool.intern("andy");
350     aPool.intern("ANDY");
351 
352     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong string count.", static_cast<size_t>(3), aPool.getCount());
353     CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong case insensitive string count.", static_cast<size_t>(1), aPool.getCountIgnoreCase());
354 
355     // Since no string objects referencing the pooled strings exist, purging
356     // the pool should empty it.
357     aPool.purge();
358     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), aPool.getCount());
359     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), aPool.getCountIgnoreCase());
360 
361     // Now, create string objects on the heap.
362     std::unique_ptr<OUString> pStr1(new OUString("Andy"));
363     std::unique_ptr<OUString> pStr2(new OUString("andy"));
364     std::unique_ptr<OUString> pStr3(new OUString("ANDY"));
365     std::unique_ptr<OUString> pStr4(new OUString("Bruce"));
366     aPool.intern(*pStr1);
367     aPool.intern(*pStr2);
368     aPool.intern(*pStr3);
369     aPool.intern(*pStr4);
370 
371     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), aPool.getCount());
372     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPool.getCountIgnoreCase());
373 
374     // This shouldn't purge anything.
375     aPool.purge();
376     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), aPool.getCount());
377     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPool.getCountIgnoreCase());
378 
379     // Delete one heap string object, and purge. That should purge one string.
380     pStr1.reset();
381     aPool.purge();
382     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aPool.getCount());
383     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPool.getCountIgnoreCase());
384 
385     // Ditto...
386     pStr3.reset();
387     aPool.purge();
388     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPool.getCount());
389     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPool.getCountIgnoreCase());
390 
391     // Again.
392     pStr2.reset();
393     aPool.purge();
394     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPool.getCount());
395     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPool.getCountIgnoreCase());
396 
397     // Delete 'Bruce' and purge.
398     pStr4.reset();
399     aPool.purge();
400     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), aPool.getCount());
401     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), aPool.getCountIgnoreCase());
402 }
403 
checkPreviewString(SvNumberFormatter & aFormatter,const OUString & sCode,double fPreviewNumber,LanguageType eLang,OUString const & sExpected)404 void Test::checkPreviewString(SvNumberFormatter& aFormatter,
405                               const OUString& sCode,
406                               double fPreviewNumber,
407                               LanguageType eLang,
408                               OUString const & sExpected)
409 {
410     OUString sStr;
411     Color* pColor = nullptr;
412     Color** ppColor = &pColor;
413     if (!aFormatter.GetPreviewString(sCode, fPreviewNumber, sStr, ppColor, eLang))
414     {
415         OString aMessage = "GetPreviewString( \"" +
416             OUStringToOString( sCode, RTL_TEXTENCODING_ASCII_US ) +
417             "\", " +
418             OString::number( fPreviewNumber ) +
419             ", sStr, ppColor, ";
420         aMessage += OString::number( static_cast<sal_uInt16>(eLang) ) +
421             " ) failed";
422         CPPUNIT_FAIL( aMessage.getStr() );
423     }
424     CPPUNIT_ASSERT_EQUAL(sExpected, sStr);
425 }
426 
testFdo60915()427 void Test::testFdo60915()
428 {
429     LanguageType eLang = LANGUAGE_THAI;
430     OUString sCode, sExpected;
431     double fPreviewNumber = 36486; // equals 1999-11-22 (2542 B.E.)
432     SvNumberFormatter aFormatter(m_xContext, eLang);
433     {
434         sCode = "[~buddhist]D/MM/YYYY";
435         sExpected = "22/11/2542";
436         checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
437     }
438     {
439         sCode = "[~buddhist]D/MM/YY";
440         sExpected = "22/11/42";
441         checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
442     }
443     {
444         sCode = "[NatNum1][$-41E][~buddhist]D/MM/YYYY";
445         sal_Unicode sTemp[] =
446         {
447             0x0E52, 0x0E52, 0x002F,
448             0x0E51, 0x0E51, 0x002F,
449             0x0E52, 0x0E55, 0x0E54, 0x0E52
450         };
451         sExpected = OUString(sTemp, SAL_N_ELEMENTS(sTemp));
452         checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
453     }
454     {
455         sCode = "[NatNum1][$-41E][~buddhist]D/MM/YY";
456         sal_Unicode sTemp[] =
457         {
458             0x0E52, 0x0E52, 0x002F,
459             0x0E51, 0x0E51, 0x002F,
460             0x0E54, 0x0E52
461         };
462         sExpected = OUString(sTemp, SAL_N_ELEMENTS(sTemp));
463         checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
464     }
465 }
466 
467 // https://bz.apache.org/ooo/show_bug.cgi?id=116701
testI116701()468 void Test::testI116701()
469 {
470     LanguageType eLang = LANGUAGE_CHINESE_TRADITIONAL;
471     OUString sCode, sExpected;
472     double fPreviewNumber = 40573; // equals 30/01/2011
473     SvNumberFormatter aFormatter(m_xContext, eLang);
474     // DateFormatskey25 in i18npool/source/localedata/data/zh_TW.xml
475     sal_Unicode CODE1[] =
476     {
477         0x0047, 0x0047, 0x0047, 0x0045, 0x0045, // GGGEE
478         0x0022, 0x5E74, 0x0022,
479         0x004D, // M
480         0x0022, 0x6708, 0x0022,
481         0x0044, // D
482         0x0022, 0x65E5, 0x0022
483     };
484     sCode = OUString(CODE1, SAL_N_ELEMENTS(CODE1));
485     sal_Unicode EXPECTED[] =
486     {
487         0x4E2D, 0x83EF, 0x6C11, 0x570B,
488         0x0031, 0x0030, 0x0030, // 100
489         0x5E74,
490         0x0031, // 1
491         0x6708,
492         0x0033, 0x0030, // 30
493         0x65E5
494     };
495     sExpected = OUString(EXPECTED, SAL_N_ELEMENTS(EXPECTED));
496     checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
497     sal_Unicode CODE2[] =
498     {
499         0x0047, 0x0047, 0x0047, 0x0045, // GGGE
500         0x0022, 0x5E74, 0x0022,
501         0x004D, // M
502         0x0022, 0x6708, 0x0022,
503         0x0044, // D
504         0x0022, 0x65E5, 0x0022
505     };
506     sCode = OUString(CODE2, SAL_N_ELEMENTS(CODE2));
507     checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
508 }
509 
testTdf103060()510 void Test::testTdf103060()
511 {
512     LanguageType eLang = LANGUAGE_JAPANESE;
513     OUString sCode, sExpected;
514     double fPreviewNumber = 42655; // equals 2016-10-12
515     SvNumberFormatter aFormatter(m_xContext, eLang);
516     sCode = "G";
517     sExpected = "H"; // Heisei era
518     checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
519     sCode = "GG";
520     const sal_Unicode EXPECTED_G2[] = {0x5E73};
521     sExpected = OUString(EXPECTED_G2, SAL_N_ELEMENTS(EXPECTED_G2));
522     checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
523     sCode = "GGG";
524     const sal_Unicode EXPECTED_G3[] = {0x5E73, 0x6210};
525     sExpected = OUString(EXPECTED_G3, SAL_N_ELEMENTS(EXPECTED_G3));
526     checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
527 }
528 
testDateInput()529 void Test::testDateInput()
530 {
531     const char* aData[][2] = {
532         { "Europe/Paris", "1938-10-07" },      // i#76623
533         { "Europe/Moscow", "1919-07-01" },     // i#86094
534         { "America/St_Johns", "1935-03-30" },  // i#86094 i#90627
535         { "Europe/Tallinn", "1790-03-01" },    // i#105864
536         { "Australia/Perth", "2004-04-11" },   // i#17222
537         { "America/Sao_Paulo", "1902-04-22" }, // tdf#44286
538         { "Europe/Berlin", "1790-07-27" },
539         { "US/Mountain", "1790-07-26" },
540         { "Asia/Tehran", "1999-03-22" },
541 
542         // Data from https://bugs.documentfoundation.org/show_bug.cgi?id=63230
543         // https://bugs.documentfoundation.org/attachment.cgi?id=79051
544         // https://bugs.documentfoundation.org/show_bug.cgi?id=79663
545         { "Africa/Accra", "1800-01-01" },
546         { "Africa/Accra", "1800-04-10" },
547         { "Africa/Addis_Ababa", "1870-01-01" },
548         { "Africa/Addis_Ababa", "1936-05-05" },
549         { "Africa/Algiers", "1956-01-29" },
550         { "Africa/Algiers", "1981-05-01" },
551         { "Africa/Asmara", "1936-05-05" },
552         { "Africa/Asmera", "1936-05-05" },
553         { "Africa/Bujumbura", "1890-01-01" },
554         { "Africa/Casablanca", "1984-03-16" },
555         { "Africa/Ceuta", "1984-03-16" },
556         { "Africa/Dar_es_Salaam", "1931-01-01" },
557         { "Africa/Dar_es_Salaam", "1961-01-01" },
558         { "Africa/Djibouti", "1911-07-01" },
559         { "Africa/Douala", "1912-01-01" },
560         { "Africa/El_Aaiun", "1934-01-01" },
561         { "Africa/Freetown", "1913-06-01" },
562         { "Africa/Gaborone", "1885-01-01" },
563         { "Africa/Johannesburg", "1903-03-01" },
564         { "Africa/Kampala", "1928-07-01" },
565         { "Africa/Kampala", "1948-01-01" },
566         { "Africa/Kampala", "1957-01-01" },
567         { "Africa/Lagos", "1919-09-01" },
568         { "Africa/Libreville", "1912-01-01" },
569         { "Africa/Luanda", "1911-05-26" },
570         { "Africa/Lubumbashi", "1897-11-09" },
571         { "Africa/Lusaka", "1903-03-01" },
572         { "Africa/Malabo", "1963-12-15" },
573         { "Africa/Maseru", "1903-03-01" },
574         { "Africa/Mogadishu", "1957-01-01" },
575         { "Africa/Monrovia", "1919-03-01" },
576         { "Africa/Nairobi", "1928-07-01" },
577         { "Africa/Nairobi", "1940-01-01" },
578         { "Africa/Nairobi", "1960-01-01" },
579         { "Africa/Niamey", "1960-01-01" },
580         { "Africa/Porto-Novo", "1934-02-26" },
581         { "Africa/Tripoli", "1920-01-01" },
582         { "Africa/Tripoli", "1959-01-01" },
583         { "Africa/Tripoli", "1990-05-04" },
584         { "Africa/Tunis", "1911-03-11" },
585         { "Africa/Windhoek", "1892-02-08" },
586         { "Africa/Windhoek", "1903-03-01" },
587         { "America/Antigua", "1912-03-02" },
588         { "America/Argentina/Buenos_Aires", "1894-10-31" },
589         { "America/Argentina/Catamarca", "1991-10-20" },
590         { "America/Argentina/Catamarca", "2004-06-01" },
591         { "America/Argentina/ComodRivadavia", "1991-10-20" },
592         { "America/Argentina/ComodRivadavia", "2004-06-01" },
593         { "America/Argentina/Cordoba", "1991-10-20" },
594         { "America/Argentina/Jujuy", "1991-10-06" },
595         { "America/Argentina/La_Rioja", "2004-06-01" },
596         { "America/Argentina/Mendoza", "1992-10-18" },
597         { "America/Argentina/Mendoza", "2004-05-23" },
598         { "America/Argentina/Rio_Gallegos", "2004-06-01" },
599         { "America/Argentina/Salta", "1991-10-20" },
600         { "America/Argentina/San_Juan", "2004-05-31" },
601         { "America/Argentina/San_Luis", "2004-05-31" },
602         { "America/Argentina/San_Luis", "2008-01-21" },
603         { "America/Argentina/Tucuman", "1991-10-20" },
604         { "America/Argentina/Tucuman", "2004-06-01" },
605         { "America/Argentina/Ushuaia", "2004-05-30" },
606         { "America/Asuncion", "1931-10-10" },
607         { "America/Asuncion", "1974-04-01" },
608         { "America/Bahia", "1914-01-01" },
609         { "America/Bahia_Banderas", "1930-11-15" },
610         { "America/Bahia_Banderas", "1931-10-01" },
611         { "America/Bahia_Banderas", "1942-04-24" },
612         { "America/Bahia_Banderas", "1949-01-14" },
613         { "America/Barbados", "1932-01-01" },
614         { "America/Belize", "1912-04-01" },
615         { "America/Blanc-Sablon", "1884-01-01" },
616         { "America/Bogota", "1914-11-23" },
617         { "America/Buenos_Aires", "1894-10-31" },
618         { "America/Cambridge_Bay", "2000-11-05" },
619         { "America/Campo_Grande", "1914-01-01" },
620         { "America/Caracas", "1912-02-12" },
621         { "America/Catamarca", "1991-10-20" },
622         { "America/Catamarca", "2004-06-01" },
623         { "America/Cayenne", "1911-07-01" },
624         { "America/Chihuahua", "1930-11-15" },
625         { "America/Chihuahua", "1931-10-01" },
626         { "America/Cordoba", "1991-10-20" },
627         { "America/Costa_Rica", "1921-01-15" },
628         { "America/Cuiaba", "1914-01-01" },
629         { "America/Danmarkshavn", "1916-07-28" },
630         { "America/Detroit", "1905-01-01" },
631         { "America/Eirunepe", "1914-01-01" },
632         { "America/El_Salvador", "1921-01-01" },
633         { "America/Ensenada", "1924-01-01" },
634         { "America/Ensenada", "1930-11-15" },
635         { "America/Fortaleza", "1914-01-01" },
636         { "America/Glace_Bay", "1902-06-15" },
637         { "America/Grand_Turk", "1890-01-01" },
638         { "America/Guyana", "1991-01-01" },
639         { "America/Havana", "1890-01-01" },
640         { "America/Hermosillo", "1930-11-15" },
641         { "America/Hermosillo", "1931-10-01" },
642         { "America/Hermosillo", "1942-04-24" },
643         { "America/Hermosillo", "1949-01-14" },
644         { "America/Jujuy", "1991-10-06" },
645         { "America/Lima", "1890-01-01" },
646         { "America/Maceio", "1914-01-01" },
647         { "America/Managua", "1890-01-01" },
648         { "America/Managua", "1934-06-23" },
649         { "America/Managua", "1975-02-16" },
650         { "America/Managua", "1992-09-24" },
651         { "America/Managua", "1997-01-01" },
652         { "America/Mazatlan", "1930-11-15" },
653         { "America/Mazatlan", "1931-10-01" },
654         { "America/Mazatlan", "1942-04-24" },
655         { "America/Mazatlan", "1949-01-14" },
656         { "America/Mendoza", "1992-10-18" },
657         { "America/Mendoza", "2004-05-23" },
658         { "America/Merida", "1982-12-02" },
659         { "America/Mexico_City", "1930-11-15" },
660         { "America/Mexico_City", "1931-10-01" },
661         { "America/Miquelon", "1911-05-15" },
662         { "America/Moncton", "1883-12-09" },
663         { "America/Montevideo", "1942-12-14" },
664         { "America/Montevideo", "1974-12-22" },
665         { "America/Montreal", "1884-01-01" },
666         { "America/Ojinaga", "1930-11-15" },
667         { "America/Ojinaga", "1931-10-01" },
668         { "America/Panama", "1890-01-01" },
669         { "America/Paramaribo", "1911-01-01" },
670         { "America/Porto_Acre", "1914-01-01" },
671         { "America/Recife", "1914-01-01" },
672         { "America/Regina", "1905-09-01" },
673         { "America/Rio_Branco", "1914-01-01" },
674         { "America/Rosario", "1991-10-20" },
675         { "America/Santa_Isabel", "1924-01-01" },
676         { "America/Santa_Isabel", "1930-11-15" },
677         { "America/Santarem", "1914-01-01" },
678         { "America/Santiago", "1910-01-01" },
679         { "America/Santiago", "1919-07-01" },
680         { "America/Santo_Domingo", "1890-01-01" },
681         { "America/Scoresbysund", "1916-07-28" },
682         { "America/Scoresbysund", "1981-03-29" },
683         { "America/Tegucigalpa", "1921-04-01" },
684         { "America/Thunder_Bay", "1895-01-01" },
685         { "America/Tijuana", "1924-01-01" },
686         { "America/Tijuana", "1930-11-15" },
687         { "Antarctica/Casey", "1969-01-01" },
688         { "Antarctica/Casey", "2009-10-18" },
689         { "Antarctica/Davis", "1957-01-13" },
690         { "Antarctica/Davis", "1969-02-01" },
691         { "Antarctica/Davis", "2010-03-11" },
692         { "Antarctica/DumontDUrville", "1947-01-01" },
693         { "Antarctica/DumontDUrville", "1956-11-01" },
694         { "Antarctica/Macquarie", "1911-01-01" },
695         { "Antarctica/Mawson", "1954-02-13" },
696         { "Antarctica/McMurdo", "1956-01-01" },
697         { "Antarctica/Palmer", "1982-05-01" },
698         { "Antarctica/South_Pole", "1956-01-01" },
699         { "Antarctica/Syowa", "1957-01-29" },
700         { "Antarctica/Vostok", "1957-12-16" },
701         { "Arctic/Longyearbyen", "1895-01-01" },
702         { "Asia/Almaty", "1930-06-21" },
703         { "Asia/Anadyr", "1924-05-02" },
704         { "Asia/Anadyr", "1930-06-21" },
705         { "Asia/Anadyr", "1992-01-19" },
706         { "Asia/Anadyr", "2011-03-27" },
707         { "Asia/Aqtau", "1924-05-02" },
708         { "Asia/Aqtau", "1930-06-21" },
709         { "Asia/Aqtau", "1981-10-01" },
710         { "Asia/Aqtau", "2005-03-15" },
711         { "Asia/Aqtobe", "1924-05-02" },
712         { "Asia/Aqtobe", "1930-06-21" },
713         { "Asia/Ashgabat", "1924-05-02" },
714         { "Asia/Ashgabat", "1930-06-21" },
715         { "Asia/Ashgabat", "1992-01-19" },
716         { "Asia/Ashkhabad", "1924-05-02" },
717         { "Asia/Ashkhabad", "1930-06-21" },
718         { "Asia/Ashkhabad", "1992-01-19" },
719         { "Asia/Baghdad", "1918-01-01" },
720         { "Asia/Bahrain", "1920-01-01" },
721         { "Asia/Baku", "1957-03-01" },
722         { "Asia/Bangkok", "1920-04-01" },
723         { "Asia/Bishkek", "1924-05-02" },
724         { "Asia/Bishkek", "1930-06-21" },
725         { "Asia/Brunei", "1933-01-01" },
726         { "Asia/Calcutta", "1941-10-01" },
727         { "Asia/Choibalsan", "1978-01-01" },
728         { "Asia/Chongqing", "1980-05-01" },
729         { "Asia/Chungking", "1980-05-01" },
730         { "Asia/Colombo", "1880-01-01" },
731         { "Asia/Colombo", "1906-01-01" },
732         { "Asia/Colombo", "1942-09-01" },
733         { "Asia/Colombo", "1996-05-25" },
734         { "Asia/Dacca", "1941-10-01" },
735         { "Asia/Dacca", "1942-09-01" },
736         { "Asia/Dhaka", "1941-10-01" },
737         { "Asia/Dhaka", "1942-09-01" },
738         { "Asia/Dili", "2000-09-17" },
739         { "Asia/Dubai", "1920-01-01" },
740         { "Asia/Dushanbe", "1924-05-02" },
741         { "Asia/Dushanbe", "1930-06-21" },
742         { "Asia/Harbin", "1928-01-01" },
743         { "Asia/Harbin", "1940-01-01" },
744         { "Asia/Ho_Chi_Minh", "1912-05-01" },
745         { "Asia/Hong_Kong", "1904-10-30" },
746         { "Asia/Hong_Kong", "1941-12-25" },
747         { "Asia/Hovd", "1978-01-01" },
748         { "Asia/Irkutsk", "1920-01-25" },
749         { "Asia/Irkutsk", "1930-06-21" },
750         { "Asia/Irkutsk", "1992-01-19" },
751         { "Asia/Irkutsk", "2011-03-27" },
752         { "Asia/Istanbul", "1880-01-01" },
753         { "Asia/Istanbul", "1910-10-01" },
754         { "Asia/Istanbul", "1978-10-15" },
755         { "Asia/Jakarta", "1932-11-01" },
756         { "Asia/Jakarta", "1942-03-23" },
757         { "Asia/Jakarta", "1948-05-01" },
758         { "Asia/Jayapura", "1944-09-01" },
759         { "Asia/Kabul", "1945-01-01" },
760         { "Asia/Kamchatka", "1922-11-10" },
761         { "Asia/Kamchatka", "1930-06-21" },
762         { "Asia/Kamchatka", "1992-01-19" },
763         { "Asia/Kamchatka", "2011-03-27" },
764         { "Asia/Karachi", "1907-01-01" },
765         { "Asia/Kashgar", "1928-01-01" },
766         { "Asia/Kashgar", "1980-05-01" },
767         { "Asia/Kathmandu", "1986-01-01" },
768         { "Asia/Katmandu", "1986-01-01" },
769         { "Asia/Kolkata", "1941-10-01" },
770         { "Asia/Krasnoyarsk", "1930-06-21" },
771         { "Asia/Krasnoyarsk", "1992-01-19" },
772         { "Asia/Krasnoyarsk", "2011-03-27" },
773         { "Asia/Kuala_Lumpur", "1901-01-01" },
774         { "Asia/Kuala_Lumpur", "1905-06-01" },
775         { "Asia/Kuala_Lumpur", "1941-09-01" },
776         { "Asia/Kuala_Lumpur", "1942-02-16" },
777         { "Asia/Kuala_Lumpur", "1982-01-01" },
778         { "Asia/Kuching", "1926-03-01" },
779         { "Asia/Kuching", "1933-01-01" },
780         { "Asia/Kuching", "1942-02-16" },
781         { "Asia/Macao", "1912-01-01" },
782         { "Asia/Macau", "1912-01-01" },
783         { "Asia/Magadan", "1930-06-21" },
784         { "Asia/Magadan", "1992-01-19" },
785         { "Asia/Magadan", "2011-03-27" },
786         { "Asia/Makassar", "1932-11-01" },
787         { "Asia/Makassar", "1942-02-09" },
788         { "Asia/Manila", "1942-05-01" },
789         { "Asia/Muscat", "1920-01-01" },
790         { "Asia/Novokuznetsk", "1920-01-06" },
791         { "Asia/Novokuznetsk", "1930-06-21" },
792         { "Asia/Novokuznetsk", "1992-01-19" },
793         { "Asia/Novokuznetsk", "2011-03-27" },
794         { "Asia/Novosibirsk", "1930-06-21" },
795         { "Asia/Novosibirsk", "1992-01-19" },
796         { "Asia/Novosibirsk", "2011-03-27" },
797         { "Asia/Omsk", "1919-11-14" },
798         { "Asia/Omsk", "1930-06-21" },
799         { "Asia/Omsk", "1992-01-19" },
800         { "Asia/Omsk", "2011-03-27" },
801         { "Asia/Oral", "1924-05-02" },
802         { "Asia/Oral", "1930-06-21" },
803         { "Asia/Oral", "2005-03-15" },
804         { "Asia/Phnom_Penh", "1906-06-09" },
805         { "Asia/Phnom_Penh", "1912-05-01" },
806         { "Asia/Pontianak", "1932-11-01" },
807         { "Asia/Pontianak", "1942-01-29" },
808         { "Asia/Pontianak", "1948-05-01" },
809         { "Asia/Pontianak", "1964-01-01" },
810         { "Asia/Pyongyang", "1890-01-01" },
811         { "Asia/Pyongyang", "1904-12-01" },
812         { "Asia/Pyongyang", "1932-01-01" },
813         { "Asia/Pyongyang", "1961-08-10" },
814         { "Asia/Qatar", "1920-01-01" },
815         { "Asia/Qyzylorda", "1930-06-21" },
816         { "Asia/Qyzylorda", "1992-01-19" },
817         { "Asia/Rangoon", "1920-01-01" },
818         { "Asia/Rangoon", "1942-05-01" },
819         { "Asia/Saigon", "1912-05-01" },
820         { "Asia/Sakhalin", "1945-08-25" },
821         { "Asia/Sakhalin", "1992-01-19" },
822         { "Asia/Sakhalin", "2011-03-27" },
823         { "Asia/Samarkand", "1930-06-21" },
824         { "Asia/Seoul", "1890-01-01" },
825         { "Asia/Seoul", "1904-12-01" },
826         { "Asia/Seoul", "1932-01-01" },
827         { "Asia/Seoul", "1961-08-10" },
828         { "Asia/Seoul", "1968-10-01" },
829         { "Asia/Singapore", "1905-06-01" },
830         { "Asia/Singapore", "1941-09-01" },
831         { "Asia/Singapore", "1942-02-16" },
832         { "Asia/Singapore", "1982-01-01" },
833         { "Asia/Tashkent", "1924-05-02" },
834         { "Asia/Tashkent", "1930-06-21" },
835         { "Asia/Tbilisi", "1924-05-02" },
836         { "Asia/Tbilisi", "1957-03-01" },
837         { "Asia/Tbilisi", "2005-03-27" },
838         { "Asia/Tehran", "1946-01-01" },
839         { "Asia/Tehran", "1977-11-01" },
840         { "Asia/Thimbu", "1987-10-01" },
841         { "Asia/Thimphu", "1987-10-01" },
842         { "Asia/Ujung_Pandang", "1932-11-01" },
843         { "Asia/Ujung_Pandang", "1942-02-09" },
844         { "Asia/Ulaanbaatar", "1978-01-01" },
845         { "Asia/Ulan_Bator", "1978-01-01" },
846         { "Asia/Urumqi", "1928-01-01" },
847         { "Asia/Urumqi", "1980-05-01" },
848         { "Asia/Vientiane", "1906-06-09" },
849         { "Asia/Vientiane", "1912-05-01" },
850         { "Asia/Vladivostok", "1922-11-15" },
851         { "Asia/Vladivostok", "1930-06-21" },
852         { "Asia/Vladivostok", "1992-01-19" },
853         { "Asia/Vladivostok", "2011-03-27" },
854         { "Asia/Yakutsk", "1930-06-21" },
855         { "Asia/Yakutsk", "1992-01-19" },
856         { "Asia/Yakutsk", "2011-03-27" },
857         { "Asia/Yekaterinburg", "1930-06-21" },
858         { "Asia/Yekaterinburg", "1992-01-19" },
859         { "Asia/Yekaterinburg", "2011-03-27" },
860         { "Asia/Yerevan", "1924-05-02" },
861         { "Asia/Yerevan", "1957-03-01" },
862         { "Atlantic/Azores", "1884-01-01" },
863         { "Atlantic/Azores", "1911-05-24" },
864         { "Atlantic/Azores", "1942-04-25" },
865         { "Atlantic/Azores", "1943-04-17" },
866         { "Atlantic/Azores", "1944-04-22" },
867         { "Atlantic/Azores", "1945-04-21" },
868         { "Atlantic/Cape_Verde", "1907-01-01" },
869         { "Atlantic/Jan_Mayen", "1895-01-01" },
870         { "Atlantic/Madeira", "1942-04-25" },
871         { "Atlantic/Madeira", "1943-04-17" },
872         { "Atlantic/Madeira", "1944-04-22" },
873         { "Atlantic/Madeira", "1945-04-21" },
874         { "Atlantic/Reykjavik", "1837-01-01" },
875         { "Atlantic/Stanley", "1912-03-12" },
876         { "Australia/Adelaide", "1899-05-01" },
877         { "Australia/Broken_Hill", "1895-02-01" },
878         { "Australia/Broken_Hill", "1899-05-01" },
879         { "Australia/Currie", "1895-09-01" },
880         { "Australia/Darwin", "1895-02-01" },
881         { "Australia/Darwin", "1899-05-01" },
882         { "Australia/Eucla", "1895-12-01" },
883         { "Australia/Hobart", "1895-09-01" },
884         { "Australia/LHI", "1981-03-01" },
885         { "Australia/Lindeman", "1895-01-01" },
886         { "Australia/Lord_Howe", "1981-03-01" },
887         { "Australia/Melbourne", "1895-02-01" },
888         { "Australia/North", "1895-02-01" },
889         { "Australia/North", "1899-05-01" },
890         { "Australia/Perth", "1895-12-01" },
891         { "Australia/South", "1899-05-01" },
892         { "Australia/Tasmania", "1895-09-01" },
893         { "Australia/Victoria", "1895-02-01" },
894         { "Australia/West", "1895-12-01" },
895         { "Australia/Yancowinna", "1895-02-01" },
896         { "Australia/Yancowinna", "1899-05-01" },
897         { "Brazil/Acre", "1914-01-01" },
898         { "Canada/East-Saskatchewan", "1905-09-01" },
899         { "Canada/Saskatchewan", "1905-09-01" },
900         { "Chile/Continental", "1910-01-01" },
901         { "Chile/Continental", "1919-07-01" },
902         { "Chile/EasterIsland", "1932-09-01" },
903         { "Cuba", "1890-01-01" },
904         { "Eire", "1880-08-02" },
905         { "Europe/Amsterdam", "1937-07-01" },
906         { "Europe/Andorra", "1946-09-30" },
907         { "Europe/Athens", "1916-07-28" },
908         { "Europe/Athens", "1944-04-04" },
909         { "Europe/Berlin", "1893-04-01" },
910         { "Europe/Bratislava", "1891-10-01" },
911         { "Europe/Brussels", "1914-11-08" },
912         { "Europe/Bucharest", "1931-07-24" },
913         { "Europe/Chisinau", "1931-07-24" },
914         { "Europe/Copenhagen", "1894-01-01" },
915         { "Europe/Dublin", "1880-08-02" },
916         { "Europe/Gibraltar", "1941-05-04" },
917         { "Europe/Gibraltar", "1942-04-05" },
918         { "Europe/Gibraltar", "1943-04-04" },
919         { "Europe/Gibraltar", "1944-04-02" },
920         { "Europe/Gibraltar", "1945-04-02" },
921         { "Europe/Gibraltar", "1947-04-13" },
922         { "Europe/Helsinki", "1921-05-01" },
923         { "Europe/Istanbul", "1880-01-01" },
924         { "Europe/Istanbul", "1910-10-01" },
925         { "Europe/Istanbul", "1978-10-15" },
926         { "Europe/Kaliningrad", "1945-01-01" },
927         { "Europe/Kaliningrad", "1946-01-01" },
928         { "Europe/Kaliningrad", "2011-03-27" },
929         { "Europe/Kiev", "1930-06-21" },
930         { "Europe/Kiev", "1943-11-06" },
931         { "Europe/Luxembourg", "1904-06-01" },
932         { "Europe/Madrid", "1942-05-02" },
933         { "Europe/Madrid", "1943-04-17" },
934         { "Europe/Madrid", "1944-04-15" },
935         { "Europe/Madrid", "1945-04-14" },
936         { "Europe/Madrid", "1946-04-13" },
937         { "Europe/Malta", "1893-11-02" },
938         { "Europe/Mariehamn", "1921-05-01" },
939         { "Europe/Minsk", "1924-05-02" },
940         { "Europe/Minsk", "1930-06-21" },
941         { "Europe/Minsk", "2011-03-27" },
942         { "Europe/Monaco", "1941-05-05" },
943         { "Europe/Monaco", "1942-03-09" },
944         { "Europe/Monaco", "1943-03-29" },
945         { "Europe/Monaco", "1944-04-03" },
946         { "Europe/Monaco", "1945-04-02" },
947         { "Europe/Moscow", "1916-07-03" },
948         { "Europe/Moscow", "1919-05-31" },
949         { "Europe/Moscow", "1930-06-21" },
950         { "Europe/Moscow", "1992-01-19" },
951         { "Europe/Moscow", "2011-03-27" },
952         { "Europe/Oslo", "1895-01-01" },
953         { "Europe/Paris", "1945-04-02" },
954         { "Europe/Prague", "1891-10-01" },
955         { "Europe/Riga", "1926-05-11" },
956         { "Europe/Riga", "1940-08-05" },
957         { "Europe/Riga", "1944-10-13" },
958         { "Europe/Rome", "1893-11-01" },
959         { "Europe/Samara", "1930-06-21" },
960         { "Europe/Samara", "1991-10-20" },
961         { "Europe/Samara", "2011-03-27" },
962         { "Europe/San_Marino", "1893-11-01" },
963         { "Europe/Simferopol", "1930-06-21" },
964         { "Europe/Simferopol", "1994-05-01" },
965         { "Europe/Sofia", "1880-01-01" },
966         { "Europe/Sofia", "1894-11-30" },
967         { "Europe/Tallinn", "1919-07-01" },
968         { "Europe/Tallinn", "1921-05-01" },
969         { "Europe/Tallinn", "1940-08-06" },
970         { "Europe/Tiraspol", "1931-07-24" },
971         { "Europe/Uzhgorod", "1945-06-29" },
972         { "Europe/Vaduz", "1894-06-01" },
973         { "Europe/Vatican", "1893-11-01" },
974         { "Europe/Vilnius", "1917-01-01" },
975         { "Europe/Vilnius", "1920-07-12" },
976         { "Europe/Vilnius", "1940-08-03" },
977         { "Europe/Volgograd", "1920-01-03" },
978         { "Europe/Volgograd", "1930-06-21" },
979         { "Europe/Volgograd", "1991-03-31" },
980         { "Europe/Volgograd", "2011-03-27" },
981         { "Europe/Zaporozhye", "1930-06-21" },
982         { "Europe/Zaporozhye", "1943-10-25" },
983         { "Europe/Zurich", "1894-06-01" },
984         { "Hongkong", "1904-10-30" },
985         { "Hongkong", "1941-12-25" },
986         { "Iceland", "1837-01-01" },
987         { "Indian/Chagos", "1907-01-01" },
988         { "Indian/Chagos", "1996-01-01" },
989         { "Indian/Cocos", "1900-01-01" },
990         { "Indian/Comoro", "1911-07-01" },
991         { "Indian/Kerguelen", "1950-01-01" },
992         { "Indian/Mahe", "1906-06-01" },
993         { "Indian/Maldives", "1960-01-01" },
994         { "Indian/Mauritius", "1907-01-01" },
995         { "Indian/Reunion", "1911-06-01" },
996         { "Iran", "1946-01-01" },
997         { "Iran", "1977-11-01" },
998         { "Libya", "1920-01-01" },
999         { "Libya", "1959-01-01" },
1000         { "Libya", "1990-05-04" },
1001         { "Mexico/BajaNorte", "1924-01-01" },
1002         { "Mexico/BajaNorte", "1930-11-15" },
1003         { "Mexico/BajaSur", "1930-11-15" },
1004         { "Mexico/BajaSur", "1931-10-01" },
1005         { "Mexico/BajaSur", "1942-04-24" },
1006         { "Mexico/BajaSur", "1949-01-14" },
1007         { "Mexico/General", "1930-11-15" },
1008         { "Mexico/General", "1931-10-01" },
1009         { "NZ-CHAT", "1957-01-01" },
1010         { "Pacific/Apia", "1911-01-01" },
1011         { "Pacific/Apia", "2011-12-30" },
1012         { "Pacific/Chatham", "1957-01-01" },
1013         { "Pacific/Easter", "1932-09-01" },
1014         { "Pacific/Enderbury", "1901-01-01" },
1015         { "Pacific/Enderbury", "1995-01-01" },
1016         { "Pacific/Fakaofo", "2011-12-30" },
1017         { "Pacific/Fiji", "1915-10-26" },
1018         { "Pacific/Funafuti", "1901-01-01" },
1019         { "Pacific/Galapagos", "1986-01-01" },
1020         { "Pacific/Gambier", "1912-10-01" },
1021         { "Pacific/Guadalcanal", "1912-10-01" },
1022         { "Pacific/Guam", "1901-01-01" },
1023         { "Pacific/Kiritimati", "1901-01-01" },
1024         { "Pacific/Kiritimati", "1995-01-01" },
1025         { "Pacific/Kosrae", "1901-01-01" },
1026         { "Pacific/Kosrae", "1969-10-01" },
1027         { "Pacific/Kwajalein", "1993-08-20" },
1028         { "Pacific/Majuro", "1969-10-01" },
1029         { "Pacific/Marquesas", "1912-10-01" },
1030         { "Pacific/Nauru", "1921-01-15" },
1031         { "Pacific/Nauru", "1944-08-15" },
1032         { "Pacific/Nauru", "1979-05-01" },
1033         { "Pacific/Niue", "1901-01-01" },
1034         { "Pacific/Niue", "1951-01-01" },
1035         { "Pacific/Norfolk", "1901-01-01" },
1036         { "Pacific/Norfolk", "1951-01-01" },
1037         { "Pacific/Pago_Pago", "1911-01-01" },
1038         { "Pacific/Palau", "1901-01-01" },
1039         { "Pacific/Pohnpei", "1901-01-01" },
1040         { "Pacific/Ponape", "1901-01-01" },
1041         { "Pacific/Port_Moresby", "1895-01-01" },
1042         { "Pacific/Rarotonga", "1978-11-12" },
1043         { "Pacific/Saipan", "1969-10-01" },
1044         { "Pacific/Samoa", "1911-01-01" },
1045         { "Pacific/Tahiti", "1912-10-01" },
1046         { "Pacific/Tarawa", "1901-01-01" },
1047         { "Pacific/Tongatapu", "1901-01-01" },
1048         { "Pacific/Tongatapu", "1941-01-01" },
1049         { "Pacific/Wake", "1901-01-01" },
1050         { "ROK", "1890-01-01" },
1051         { "ROK", "1904-12-01" },
1052         { "ROK", "1932-01-01" },
1053         { "ROK", "1961-08-10" },
1054         { "ROK", "1968-10-01" },
1055         { "Singapore", "1905-06-01" },
1056         { "Singapore", "1941-09-01" },
1057         { "Singapore", "1942-02-16" },
1058         { "Singapore", "1982-01-01" },
1059         { "Turkey", "1880-01-01" },
1060         { "Turkey", "1910-10-01" },
1061         { "Turkey", "1978-10-15" },
1062         { "US/Michigan", "1905-01-01" },
1063         { "US/Samoa", "1911-01-01" },
1064         { "W-SU", "1916-07-03" },
1065         { "W-SU", "1930-06-21" },
1066         { "W-SU", "1992-01-19" },
1067         { "W-SU", "2011-03-27" }
1068     };
1069 
1070     LanguageType eLang = LANGUAGE_ENGLISH_US;
1071     SvNumberFormatter aFormatter(m_xContext, eLang);
1072 
1073     for (size_t i=0; i < SAL_N_ELEMENTS(aData); ++i)
1074     {
1075         checkDateInput( aFormatter, aData[i][0], aData[i][1]);
1076     }
1077 }
1078 
checkDateInput(SvNumberFormatter & rFormatter,const char * pTimezone,const char * pIsoDate)1079 void Test::checkDateInput( SvNumberFormatter& rFormatter, const char* pTimezone, const char* pIsoDate )
1080 {
1081     icu::TimeZone::adoptDefault( icu::TimeZone::createTimeZone( pTimezone));
1082     OUString aDate( OUString::createFromAscii(pIsoDate));
1083     sal_uInt32 nIndex = 0;
1084     double fVal = 0.0;
1085     bool bVal = rFormatter.IsNumberFormat( aDate, nIndex, fVal);
1086     CPPUNIT_ASSERT_MESSAGE( OString(OStringLiteral("Date not recognized: ") +
1087                 pTimezone + " " + pIsoDate).getStr(), bVal);
1088     CPPUNIT_ASSERT_MESSAGE("Format parsed is not date.",
1089             (rFormatter.GetType(nIndex) & SvNumFormatType::DATE));
1090     OUString aOutString;
1091     Color *pColor;
1092     rFormatter.GetOutputString( fVal, nIndex, aOutString, &pColor);
1093     CPPUNIT_ASSERT_EQUAL( aDate, aOutString);
1094 }
1095 
testIsNumberFormat()1096 void Test::testIsNumberFormat()
1097 {
1098     LanguageType eLang = LANGUAGE_ENGLISH_US;
1099     SvNumberFormatter aFormatter(m_xContext, eLang);
1100 
1101     static struct NumberFormatData
1102     {
1103         const char* pFormat;
1104         bool const bIsNumber;
1105     } const aTests[] = {
1106         { "20.3", true },
1107         { "2", true },
1108         { "test", false },
1109         { "Jan1", false },
1110         { "Jan1 2000", true },
1111         { "Jan 1", true },
1112         { "Jan 1 2000", true },
1113         { "5-12-14", false },
1114         { "005-12-14", true },
1115         { "15-10-30", true },
1116         { "2015-10-30", true },
1117         { "1999-11-23T12:34:56", true },
1118         { "1999-11-23 12:34:56", true },
1119         { "1999-11-23T12:34:56.789", true },
1120         { "1999-11-23T12:34:56,789", true },    // ISO 8601 defines both dot and comma as fractional separator
1121         { "1999-11-23 12:34:56.789", true },
1122         { "1999-11-23 12:34:56,789", false },   // comma not in en-US if 'T' separator is not present,
1123                                                 // debatable, 'T' "may be omitted by mutual consent of those
1124                                                 // interchanging data, if ambiguity can be avoided."
1125         { "1999-11-23T12:34:56/789", false }
1126     };
1127 
1128     for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
1129     {
1130         sal_uInt32 nIndex = 0;
1131         double nNumber = 0;
1132         OUString aString = OUString::createFromAscii(aTests[i].pFormat);
1133         bool bIsNumber = aFormatter.IsNumberFormat(aString, nIndex, nNumber);
1134         CPPUNIT_ASSERT_EQUAL_MESSAGE(aTests[i].pFormat, aTests[i].bIsNumber, bIsNumber);
1135 
1136     }
1137 }
1138 
1139 struct FormatInputOutput
1140 {
1141     const char*      mpInput;
1142     const bool       mbNumber;
1143     const char*      mpOutput;
1144     const sal_uInt32 mnOutputIndex;
1145 };
1146 
checkSpecificNumberFormats(SvNumberFormatter & rFormatter,const std::vector<FormatInputOutput> & rVec,const char * pName)1147 void checkSpecificNumberFormats( SvNumberFormatter& rFormatter,
1148         const std::vector<FormatInputOutput>& rVec, const char* pName )
1149 {
1150 
1151     for (size_t i = 0; i < rVec.size(); ++i)
1152     {
1153         sal_uInt32 nIndex = 0;
1154         double fNumber = 0;
1155         OUString aString( OUString::fromUtf8( rVec[i].mpInput));
1156         const bool bIsNumber = rFormatter.IsNumberFormat( aString, nIndex, fNumber);
1157         CPPUNIT_ASSERT_EQUAL_MESSAGE( OString( pName + OStringLiteral(" ") + OString::number(i) +
1158                     (rVec[i].mbNumber ? " not recognized: " : " should not be recognized: ") +
1159                     OUStringToOString( aString, RTL_TEXTENCODING_UTF8)).getStr(), rVec[i].mbNumber, bIsNumber);
1160         if (bIsNumber)
1161         {
1162             if (rVec[i].mnOutputIndex)
1163                 nIndex = rVec[i].mnOutputIndex;
1164             Color* pColor;
1165             rFormatter.GetOutputString( fNumber, nIndex, aString, &pColor);
1166             CPPUNIT_ASSERT_EQUAL_MESSAGE( OString( pName + OStringLiteral(" ") + OString::number(i)  + " mismatch").getStr(),
1167                     OUString::fromUtf8( rVec[i].mpOutput), aString);
1168         }
1169     }
1170 }
1171 
testIsNumberFormatSpecific()1172 void Test::testIsNumberFormatSpecific()
1173 {
1174     {
1175         // en-US uses M/D/Y format, test that a-b-c input with a<=31 and b<=12
1176         // does not lead to a/b/c date output
1177         SvNumberFormatter aFormatter(m_xContext, LANGUAGE_ENGLISH_US);
1178 
1179         std::vector<FormatInputOutput> aIO = {
1180             {  "5-12-14", false, "", 0 },
1181             { "32-12-14",  true, "1932-12-14", 0 }
1182         };
1183 
1184         checkSpecificNumberFormats( aFormatter, aIO, "[en-US] date");
1185     }
1186 
1187     {
1188         // de-DE uses D.M.Y format, test that a-b-c input with a<=31 and b<=12
1189         // does not lead to a.b.c date output
1190         SvNumberFormatter aFormatter(m_xContext, LANGUAGE_GERMAN);
1191 
1192         std::vector<FormatInputOutput> aIO = {
1193             {  "5-12-14", false, "", 0 },
1194             { "32-12-14",  true, "1932-12-14", 0 }
1195         };
1196 
1197         checkSpecificNumberFormats( aFormatter, aIO, "[de-DE] date");
1198     }
1199 
1200     {
1201         // nl-NL uses D-M-Y format, test that D-M-Y input leads to D-M-Y output
1202         // and ISO Y-M-D input leads to Y-M-D output.
1203         SvNumberFormatter aFormatter(m_xContext, LANGUAGE_DUTCH);
1204 
1205         std::vector<FormatInputOutput> aIO = {
1206             { "22-11-1999", true, "22-11-99", 0 },      // if default YY changes to YYYY adapt this
1207             { "1999-11-22", true, "1999-11-22", 0 },
1208             { "1-2-11",     true, "01-02-11", 0 },      // if default YY changes to YYYY adapt this
1209             { "99-2-11",    true, "1999-02-11", 0 }
1210         };
1211 
1212         checkSpecificNumberFormats( aFormatter, aIO, "[nl-NL] date");
1213     }
1214 
1215     {
1216         // en-ZA uses Y-M-D and Y/M/D format, test that either are accepted.
1217         // The default format changed from YY/MM/DD to YYYY-MM-DD.
1218         SvNumberFormatter aFormatter(m_xContext, LANGUAGE_ENGLISH_SAFRICA);
1219 
1220         std::vector<FormatInputOutput> aIO = {
1221             { "1999/11/22", true, "1999-11-22", 0 },
1222             { "1999-11-22", true, "1999-11-22", 0 },
1223             { "11/2/1",     true, "2011-02-01", 0 },
1224             { "99-2-11",    true, "1999-02-11", 0 },
1225             { "22-2-11",    true, "2022-02-11", 0 }
1226         };
1227 
1228         checkSpecificNumberFormats( aFormatter, aIO, "[en-ZA] date");
1229     }
1230 
1231     {
1232         // fr-FR uses D/M/Y format with additional D.M.Y and D-M-Y date
1233         // acceptance patterns, test combinations.
1234         SvNumberFormatter aFormatter(m_xContext, LANGUAGE_FRENCH);
1235 
1236         std::vector<FormatInputOutput> aIO = {
1237             { "22/11/1999", true, "22/11/99", 0 },      // if default YY changes to YYYY adapt this
1238             { "1999-11-22", true, "1999-11-22", 0 },
1239             { "1/2/11",     true, "01/02/11", 0 },      // if default YY changes to YYYY adapt this
1240             { "99-2-11",    true, "1999-02-11", 0 },
1241             { "22-2-11",    true, "22/02/11", 0 },      // if default YY changes to YYYY adapt this
1242             { "22.2.11",    true, "22/02/11", 0 }       // if default YY changes to YYYY adapt this
1243         };
1244 
1245         checkSpecificNumberFormats( aFormatter, aIO, "[fr-FR] date");
1246     }
1247 
1248     {
1249         // Test Spanish "mar" short name ambiguity, day "martes" or month "marzo".
1250         // Day of week names are only parsed away, not evaluated if they actually
1251         // correspond to the date given.
1252         SvNumberFormatter aFormatter(m_xContext, LANGUAGE_SPANISH);
1253 
1254         const sal_uInt32 n = aFormatter.GetFormatIndex( NF_DATE_SYS_DDMMYYYY, LANGUAGE_SPANISH);
1255         std::vector<FormatInputOutput> aIO = {
1256             { "22/11/1999", true, "22/11/1999", n },
1257             { "Lun 22/11/1999", true, "22/11/1999", n },
1258             { "Mar 22/11/1999", true, "22/11/1999", n },
1259             { "Abr 22/11/1999", false, "", n },             // month name AND numeric month don't go along
1260             { "Lun Mar 22/11/1999", false, "", n },         // month name AND numeric month don't go along
1261             { "Mar Mar 22/11/1999", false, "", n },         // month name AND numeric month don't go along
1262             { "Lun Mar 22 1999", true, "22/03/1999", n },
1263             { "Mar Mar 22 1999", true, "22/03/1999", n },
1264             { "Mar Lun 22 1999", false, "", n }             // day name only at the beginning (could change?)
1265         };
1266 
1267         checkSpecificNumberFormats( aFormatter, aIO, "[es-ES] date");
1268     }
1269 
1270     {
1271         // Test that de-DE accepts Januar and Jänner.
1272         SvNumberFormatter aFormatter(m_xContext, LANGUAGE_GERMAN);
1273 
1274         const sal_uInt32 n = aFormatter.GetFormatIndex( NF_DATE_SYS_DDMMYYYY, LANGUAGE_GERMAN);
1275         std::vector<FormatInputOutput> aIO = {
1276             { "23. Januar 1999", true, "23.01.1999", n },
1277             { "23. J\xC3\xA4nner 1999", true, "23.01.1999", n },
1278             { "23. Jan. 1999", true, "23.01.1999", n },
1279             { "23. J\xC3\xA4n. 1999", true, "23.01.1999", n },
1280         };
1281 
1282         checkSpecificNumberFormats( aFormatter, aIO, "[de-DE] date January month names");
1283     }
1284 
1285     {
1286         // Test that de-AT accepts Januar and Jänner.
1287         SvNumberFormatter aFormatter(m_xContext, LANGUAGE_GERMAN_AUSTRIAN);
1288 
1289         const sal_uInt32 n = aFormatter.GetFormatIndex( NF_DATE_SYS_DDMMYYYY, LANGUAGE_GERMAN_AUSTRIAN);
1290         std::vector<FormatInputOutput> aIO = {
1291             { "23. Januar 1999", true, "23.01.1999", n },
1292             { "23. J\xC3\xA4nner 1999", true, "23.01.1999", n },
1293             { "23. Jan. 1999", true, "23.01.1999", n },
1294             { "23. J\xC3\xA4n. 1999", true, "23.01.1999", n },
1295         };
1296 
1297         checkSpecificNumberFormats( aFormatter, aIO, "[de-AT] date January month names");
1298     }
1299 }
1300 
testUserDefinedNumberFormats()1301 void Test::testUserDefinedNumberFormats()
1302 {
1303     LanguageType eLang = LANGUAGE_ENGLISH_US;
1304     OUString sCode, sExpected;
1305     SvNumberFormatter aFormatter(m_xContext, eLang);
1306     {  // tdf#97835: suppress decimal separator
1307         sCode = "0.##\" m\"";
1308         sExpected = "12 m";
1309         checkPreviewString(aFormatter, sCode, 12.0, eLang, sExpected);
1310     }
1311     {  // tdf#61996: skip quoted text
1312         sCode = "0.00\" ;\"";
1313         sExpected = "-12.00 ;";
1314         checkPreviewString(aFormatter, sCode, -12.0, eLang, sExpected);
1315     }
1316     {  // tdf#95339: detect SSMM as second minute
1317         sCode =     "SS:MM:HH DD/MM/YY"; // Month not detected by Excel, but we do not follow that.
1318         sExpected = "53:23:03 02/01/00";
1319         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1320     }
1321     {  // tdf#101147: detect SSMM as second month
1322         sCode =     "HH:MM:SS MM/DD";
1323         sExpected = "03:23:53 01/02";
1324         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1325     }
1326     {  // tdf#101096: different detection of month/minute with Excel
1327         sCode =     "HH DD MM"; // month detected because of previous DD
1328         sExpected = "03 02 01";
1329         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1330         sCode =     "HH:MM HH DD/MM"; // month detected because of previous DD
1331         sExpected = "03:23 03 02/01";
1332         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1333         sCode =     "SS:DD-MM-YY SS:MM"; // 1st is month, because of previous DD; 2nd is minute as SS has not minute
1334         sExpected = "53:02-01-00 53:23";
1335         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1336     }
1337     {  // tdf#99996: better algorithm for fraction representation
1338         sCode = "# ?/???";
1339         sExpected = "-575 540/697";
1340         checkPreviewString(aFormatter, sCode, -575.774749601315, eLang, sExpected);
1341     }
1342     {  // tdf#102507: left alignment of denominator
1343         sCode = "# ?/???";
1344         sExpected = "3 1/2  ";
1345         checkPreviewString(aFormatter, sCode, 3.5, eLang, sExpected);
1346     }
1347     {  // tdf#100594: forced denominator
1348         sCode = "# ?/100";
1349         sExpected = " 6/100";
1350         checkPreviewString(aFormatter, sCode, 0.06, eLang, sExpected);
1351     }
1352     {  // tdf#100754: forced denominator with text after fraction
1353         sCode = "# ?/16\" inch\"";
1354         sExpected = "2 6/16 inch";
1355         checkPreviewString(aFormatter, sCode, 2.379, eLang, sExpected);
1356     }
1357     {  // tdf#100842: text before/after fraction
1358         sCode = "\"before \"?/?\" after\"";
1359         sExpected = "before 11/9 after";
1360         checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
1361         sCode = "\"before \"# ?/?\" after\"";
1362         sExpected = "before 1 2/9 after";
1363         checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
1364         sCode = "\"before \"0.0\"inside\"0E+0\"middle\"0\" after\"";
1365         sExpected = "before 1.2inside3E+0middle4 after";
1366         checkPreviewString(aFormatter, sCode, 12345.667, eLang, sExpected);
1367     }
1368     {  // tdf#106190: text after fraction bar
1369         sCode = "?/ ?";
1370         sExpected = "11/ 9";
1371         checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
1372         sCode = "?/ 12";
1373         sExpected = "15/ 12";
1374         checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
1375         sCode = "# ?/\" divisor \"?";
1376         sExpected = "1 2/ divisor 9";
1377         checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
1378         sCode = "# ?/\"divided by \"?";
1379         sExpected = "1 2/divided by 9";
1380         checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
1381         sCode = "?/\" \"12";
1382         sExpected = "15/ 12";
1383         checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
1384         sCode = "?/\\ 12";
1385         sExpected = "15/ 12";
1386         checkPreviewString(aFormatter, sCode, 1.2345667, eLang, sExpected);
1387         sCode = "# ?/ ???";
1388         sExpected = "3 1/ 2  ";
1389         checkPreviewString(aFormatter, sCode, 3.5, eLang, sExpected);
1390     }
1391     {  // Display 1.96 as 2 and not 1 1/1
1392         sCode = "# ?/?";
1393         sExpected = "2    ";
1394         checkPreviewString(aFormatter, sCode, 1.96, eLang, sExpected);
1395         sCode = "# ?/ ?";
1396         sExpected = "2     ";
1397         checkPreviewString(aFormatter, sCode, 1.96, eLang, sExpected);
1398         sCode = "# #/#";
1399         sExpected = "2";
1400         checkPreviewString(aFormatter, sCode, 1.96, eLang, sExpected);
1401     }
1402     {  // tdf#79399 tdf#101462 Native Number Formats
1403         sCode = "[NatNum5][$-0404]General\\ ";
1404         // Chinese upper case number characters for 120
1405         sExpected = u"\u58F9\u4F70\u8CB3\u62FE ";
1406         checkPreviewString(aFormatter, sCode, 120, eLang, sExpected);
1407         sCode = "[DBNum2][$-0404]General\\ ";
1408         checkPreviewString(aFormatter, sCode, 120, eLang, sExpected);
1409 #if ENABLE_LIBNUMBERTEXT
1410         // tdf#115007 - cardinal/ordinal number names/indicators
1411         sCode = "[NatNum12]0";
1412         sExpected = "one hundred twenty-three";
1413         checkPreviewString(aFormatter, sCode, 123, eLang, sExpected);
1414         sCode = "[NatNum12]0.00";
1415         sExpected = "one hundred twenty-three point four five";
1416         checkPreviewString(aFormatter, sCode, 123.45, eLang, sExpected);
1417         sCode = "[NatNum12 ordinal]0";
1418         sExpected = "one hundred twenty-third";
1419         checkPreviewString(aFormatter, sCode, 123, eLang, sExpected);
1420         sCode = "[NatNum12 ordinal-number]0";
1421         sExpected = "123rd";
1422         checkPreviewString(aFormatter, sCode, 123, eLang, sExpected);
1423         sCode = "[NatNum12 capitalize]0";
1424         sExpected = "One hundred twenty-three";
1425         checkPreviewString(aFormatter, sCode, 123, eLang, sExpected);
1426         sCode = "[NatNum12 title ordinal]0";
1427         sExpected = "One Thousand Two Hundred Thirty-Fourth";
1428         checkPreviewString(aFormatter, sCode, 1234, eLang, sExpected);
1429         sCode = "[NatNum12 upper ordinal-number]0";
1430         sExpected = "12345TH";
1431         checkPreviewString(aFormatter, sCode, 12345, eLang, sExpected);
1432         sCode = "[NatNum12 D=ordinal-number]D\" of \"MMMM";
1433         sExpected = "2nd of January";
1434         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1435         sCode = "[NatNum12 D=ordinal-number,YYYY=year]D\" of \"MMMM\", \"YYYY";
1436         sExpected = "2nd of January, nineteen hundred";
1437         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1438         sCode = "[NatNum12 YYYY=title year, D=capitalize ordinal]D\" of \"MMMM\", \"YYYY";
1439         sExpected = "Second of January, Nineteen Hundred";
1440         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1441 #endif
1442     }
1443     {  // tdf#105968 engineering format with value rounded up to next magnitude
1444         sCode = "##0.00E+00";
1445         sExpected = "100.00E+00";
1446         checkPreviewString(aFormatter, sCode, 99.995, eLang, sExpected);
1447         // test '1'=='1' assumption
1448         checkPreviewString(aFormatter, sCode, 100.0, eLang, sExpected);
1449         sExpected = "199.99E+00";
1450         checkPreviewString(aFormatter, sCode, 199.99, eLang, sExpected);
1451         sExpected = "1.00E+03";
1452         checkPreviewString(aFormatter, sCode, 1000.0, eLang, sExpected);
1453         // and another just "normally" rounded value
1454         sExpected = "894.55E-06";
1455         checkPreviewString(aFormatter, sCode, 0.000894549, eLang, sExpected);
1456         // not expecting rounding into another magnitude
1457         sExpected = "999.99E-06";
1458         checkPreviewString(aFormatter, sCode, 0.000999991, eLang, sExpected);
1459         // expecting rounding into another magnitude
1460         sExpected = "1.00E-03";
1461         checkPreviewString(aFormatter, sCode, 0.000999999, eLang, sExpected);
1462 
1463         // Now the same all negative values.
1464         sExpected = "-100.00E+00";
1465         checkPreviewString(aFormatter, sCode, -99.995, eLang, sExpected);
1466         checkPreviewString(aFormatter, sCode, -100.0, eLang, sExpected);
1467         sExpected = "-199.99E+00";
1468         checkPreviewString(aFormatter, sCode, -199.99, eLang, sExpected);
1469         sExpected = "-1.00E+03";
1470         checkPreviewString(aFormatter, sCode, -1000.0, eLang, sExpected);
1471         sExpected = "-894.55E-06";
1472         checkPreviewString(aFormatter, sCode, -0.000894549, eLang, sExpected);
1473         sExpected = "-999.99E-06";
1474         checkPreviewString(aFormatter, sCode, -0.000999991, eLang, sExpected);
1475         sExpected = "-1.00E-03";
1476         checkPreviewString(aFormatter, sCode, -0.000999999, eLang, sExpected);
1477     }
1478     {   // tdf#112933 one decimal seconds fraction
1479         sCode =     "MM:SS.0";
1480         sExpected = "23:53.6";
1481         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1482         // Two decimals.
1483         sCode =     "MM:SS.00";
1484         sExpected = "23:53.61";
1485         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1486         // Three decimals.
1487         sCode =     "MM:SS.000";
1488         sExpected = "23:53.605";
1489         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1490 
1491         // Same with date+time.
1492         sCode =     "YYYY-MM-DD MM:SS.0";
1493         sExpected = "1900-01-02 23:53.6";
1494         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1495         sCode =     "YYYY-MM-DD MM:SS.00";
1496         sExpected = "1900-01-02 23:53.61";
1497         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1498         sCode =     "YYYY-MM-DD MM:SS.000";
1499         sExpected = "1900-01-02 23:53.605";
1500         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1501     }
1502     {   // tdf#33689 use English NfKeywords in non-English language
1503         eLang = LANGUAGE_DUTCH;
1504         sExpected = "Dutch: 1900/01/02 03:23:53";
1505         sCode =     "\"Dutch:\" JJJJ/MM/DD UU:MM:SS";
1506         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1507         sCode =     "\"Dutch: \"YYYY/MM/DD HH:MM:SS";
1508         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1509         eLang = LANGUAGE_GERMAN;
1510         sExpected = "German: 1900/01/02 03:23:53";
1511         sCode =     "\"German: \"JJJJ/MM/TT HH:MM:SS";
1512         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1513         sCode =     "\"German: \"YYYY/MM/DD HH:MM:SS";
1514         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1515         eLang = LANGUAGE_FRENCH;
1516         sExpected = "French: 1900/01/02 03:23:53";
1517         sCode =     "\"French: \"AAAA/MM/JJ HH:MM:SS";
1518         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1519         sCode =     "\"French: \"YYYY/MM/DD HH:MM:SS";
1520         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1521         eLang = LANGUAGE_ITALIAN;
1522         sExpected = "Italian: 1900/01/02 03:23:53";
1523         sCode =     "\"Italian: \"AAAA/MM/GG HH:MM:SS";
1524         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1525         sCode =     "\"Italian: \"YYYY/MM/DD HH:MM:SS";
1526         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1527         eLang = LANGUAGE_PORTUGUESE;
1528         sExpected = "Portuguese: 1900/01/02 03:23:53";
1529         sCode =     "\"Portuguese: \"AAAA/MM/DD HH:MM:SS";
1530         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1531         sCode =     "\"Portuguese: \"YYYY/MM/DD HH:MM:SS";
1532         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1533         eLang = LANGUAGE_SPANISH_MODERN;
1534         sExpected = "Spanish: 1900/01/02 03:23:53";
1535         sCode =     "\"Spanish: \"AAAA/MM/DD HH:MM:SS";
1536         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1537         sCode =     "\"Spanish: \"YYYY/MM/DD HH:MM:SS";
1538         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1539         eLang = LANGUAGE_DANISH;
1540         sExpected = "Danish: 1900/01/02 03:23:53";
1541         sCode =     "\"Danish: \"YYYY/MM/DD TT:MM:SS";
1542         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1543         sCode =     "\"Danish: \"YYYY/MM/DD HH:MM:SS";
1544         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1545         eLang = LANGUAGE_FINNISH;
1546         sExpected = "Finnish: 1900/01/02 03:23:53";
1547         sCode =     "\"Finnish: \"VVVV/KK/PP TT:MM:SS";
1548         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1549         sCode =     "\"Finnish: \"YYYY/MM/DD HH:MM:SS";
1550         checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected);
1551     }
1552     {   // tdf#117819 wrong separator positions when displaying integers with
1553         // more decimals than rtl::math::doubleToUString delivers.
1554         sCode = "#,##0.00000000000000000000";
1555         sExpected = "117,669,030,460,994.00000000000000000000";
1556         checkPreviewString(aFormatter, sCode, 117669030460994.0, LANGUAGE_ENGLISH_US, sExpected);
1557     }
1558     {   // tdf#117575 treat thousand separator with '?' in integer part
1559         sCode = "\"Value= \"?,??0.00";
1560         sExpected = "Value=     3.14";
1561         checkPreviewString(aFormatter, sCode, M_PI, LANGUAGE_ENGLISH_US, sExpected);
1562         sExpected = "Value=    12.00";
1563         checkPreviewString(aFormatter, sCode, 12, LANGUAGE_ENGLISH_US, sExpected);
1564         sExpected = "Value=   123.00";
1565         checkPreviewString(aFormatter, sCode, 123, LANGUAGE_ENGLISH_US, sExpected);
1566         sExpected = "Value= 1,234.00";
1567         checkPreviewString(aFormatter, sCode, 1234, LANGUAGE_ENGLISH_US, sExpected);
1568         sExpected = "Value= 12,345.00";
1569         checkPreviewString(aFormatter, sCode, 12345, LANGUAGE_ENGLISH_US, sExpected);
1570     }
1571 }
1572 
testNfEnglishKeywordsIntegrity()1573 void Test::testNfEnglishKeywordsIntegrity()
1574 {
1575     SvNumberFormatter aFormatter(m_xContext, LANGUAGE_ENGLISH_US);
1576     const NfKeywordTable& rEnglishKeywords = aFormatter.GetEnglishKeywords();
1577     const NfKeywordTable& sKeywords = aFormatter.GetKeywords(0);
1578     CPPUNIT_ASSERT_EQUAL( size_t(NF_KEYWORD_ENTRIES_COUNT), rEnglishKeywords.size() );
1579     for (size_t i = 0; i < size_t(NF_KEYWORD_ENTRIES_COUNT); ++i)
1580     {
1581         CPPUNIT_ASSERT_EQUAL( sKeywords[i], rEnglishKeywords[i] );
1582     }
1583     // Check the order of sEnglishKeyword
1584     CPPUNIT_ASSERT_EQUAL( OUString("E"),     rEnglishKeywords[NF_KEY_E] );
1585     CPPUNIT_ASSERT_EQUAL( OUString("AM/PM"), rEnglishKeywords[NF_KEY_AMPM] );
1586     CPPUNIT_ASSERT_EQUAL( OUString("A/P"),   rEnglishKeywords[NF_KEY_AP] );
1587     CPPUNIT_ASSERT_EQUAL( OUString("M"),     rEnglishKeywords[NF_KEY_MI]  );
1588     CPPUNIT_ASSERT_EQUAL( OUString("MM"),    rEnglishKeywords[NF_KEY_MMI] );
1589     CPPUNIT_ASSERT_EQUAL( OUString("M"),     rEnglishKeywords[NF_KEY_M] );
1590     CPPUNIT_ASSERT_EQUAL( OUString("MM"),    rEnglishKeywords[NF_KEY_MM] );
1591     CPPUNIT_ASSERT_EQUAL( OUString("MMM"),   rEnglishKeywords[NF_KEY_MMM] );
1592     CPPUNIT_ASSERT_EQUAL( OUString("MMMM"),  rEnglishKeywords[NF_KEY_MMMM] );
1593     CPPUNIT_ASSERT_EQUAL( OUString("H"),     rEnglishKeywords[NF_KEY_H] );
1594     CPPUNIT_ASSERT_EQUAL( OUString("HH"),    rEnglishKeywords[NF_KEY_HH] );
1595     CPPUNIT_ASSERT_EQUAL( OUString("S"),     rEnglishKeywords[NF_KEY_S] );
1596     CPPUNIT_ASSERT_EQUAL( OUString("SS"),    rEnglishKeywords[NF_KEY_SS] );
1597     CPPUNIT_ASSERT_EQUAL( OUString("Q"),     rEnglishKeywords[NF_KEY_Q] );
1598     CPPUNIT_ASSERT_EQUAL( OUString("QQ"),    rEnglishKeywords[NF_KEY_QQ] );
1599     CPPUNIT_ASSERT_EQUAL( OUString("D"),     rEnglishKeywords[NF_KEY_D] );
1600     CPPUNIT_ASSERT_EQUAL( OUString("DD"),    rEnglishKeywords[NF_KEY_DD] );
1601     CPPUNIT_ASSERT_EQUAL( OUString("DDD"),   rEnglishKeywords[NF_KEY_DDD] );
1602     CPPUNIT_ASSERT_EQUAL( OUString("DDDD"),  rEnglishKeywords[NF_KEY_DDDD] );
1603     CPPUNIT_ASSERT_EQUAL( OUString("YY"),    rEnglishKeywords[NF_KEY_YY] );
1604     CPPUNIT_ASSERT_EQUAL( OUString("YYYY"),  rEnglishKeywords[NF_KEY_YYYY] );
1605     CPPUNIT_ASSERT_EQUAL( OUString("NN"),    rEnglishKeywords[NF_KEY_NN] );
1606     CPPUNIT_ASSERT_EQUAL( OUString("NNNN"),  rEnglishKeywords[NF_KEY_NNNN] );
1607     CPPUNIT_ASSERT_EQUAL( OUString("CCC"),   rEnglishKeywords[NF_KEY_CCC] );
1608     CPPUNIT_ASSERT_EQUAL( OUString("GENERAL"), rEnglishKeywords[NF_KEY_GENERAL] );
1609     CPPUNIT_ASSERT_EQUAL( OUString("NNN"),     rEnglishKeywords[NF_KEY_NNN] );
1610     CPPUNIT_ASSERT_EQUAL( OUString("WW"),      rEnglishKeywords[NF_KEY_WW] );
1611     CPPUNIT_ASSERT_EQUAL( OUString("MMMMM"),   rEnglishKeywords[NF_KEY_MMMMM] );
1612     CPPUNIT_ASSERT_EQUAL( OUString("TRUE"),    rEnglishKeywords[NF_KEY_TRUE] );
1613     CPPUNIT_ASSERT_EQUAL( OUString("FALSE"),   rEnglishKeywords[NF_KEY_FALSE] );
1614     CPPUNIT_ASSERT_EQUAL( OUString("BOOLEAN"), rEnglishKeywords[NF_KEY_BOOLEAN] );
1615     CPPUNIT_ASSERT_EQUAL( OUString("COLOR"),   rEnglishKeywords[NF_KEY_COLOR] );
1616     CPPUNIT_ASSERT_EQUAL( OUString("BLACK"),   rEnglishKeywords[NF_KEY_BLACK] );
1617     CPPUNIT_ASSERT_EQUAL( OUString("BLUE"),    rEnglishKeywords[NF_KEY_BLUE] );
1618     CPPUNIT_ASSERT_EQUAL( OUString("GREEN"),   rEnglishKeywords[NF_KEY_GREEN] );
1619     CPPUNIT_ASSERT_EQUAL( OUString("CYAN"),    rEnglishKeywords[NF_KEY_CYAN] );
1620     CPPUNIT_ASSERT_EQUAL( OUString("RED"),     rEnglishKeywords[NF_KEY_RED] );
1621     CPPUNIT_ASSERT_EQUAL( OUString("MAGENTA"), rEnglishKeywords[NF_KEY_MAGENTA] );
1622     CPPUNIT_ASSERT_EQUAL( OUString("BROWN"),   rEnglishKeywords[NF_KEY_BROWN] );
1623     CPPUNIT_ASSERT_EQUAL( OUString("GREY"),    rEnglishKeywords[NF_KEY_GREY] );
1624     CPPUNIT_ASSERT_EQUAL( OUString("YELLOW"),  rEnglishKeywords[NF_KEY_YELLOW] );
1625     CPPUNIT_ASSERT_EQUAL( OUString("WHITE"),   rEnglishKeywords[NF_KEY_WHITE] );
1626     CPPUNIT_ASSERT_EQUAL( OUString("AAA"),   rEnglishKeywords[NF_KEY_AAA]);
1627     CPPUNIT_ASSERT_EQUAL( OUString("AAAA"),  rEnglishKeywords[NF_KEY_AAAA] );
1628     CPPUNIT_ASSERT_EQUAL( OUString("E"),     rEnglishKeywords[NF_KEY_EC] );
1629     CPPUNIT_ASSERT_EQUAL( OUString("EE"),    rEnglishKeywords[NF_KEY_EEC] );
1630     CPPUNIT_ASSERT_EQUAL( OUString("G"),     rEnglishKeywords[NF_KEY_G] );
1631     CPPUNIT_ASSERT_EQUAL( OUString("GG"),    rEnglishKeywords[NF_KEY_GG] );
1632     CPPUNIT_ASSERT_EQUAL( OUString("GGG"),   rEnglishKeywords[NF_KEY_GGG] );
1633     CPPUNIT_ASSERT_EQUAL( OUString("R"),     rEnglishKeywords[NF_KEY_R]  );
1634     CPPUNIT_ASSERT_EQUAL( OUString("RR"),    rEnglishKeywords[NF_KEY_RR]  );
1635     CPPUNIT_ASSERT_EQUAL( OUString("t"),     rEnglishKeywords[NF_KEY_THAI_T] );
1636 }
1637 
testStandardColorIntegrity()1638 void Test::testStandardColorIntegrity()
1639 {
1640     SvNumberFormatter aFormatter(m_xContext, LANGUAGE_ENGLISH_US);
1641     const ::std::vector<Color> & rStandardColors = aFormatter.GetStandardColors();
1642     const size_t nMaxDefaultColors = aFormatter.GetMaxDefaultColors();
1643     CPPUNIT_ASSERT_EQUAL( size_t(NF_KEY_LASTCOLOR) - size_t(NF_KEY_FIRSTCOLOR) + 1, nMaxDefaultColors );
1644     CPPUNIT_ASSERT_EQUAL( nMaxDefaultColors, rStandardColors.size() );
1645     // Colors must follow same order as in sEnglishKeyword
1646     CPPUNIT_ASSERT_EQUAL( rStandardColors[0], COL_BLACK );
1647     CPPUNIT_ASSERT_EQUAL( rStandardColors[1], COL_LIGHTBLUE );
1648     CPPUNIT_ASSERT_EQUAL( rStandardColors[2], COL_LIGHTGREEN );
1649     CPPUNIT_ASSERT_EQUAL( rStandardColors[3], COL_LIGHTCYAN );
1650     CPPUNIT_ASSERT_EQUAL( rStandardColors[4], COL_LIGHTRED );
1651     CPPUNIT_ASSERT_EQUAL( rStandardColors[5], COL_LIGHTMAGENTA );
1652     CPPUNIT_ASSERT_EQUAL( rStandardColors[6], COL_BROWN );
1653     CPPUNIT_ASSERT_EQUAL( rStandardColors[7], COL_GRAY );
1654     CPPUNIT_ASSERT_EQUAL( rStandardColors[8], COL_YELLOW );
1655     CPPUNIT_ASSERT_EQUAL( rStandardColors[9], COL_WHITE );
1656 }
1657 
testColorNamesConversion()1658 void Test::testColorNamesConversion()
1659 {
1660     SvNumberFormatter aFormatter(m_xContext, LANGUAGE_GERMAN);
1661     const NfKeywordTable& rEnglishKeywords = aFormatter.GetEnglishKeywords();
1662     const NfKeywordTable& rKeywords = aFormatter.GetKeywords(0);
1663 
1664     // Holding a reference to the NfKeywordTable doesn't help if we switch
1665     // locales internally, so copy the relevant parts in advance.
1666     std::vector<OUString> aGermanKeywords(NF_KEYWORD_ENTRIES_COUNT);
1667     for (size_t i = NF_KEY_COLOR; i <= NF_KEY_WHITE; ++i)
1668         aGermanKeywords[i] = rKeywords[i];
1669 
1670     // Check that we actually have German and English keywords.
1671     CPPUNIT_ASSERT_EQUAL( OUString("FARBE"), aGermanKeywords[NF_KEY_COLOR]);
1672     CPPUNIT_ASSERT_EQUAL( OUString("COLOR"), rEnglishKeywords[NF_KEY_COLOR]);
1673 
1674     // Test each color conversion.
1675     // [FARBE1] -> [COLOR1] can't be tested because we have no color table link
1676     // set, so the scanner returns nCheckPos error.
1677     sal_Int32 nCheckPos;
1678     SvNumFormatType nType;
1679     sal_uInt32 nKey;
1680     OUString aFormatCode;
1681 
1682     for (size_t i = NF_KEY_BLACK; i <= NF_KEY_WHITE; ++i)
1683     {
1684         aFormatCode = "[" + aGermanKeywords[i] + "]0";
1685         aFormatter.PutandConvertEntry( aFormatCode, nCheckPos, nType, nKey, LANGUAGE_GERMAN, LANGUAGE_ENGLISH_US, false);
1686         CPPUNIT_ASSERT_EQUAL_MESSAGE("CheckPos should be 0.", sal_Int32(0), nCheckPos);
1687         CPPUNIT_ASSERT_EQUAL_MESSAGE("Type should be NUMBER.", SvNumFormatType::NUMBER, nType);
1688         CPPUNIT_ASSERT_EQUAL( OUString("[" + rEnglishKeywords[i] + "]0"), aFormatCode);
1689     }
1690 }
1691 
testExcelExportFormats()1692 void Test::testExcelExportFormats()
1693 {
1694     // Create a formatter with "system" locale other than the specific formats'
1695     // locale, and different from the en-US export locale.
1696     SvNumberFormatter aFormatter( m_xContext, LANGUAGE_ENGLISH_UK);
1697 
1698     OUString aCode;
1699     sal_Int32 nCheckPos;
1700     SvNumFormatType eType;
1701     sal_uInt32 nKey1, nKey2;
1702 
1703     aCode = "00.00";
1704     aFormatter.PutandConvertEntry( aCode, nCheckPos, eType, nKey1,
1705             LANGUAGE_ENGLISH_US, LANGUAGE_ENGLISH_SAFRICA, false);
1706     CPPUNIT_ASSERT_EQUAL_MESSAGE("CheckPos should be 0.", sal_Int32(0), nCheckPos);
1707     CPPUNIT_ASSERT_MESSAGE("Key should be greater than system locale's keys.",
1708             nKey1 > SV_COUNTRY_LANGUAGE_OFFSET);
1709 
1710     aCode = "[$R-1C09] #,##0.0;[$R-1C09]-#,##0.0";
1711     aFormatter.PutandConvertEntry( aCode, nCheckPos, eType, nKey2,
1712             LANGUAGE_ENGLISH_US, LANGUAGE_ENGLISH_SAFRICA, false);
1713     CPPUNIT_ASSERT_EQUAL_MESSAGE("CheckPos should be 0.", sal_Int32(0), nCheckPos);
1714     CPPUNIT_ASSERT_MESSAGE("Key should be greater than system locale's keys.",
1715             nKey2 > SV_COUNTRY_LANGUAGE_OFFSET);
1716 
1717     // The export formatter.
1718     SvNumberFormatter aTempFormatter( m_xContext, LANGUAGE_ENGLISH_US);
1719     NfKeywordTable aKeywords;
1720     aTempFormatter.FillKeywordTableForExcel( aKeywords);
1721 
1722     aCode = aFormatter.GetFormatStringForExcel( nKey1, aKeywords, aTempFormatter);
1723     // Test that LCID is prepended.
1724     CPPUNIT_ASSERT_EQUAL( OUString("[$-1C09]00.00"), aCode);
1725 
1726     aCode = aFormatter.GetFormatStringForExcel( nKey2, aKeywords, aTempFormatter);
1727     // Test that LCID is not prepended. Note that literal characters are escaped.
1728     CPPUNIT_ASSERT_EQUAL( OUString("[$R-1C09]\\ #,##0.0;[$R-1C09]\\-#,##0.0"), aCode);
1729 }
1730 
1731 CPPUNIT_TEST_SUITE_REGISTRATION(Test);
1732 
1733 }
1734 
1735 CPPUNIT_PLUGIN_IMPLEMENT();
1736 
1737 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1738