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