xref: /core/svl/source/numbers/zforfind.cxx (revision cd747133)
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  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <cstdlib>
21 #include <dtoa.h>
22 #include <float.h>
23 #include <comphelper/string.hxx>
24 #include <sal/log.hxx>
25 #include <tools/date.hxx>
26 #include <rtl/math.hxx>
27 #include <rtl/character.hxx>
28 #include <unotools/charclass.hxx>
29 #include <unotools/calendarwrapper.hxx>
30 #include <unotools/localedatawrapper.hxx>
31 #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
32 #include <com/sun/star/i18n/LocaleCalendar2.hpp>
33 #include <unotools/digitgroupingiterator.hxx>
34 #include <comphelper/sequence.hxx>
35 
36 #include <svl/zforlist.hxx>
37 #include "zforscan.hxx"
38 #include <svl/zformat.hxx>
39 
40 #include <memory>
41 
42 #include "zforfind.hxx"
43 
44 #ifndef DBG_UTIL
45 #define NF_TEST_CALENDAR 0
46 #else
47 #define NF_TEST_CALENDAR 0
48 #endif
49 #if NF_TEST_CALENDAR
50 #include <comphelper/processfactory.hxx>
51 #include <com/sun/star/i18n/XCalendar4.hpp>
52 #endif
53 
54 
55 const sal_uInt8 ImpSvNumberInputScan::nMatchedEndString    = 0x01;
56 const sal_uInt8 ImpSvNumberInputScan::nMatchedMidString    = 0x02;
57 const sal_uInt8 ImpSvNumberInputScan::nMatchedStartString  = 0x04;
58 const sal_uInt8 ImpSvNumberInputScan::nMatchedVirgin       = 0x08;
59 const sal_uInt8 ImpSvNumberInputScan::nMatchedUsedAsReturn = 0x10;
60 
61 /* It is not clear how we want timezones to be handled. Convert them to local
62  * time isn't wanted, as it isn't done in any other place and timezone
63  * information isn't stored anywhere. Ignoring them and pretending local time
64  * may be wrong too and might not be what the user expects. Keep the input as
65  * string so that no information is lost.
66  * Anyway, defining NF_RECOGNIZE_ISO8601_TIMEZONES to 1 would be the way how it
67  * would work, together with the nTimezonePos handling in GetTimeRef(). */
68 #define NF_RECOGNIZE_ISO8601_TIMEZONES 0
69 
70 static const sal_Unicode cNoBreakSpace = 0xA0;
71 static const sal_Unicode cNarrowNoBreakSpace = 0x202F;
72 static const bool kDefaultEra = true;     // Gregorian CE, positive year
73 
74 ImpSvNumberInputScan::ImpSvNumberInputScan( SvNumberFormatter* pFormatterP )
75         :
76         bTextInitialized( false ),
77         bScanGenitiveMonths( false ),
78         bScanPartitiveMonths( false ),
79         eScannedType( SvNumFormatType::UNDEFINED ),
80         eSetType( SvNumFormatType::UNDEFINED )
81 {
82     pFormatter = pFormatterP;
83     pNullDate.reset( new Date(30,12,1899) );
84     nYear2000 = SvNumberFormatter::GetYear2000Default();
85     Reset();
86     ChangeIntl();
87 }
88 
89 
90 ImpSvNumberInputScan::~ImpSvNumberInputScan()
91 {
92 }
93 
94 
95 void ImpSvNumberInputScan::Reset()
96 {
97     mpFormat     = nullptr;
98     nMonth       = 0;
99     nMonthPos    = 0;
100     nDayOfWeek   = 0;
101     nTimePos     = 0;
102     nSign        = 0;
103     nESign       = 0;
104     nDecPos      = 0;
105     bNegCheck    = false;
106     nStringsCnt  = 0;
107     nNumericsCnt = 0;
108     nThousand    = 0;
109     eScannedType = SvNumFormatType::UNDEFINED;
110     nAmPm        = 0;
111     nPosThousandString = 0;
112     nLogical     = 0;
113     mbEraCE        = kDefaultEra;
114     nStringScanNumFor = 0;
115     nStringScanSign = 0;
116     nMatchedAllStrings = nMatchedVirgin;
117     nMayBeIso8601 = 0;
118     bIso8601Tsep = false;
119     nMayBeMonthDate = 0;
120     nAcceptedDatePattern = -2;
121     nDatePatternStart = 0;
122     nDatePatternNumbers = 0;
123 
124     for (sal_uInt32 i = 0; i < SV_MAX_COUNT_INPUT_STRINGS; i++)
125     {
126         IsNum[i] = false;
127         nNums[i] = 0;
128     }
129 }
130 
131 // native number transliteration if necessary
132 static void TransformInput( SvNumberFormatter const * pFormatter, OUString& rStr )
133 {
134     sal_Int32 nPos, nLen;
135     for ( nPos = 0, nLen = rStr.getLength(); nPos < nLen; ++nPos )
136     {
137         if ( 256 <= rStr[ nPos ] &&
138              pFormatter->GetCharClass()->isDigit( rStr, nPos ) )
139         {
140             break;
141         }
142     }
143     if ( nPos < nLen )
144     {
145         rStr = pFormatter->GetNatNum()->getNativeNumberString( rStr,
146                                                                pFormatter->GetLanguageTag().getLocale(), 0 );
147     }
148 }
149 
150 
151 /**
152  * Only simple unsigned floating point values without any error detection,
153  * decimal separator has to be '.'
154  */
155 double ImpSvNumberInputScan::StringToDouble( const OUString& rStr, bool bForceFraction )
156 {
157     std::unique_ptr<char[]> bufInHeap;
158     constexpr int bufOnStackSize = 256;
159     char bufOnStack[bufOnStackSize];
160     char* buf = bufOnStack;
161     const sal_Int32 bufsize = rStr.getLength() + (bForceFraction ? 2 : 1);
162     if (bufsize > bufOnStackSize)
163     {
164         bufInHeap = std::make_unique<char[]>(bufsize);
165         buf = bufInHeap.get();
166     }
167     char* p = buf;
168     if (bForceFraction)
169         *p++ = '.';
170     for (sal_Int32 nPos = 0; nPos < rStr.getLength(); ++nPos)
171     {
172         sal_Unicode c = rStr[nPos];
173         if (c == '.' || (c >= '0' && c <= '9'))
174             *p++ = static_cast<char>(c);
175         else
176             break;
177     }
178     *p = '\0';
179 
180     return strtod_nolocale(buf, nullptr);
181 }
182 
183 namespace {
184 
185 /**
186  * Splits up the input into numbers and strings for further processing
187  * (by the Turing machine).
188  *
189  * Starting state = GetChar
190  * ---------------+-------------------+-----------------------------+---------------
191  *  Old State     | Character read    | Event                       | New state
192  * ---------------+-------------------+-----------------------------+---------------
193  *  GetChar       | Number            | Symbol = Character          | GetValue
194  *                | Else              | Symbol = Character          | GetString
195  * ---------------|-------------------+-----------------------------+---------------
196  *  GetValue      | Number            | Symbol = Symbol + Character | GetValue
197  *                | Else              | Dec(CharPos)                | Stop
198  * ---------------+-------------------+-----------------------------+---------------
199  *  GetString     | Number            | Dec(CharPos)                | Stop
200  *                | Else              | Symbol = Symbol + Character | GetString
201  * ---------------+-------------------+-----------------------------+---------------
202  */
203 enum ScanState  // States of the Turing machine
204 {
205     SsStop      = 0,
206     SsStart     = 1,
207     SsGetValue  = 2,
208     SsGetString = 3
209 };
210 
211 }
212 
213 bool ImpSvNumberInputScan::NextNumberStringSymbol( const sal_Unicode*& pStr,
214                                                    OUString& rSymbol )
215 {
216     bool isNumber = false;
217     sal_Unicode cToken;
218     ScanState eState = SsStart;
219     const sal_Unicode* pHere = pStr;
220     sal_Int32 nChars = 0;
221 
222     for (;;)
223     {
224         cToken = *pHere;
225         if (cToken == 0 || eState == SsStop)
226             break;
227         pHere++;
228         switch (eState)
229         {
230         case SsStart:
231             if ( rtl::isAsciiDigit( cToken ) )
232             {
233                 eState = SsGetValue;
234                 isNumber = true;
235             }
236             else
237             {
238                 eState = SsGetString;
239             }
240             nChars++;
241             break;
242         case SsGetValue:
243             if ( rtl::isAsciiDigit( cToken ) )
244             {
245                 nChars++;
246             }
247             else
248             {
249                 eState = SsStop;
250                 pHere--;
251             }
252             break;
253         case SsGetString:
254             if ( !rtl::isAsciiDigit( cToken ) )
255             {
256                 nChars++;
257             }
258             else
259             {
260                 eState = SsStop;
261                 pHere--;
262             }
263             break;
264         default:
265             break;
266         } // switch
267     } // while
268 
269     if ( nChars )
270     {
271         rSymbol = OUString( pStr, nChars );
272     }
273     else
274     {
275         rSymbol.clear();
276     }
277 
278     pStr = pHere;
279 
280     return isNumber;
281 }
282 
283 
284 // FIXME: should be grouping; it is only used though in case nStringsCnt is
285 // near SV_MAX_COUNT_INPUT_STRINGS, in NumberStringDivision().
286 
287 bool ImpSvNumberInputScan::SkipThousands( const sal_Unicode*& pStr,
288                                           OUString& rSymbol ) const
289 {
290     bool res = false;
291     OUStringBuffer sBuff(rSymbol);
292     sal_Unicode cToken;
293     const OUString& rThSep = pFormatter->GetNumThousandSep();
294     const sal_Unicode* pHere = pStr;
295     ScanState eState = SsStart;
296     sal_Int32 nCounter = 0; // counts 3 digits
297 
298     for (;;)
299     {
300         cToken = *pHere;
301         if (cToken == 0 || eState == SsStop)
302             break;
303         pHere++;
304         switch (eState)
305         {
306         case SsStart:
307             if ( StringPtrContains( rThSep, pHere-1, 0 ) )
308             {
309                 nCounter = 0;
310                 eState = SsGetValue;
311                 pHere += rThSep.getLength() - 1;
312             }
313             else
314             {
315                 eState = SsStop;
316                 pHere--;
317             }
318             break;
319         case SsGetValue:
320             if ( rtl::isAsciiDigit( cToken ) )
321             {
322                 sBuff.append(cToken);
323                 nCounter++;
324                 if (nCounter == 3)
325                 {
326                     eState = SsStart;
327                     res = true; // .000 combination found
328                 }
329             }
330             else
331             {
332                 eState = SsStop;
333                 pHere--;
334             }
335             break;
336         default:
337             break;
338         } // switch
339     } // while
340 
341     if (eState == SsGetValue) // break with less than 3 digits
342     {
343         if ( nCounter )
344         {
345             sBuff.remove( sBuff.getLength() - nCounter, nCounter );
346         }
347         pHere -= nCounter + rThSep.getLength(); // put back ThSep also
348     }
349     rSymbol = sBuff.makeStringAndClear();
350     pStr = pHere;
351 
352     return res;
353 }
354 
355 
356 void ImpSvNumberInputScan::NumberStringDivision( const OUString& rString )
357 {
358     const sal_Unicode* pStr = rString.getStr();
359     const sal_Unicode* const pEnd = pStr + rString.getLength();
360     while ( pStr < pEnd && nStringsCnt < SV_MAX_COUNT_INPUT_STRINGS )
361     {
362         if ( NextNumberStringSymbol( pStr, sStrArray[nStringsCnt] ) )
363         {   // Number
364             IsNum[nStringsCnt] = true;
365             nNums[nNumericsCnt] = nStringsCnt;
366             nNumericsCnt++;
367             if (nStringsCnt >= SV_MAX_COUNT_INPUT_STRINGS - 7 &&
368                 nPosThousandString == 0) // Only once
369             {
370                 if ( SkipThousands( pStr, sStrArray[nStringsCnt] ) )
371                 {
372                     nPosThousandString = nStringsCnt;
373                 }
374             }
375         }
376         else
377         {
378             IsNum[nStringsCnt] = false;
379         }
380         nStringsCnt++;
381     }
382 }
383 
384 
385 /**
386  * Whether rString contains rWhat at nPos
387  */
388 bool ImpSvNumberInputScan::StringContainsImpl( const OUString& rWhat,
389                                                const OUString& rString, sal_Int32 nPos )
390 {
391     if ( nPos + rWhat.getLength() <= rString.getLength() )
392     {
393         return StringPtrContainsImpl( rWhat, rString.getStr(), nPos );
394     }
395     return false;
396 }
397 
398 
399 /**
400  * Whether pString contains rWhat at nPos
401  */
402 bool ImpSvNumberInputScan::StringPtrContainsImpl( const OUString& rWhat,
403                                                   const sal_Unicode* pString, sal_Int32 nPos )
404 {
405     if ( rWhat.isEmpty() )
406     {
407         return false;
408     }
409     const sal_Unicode* pWhat = rWhat.getStr();
410     const sal_Unicode* const pEnd = pWhat + rWhat.getLength();
411     const sal_Unicode* pStr = pString + nPos;
412     while ( pWhat < pEnd )
413     {
414         if ( *pWhat != *pStr )
415         {
416             return false;
417         }
418         pWhat++;
419         pStr++;
420     }
421     return true;
422 }
423 
424 
425 /**
426  * Whether rString contains word rWhat at nPos
427  */
428 bool ImpSvNumberInputScan::StringContainsWord( const OUString& rWhat,
429                                                const OUString& rString, sal_Int32 nPos ) const
430 {
431     if (rWhat.isEmpty() || rString.getLength() < nPos + rWhat.getLength())
432         return false;
433 
434     if (StringPtrContainsImpl( rWhat, rString.getStr(), nPos))
435     {
436         nPos += rWhat.getLength();
437         if (nPos == rString.getLength())
438             return true;    // word at end of string
439 
440         /* TODO: we COULD invoke bells and whistles word break iterator to find
441          * the next boundary, but really ... this is called for date input, so
442          * how many languages do not separate the day and month names in some
443          * form? */
444 
445         // Check simple ASCII first before invoking i18n or anything else.
446         const sal_Unicode c = rString[nPos];
447 
448         // Common separating ASCII characters in date context.
449         switch (c)
450         {
451             case ' ':
452             case '-':
453             case '.':
454             case '/':
455                 return true;
456             default:
457                 ;   // nothing
458         }
459 
460         if (rtl::isAsciiAlphanumeric( c ))
461             return false;   // Alpha or numeric is not word gap.
462 
463         sal_Int32 nIndex = nPos;
464         rString.iterateCodePoints( &nIndex);
465         if (nPos+1 < nIndex)
466             return true;    // Surrogate, assume these to be new words.
467 
468         const sal_Int32 nType = pFormatter->GetCharClass()->getCharacterType( rString, nPos);
469         using namespace ::com::sun::star::i18n;
470 
471         if ((nType & (KCharacterType::UPPER | KCharacterType::LOWER | KCharacterType::DIGIT)) != 0)
472             return false;   // Alpha or numeric is not word gap.
473 
474         if (nType & KCharacterType::LETTER)
475             return true;    // Letter other than alpha is new word. (Is it?)
476 
477         return true;        // Catch all remaining as gap until we know better.
478     }
479 
480     return false;
481 }
482 
483 
484 /**
485  * Skips the supplied char
486  */
487 inline bool ImpSvNumberInputScan::SkipChar( sal_Unicode c, const OUString& rString,
488                                             sal_Int32& nPos )
489 {
490     if ((nPos < rString.getLength()) && (rString[nPos] == c))
491     {
492         nPos++;
493         return true;
494     }
495     return false;
496 }
497 
498 
499 /**
500  * Skips blanks
501  */
502 inline void ImpSvNumberInputScan::SkipBlanks( const OUString& rString,
503                                               sal_Int32& nPos )
504 {
505     if ( nPos < rString.getLength() )
506     {
507         const sal_Unicode* p = rString.getStr() + nPos;
508         while ( *p == ' ' || *p == cNoBreakSpace || *p == cNarrowNoBreakSpace )
509         {
510             nPos++;
511             p++;
512         }
513     }
514 }
515 
516 
517 /**
518  * jump over rWhat in rString at nPos
519  */
520 inline bool ImpSvNumberInputScan::SkipString( const OUString& rWhat,
521                                               const OUString& rString, sal_Int32& nPos )
522 {
523     if ( StringContains( rWhat, rString, nPos ) )
524     {
525         nPos = nPos + rWhat.getLength();
526         return true;
527     }
528     return false;
529 }
530 
531 
532 /**
533  * Recognizes exactly ,111 in {3} and {3,2} or ,11 in {3,2} grouping
534  */
535 inline bool ImpSvNumberInputScan::GetThousandSep( const OUString& rString,
536                                                   sal_Int32& nPos,
537                                                   sal_uInt16 nStringPos ) const
538 {
539     const OUString& rSep = pFormatter->GetNumThousandSep();
540     // Is it an ordinary space instead of a no-break space?
541     bool bSpaceBreak = (rSep[0] == cNoBreakSpace || rSep[0] == cNarrowNoBreakSpace) &&
542         rString[0] == u' ' &&
543         rSep.getLength() == 1 && rString.getLength() == 1;
544     if (!((rString == rSep || bSpaceBreak) &&      // nothing else
545            nStringPos < nStringsCnt - 1 &&         // safety first!
546            IsNum[ nStringPos + 1 ] ))              // number follows
547     {
548         return false; // no? => out
549     }
550 
551     utl::DigitGroupingIterator aGrouping( pFormatter->GetLocaleData()->getDigitGrouping());
552     // Match ,### in {3} or ,## in {3,2}
553     /* FIXME: this could be refined to match ,## in {3,2} only if ,##,## or
554      * ,##,### and to match ,### in {3,2} only if it's the last. However,
555      * currently there is no track kept where group separators occur. In {3,2}
556      * #,###,### and #,##,## would be valid input, which maybe isn't even bad
557      * for #,###,###. Other combinations such as #,###,## maybe not. */
558     sal_Int32 nLen = sStrArray[ nStringPos + 1 ].getLength();
559     if (nLen == aGrouping.get() ||                  // with 3 (or so) digits
560         nLen == aGrouping.advance().get() ||        // or with 2 (or 3 or so) digits
561         nPosThousandString == nStringPos + 1 )      // or concatenated
562     {
563         nPos = nPos + rSep.getLength();
564         return true;
565     }
566     return false;
567 }
568 
569 
570 /**
571  * Conversion of text to logical value
572  *  "true" =>  1:
573  *  "false"=> -1:
574  *  else   =>  0:
575  */
576 short ImpSvNumberInputScan::GetLogical( const OUString& rString ) const
577 {
578     short res;
579 
580     const ImpSvNumberformatScan* pFS = pFormatter->GetFormatScanner();
581     if ( rString == pFS->GetTrueString() )
582     {
583         res = 1;
584     }
585     else if ( rString == pFS->GetFalseString() )
586     {
587         res = -1;
588     }
589     else
590     {
591         res = 0;
592     }
593     return res;
594 }
595 
596 
597 /**
598  * Converts a string containing a month name (JAN, January) at nPos into the
599  * month number (negative if abbreviated), returns 0 if nothing found
600  */
601 short ImpSvNumberInputScan::GetMonth( const OUString& rString, sal_Int32& nPos )
602 {
603     short res = 0; // no month found
604 
605     if (rString.getLength() > nPos) // only if needed
606     {
607         if ( !bTextInitialized )
608         {
609             InitText();
610         }
611         sal_Int16 nMonths = pFormatter->GetCalendar()->getNumberOfMonthsInYear();
612         for ( sal_Int16 i = 0; i < nMonths; i++ )
613         {
614             if ( bScanGenitiveMonths && StringContainsWord( pUpperGenitiveMonthText[i], rString, nPos ) )
615             {   // genitive full names first
616                 nPos = nPos + pUpperGenitiveMonthText[i].getLength();
617                 res = i + 1;
618                 break;  // for
619             }
620             else if ( bScanGenitiveMonths && StringContainsWord( pUpperGenitiveAbbrevMonthText[i], rString, nPos ) )
621             {   // genitive abbreviated
622                 nPos = nPos + pUpperGenitiveAbbrevMonthText[i].getLength();
623                 res = sal::static_int_cast< short >(-(i+1)); // negative
624                 break;  // for
625             }
626             else if ( bScanPartitiveMonths && StringContainsWord( pUpperPartitiveMonthText[i], rString, nPos ) )
627             {   // partitive full names
628                 nPos = nPos + pUpperPartitiveMonthText[i].getLength();
629                 res = i+1;
630                 break;  // for
631             }
632             else if ( bScanPartitiveMonths && StringContainsWord( pUpperPartitiveAbbrevMonthText[i], rString, nPos ) )
633             {   // partitive abbreviated
634                 nPos = nPos + pUpperPartitiveAbbrevMonthText[i].getLength();
635                 res = sal::static_int_cast< short >(-(i+1)); // negative
636                 break;  // for
637             }
638             else if ( StringContainsWord( pUpperMonthText[i], rString, nPos ) )
639             {   // noun full names
640                 nPos = nPos + pUpperMonthText[i].getLength();
641                 res = i+1;
642                 break;  // for
643             }
644             else if ( StringContainsWord( pUpperAbbrevMonthText[i], rString, nPos ) )
645             {   // noun abbreviated
646                 nPos = nPos + pUpperAbbrevMonthText[i].getLength();
647                 res = sal::static_int_cast< short >(-(i+1)); // negative
648                 break;  // for
649             }
650             else if (i == 8)
651             {
652                 // This assumes the weirdness is applicable to all locales.
653                 if (pUpperAbbrevMonthText[i] == "SEPT" && StringContainsWord( "SEP", rString, nPos))
654                 {   // #102136# The correct English form of month September abbreviated is
655                     // SEPT, but almost every data contains SEP instead.
656                     nPos = nPos + 3;
657                     res = sal::static_int_cast< short >(-(i+1)); // negative
658                     break;  // for
659                 }
660                 else if (pUpperAbbrevMonthText[i] == "SEP" && StringContainsWord( "SEPT", rString, nPos))
661                 {   // And vice versa, accept SEPT for SEP
662                     nPos = nPos + 4;
663                     res = sal::static_int_cast< short >(-(i+1)); // negative
664                     break;  // for
665                 }
666             }
667         }
668         if (!res)
669         {
670             // Brutal hack for German locales that know "Januar" or "Jänner".
671             /* TODO: add alternative month names to locale data? if there are
672              * more languages... */
673             const LanguageTag& rLanguageTag = pFormatter->GetLanguageTag();
674             if (rLanguageTag.getLanguage() == "de")
675             {
676                 if (rLanguageTag.getCountry() == "AT")
677                 {
678                     // Locale data has Jänner/Jän
679                     assert(pUpperMonthText[0] == u"J\u00C4NNER");
680                     if (StringContainsWord( "JANUAR", rString, nPos))
681                     {
682                         nPos += 6;
683                         res = 1;
684                     }
685                     else if (StringContainsWord( "JAN", rString, nPos))
686                     {
687                         nPos += 3;
688                         res = -1;
689                     }
690                 }
691                 else
692                 {
693                     // Locale data has Januar/Jan
694                     assert(pUpperMonthText[0] == "JANUAR");
695                     if (StringContainsWord( u"J\u00C4NNER", rString, nPos))
696                     {
697                         nPos += 6;
698                         res = 1;
699                     }
700                     else if (StringContainsWord( u"J\u00C4N", rString, nPos))
701                     {
702                         nPos += 3;
703                         res = -1;
704                     }
705                 }
706             }
707         }
708     }
709 
710     return res;
711 }
712 
713 
714 /**
715  * Converts a string containing a DayOfWeek name (Mon, Monday) at nPos into the
716  * DayOfWeek number + 1 (negative if abbreviated), returns 0 if nothing found
717  */
718 int ImpSvNumberInputScan::GetDayOfWeek( const OUString& rString, sal_Int32& nPos )
719 {
720     int res = 0; // no day found
721 
722     if (rString.getLength() > nPos) // only if needed
723     {
724         if ( !bTextInitialized )
725         {
726             InitText();
727         }
728         sal_Int16 nDays = pFormatter->GetCalendar()->getNumberOfDaysInWeek();
729         for ( sal_Int16 i = 0; i < nDays; i++ )
730         {
731             if ( StringContainsWord( pUpperDayText[i], rString, nPos ) )
732             {   // full names first
733                 nPos = nPos + pUpperDayText[i].getLength();
734                 res = i + 1;
735                 break;  // for
736             }
737             if ( StringContainsWord( pUpperAbbrevDayText[i], rString, nPos ) )
738             {   // abbreviated
739                 nPos = nPos + pUpperAbbrevDayText[i].getLength();
740                 res = -(i + 1); // negative
741                 break;  // for
742             }
743         }
744     }
745 
746     return res;
747 }
748 
749 
750 /**
751  * Reading a currency symbol
752  * '$'   => true
753  * else => false
754  */
755 bool ImpSvNumberInputScan::GetCurrency( const OUString& rString, sal_Int32& nPos )
756 {
757     if ( rString.getLength() > nPos )
758     {
759         if ( !aUpperCurrSymbol.getLength() )
760         {   // If no format specified the currency of the currently active locale.
761             LanguageType eLang = (mpFormat ? mpFormat->GetLanguage() :
762                     pFormatter->GetLocaleData()->getLanguageTag().getLanguageType());
763             aUpperCurrSymbol = pFormatter->GetCharClass()->uppercase(
764                 SvNumberFormatter::GetCurrencyEntry( eLang ).GetSymbol() );
765         }
766         if ( StringContains( aUpperCurrSymbol, rString, nPos ) )
767         {
768             nPos = nPos + aUpperCurrSymbol.getLength();
769             return true;
770         }
771         if ( mpFormat )
772         {
773             OUString aSymbol, aExtension;
774             if ( mpFormat->GetNewCurrencySymbol( aSymbol, aExtension ) )
775             {
776                 if ( aSymbol.getLength() <= rString.getLength() - nPos )
777                 {
778                     aSymbol = pFormatter->GetCharClass()->uppercase(aSymbol);
779                     if ( StringContains( aSymbol, rString, nPos ) )
780                     {
781                         nPos = nPos + aSymbol.getLength();
782                         return true;
783                     }
784                 }
785             }
786         }
787     }
788 
789     return false;
790 }
791 
792 
793 /**
794  * Reading the time period specifier (AM/PM) for the 12 hour clock
795  *
796  *  Returns:
797  *   "AM" or "PM" => true
798  *   else         => false
799  *
800  *  nAmPos:
801  *   "AM"  =>  1
802  *   "PM"  => -1
803  *   else =>  0
804 */
805 bool ImpSvNumberInputScan::GetTimeAmPm( const OUString& rString, sal_Int32& nPos )
806 {
807 
808     if ( rString.getLength() > nPos )
809     {
810         const CharClass* pChr = pFormatter->GetCharClass();
811         const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
812         if ( StringContains( pChr->uppercase( pLoc->getTimeAM() ), rString, nPos ) )
813         {
814             nAmPm = 1;
815             nPos = nPos + pLoc->getTimeAM().getLength();
816             return true;
817         }
818         else if ( StringContains( pChr->uppercase( pLoc->getTimePM() ), rString, nPos ) )
819         {
820             nAmPm = -1;
821             nPos = nPos + pLoc->getTimePM().getLength();
822             return true;
823         }
824     }
825 
826     return false;
827 }
828 
829 
830 /**
831  * Read a decimal separator (',')
832  * ','   => true
833  * else => false
834  */
835 inline bool ImpSvNumberInputScan::GetDecSep( const OUString& rString, sal_Int32& nPos ) const
836 {
837     if ( rString.getLength() > nPos )
838     {
839         const OUString& rSep = pFormatter->GetNumDecimalSep();
840         if ( rString.match( rSep, nPos) )
841         {
842             nPos = nPos + rSep.getLength();
843             return true;
844         }
845         const OUString& rSepAlt = pFormatter->GetNumDecimalSepAlt();
846         if ( !rSepAlt.isEmpty() && rString.match( rSepAlt, nPos) )
847         {
848             nPos = nPos + rSepAlt.getLength();
849             return true;
850         }
851     }
852     return false;
853 }
854 
855 
856 /**
857  * Reading a hundredth seconds separator
858  */
859 inline bool ImpSvNumberInputScan::GetTime100SecSep( const OUString& rString, sal_Int32& nPos ) const
860 {
861     if ( rString.getLength() > nPos )
862     {
863         if (bIso8601Tsep)
864         {
865             // ISO 8601 specifies both '.' dot and ',' comma as fractional
866             // separator.
867             if (rString[nPos] == '.' || rString[nPos] == ',')
868             {
869                 ++nPos;
870                 return true;
871             }
872         }
873         // Even in an otherwise ISO 8601 string be lenient and accept the
874         // locale defined separator.
875         const OUString& rSep = pFormatter->GetLocaleData()->getTime100SecSep();
876         if ( rString.match( rSep, nPos ))
877         {
878             nPos = nPos + rSep.getLength();
879             return true;
880         }
881     }
882     return false;
883 }
884 
885 
886 /**
887  * Read a sign including brackets
888  * '+'   =>  1
889  * '-'   => -1
890  *  '('   => -1, bNegCheck = 1
891  * else =>  0
892  */
893 int ImpSvNumberInputScan::GetSign( const OUString& rString, sal_Int32& nPos )
894 {
895     if (rString.getLength() > nPos)
896         switch (rString[ nPos ])
897         {
898         case '+':
899             nPos++;
900             return 1;
901         case '(': // '(' similar to '-' ?!?
902             bNegCheck = true;
903             [[fallthrough]];
904         case '-':
905             nPos++;
906             return -1;
907         default:
908             break;
909         }
910 
911     return 0;
912 }
913 
914 
915 /**
916  * Read a sign with an exponent
917  * '+'   =>  1
918  * '-'   => -1
919  * else =>  0
920  */
921 short ImpSvNumberInputScan::GetESign( const OUString& rString, sal_Int32& nPos )
922 {
923     if (rString.getLength() > nPos)
924     {
925         switch (rString[nPos])
926         {
927         case '+':
928             nPos++;
929             return 1;
930         case '-':
931             nPos++;
932             return -1;
933         default:
934             break;
935         }
936     }
937     return 0;
938 }
939 
940 
941 /**
942  * i counts string portions, j counts numbers thereof.
943  * It should had been called SkipNumber instead.
944  */
945 inline bool ImpSvNumberInputScan::GetNextNumber( sal_uInt16& i, sal_uInt16& j ) const
946 {
947     if ( i < nStringsCnt && IsNum[i] )
948     {
949         j++;
950         i++;
951         return true;
952     }
953     return false;
954 }
955 
956 
957 bool ImpSvNumberInputScan::GetTimeRef( double& fOutNumber,
958                                        sal_uInt16 nIndex, // j-value of the first numeric time part of input, default 0
959                                        sal_uInt16 nCnt ) const // count of numeric time parts
960 {
961     bool bRet = true;
962     sal_uInt16 nHour;
963     sal_uInt16 nMinute = 0;
964     sal_uInt16 nSecond = 0;
965     double fSecond100 = 0.0;
966     sal_uInt16 nStartIndex = nIndex;
967 
968     if (nDecPos == 2 && (nCnt == 3 || nCnt == 2)) // 20:45.5 or 45.5
969     {
970         nHour = 0;
971     }
972     else if (mpFormat && nDecPos == 0 && nCnt == 2 && mpFormat->IsMinuteSecondFormat())
973     {
974         // Input on MM:SS format, instead of doing HH:MM:00
975         nHour = 0;
976     }
977     else if (nIndex - nStartIndex < nCnt)
978     {
979         nHour   = static_cast<sal_uInt16>(sStrArray[nNums[nIndex++]].toInt32());
980     }
981     else
982     {
983         nHour = 0;
984         bRet = false;
985         SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetTimeRef: bad number index");
986     }
987 
988     if (nAmPm && nHour > 12) // not a valid AM/PM clock time
989     {
990         bRet = false;
991     }
992     else if (nAmPm == -1 && nHour != 12) // PM
993     {
994         nHour += 12;
995     }
996     else if (nAmPm == 1 && nHour == 12) // 12 AM
997     {
998         nHour = 0;
999     }
1000 
1001     if (nDecPos == 2 && nCnt == 2) // 45.5
1002     {
1003         nMinute = 0;
1004     }
1005     else if (nIndex - nStartIndex < nCnt)
1006     {
1007         nMinute = static_cast<sal_uInt16>(sStrArray[nNums[nIndex++]].toInt32());
1008         if (nIndex > 1 && nMinute > 59)
1009             bRet = false;   // 1:60 or 1:123 is invalid, 123:1 is valid
1010     }
1011     if (nIndex - nStartIndex < nCnt)
1012     {
1013         nSecond = static_cast<sal_uInt16>(sStrArray[nNums[nIndex++]].toInt32());
1014         if (nIndex > 1 && nSecond > 59 && !(nHour == 23 && nMinute == 59 && nSecond == 60))
1015             bRet = false;   // 1:60 or 1:123 or 1:1:123 is invalid, 123:1 or 123:1:1 is valid, or leap second
1016     }
1017     if (nIndex - nStartIndex < nCnt)
1018     {
1019         fSecond100 = StringToDouble( sStrArray[nNums[nIndex]], true );
1020     }
1021     fOutNumber = (static_cast<double>(nHour)*3600 +
1022                   static_cast<double>(nMinute)*60 +
1023                   static_cast<double>(nSecond) +
1024                   fSecond100)/86400.0;
1025     return bRet;
1026 }
1027 
1028 
1029 sal_uInt16 ImpSvNumberInputScan::ImplGetDay( sal_uInt16 nIndex ) const
1030 {
1031     sal_uInt16 nRes = 0;
1032 
1033     if (sStrArray[nNums[nIndex]].getLength() <= 2)
1034     {
1035         sal_uInt16 nNum = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1036         if (nNum <= 31)
1037         {
1038             nRes = nNum;
1039         }
1040     }
1041 
1042     return nRes;
1043 }
1044 
1045 
1046 sal_uInt16 ImpSvNumberInputScan::ImplGetMonth( sal_uInt16 nIndex ) const
1047 {
1048     // Preset invalid month number
1049     sal_uInt16 nRes = pFormatter->GetCalendar()->getNumberOfMonthsInYear();
1050 
1051     if (sStrArray[nNums[nIndex]].getLength() <= 2)
1052     {
1053         sal_uInt16 nNum = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1054         if ( 0 < nNum && nNum <= nRes )
1055         {
1056             nRes = nNum - 1; // zero based for CalendarFieldIndex::MONTH
1057         }
1058     }
1059 
1060     return nRes;
1061 }
1062 
1063 
1064 /**
1065  * 30 -> 1930, 29 -> 2029, or 56 -> 1756, 55 -> 1855, ...
1066  */
1067 sal_uInt16 ImpSvNumberInputScan::ImplGetYear( sal_uInt16 nIndex )
1068 {
1069     sal_uInt16 nYear = 0;
1070 
1071     sal_Int32 nLen = sStrArray[nNums[nIndex]].getLength();
1072     // 16-bit integer year width can have 5 digits, allow for one additional
1073     // leading zero as convention.
1074     if (nLen <= 6)
1075     {
1076         nYear = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1077         // A year in another, not Gregorian CE era is never expanded.
1078         // A year < 100 entered with at least 3 digits with leading 0 is taken
1079         // as is without expansion.
1080         if (mbEraCE == kDefaultEra && nYear < 100 && nLen < 3)
1081         {
1082             nYear = SvNumberFormatter::ExpandTwoDigitYear( nYear, nYear2000 );
1083         }
1084     }
1085 
1086     return nYear;
1087 }
1088 
1089 
1090 bool ImpSvNumberInputScan::MayBeIso8601()
1091 {
1092     if (nMayBeIso8601 == 0)
1093     {
1094         nMayBeIso8601 = 1;
1095         sal_Int32 nLen = ((nNumericsCnt >= 1 && nNums[0] < nStringsCnt) ? sStrArray[nNums[0]].getLength() : 0);
1096         if (nLen)
1097         {
1098             sal_Int32 n;
1099             if (nNumericsCnt >= 3 && nNums[2] < nStringsCnt &&
1100                 sStrArray[nNums[0]+1] == "-" && // separator year-month
1101                 (n = sStrArray[nNums[1]].toInt32()) >= 1 && n <= 12 &&  // month
1102                 sStrArray[nNums[1]+1] == "-" && // separator month-day
1103                 (n = sStrArray[nNums[2]].toInt32()) >= 1 && n <= 31)    // day
1104             {
1105                 // Year (nNums[0]) value not checked, may be anything, but
1106                 // length (number of digits) is checked.
1107                 nMayBeIso8601 = (nLen >= 4 ? 4 : (nLen == 3 ? 3 : (nLen > 0 ? 2 : 1)));
1108             }
1109         }
1110     }
1111     return nMayBeIso8601 > 1;
1112 }
1113 
1114 
1115 bool ImpSvNumberInputScan::CanForceToIso8601( DateOrder eDateOrder )
1116 {
1117     int nCanForceToIso8601 = 0;
1118     if (!MayBeIso8601())
1119     {
1120         return false;
1121     }
1122     else if (nMayBeIso8601 >= 3)
1123     {
1124         return true;    // at least 3 digits in year
1125     }
1126     else
1127     {
1128         if (eDateOrder == DateOrder::Invalid)
1129         {
1130             // As if any of the cases below can be applied, but only if a
1131             // locale dependent date pattern was not matched.
1132             if ((GetDatePatternNumbers() == nNumericsCnt) && IsDatePatternNumberOfType(0,'Y'))
1133                 return false;
1134             eDateOrder = GetDateOrder();
1135         }
1136 
1137         nCanForceToIso8601 = 1;
1138     }
1139 
1140     sal_Int32 n;
1141     switch (eDateOrder)
1142     {
1143         case DateOrder::DMY:               // "day" value out of range => ISO 8601 year
1144             n = sStrArray[nNums[0]].toInt32();
1145             if (n < 1 || n > 31)
1146             {
1147                 nCanForceToIso8601 = 2;
1148             }
1149         break;
1150         case DateOrder::MDY:               // "month" value out of range => ISO 8601 year
1151             n = sStrArray[nNums[0]].toInt32();
1152             if (n < 1 || n > 12)
1153             {
1154                 nCanForceToIso8601 = 2;
1155             }
1156         break;
1157         case DateOrder::YMD:               // always possible
1158             nCanForceToIso8601 = 2;
1159         break;
1160         default: break;
1161     }
1162     return nCanForceToIso8601 > 1;
1163 }
1164 
1165 
1166 bool ImpSvNumberInputScan::IsAcceptableIso8601()
1167 {
1168     if (mpFormat && (mpFormat->GetType() & SvNumFormatType::DATE))
1169     {
1170         switch (pFormatter->GetEvalDateFormat())
1171         {
1172             case NF_EVALDATEFORMAT_INTL:
1173                 return CanForceToIso8601( GetDateOrder());
1174             case NF_EVALDATEFORMAT_FORMAT:
1175                 return CanForceToIso8601( mpFormat->GetDateOrder());
1176             default:
1177                 return CanForceToIso8601( GetDateOrder()) || CanForceToIso8601( mpFormat->GetDateOrder());
1178         }
1179     }
1180     return CanForceToIso8601( GetDateOrder());
1181 }
1182 
1183 
1184 bool ImpSvNumberInputScan::MayBeMonthDate()
1185 {
1186     if (nMayBeMonthDate == 0)
1187     {
1188         nMayBeMonthDate = 1;
1189         if (nNumericsCnt >= 2 && nNums[1] < nStringsCnt)
1190         {
1191             // "-Jan-"
1192             const OUString& rM = sStrArray[ nNums[ 0 ] + 1 ];
1193             if (rM.getLength() >= 3 && rM[0] == '-' && rM[ rM.getLength() - 1] == '-')
1194             {
1195                 // Check year length assuming at least 3 digits (including
1196                 // leading zero). Two digit years 1..31 are out of luck here
1197                 // and may be taken as day of month.
1198                 bool bYear1 = (sStrArray[nNums[0]].getLength() >= 3);
1199                 bool bYear2 = (sStrArray[nNums[1]].getLength() >= 3);
1200                 sal_Int32 n;
1201                 bool bDay1 = !bYear1;
1202                 if (bDay1)
1203                 {
1204                     n = sStrArray[nNums[0]].toInt32();
1205                     bDay1 = n >= 1 && n <= 31;
1206                 }
1207                 bool bDay2 = !bYear2;
1208                 if (bDay2)
1209                 {
1210                     n = sStrArray[nNums[1]].toInt32();
1211                     bDay2 = n >= 1 && n <= 31;
1212                 }
1213 
1214                 if (bDay1 && !bDay2)
1215                 {
1216                     nMayBeMonthDate = 2;        // dd-month-yy
1217                 }
1218                 else if (!bDay1 && bDay2)
1219                 {
1220                     nMayBeMonthDate = 3;        // yy-month-dd
1221                 }
1222                 else if (bDay1 && bDay2)
1223                 {
1224                     // Ambiguous ##-MMM-## date, but some big vendor's database
1225                     // reports write this crap, assume this always to be
1226                     nMayBeMonthDate = 2;        // dd-month-yy
1227                 }
1228             }
1229         }
1230     }
1231     return nMayBeMonthDate > 1;
1232 }
1233 
1234 
1235 /** If a string is a separator plus '-' minus sign preceding a 'Y' year in
1236     a date pattern at position nPat.
1237  */
1238 static bool lcl_IsSignedYearSep( const OUString& rStr, const OUString& rPat, sal_Int32 nPat )
1239 {
1240     bool bOk = false;
1241     sal_Int32 nLen = rStr.getLength();
1242     if (nLen > 1 && rStr[nLen-1] == '-')
1243     {
1244         --nLen;
1245         if (nPat + nLen < rPat.getLength() && rPat[nPat+nLen] == 'Y')
1246         {
1247             // Signed year is possible.
1248             bOk = (rPat.indexOf( rStr.copy( 0, nLen), nPat) == nPat);
1249         }
1250     }
1251     return bOk;
1252 }
1253 
1254 
1255 bool ImpSvNumberInputScan::IsAcceptedDatePattern( sal_uInt16 nStartPatternAt )
1256 {
1257     if (nAcceptedDatePattern >= -1)
1258     {
1259         return (nAcceptedDatePattern >= 0);
1260     }
1261     if (!nNumericsCnt)
1262     {
1263         nAcceptedDatePattern = -1;
1264     }
1265     else if (!sDateAcceptancePatterns.hasElements())
1266     {
1267         // The current locale is the format's locale, if a format is present.
1268         const NfEvalDateFormat eEDF = pFormatter->GetEvalDateFormat();
1269         if (!mpFormat || eEDF == NF_EVALDATEFORMAT_FORMAT || mpFormat->GetLanguage() == pFormatter->GetLanguage())
1270         {
1271             sDateAcceptancePatterns = pFormatter->GetLocaleData()->getDateAcceptancePatterns();
1272         }
1273         else
1274         {
1275             OnDemandLocaleDataWrapper& xLocaleData = pFormatter->GetOnDemandLocaleDataWrapper(
1276                     SvNumberFormatter::InputScannerPrivateAccess());
1277             const LanguageTag aSaveLocale( xLocaleData->getLanguageTag() );
1278             assert(mpFormat->GetLanguage() == aSaveLocale.getLanguageType());   // prerequisite
1279             // Obtain formatter's locale's (e.g. system) patterns.
1280             xLocaleData.changeLocale( LanguageTag( pFormatter->GetLanguage()));
1281             const css::uno::Sequence<OUString> aLocalePatterns( xLocaleData->getDateAcceptancePatterns());
1282             // Reset to format's locale.
1283             xLocaleData.changeLocale( aSaveLocale);
1284             // When concatenating don't care about duplicates, combining
1285             // weeding those out reallocs yet another time and probably doesn't
1286             // take less time than looping over two additional patterns below...
1287             switch (eEDF)
1288             {
1289                 case NF_EVALDATEFORMAT_FORMAT:
1290                     assert(!"shouldn't reach here");
1291                 break;
1292                 case NF_EVALDATEFORMAT_INTL:
1293                     sDateAcceptancePatterns = aLocalePatterns;
1294                 break;
1295                 case NF_EVALDATEFORMAT_INTL_FORMAT:
1296                     sDateAcceptancePatterns = comphelper::concatSequences(
1297                             aLocalePatterns,
1298                             xLocaleData->getDateAcceptancePatterns());
1299                 break;
1300                 case NF_EVALDATEFORMAT_FORMAT_INTL:
1301                     sDateAcceptancePatterns = comphelper::concatSequences(
1302                             xLocaleData->getDateAcceptancePatterns(),
1303                             aLocalePatterns);
1304                 break;
1305             }
1306         }
1307         SAL_WARN_IF( !sDateAcceptancePatterns.hasElements(), "svl.numbers", "ImpSvNumberInputScan::IsAcceptedDatePattern: no date acceptance patterns");
1308         nAcceptedDatePattern = (sDateAcceptancePatterns.hasElements() ? -2 : -1);
1309     }
1310 
1311     if (nAcceptedDatePattern == -1)
1312     {
1313         return false;
1314     }
1315     nDatePatternStart = nStartPatternAt; // remember start particle
1316 
1317     const sal_Int32 nMonthsInYear = pFormatter->GetCalendar()->getNumberOfMonthsInYear();
1318 
1319     for (sal_Int32 nPattern=0; nPattern < sDateAcceptancePatterns.getLength(); ++nPattern)
1320     {
1321         sal_uInt16 nNext = nDatePatternStart;
1322         nDatePatternNumbers = 0;
1323         bool bOk = true;
1324         const OUString& rPat = sDateAcceptancePatterns[nPattern];
1325         sal_Int32 nPat = 0;
1326         for ( ; nPat < rPat.getLength() && bOk && nNext < nStringsCnt; ++nPat, ++nNext)
1327         {
1328             const sal_Unicode c = rPat[nPat];
1329             switch (c)
1330             {
1331             case 'Y':
1332             case 'M':
1333             case 'D':
1334                 bOk = IsNum[nNext];
1335                 if (bOk && (c == 'M' || c == 'D'))
1336                 {
1337                     // Check the D and M cases for plausibility. This also
1338                     // prevents recognition of date instead of number with a
1339                     // numeric group input if date separator is identical to
1340                     // group separator, for example with D.M as a pattern and
1341                     // #.### as a group.
1342                     sal_Int32 nMaxLen, nMaxVal;
1343                     switch (c)
1344                     {
1345                         case 'M':
1346                             nMaxLen = 2;
1347                             nMaxVal = nMonthsInYear;
1348                             break;
1349                         case 'D':
1350                             nMaxLen = 2;
1351                             nMaxVal = 31;
1352                             break;
1353                         default:
1354                             // This merely exists against
1355                             // -Werror=maybe-uninitialized, which is nonsense
1356                             // after the (c == 'M' || c == 'D') check above,
1357                             // but ...
1358                             nMaxLen = 2;
1359                             nMaxVal = 31;
1360                     }
1361                     bOk = (sStrArray[nNext].getLength() <= nMaxLen);
1362                     if (bOk)
1363                     {
1364                         sal_Int32 nNum = sStrArray[nNext].toInt32();
1365                         bOk = (1 <= nNum && nNum <= nMaxVal);
1366                     }
1367                 }
1368                 if (bOk)
1369                     ++nDatePatternNumbers;
1370                 break;
1371             default:
1372                 bOk = !IsNum[nNext];
1373                 if (bOk)
1374                 {
1375                     const sal_Int32 nLen = sStrArray[nNext].getLength();
1376                     bOk = (rPat.indexOf( sStrArray[nNext], nPat) == nPat);
1377                     if (bOk)
1378                     {
1379                         nPat += nLen - 1;
1380                     }
1381                     else if ((bOk = lcl_IsSignedYearSep( sStrArray[nNext], rPat, nPat)))
1382                     {
1383                         nPat += nLen - 2;
1384                     }
1385                     else if (nPat + nLen > rPat.getLength() && sStrArray[nNext][ nLen - 1 ] == ' ')
1386                     {
1387                         using namespace comphelper::string;
1388                         // Trailing blanks in input.
1389                         OUStringBuffer aBuf(sStrArray[nNext]);
1390                         aBuf.stripEnd();
1391                         // Expand again in case of pattern "M. D. " and
1392                         // input "M. D.  ", maybe fetched far, but...
1393                         padToLength(aBuf, rPat.getLength() - nPat, ' ');
1394                         OUString aStr = aBuf.makeStringAndClear();
1395                         bOk = (rPat.indexOf( aStr, nPat) == nPat);
1396                         if (bOk)
1397                         {
1398                             nPat += aStr.getLength() - 1;
1399                         }
1400                     }
1401                 }
1402                 break;
1403             }
1404         }
1405         if (bOk)
1406         {
1407             // Check for trailing characters mismatch.
1408             if (nNext < nStringsCnt)
1409             {
1410                 // Pattern end but not input end.
1411                 // A trailing blank may be part of the current pattern input,
1412                 // if pattern is "D.M." and input is "D.M. hh:mm" last was
1413                 // ". ", or may be following the current pattern input, if
1414                 // pattern is "D.M" and input is "D.M hh:mm" last was "M".
1415                 sal_Int32 nPos = 0;
1416                 sal_uInt16 nCheck;
1417                 if (nPat > 0 && nNext > 0)
1418                 {
1419                     // nPat is one behind after the for loop.
1420                     sal_Int32 nPatCheck = nPat - 1;
1421                     switch (rPat[nPatCheck])
1422                     {
1423                         case 'Y':
1424                         case 'M':
1425                         case 'D':
1426                             nCheck = nNext;
1427                             break;
1428                         default:
1429                             {
1430                                 nCheck = nNext - 1;
1431                                 // Advance position in input to match length of
1432                                 // non-YMD (separator) characters in pattern.
1433                                 sal_Unicode c;
1434                                 do
1435                                 {
1436                                     ++nPos;
1437                                     c = rPat[--nPatCheck];
1438                                 } while (c != 'Y' && c != 'M' && c != 'D');
1439                             }
1440                     }
1441                 }
1442                 else
1443                 {
1444                     nCheck = nNext;
1445                 }
1446                 if (!IsNum[nCheck])
1447                 {
1448                     // Trailing (or separating if time follows) blanks are ok.
1449                     SkipBlanks( sStrArray[nCheck], nPos);
1450                     if (nPos == sStrArray[nCheck].getLength())
1451                     {
1452                         nAcceptedDatePattern = nPattern;
1453                         return true;
1454                     }
1455                 }
1456             }
1457             else if (nPat == rPat.getLength())
1458             {
1459                 // Input end and pattern end => match.
1460                 nAcceptedDatePattern = nPattern;
1461                 return true;
1462             }
1463             // else Input end but not pattern end, no match.
1464         }
1465     }
1466     nAcceptedDatePattern = -1;
1467     return false;
1468 }
1469 
1470 
1471 bool ImpSvNumberInputScan::SkipDatePatternSeparator( sal_uInt16 nParticle, sal_Int32 & rPos, bool & rSignedYear )
1472 {
1473     // If not initialized yet start with first number, if any.
1474     if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1475     {
1476         return false;
1477     }
1478     if (nParticle < nDatePatternStart || nParticle >= nStringsCnt || IsNum[nParticle])
1479     {
1480         return false;
1481     }
1482     sal_uInt16 nNext = nDatePatternStart;
1483     const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1484     for (sal_Int32 nPat = 0; nPat < rPat.getLength() && nNext < nStringsCnt; ++nPat, ++nNext)
1485     {
1486         switch (rPat[nPat])
1487         {
1488         case 'Y':
1489         case 'M':
1490         case 'D':
1491             break;
1492         default:
1493             if (nNext == nParticle)
1494             {
1495                 const sal_Int32 nLen = sStrArray[nNext].getLength();
1496                 bool bOk = (rPat.indexOf( sStrArray[nNext], nPat) == nPat);
1497                 if (!bOk)
1498                 {
1499                     bOk = lcl_IsSignedYearSep( sStrArray[nNext], rPat, nPat);
1500                     if (bOk)
1501                         rSignedYear = true;
1502                 }
1503                 if (!bOk && (nPat + nLen > rPat.getLength() && sStrArray[nNext][ nLen - 1 ] == ' '))
1504                 {
1505                     // The same ugly trailing blanks check as in
1506                     // IsAcceptedDatePattern().
1507                     using namespace comphelper::string;
1508                     OUStringBuffer aBuf(sStrArray[nNext]);
1509                     aBuf.stripEnd();
1510                     padToLength(aBuf, rPat.getLength() - nPat, ' ');
1511                     bOk = (rPat.indexOf( aBuf.makeStringAndClear(), nPat) == nPat);
1512                 }
1513                 if (bOk)
1514                 {
1515                     rPos = nLen; // yes, set, not add!
1516                     return true;
1517                 }
1518                 else
1519                     return false;
1520             }
1521             nPat += sStrArray[nNext].getLength() - 1;
1522             break;
1523         }
1524     }
1525     return false;
1526 }
1527 
1528 
1529 sal_uInt16 ImpSvNumberInputScan::GetDatePatternNumbers()
1530 {
1531     // If not initialized yet start with first number, if any.
1532     if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1533     {
1534         return 0;
1535     }
1536     return nDatePatternNumbers;
1537 }
1538 
1539 
1540 bool ImpSvNumberInputScan::IsDatePatternNumberOfType( sal_uInt16 nNumber, sal_Unicode cType )
1541 {
1542     if (GetDatePatternNumbers() <= nNumber)
1543         return false;
1544 
1545     sal_uInt16 nNum = 0;
1546     const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1547     for (sal_Int32 nPat = 0; nPat < rPat.getLength(); ++nPat)
1548     {
1549         switch (rPat[nPat])
1550         {
1551             case 'Y':
1552             case 'M':
1553             case 'D':
1554                 if (nNum == nNumber)
1555                     return rPat[nPat] == cType;
1556                 ++nNum;
1557             break;
1558         }
1559     }
1560     return false;
1561 }
1562 
1563 
1564 sal_uInt32 ImpSvNumberInputScan::GetDatePatternOrder()
1565 {
1566     // If not initialized yet start with first number, if any.
1567     if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1568     {
1569         return 0;
1570     }
1571     sal_uInt32 nOrder = 0;
1572     const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1573     for (sal_Int32 nPat = 0; nPat < rPat.getLength() && !(nOrder & 0xff0000); ++nPat)
1574     {
1575         switch (rPat[nPat])
1576         {
1577         case 'Y':
1578         case 'M':
1579         case 'D':
1580             nOrder = (nOrder << 8) | rPat[nPat];
1581             break;
1582         }
1583     }
1584     return nOrder;
1585 }
1586 
1587 
1588 DateOrder ImpSvNumberInputScan::GetDateOrder( bool bFromFormatIfNoPattern )
1589 {
1590     sal_uInt32 nOrder = GetDatePatternOrder();
1591     if (!nOrder)
1592     {
1593         if (bFromFormatIfNoPattern && mpFormat)
1594             return mpFormat->GetDateOrder();
1595         else
1596             return pFormatter->GetLocaleData()->getDateOrder();
1597     }
1598     switch ((nOrder & 0xff0000) >> 16)
1599     {
1600     case 'Y':
1601         if ((((nOrder & 0xff00) >> 8) == 'M') && ((nOrder & 0xff) == 'D'))
1602         {
1603             return DateOrder::YMD;
1604         }
1605         break;
1606     case 'M':
1607         if ((((nOrder & 0xff00) >> 8) == 'D') && ((nOrder & 0xff) == 'Y'))
1608         {
1609             return DateOrder::MDY;
1610         }
1611         break;
1612     case 'D':
1613         if ((((nOrder & 0xff00) >> 8) == 'M') && ((nOrder & 0xff) == 'Y'))
1614         {
1615             return DateOrder::DMY;
1616         }
1617         break;
1618     default:
1619     case 0:
1620         switch ((nOrder & 0xff00) >> 8)
1621         {
1622         case 'Y':
1623             switch (nOrder & 0xff)
1624             {
1625             case 'M':
1626                 return DateOrder::YMD;
1627             }
1628             break;
1629         case 'M':
1630             switch (nOrder & 0xff)
1631             {
1632             case 'Y':
1633                 return DateOrder::DMY;
1634             case 'D':
1635                 return DateOrder::MDY;
1636             }
1637             break;
1638         case 'D':
1639             switch (nOrder & 0xff)
1640             {
1641             case 'Y':
1642                 return DateOrder::MDY;
1643             case 'M':
1644                 return DateOrder::DMY;
1645             }
1646             break;
1647         default:
1648         case 0:
1649             switch (nOrder & 0xff)
1650             {
1651             case 'Y':
1652                 return DateOrder::YMD;
1653             case 'M':
1654                 return DateOrder::MDY;
1655             case 'D':
1656                 return DateOrder::DMY;
1657             }
1658             break;
1659         }
1660     }
1661     SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateOrder: undefined, falling back to locale's default");
1662     return pFormatter->GetLocaleData()->getDateOrder();
1663 }
1664 
1665 bool ImpSvNumberInputScan::GetDateRef( double& fDays, sal_uInt16& nCounter )
1666 {
1667     using namespace ::com::sun::star::i18n;
1668     NfEvalDateFormat eEDF;
1669     int nFormatOrder;
1670     if ( mpFormat && (mpFormat->GetType() & SvNumFormatType::DATE) )
1671     {
1672         eEDF = pFormatter->GetEvalDateFormat();
1673         switch ( eEDF )
1674         {
1675         case NF_EVALDATEFORMAT_INTL :
1676         case NF_EVALDATEFORMAT_FORMAT :
1677             nFormatOrder = 1; // only one loop
1678             break;
1679         default:
1680             nFormatOrder = 2;
1681             if ( nMatchedAllStrings )
1682             {
1683                 eEDF = NF_EVALDATEFORMAT_FORMAT_INTL;
1684                 // we have a complete match, use it
1685             }
1686         }
1687     }
1688     else
1689     {
1690         eEDF = NF_EVALDATEFORMAT_INTL;
1691         nFormatOrder = 1;
1692     }
1693     bool res = true;
1694 
1695     const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
1696     CalendarWrapper* pCal = pFormatter->GetCalendar();
1697     for ( int nTryOrder = 1; nTryOrder <= nFormatOrder; nTryOrder++ )
1698     {
1699         pCal->setGregorianDateTime( Date( Date::SYSTEM ) ); // today
1700         OUString aOrgCalendar; // empty => not changed yet
1701         DateOrder DateFmt;
1702         bool bFormatTurn;
1703         switch ( eEDF )
1704         {
1705         case NF_EVALDATEFORMAT_INTL :
1706             bFormatTurn = false;
1707             DateFmt = GetDateOrder();
1708             break;
1709         case NF_EVALDATEFORMAT_FORMAT :
1710             bFormatTurn = true;
1711             DateFmt = mpFormat->GetDateOrder();
1712             break;
1713         case NF_EVALDATEFORMAT_INTL_FORMAT :
1714             if ( nTryOrder == 1 )
1715             {
1716                 bFormatTurn = false;
1717                 DateFmt = GetDateOrder();
1718             }
1719             else
1720             {
1721                 bFormatTurn = true;
1722                 DateFmt = mpFormat->GetDateOrder();
1723             }
1724             break;
1725         case NF_EVALDATEFORMAT_FORMAT_INTL :
1726             if ( nTryOrder == 2 )
1727             {
1728                 bFormatTurn = false;
1729                 DateFmt = GetDateOrder();
1730             }
1731             else
1732             {
1733                 bFormatTurn = true;
1734                 // Even if the format pattern is to be preferred, the input may
1735                 // have matched a pattern of the current locale, which then
1736                 // again is to be preferred. Both date orders can be different
1737                 // so we need to obtain the actual match. For example ISO
1738                 // YYYY-MM-DD format vs locale's DD.MM.YY input.
1739                 // If no pattern was matched, obtain from format.
1740                 // Note that patterns may have been constructed from the
1741                 // format's locale and prepended to the current locale's
1742                 // patterns, it doesn't necessarily mean a current locale's
1743                 // pattern was matched, but may if the format's locale's
1744                 // patterns didn't match, which were tried first.
1745                 DateFmt = GetDateOrder(true);
1746             }
1747             break;
1748         default:
1749             SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateRef: unknown NfEvalDateFormat" );
1750             DateFmt = DateOrder::YMD;
1751             bFormatTurn = false;
1752         }
1753         if ( bFormatTurn )
1754         {
1755 /* TODO:
1756 We are currently not able to fully support a switch to another calendar during
1757 input for the following reasons:
1758 1. We do have a problem if both (locale's default and format's) calendars
1759    define the same YMD order and use the same date separator, there is no way
1760    to distinguish between them if the input results in valid calendar input for
1761    both calendars. How to solve? Would NfEvalDateFormat be sufficient? Should
1762    it always be set to NF_EVALDATEFORMAT_FORMAT_INTL and thus the format's
1763    calendar be preferred? This could be confusing if a Calc cell was formatted
1764    different to the locale's default and has no content yet, then the user has
1765    no clue about the format or calendar being set.
1766 2. In Calc cell edit mode a date is always displayed and edited using the
1767    default edit format of the default calendar (normally being Gregorian). If
1768    input was ambiguous due to issue #1 we'd need a mechanism to tell that a
1769    date was edited and not newly entered. Not feasible. Otherwise we'd need a
1770    mechanism to use a specific edit format with a specific calendar according
1771    to the format set.
1772 3. For some calendars like Japanese Gengou we'd need era input, which isn't
1773    implemented at all. Though this is a rare and special case, forcing a
1774    calendar dependent edit format as suggested in item #2 might require era
1775    input, if it shouldn't result in a fallback to Gregorian calendar.
1776 4. Last and least: the GetMonth() method currently only matches month names of
1777    the default calendar. Alternating month names of the actual format's
1778    calendar would have to be implemented. No problem.
1779 
1780 */
1781 #ifdef THE_FUTURE
1782             if ( mpFormat->IsOtherCalendar( nStringScanNumFor ) )
1783             {
1784                 mpFormat->SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
1785             }
1786             else
1787             {
1788                 mpFormat->SwitchToSpecifiedCalendar( aOrgCalendar, fOrgDateTime,
1789                                                     nStringScanNumFor );
1790             }
1791 #endif
1792         }
1793 
1794         res = true;
1795         nCounter = 0;
1796         // For incomplete dates, always assume first day of month if not specified.
1797         pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
1798 
1799         switch (nNumericsCnt) // count of numbers in string
1800         {
1801         case 0:                 // none
1802             if (nMonthPos)      // only month (Jan)
1803             {
1804                 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1805             }
1806             else
1807             {
1808                 res = false;
1809             }
1810             break;
1811 
1812         case 1:                 // only one number
1813             nCounter = 1;
1814             switch (nMonthPos)  // where is the month
1815             {
1816             case 0:             // not found
1817             {
1818                 // If input matched a date pattern, use the pattern
1819                 // to determine if it is a day, month or year. The
1820                 // pattern should have only one single value then,
1821                 // 'D-', 'M-' or 'Y-'. If input did not match a
1822                 // pattern assume the usual day of current month.
1823                 sal_uInt32 nDateOrder = (bFormatTurn ?
1824                                          mpFormat->GetExactDateOrder() :
1825                                          GetDatePatternOrder());
1826                 switch (nDateOrder)
1827                 {
1828                 case 'Y':
1829                     pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1830                     break;
1831                 case 'M':
1832                     pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1833                     break;
1834                 case 'D':
1835                 default:
1836                     pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1837                     break;
1838                 }
1839                 break;
1840             }
1841             case 1:             // month at the beginning (Jan 01)
1842                 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1843                 switch (DateFmt)
1844                 {
1845                 case DateOrder::MDY:
1846                 case DateOrder::YMD:
1847                 {
1848                     sal_uInt16 nDay = ImplGetDay(0);
1849                     sal_uInt16 nYear = ImplGetYear(0);
1850                     if (nDay == 0 || nDay > 32)
1851                     {
1852                         pCal->setValue( CalendarFieldIndex::YEAR, nYear);
1853                     }
1854                     else
1855                     {
1856                         pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1857                     }
1858                     break;
1859                 }
1860                 case DateOrder::DMY:
1861                     pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1862                     break;
1863                 default:
1864                     res = false;
1865                     break;
1866                 }
1867                 break;
1868             case 3:             // month at the end (10 Jan)
1869                 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1870                 switch (DateFmt)
1871                 {
1872                 case DateOrder::DMY:
1873                     pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1874                     break;
1875                 case DateOrder::YMD:
1876                     pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1877                     break;
1878                 default:
1879                     res = false;
1880                     break;
1881                 }
1882                 break;
1883             default:
1884                 res = false;
1885                 break;
1886             }   // switch (nMonthPos)
1887             break;
1888 
1889         case 2:                 // 2 numbers
1890             nCounter = 2;
1891             switch (nMonthPos)  // where is the month
1892             {
1893             case 0:             // not found
1894             {
1895                 sal_uInt32 nExactDateOrder = (bFormatTurn ?
1896                                               mpFormat->GetExactDateOrder() :
1897                                               GetDatePatternOrder());
1898                 bool bIsExact = (0xff < nExactDateOrder && nExactDateOrder <= 0xffff);
1899                 if (!bIsExact && bFormatTurn && IsAcceptedDatePattern( nNums[0]))
1900                 {
1901                     // If input does not match format but pattern, use pattern
1902                     // instead, even if eEDF==NF_EVALDATEFORMAT_FORMAT_INTL.
1903                     // For example, format has "Y-M-D" and pattern is "D.M.",
1904                     // input with 2 numbers can't match format and 31.12. would
1905                     // lead to 1931-12-01 (fdo#54344)
1906                     nExactDateOrder = GetDatePatternOrder();
1907                     bIsExact = (0xff < nExactDateOrder && nExactDateOrder <= 0xffff);
1908                 }
1909                 bool bHadExact;
1910                 if (bIsExact)
1911                 {
1912                     // formatted as date and exactly 2 parts
1913                     bHadExact = true;
1914                     switch ( (nExactDateOrder >> 8) & 0xff )
1915                     {
1916                     case 'Y':
1917                         pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1918                         break;
1919                     case 'M':
1920                         pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1921                         break;
1922                     case 'D':
1923                         pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1924                         break;
1925                     default:
1926                         bHadExact = false;
1927                     }
1928                     switch ( nExactDateOrder & 0xff )
1929                     {
1930                     case 'Y':
1931                         pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
1932                         break;
1933                     case 'M':
1934                         pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
1935                         break;
1936                     case 'D':
1937                         pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
1938                         break;
1939                     default:
1940                         bHadExact = false;
1941                     }
1942                     SAL_WARN_IF( !bHadExact, "svl.numbers", "ImpSvNumberInputScan::GetDateRef: error in exact date order");
1943                 }
1944                 else
1945                 {
1946                     bHadExact = false;
1947                 }
1948                 // If input matched against a date acceptance pattern
1949                 // do not attempt to mess around with guessing the
1950                 // order, either it matches or it doesn't.
1951                 if ((bFormatTurn || !bIsExact) && (!bHadExact || !pCal->isValid()))
1952                 {
1953                     if ( !bHadExact && nExactDateOrder )
1954                     {
1955                         pCal->setGregorianDateTime( Date( Date::SYSTEM ) ); // reset today
1956                     }
1957                     switch (DateFmt)
1958                     {
1959                     case DateOrder::MDY:
1960                         // M D
1961                         pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
1962                         pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1963                         if ( !pCal->isValid() )             // 2nd try
1964                         {                                   // M Y
1965                             pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
1966                             pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1967                             pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
1968                         }
1969                         break;
1970                     case DateOrder::DMY:
1971                         // D M
1972                         pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1973                         pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
1974                         if ( !pCal->isValid() )             // 2nd try
1975                         {                                   // M Y
1976                             pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
1977                             pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1978                             pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
1979                         }
1980                         break;
1981                     case DateOrder::YMD:
1982                         // M D
1983                         pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
1984                         pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1985                         if ( !pCal->isValid() )             // 2nd try
1986                         {                                   // Y M
1987                             pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
1988                             pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
1989                             pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1990                         }
1991                         break;
1992                     default:
1993                         res = false;
1994                         break;
1995                     }
1996                 }
1997             }
1998             break;
1999             case 1:             // month at the beginning (Jan 01 01)
2000             {
2001                 // The input is valid as MDY in almost any
2002                 // constellation, there is no date order (M)YD except if
2003                 // set in a format applied.
2004                 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2005                 sal_uInt32 nExactDateOrder = (bFormatTurn ? mpFormat->GetExactDateOrder() : 0);
2006                 if ((((nExactDateOrder >> 8) & 0xff) == 'Y') && ((nExactDateOrder & 0xff) == 'D'))
2007                 {
2008                     pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2009                     pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2010                 }
2011                 else
2012                 {
2013                     pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2014                     pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2015                 }
2016                 break;
2017             }
2018             case 2:             // month in the middle (10 Jan 94)
2019             {
2020                 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2021                 DateOrder eDF = (MayBeMonthDate() ? (nMayBeMonthDate == 2 ? DateOrder::DMY : DateOrder::YMD) : DateFmt);
2022                 switch (eDF)
2023                 {
2024                 case DateOrder::DMY:
2025                     pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2026                     pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2027                     break;
2028                 case DateOrder::YMD:
2029                     pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2030                     pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2031                     break;
2032                 default:
2033                     res = false;
2034                     break;
2035                 }
2036                 break;
2037             }
2038             default:            // else, e.g. month at the end (94 10 Jan)
2039                 res = false;
2040                 break;
2041             }   // switch (nMonthPos)
2042             break;
2043 
2044         default:                // more than two numbers (31.12.94 8:23) (31.12. 8:23)
2045             switch (nMonthPos)  // where is the month
2046             {
2047             case 0:             // not found
2048             {
2049                 nCounter = 3;
2050                 if ( nTimePos > 1 )
2051                 {   // find first time number index (should only be 3 or 2 anyway)
2052                     for ( sal_uInt16 j = 0; j < nNumericsCnt; j++ )
2053                     {
2054                         if ( nNums[j] == nTimePos - 2 )
2055                         {
2056                             nCounter = j;
2057                             break; // for
2058                         }
2059                     }
2060                 }
2061                 // ISO 8601 yyyy-mm-dd forced recognition
2062                 DateOrder eDF = (CanForceToIso8601( DateFmt) ? DateOrder::YMD : DateFmt);
2063                 switch (eDF)
2064                 {
2065                 case DateOrder::MDY:
2066                     pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2067                     pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2068                     if ( nCounter > 2 )
2069                         pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(2) );
2070                     break;
2071                 case DateOrder::DMY:
2072                     pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2073                     pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2074                     if ( nCounter > 2 )
2075                         pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(2) );
2076                     break;
2077                 case DateOrder::YMD:
2078                     if ( nCounter > 2 )
2079                         pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(2) );
2080                     pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2081                     pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2082                     break;
2083                 default:
2084                     res = false;
2085                     break;
2086                 }
2087                 break;
2088             }
2089             case 1:             // month at the beginning (Jan 01 01 8:23)
2090                 nCounter = 2;
2091                 switch (DateFmt)
2092                 {
2093                 case DateOrder::MDY:
2094                     pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2095                     pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2096                     pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2097                     break;
2098                 default:
2099                     res = false;
2100                     break;
2101                 }
2102                 break;
2103             case 2:             // month in the middle (10 Jan 94 8:23)
2104                 nCounter = 2;
2105                 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2106                 switch (DateFmt)
2107                 {
2108                 case DateOrder::DMY:
2109                     pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2110                     pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2111                     break;
2112                 case DateOrder::YMD:
2113                     pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2114                     pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2115                     break;
2116                 default:
2117                     res = false;
2118                     break;
2119                 }
2120                 break;
2121             default:            // else, e.g. month at the end (94 10 Jan 8:23)
2122                 nCounter = 2;
2123                 res = false;
2124                 break;
2125             }   // switch (nMonthPos)
2126             break;
2127         }   // switch (nNumericsCnt)
2128 
2129         if (mbEraCE != kDefaultEra)
2130             pCal->setValue( CalendarFieldIndex::ERA, mbEraCE ? 1 : 0);
2131 
2132         if ( res && pCal->isValid() )
2133         {
2134             double fDiff = DateTime(*pNullDate) - pCal->getEpochStart();
2135             fDays = ::rtl::math::approxFloor( pCal->getLocalDateTime() );
2136             fDays -= fDiff;
2137             nTryOrder = nFormatOrder; // break for
2138         }
2139         else
2140         {
2141             res = false;
2142         }
2143         if ( aOrgCalendar.getLength() )
2144         {
2145             pCal->loadCalendar( aOrgCalendar, pLoc->getLanguageTag().getLocale() ); // restore calendar
2146         }
2147 #if NF_TEST_CALENDAR
2148         {
2149             using namespace ::com::sun::star;
2150             struct entry { const char* lan; const char* cou; const char* cal; };
2151             const entry cals[] = {
2152                 { "en", "US",  "gregorian" },
2153                 { "ar", "TN",      "hijri" },
2154                 { "he", "IL",     "jewish" },
2155                 { "ja", "JP",     "gengou" },
2156                 { "ko", "KR", "hanja_yoil" },
2157                 { "th", "TH",   "buddhist" },
2158                 { "zh", "TW",        "ROC" },
2159                 {0,0,0}
2160             };
2161             lang::Locale aLocale;
2162             bool bValid;
2163             sal_Int16 nDay, nMyMonth, nYear, nHour, nMinute, nSecond;
2164             sal_Int16 nDaySet, nMonthSet, nYearSet, nHourSet, nMinuteSet, nSecondSet;
2165             sal_Int16 nZO, nDST1, nDST2, nDST, nZOmillis, nDST1millis, nDST2millis, nDSTmillis;
2166             sal_Int32 nZoneInMillis, nDST1InMillis, nDST2InMillis;
2167             uno::Reference< uno::XComponentContext > xContext =
2168                 ::comphelper::getProcessComponentContext();
2169             uno::Reference< i18n::XCalendar4 > xCal = i18n::LocaleCalendar2::create(xContext);
2170             for ( const entry* p = cals; p->lan; ++p )
2171             {
2172                 aLocale.Language = OUString::createFromAscii( p->lan );
2173                 aLocale.Country  = OUString::createFromAscii( p->cou );
2174                 xCal->loadCalendar( OUString::createFromAscii( p->cal ),
2175                                     aLocale );
2176                 double nDateTime = 0.0; // 1-Jan-1970 00:00:00
2177                 nZO           = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET );
2178                 nZOmillis     = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS );
2179                 nZoneInMillis = static_cast<sal_Int32>(nZO) * 60000 +
2180                     (nZO < 0 ? -1 : 1) * static_cast<sal_uInt16>(nZOmillis);
2181                 nDST1         = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2182                 nDST1millis   = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2183                 nDST1InMillis = static_cast<sal_Int32>(nDST1) * 60000 +
2184                     (nDST1 < 0 ? -1 : 1) * static_cast<sal_uInt16>(nDST1millis);
2185                 nDateTime    -= (double)(nZoneInMillis + nDST1InMillis) / 1000.0 / 60.0 / 60.0 / 24.0;
2186                 xCal->setDateTime( nDateTime );
2187                 nDST2         = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2188                 nDST2millis   = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2189                 nDST2InMillis = static_cast<sal_Int32>(nDST2) * 60000 +
2190                     (nDST2 < 0 ? -1 : 1) * static_cast<sal_uInt16>(nDST2millis);
2191                 if ( nDST1InMillis != nDST2InMillis )
2192                 {
2193                     nDateTime = 0.0 - (double)(nZoneInMillis + nDST2InMillis) / 1000.0 / 60.0 / 60.0 / 24.0;
2194                     xCal->setDateTime( nDateTime );
2195                 }
2196                 nDaySet    = xCal->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH );
2197                 nMonthSet  = xCal->getValue( i18n::CalendarFieldIndex::MONTH );
2198                 nYearSet   = xCal->getValue( i18n::CalendarFieldIndex::YEAR );
2199                 nHourSet   = xCal->getValue( i18n::CalendarFieldIndex::HOUR );
2200                 nMinuteSet = xCal->getValue( i18n::CalendarFieldIndex::MINUTE );
2201                 nSecondSet = xCal->getValue( i18n::CalendarFieldIndex::SECOND );
2202                 nZO        = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET );
2203                 nZOmillis  = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS );
2204                 nDST       = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2205                 nDSTmillis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2206                 xCal->setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH, nDaySet );
2207                 xCal->setValue( i18n::CalendarFieldIndex::MONTH, nMonthSet );
2208                 xCal->setValue( i18n::CalendarFieldIndex::YEAR, nYearSet );
2209                 xCal->setValue( i18n::CalendarFieldIndex::HOUR, nHourSet );
2210                 xCal->setValue( i18n::CalendarFieldIndex::MINUTE, nMinuteSet );
2211                 xCal->setValue( i18n::CalendarFieldIndex::SECOND, nSecondSet );
2212                 bValid  = xCal->isValid();
2213                 nDay    = xCal->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH );
2214                 nMyMonth= xCal->getValue( i18n::CalendarFieldIndex::MONTH );
2215                 nYear   = xCal->getValue( i18n::CalendarFieldIndex::YEAR );
2216                 nHour   = xCal->getValue( i18n::CalendarFieldIndex::HOUR );
2217                 nMinute = xCal->getValue( i18n::CalendarFieldIndex::MINUTE );
2218                 nSecond = xCal->getValue( i18n::CalendarFieldIndex::SECOND );
2219                 bValid = bValid && nDay == nDaySet && nMyMonth == nMonthSet && nYear ==
2220                     nYearSet && nHour == nHourSet && nMinute == nMinuteSet && nSecond
2221                     == nSecondSet;
2222             }
2223         }
2224 #endif  // NF_TEST_CALENDAR
2225 
2226     }
2227 
2228     return res;
2229 }
2230 
2231 
2232 /**
2233  * Analyze first string
2234  * All gone => true
2235  * else     => false
2236  */
2237 bool ImpSvNumberInputScan::ScanStartString( const OUString& rString )
2238 {
2239     sal_Int32 nPos = 0;
2240 
2241     // First of all, eat leading blanks
2242     SkipBlanks(rString, nPos);
2243 
2244     // Yes, nMatchedAllStrings should know about the sign position
2245     nSign = GetSign(rString, nPos);
2246     if ( nSign )           // sign?
2247     {
2248         SkipBlanks(rString, nPos);
2249     }
2250     // #102371# match against format string only if start string is not a sign character
2251     if ( nMatchedAllStrings && !(nSign && rString.getLength() == 1) )
2252     {
2253         // Match against format in any case, so later on for a "x1-2-3" input
2254         // we may distinguish between a xy-m-d (or similar) date and a x0-0-0
2255         // format. No sign detection here!
2256         if ( ScanStringNumFor( rString, nPos, 0, true ) )
2257         {
2258             nMatchedAllStrings |= nMatchedStartString;
2259         }
2260         else
2261         {
2262             nMatchedAllStrings = 0;
2263         }
2264     }
2265 
2266     // Bail out early for just a sign.
2267     if (nSign && nPos == rString.getLength())
2268         return true;
2269 
2270     if ( GetDecSep(rString, nPos) )                 // decimal separator in start string
2271     {
2272         nDecPos = 1;
2273         SkipBlanks(rString, nPos);
2274     }
2275     else if ( GetCurrency(rString, nPos) )          // currency (DM 1)?
2276     {
2277         eScannedType = SvNumFormatType::CURRENCY;       // !!! it IS currency !!!
2278         SkipBlanks(rString, nPos);
2279         if (nSign == 0)                             // no sign yet
2280         {
2281             nSign = GetSign(rString, nPos);
2282             if ( nSign )   // DM -1
2283             {
2284                 SkipBlanks(rString, nPos);
2285             }
2286         }
2287         if ( GetDecSep(rString, nPos) )             // decimal separator follows currency
2288         {
2289             nDecPos = 1;
2290             SkipBlanks(rString, nPos);
2291         }
2292     }
2293     else
2294     {
2295         const sal_Int32 nMonthStart = nPos;
2296         short nTempMonth = GetMonth(rString, nPos);
2297         if (nTempMonth < 0)
2298         {
2299             // Short month and day names may be identical in some locales, e.g.
2300             // "mar" for "martes" or "marzo" in Spanish.
2301             // Do not let a month name immediately take precedence if a day
2302             // name was meant instead. Assume that both could be valid, until
2303             // encountered differently or the final evaluation in
2304             // IsNumberFormat() checks, but continue with weighing the month
2305             // name higher unless we have both day of week and month name here.
2306             sal_Int32 nTempPos = nMonthStart;
2307             nDayOfWeek = GetDayOfWeek( rString, nTempPos);
2308             if (nDayOfWeek < 0)
2309             {
2310                 SkipChar( '.', rString, nTempPos ); // abbreviated
2311                 SkipString( pFormatter->GetLocaleData()->getLongDateDayOfWeekSep(), rString, nTempPos );
2312                 SkipBlanks( rString, nTempPos);
2313                 short nTempTempMonth = GetMonth( rString, nTempPos);
2314                 if (nTempTempMonth)
2315                 {
2316                     // Fall into the else branch below that handles both.
2317                     nTempMonth = 0;
2318                     nPos = nMonthStart;
2319                     nDayOfWeek = 0;
2320                     // Do not set nDayOfWeek hereafter, anywhere.
2321                 }
2322             }
2323         }
2324         if ( nTempMonth )    // month (Jan 1)?
2325         {
2326             // Jan1 without separator is not a date, unless it is followed by a
2327             // separator and a (year) number.
2328             if (nPos < rString.getLength() || (nStringsCnt >= 4 && nNumericsCnt >= 2))
2329             {
2330                 eScannedType = SvNumFormatType::DATE;   // !!! it IS a date !!!
2331                 nMonth = nTempMonth;
2332                 nMonthPos = 1;                      // month at the beginning
2333                 if ( nMonth < 0 )
2334                 {
2335                     SkipChar( '.', rString, nPos ); // abbreviated
2336                 }
2337                 SkipBlanks(rString, nPos);
2338             }
2339             else
2340             {
2341                 nPos = nMonthStart;                 // rewind month
2342             }
2343         }
2344         else
2345         {
2346             int nTempDayOfWeek = GetDayOfWeek( rString, nPos );
2347             if ( nTempDayOfWeek )
2348             {
2349                 // day of week is just parsed away
2350                 eScannedType = SvNumFormatType::DATE;       // !!! it IS a date !!!
2351                 if ( nPos < rString.getLength() )
2352                 {
2353                     if ( nTempDayOfWeek < 0 )
2354                     {
2355                         // abbreviated
2356                         if ( rString[ nPos ] == '.' )
2357                         {
2358                             ++nPos;
2359                         }
2360                     }
2361                     else
2362                     {
2363                         // full long name
2364                         SkipBlanks(rString, nPos);
2365                         SkipString( pFormatter->GetLocaleData()->getLongDateDayOfWeekSep(), rString, nPos );
2366                     }
2367                     SkipBlanks(rString, nPos);
2368                     nTempMonth = GetMonth(rString, nPos);
2369                     if ( nTempMonth ) // month (Jan 1)?
2370                     {
2371                         // Jan1 without separator is not a date, unless it is followed by a
2372                         // separator and a (year) number.
2373                         if (nPos < rString.getLength() || (nStringsCnt >= 4 && nNumericsCnt >= 2))
2374                         {
2375                             nMonth = nTempMonth;
2376                             nMonthPos = 1; // month at the beginning
2377                             if ( nMonth < 0 )
2378                             {
2379                                 SkipChar( '.', rString, nPos ); // abbreviated
2380                             }
2381                             SkipBlanks(rString, nPos);
2382                         }
2383                         else
2384                         {
2385                             nPos = nMonthStart;                 // rewind month
2386                         }
2387                     }
2388                 }
2389                 if (!nMonth)
2390                 {
2391                     // Determine and remember following date pattern, if any.
2392                     IsAcceptedDatePattern( 1);
2393                 }
2394             }
2395         }
2396     }
2397 
2398     // skip any trailing '-' or '/' chars
2399     if (nPos < rString.getLength())
2400     {
2401         while (SkipChar ('-', rString, nPos) || SkipChar ('/', rString, nPos))
2402             ; // do nothing
2403     }
2404     if (nPos < rString.getLength()) // not everything consumed
2405     {
2406         // Does input StartString equal StartString of format?
2407         // This time with sign detection!
2408         if ( !ScanStringNumFor( rString, nPos, 0 ) )
2409         {
2410             return MatchedReturn();
2411         }
2412     }
2413 
2414     return true;
2415 }
2416 
2417 
2418 /**
2419  * Analyze string in the middle
2420  * All gone => true
2421  * else     => false
2422  */
2423 bool ImpSvNumberInputScan::ScanMidString( const OUString& rString, sal_uInt16 nStringPos )
2424 {
2425     sal_Int32 nPos = 0;
2426     SvNumFormatType eOldScannedType = eScannedType;
2427 
2428     if ( nMatchedAllStrings )
2429     {   // Match against format in any case, so later on for a "1-2-3-4" input
2430         // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2431         // format.
2432         if ( ScanStringNumFor( rString, 0, nStringPos ) )
2433         {
2434             nMatchedAllStrings |= nMatchedMidString;
2435         }
2436         else
2437         {
2438             nMatchedAllStrings = 0;
2439         }
2440     }
2441 
2442     SkipBlanks(rString, nPos);
2443     if (GetDecSep(rString, nPos))                   // decimal separator?
2444     {
2445         if (nDecPos == 1 || nDecPos == 3)           // .12.4 or 1.E2.1
2446         {
2447             return MatchedReturn();
2448         }
2449         else if (nDecPos == 2)                      // . dup: 12.4.
2450         {
2451             bool bSignedYear = false;
2452             if (bDecSepInDateSeps ||                // . also date separator
2453                 SkipDatePatternSeparator( nStringPos, nPos, bSignedYear))
2454             {
2455                 if ( eScannedType != SvNumFormatType::UNDEFINED &&
2456                      eScannedType != SvNumFormatType::DATE &&
2457                      eScannedType != SvNumFormatType::DATETIME)  // already another type
2458                 {
2459                     return MatchedReturn();
2460                 }
2461                 if (eScannedType == SvNumFormatType::UNDEFINED)
2462                 {
2463                     eScannedType = SvNumFormatType::DATE; // !!! it IS a date
2464                 }
2465                 SkipBlanks(rString, nPos);
2466             }
2467             else
2468             {
2469                 return MatchedReturn();
2470             }
2471         }
2472         else
2473         {
2474             nDecPos = 2;                            // . in mid string
2475             SkipBlanks(rString, nPos);
2476         }
2477     }
2478     else if ( (eScannedType & SvNumFormatType::TIME) &&
2479               GetTime100SecSep( rString, nPos ) )
2480     {                                               // hundredth seconds separator
2481         if ( nDecPos )
2482         {
2483             return MatchedReturn();
2484         }
2485         nDecPos = 2;                                // . in mid string
2486 
2487         // If this is exactly an ISO 8601 fractional seconds separator, bail
2488         // out early to not get confused by later checks for group separator or
2489         // other.
2490         if (bIso8601Tsep && nPos == rString.getLength() &&
2491                 eScannedType == SvNumFormatType::DATETIME && (rString == "." || rString == ","))
2492             return true;
2493 
2494         SkipBlanks(rString, nPos);
2495     }
2496 
2497     if (SkipChar('/', rString, nPos))               // fraction?
2498     {
2499         if ( eScannedType != SvNumFormatType::UNDEFINED &&  // already another type
2500              eScannedType != SvNumFormatType::DATE)       // except date
2501         {
2502             return MatchedReturn();                     // => jan/31/1994
2503         }
2504         else if (eScannedType != SvNumFormatType::DATE &&    // analyzed no date until now
2505                  ( eSetType == SvNumFormatType::FRACTION ||  // and preset was fraction
2506                    (nNumericsCnt == 3 &&                     // or 3 numbers
2507                     (nStringPos == 3 ||                  // and 3rd string particle
2508                      (nStringPos == 4 && nSign)))))      // or 4th  if signed
2509         {
2510             SkipBlanks(rString, nPos);
2511             if (nPos == rString.getLength())
2512             {
2513                 eScannedType = SvNumFormatType::FRACTION;   // !!! it IS a fraction (so far)
2514                 if (eSetType == SvNumFormatType::FRACTION &&
2515                     nNumericsCnt == 2 &&
2516                     (nStringPos == 1 ||                     // for 4/5
2517                      (nStringPos == 2 && nSign)))           // or signed -4/5
2518                 {
2519                     return true;                            // don't fall into date trap
2520                 }
2521             }
2522         }
2523         else
2524         {
2525             nPos--;                                 // put '/' back
2526         }
2527     }
2528 
2529     if (GetThousandSep(rString, nPos, nStringPos))  // 1,000
2530     {
2531         if ( eScannedType != SvNumFormatType::UNDEFINED &&   // already another type
2532              eScannedType != SvNumFormatType::CURRENCY)      // except currency
2533         {
2534             return MatchedReturn();
2535         }
2536         nThousand++;
2537     }
2538 
2539     const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
2540     bool bSignedYear = false;
2541     bool bDate = SkipDatePatternSeparator( nStringPos, nPos, bSignedYear);   // 12/31  31.12.  12/31/1999  31.12.1999
2542     if (!bDate)
2543     {
2544         const OUString& rDate = pFormatter->GetDateSep();
2545         SkipBlanks(rString, nPos);
2546         bDate = SkipString( rDate, rString, nPos);      // 10.  10-  10/
2547     }
2548     if (bDate || ((MayBeIso8601() || MayBeMonthDate()) &&    // 1999-12-31  31-Dec-1999
2549                   SkipChar( '-', rString, nPos)))
2550     {
2551         if ( eScannedType != SvNumFormatType::UNDEFINED &&  // already another type
2552              eScannedType != SvNumFormatType::DATE)       // except date
2553         {
2554             return MatchedReturn();
2555         }
2556         SkipBlanks(rString, nPos);
2557         eScannedType = SvNumFormatType::DATE;           // !!! it IS a date
2558         short nTmpMonth = GetMonth(rString, nPos);  // 10. Jan 94
2559         if (nMonth && nTmpMonth)                    // month dup
2560         {
2561             return MatchedReturn();
2562         }
2563         if (nTmpMonth)
2564         {
2565             nMonth = nTmpMonth;
2566             nMonthPos = 2;                          // month in the middle
2567             if ( nMonth < 0 && SkipChar( '.', rString, nPos ) )
2568                 ;   // short month may be abbreviated Jan.
2569             else if ( SkipChar( '-', rString, nPos ) )
2570                 ;   // #79632# recognize 17-Jan-2001 to be a date
2571                     // #99065# short and long month name
2572             else
2573             {
2574                 SkipString( pLoc->getLongDateMonthSep(), rString, nPos );
2575             }
2576             SkipBlanks(rString, nPos);
2577         }
2578         if (bSignedYear)
2579         {
2580             if (mbEraCE != kDefaultEra)               // signed year twice?
2581                 return MatchedReturn();
2582 
2583             mbEraCE = false;  // BCE
2584         }
2585     }
2586 
2587     const sal_Int32 nMonthStart = nPos;
2588     short nTempMonth = GetMonth(rString, nPos);     // month in the middle (10 Jan 94)
2589     if (nTempMonth)
2590     {
2591         if (nMonth != 0)                            // month dup
2592         {
2593             return MatchedReturn();
2594         }
2595         if ( eScannedType != SvNumFormatType::UNDEFINED &&  // already another type
2596              eScannedType != SvNumFormatType::DATE)         // except date
2597         {
2598             return MatchedReturn();
2599         }
2600         if (nMonthStart > 0 && nPos < rString.getLength())  // 10Jan or Jan94 without separator are not dates
2601         {
2602             eScannedType = SvNumFormatType::DATE;       // !!! it IS a date
2603             nMonth = nTempMonth;
2604             nMonthPos = 2;                          // month in the middle
2605             if ( nMonth < 0 )
2606             {
2607                 SkipChar( '.', rString, nPos );     // abbreviated
2608             }
2609             SkipString( pLoc->getLongDateMonthSep(), rString, nPos );
2610             SkipBlanks(rString, nPos);
2611         }
2612         else
2613         {
2614             nPos = nMonthStart;                     // rewind month
2615         }
2616     }
2617 
2618     if ( SkipChar('E', rString, nPos) ||            // 10E, 10e, 10,Ee
2619          SkipChar('e', rString, nPos) )
2620     {
2621         if (eScannedType != SvNumFormatType::UNDEFINED) // already another type
2622         {
2623             return MatchedReturn();
2624         }
2625         else
2626         {
2627             SkipBlanks(rString, nPos);
2628             eScannedType = SvNumFormatType::SCIENTIFIC; // !!! it IS scientific
2629             if ( nThousand+2 == nNumericsCnt && nDecPos == 2 ) // special case 1.E2
2630             {
2631                 nDecPos = 3;                        // 1,100.E2 1,100,100.E3
2632             }
2633         }
2634         nESign = GetESign(rString, nPos);           // signed exponent?
2635         SkipBlanks(rString, nPos);
2636     }
2637 
2638     const OUString& rTime = pLoc->getTimeSep();
2639     if ( SkipString(rTime, rString, nPos) )         // time separator?
2640     {
2641         if (nDecPos)                                // already . => maybe error
2642         {
2643             if (bDecSepInDateSeps)                  // . also date sep
2644             {
2645                 if ( eScannedType != SvNumFormatType::DATE &&    // already another type than date
2646                      eScannedType != SvNumFormatType::DATETIME)  // or date time
2647                 {
2648                     return MatchedReturn();
2649                 }
2650                 if (eScannedType == SvNumFormatType::DATE)
2651                 {
2652                     nDecPos = 0;                    // reset for time transition
2653                 }
2654             }
2655             else
2656             {
2657                 return MatchedReturn();
2658             }
2659         }
2660         if ((eScannedType == SvNumFormatType::DATE ||        // already date type
2661              eScannedType == SvNumFormatType::DATETIME) &&   // or date time
2662             nNumericsCnt > 3)                                // and more than 3 numbers? (31.Dez.94 8:23)
2663         {
2664             SkipBlanks(rString, nPos);
2665             eScannedType = SvNumFormatType::DATETIME;   // !!! it IS date with time
2666         }
2667         else if ( eScannedType != SvNumFormatType::UNDEFINED &&  // already another type
2668                   eScannedType != SvNumFormatType::TIME)         // except time
2669         {
2670             return MatchedReturn();
2671         }
2672         else
2673         {
2674             SkipBlanks(rString, nPos);
2675             eScannedType = SvNumFormatType::TIME;       // !!! it IS a time
2676         }
2677         if ( !nTimePos )
2678         {
2679             nTimePos = nStringPos + 1;
2680         }
2681     }
2682 
2683     if (nPos < rString.getLength())
2684     {
2685         switch (eScannedType)
2686         {
2687         case SvNumFormatType::DATE:
2688             if (nMonthPos == 1 && pLoc->getLongDateOrder() == DateOrder::MDY)
2689             {
2690                 // #68232# recognize long date separators like ", " in "September 5, 1999"
2691                 if (SkipString( pLoc->getLongDateDaySep(), rString, nPos ))
2692                 {
2693                     SkipBlanks( rString, nPos );
2694                 }
2695             }
2696             else if (nPos == 0 && rString.getLength() == 1 && MayBeIso8601())
2697             {
2698                 if (    (nStringPos == 5 && rString[0] == 'T') ||
2699                         (nStringPos == 6 && rString[0] == 'T' && sStrArray[0] == "-"))
2700                 {
2701                     // ISO 8601 combined date and time, yyyy-mm-ddThh:mm or -yyyy-mm-ddThh:mm
2702                     ++nPos;
2703                     bIso8601Tsep = true;
2704                 }
2705                 else if (nStringPos == 7 && rString[0] == ':')
2706                 {
2707                     // ISO 8601 combined date and time, the time part; we reach
2708                     // here if the locale's separator is not ':' so it couldn't
2709                     // be detected above in the time block.
2710                     if (nNumericsCnt >= 5)
2711                         eScannedType = SvNumFormatType::DATETIME;
2712                     ++nPos;
2713                 }
2714             }
2715             break;
2716         case SvNumFormatType::DATETIME:
2717             if (nPos == 0 && rString.getLength() == 1 && MayBeIso8601())
2718             {
2719                 if (nStringPos == 9 && rString[0] == ':')
2720                 {
2721                     // ISO 8601 combined date and time, the time part continued.
2722                     ++nPos;
2723                 }
2724             }
2725 #if NF_RECOGNIZE_ISO8601_TIMEZONES
2726             else if (nPos == 0 && rString.getLength() == 1 && nStringPos >= 9 && MayBeIso8601())
2727             {
2728                 // ISO 8601 timezone offset
2729                 switch (rString[ 0 ])
2730                 {
2731                 case '+':
2732                 case '-':
2733                     if (nStringPos == nStringsCnt - 2 ||
2734                         nStringPos == nStringsCnt - 4)
2735                     {
2736                         ++nPos;     // yyyy-mm-ddThh:mm[:ss]+xx[[:]yy]
2737                         // nTimezonePos needed for GetTimeRef()
2738                         if (!nTimezonePos)
2739                         {
2740                             nTimezonePos = nStringPos + 1;
2741                         }
2742                     }
2743                     break;
2744                 case ':':
2745                     if (nTimezonePos && nStringPos >= 11 &&
2746                         nStringPos == nStringsCnt - 2)
2747                     {
2748                         ++nPos;     // yyyy-mm-ddThh:mm[:ss]+xx:yy
2749                     }
2750                     break;
2751                 }
2752             }
2753 #endif
2754             break;
2755         default: break;
2756         }
2757     }
2758 
2759     if (nPos < rString.getLength()) // not everything consumed?
2760     {
2761         if ( nMatchedAllStrings & ~nMatchedVirgin )
2762         {
2763             eScannedType = eOldScannedType;
2764         }
2765         else
2766         {
2767             return false;
2768         }
2769     }
2770 
2771     return true;
2772 }
2773 
2774 
2775 /**
2776  * Analyze the end
2777  * All gone => true
2778  * else     => false
2779  */
2780 bool ImpSvNumberInputScan::ScanEndString( const OUString& rString )
2781 {
2782     sal_Int32 nPos = 0;
2783 
2784     if ( nMatchedAllStrings )
2785     {   // Match against format in any case, so later on for a "1-2-3-4" input
2786         // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2787         // format.
2788         if ( ScanStringNumFor( rString, 0, 0xFFFF ) )
2789         {
2790             nMatchedAllStrings |= nMatchedEndString;
2791         }
2792         else
2793         {
2794             nMatchedAllStrings = 0;
2795         }
2796     }
2797 
2798     SkipBlanks(rString, nPos);
2799     if (GetDecSep(rString, nPos))                   // decimal separator?
2800     {
2801         if (nDecPos == 1 || nDecPos == 3)           // .12.4 or 12.E4.
2802         {
2803             return MatchedReturn();
2804         }
2805         else if (nDecPos == 2)                      // . dup: 12.4.
2806         {
2807             bool bSignedYear = false;
2808             if (bDecSepInDateSeps ||                // . also date separator
2809                 SkipDatePatternSeparator( nStringsCnt-1, nPos, bSignedYear))
2810             {
2811                 if ( eScannedType != SvNumFormatType::UNDEFINED &&
2812                      eScannedType != SvNumFormatType::DATE &&
2813                      eScannedType != SvNumFormatType::DATETIME)  // already another type
2814                 {
2815                     return MatchedReturn();
2816                 }
2817                 if (eScannedType == SvNumFormatType::UNDEFINED)
2818                 {
2819                     eScannedType = SvNumFormatType::DATE;   // !!! it IS a date
2820                 }
2821                 SkipBlanks(rString, nPos);
2822             }
2823             else
2824             {
2825                 return MatchedReturn();
2826             }
2827         }
2828         else
2829         {
2830             nDecPos = 3;                            // . in end string
2831             SkipBlanks(rString, nPos);
2832         }
2833     }
2834 
2835     bool bSignDetectedHere = false;
2836     if ( nSign == 0  &&                             // conflict - not signed
2837          eScannedType != SvNumFormatType::DATE)         // and not date
2838                                                     //!? catch time too?
2839     {                                               // not signed yet
2840         nSign = GetSign(rString, nPos);             // 1- DM
2841         if (bNegCheck)                              // '(' as sign
2842         {
2843             return MatchedReturn();
2844         }
2845         if (nSign)
2846         {
2847             bSignDetectedHere = true;
2848         }
2849     }
2850 
2851     SkipBlanks(rString, nPos);
2852     if (bNegCheck && SkipChar(')', rString, nPos))  // skip ')' if appropriate
2853     {
2854         bNegCheck = false;
2855         SkipBlanks(rString, nPos);
2856     }
2857 
2858     if ( GetCurrency(rString, nPos) )               // currency symbol?
2859     {
2860         if (eScannedType != SvNumFormatType::UNDEFINED) // currency dup
2861         {
2862             return MatchedReturn();
2863         }
2864         else
2865         {
2866             SkipBlanks(rString, nPos);
2867             eScannedType = SvNumFormatType::CURRENCY;
2868         }                                           // behind currency a '-' is allowed
2869         if (nSign == 0)                             // not signed yet
2870         {
2871             nSign = GetSign(rString, nPos);         // DM -
2872             SkipBlanks(rString, nPos);
2873             if (bNegCheck)                          // 3 DM (
2874             {
2875                 return MatchedReturn();
2876             }
2877         }
2878         if ( bNegCheck && eScannedType == SvNumFormatType::CURRENCY &&
2879              SkipChar(')', rString, nPos) )
2880         {
2881             bNegCheck = false;                          // ')' skipped
2882             SkipBlanks(rString, nPos);              // only if currency
2883         }
2884     }
2885 
2886     if ( SkipChar('%', rString, nPos) )             // 1%
2887     {
2888         if (eScannedType != SvNumFormatType::UNDEFINED) // already another type
2889         {
2890             return MatchedReturn();
2891         }
2892         SkipBlanks(rString, nPos);
2893         eScannedType = SvNumFormatType::PERCENT;
2894     }
2895 
2896     const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
2897     const OUString& rTime = pLoc->getTimeSep();
2898     if ( SkipString(rTime, rString, nPos) )         // 10:
2899     {
2900         if (nDecPos)                                // already , => error
2901         {
2902             return MatchedReturn();
2903         }
2904         if (eScannedType == SvNumFormatType::DATE && nNumericsCnt > 2) // 31.Dez.94 8:
2905         {
2906             SkipBlanks(rString, nPos);
2907             eScannedType = SvNumFormatType::DATETIME;
2908         }
2909         else if (eScannedType != SvNumFormatType::UNDEFINED &&
2910                  eScannedType != SvNumFormatType::TIME) // already another type
2911         {
2912             return MatchedReturn();
2913         }
2914         else
2915         {
2916             SkipBlanks(rString, nPos);
2917             eScannedType = SvNumFormatType::TIME;
2918         }
2919         if ( !nTimePos )
2920         {
2921             nTimePos = nStringsCnt;
2922         }
2923     }
2924 
2925     bool bSignedYear = false;
2926     bool bDate = SkipDatePatternSeparator( nStringsCnt-1, nPos, bSignedYear);   // 12/31  31.12.  12/31/1999  31.12.1999
2927     if (!bDate)
2928     {
2929         const OUString& rDate = pFormatter->GetDateSep();
2930         bDate = SkipString( rDate, rString, nPos);      // 10.  10-  10/
2931     }
2932     if (bDate && bSignDetectedHere)
2933     {
2934         nSign = 0;                                  // 'D-' takes precedence over signed date
2935     }
2936     if (bDate || ((MayBeIso8601() || MayBeMonthDate())
2937                   && SkipChar( '-', rString, nPos)))
2938     {
2939         if (eScannedType != SvNumFormatType::UNDEFINED &&
2940             eScannedType != SvNumFormatType::DATE)          // already another type
2941         {
2942             return MatchedReturn();
2943         }
2944         else
2945         {
2946             SkipBlanks(rString, nPos);
2947             eScannedType = SvNumFormatType::DATE;
2948         }
2949         short nTmpMonth = GetMonth(rString, nPos);  // 10. Jan
2950         if (nMonth && nTmpMonth)                    // month dup
2951         {
2952             return MatchedReturn();
2953         }
2954         if (nTmpMonth)
2955         {
2956             nMonth = nTmpMonth;
2957             nMonthPos = 3;                          // month at end
2958             if ( nMonth < 0 )
2959             {
2960                 SkipChar( '.', rString, nPos );     // abbreviated
2961             }
2962             SkipBlanks(rString, nPos);
2963         }
2964     }
2965 
2966     const sal_Int32 nMonthStart = nPos;
2967     short nTempMonth = GetMonth(rString, nPos);     // 10 Jan
2968     if (nTempMonth)
2969     {
2970         if (nMonth)                                 // month dup
2971         {
2972             return MatchedReturn();
2973         }
2974         if (eScannedType != SvNumFormatType::UNDEFINED &&
2975             eScannedType != SvNumFormatType::DATE)      // already another type
2976         {
2977             return MatchedReturn();
2978         }
2979         if (nMonthStart > 0)                        // 10Jan without separator is not a date
2980         {
2981             eScannedType = SvNumFormatType::DATE;
2982             nMonth = nTempMonth;
2983             nMonthPos = 3;                          // month at end
2984             if ( nMonth < 0 )
2985             {
2986                 SkipChar( '.', rString, nPos );     // abbreviated
2987             }
2988             SkipBlanks(rString, nPos);
2989         }
2990         else
2991         {
2992             nPos = nMonthStart;                     // rewind month
2993         }
2994     }
2995 
2996     sal_Int32 nOrigPos = nPos;
2997     if (GetTimeAmPm(rString, nPos))
2998     {
2999         if (eScannedType != SvNumFormatType::UNDEFINED &&
3000             eScannedType != SvNumFormatType::TIME &&
3001             eScannedType != SvNumFormatType::DATETIME)  // already another type
3002         {
3003             return MatchedReturn();
3004         }
3005         else
3006         {
3007             // If not already scanned as time, 6.78am does not result in 6
3008             // seconds and 78 hundredths in the morning. Keep as suffix.
3009             if (eScannedType != SvNumFormatType::TIME && nDecPos == 2 && nNumericsCnt == 2)
3010             {
3011                 nPos = nOrigPos; // rewind am/pm
3012             }
3013             else
3014             {
3015                 SkipBlanks(rString, nPos);
3016                 if ( eScannedType != SvNumFormatType::DATETIME )
3017                 {
3018                     eScannedType = SvNumFormatType::TIME;
3019                 }
3020             }
3021         }
3022     }
3023 
3024     if ( bNegCheck && SkipChar(')', rString, nPos) )
3025     {
3026         if (eScannedType == SvNumFormatType::CURRENCY)  // only if currency
3027         {
3028             bNegCheck = false;                          // skip ')'
3029             SkipBlanks(rString, nPos);
3030         }
3031         else
3032         {
3033             return MatchedReturn();
3034         }
3035     }
3036 
3037     if ( nPos < rString.getLength() &&
3038          (eScannedType == SvNumFormatType::DATE ||
3039           eScannedType == SvNumFormatType::DATETIME) )
3040     {
3041         // day of week is just parsed away
3042         sal_Int32 nOldPos = nPos;
3043         const OUString& rSep = pFormatter->GetLocaleData()->getLongDateDayOfWeekSep();
3044         if ( StringContains( rSep, rString, nPos ) )
3045         {
3046             nPos = nPos + rSep.getLength();
3047             SkipBlanks(rString, nPos);
3048         }
3049         int nTempDayOfWeek = GetDayOfWeek( rString, nPos );
3050         if ( nTempDayOfWeek )
3051         {
3052             if ( nPos < rString.getLength() )
3053             {
3054                 if ( nTempDayOfWeek < 0 )
3055                 {   // short
3056                     if ( rString[ nPos ] == '.' )
3057                     {
3058                         ++nPos;
3059                     }
3060                 }
3061                 SkipBlanks(rString, nPos);
3062             }
3063         }
3064         else
3065         {
3066             nPos = nOldPos;
3067         }
3068     }
3069 
3070 #if NF_RECOGNIZE_ISO8601_TIMEZONES
3071     if (nPos == 0 && eScannedType == SvNumFormatType::DATETIME &&
3072         rString.getLength() == 1 && rString[ 0 ] == 'Z' && MayBeIso8601())
3073     {
3074         // ISO 8601 timezone UTC yyyy-mm-ddThh:mmZ
3075         ++nPos;
3076     }
3077 #endif
3078 
3079     if (nPos < rString.getLength()) // everything consumed?
3080     {
3081         // does input EndString equal EndString in Format?
3082         if ( !ScanStringNumFor( rString, nPos, 0xFFFF ) )
3083         {
3084             return false;
3085         }
3086     }
3087 
3088     return true;
3089 }
3090 
3091 
3092 bool ImpSvNumberInputScan::ScanStringNumFor( const OUString& rString,       // String to scan
3093                                              sal_Int32 nPos,                // Position until which was consumed
3094                                              sal_uInt16 nString,            // Substring of format, 0xFFFF => last
3095                                              bool bDontDetectNegation)      // Suppress sign detection
3096 {
3097     if ( !mpFormat )
3098     {
3099         return false;
3100     }
3101     const ::utl::TransliterationWrapper* pTransliteration = pFormatter->GetTransliteration();
3102     const OUString* pStr;
3103     OUString aString( rString );
3104     bool bFound = false;
3105     bool bFirst = true;
3106     bool bContinue = true;
3107     sal_uInt16 nSub;
3108     do
3109     {
3110         // Don't try "lower" subformats ff the very first match was the second
3111         // or third subformat.
3112         nSub = nStringScanNumFor;
3113         do
3114         {   // Step through subformats, first positive, then negative, then
3115             // other, but not the last (text) subformat.
3116             pStr = mpFormat->GetNumForString( nSub, nString, true );
3117             if ( pStr && pTransliteration->isEqual( aString, *pStr ) )
3118             {
3119                 bFound = true;
3120                 bContinue = false;
3121             }
3122             else if ( nSub < 2 )
3123             {
3124                 ++nSub;
3125             }
3126             else
3127             {
3128                 bContinue = false;
3129             }
3130         }
3131         while ( bContinue );
3132         if ( !bFound && bFirst && nPos )
3133         {
3134             // try remaining substring
3135             bFirst = false;
3136             aString = aString.copy(nPos);
3137             bContinue = true;
3138         }
3139     }
3140     while ( bContinue );
3141 
3142     if ( !bFound )
3143     {
3144         if ( !bDontDetectNegation && (nString == 0) &&
3145              !bFirst && (nSign < 0) && mpFormat->IsSecondSubformatRealNegative() )
3146         {
3147             // simply negated twice? --1
3148             aString = aString.replaceAll(" ", "");
3149             if ( (aString.getLength() == 1) && (aString[0] == '-') )
3150             {
3151                 bFound = true;
3152                 nStringScanSign = -1;
3153                 nSub = 0; //! not 1
3154             }
3155         }
3156         if ( !bFound )
3157         {
3158             return false;
3159         }
3160     }
3161     else if ( !bDontDetectNegation && (nSub == 1) &&
3162               mpFormat->IsSecondSubformatRealNegative() )
3163     {
3164         // negative
3165         if ( nStringScanSign < 0 )
3166         {
3167             if ( (nSign < 0) && (nStringScanNumFor != 1) )
3168             {
3169                 nStringScanSign = 1; // triple negated --1 yyy
3170             }
3171         }
3172         else if ( nStringScanSign == 0 )
3173         {
3174             if ( nSign < 0 )
3175             {   // nSign and nStringScanSign will be combined later,
3176                 // flip sign if doubly negated
3177                 if ( (nString == 0) && !bFirst &&
3178                      SvNumberformat::HasStringNegativeSign( aString ) )
3179                 {
3180                     nStringScanSign = -1; // direct double negation
3181                 }
3182                 else if ( mpFormat->IsNegativeWithoutSign() )
3183                 {
3184                     nStringScanSign = -1; // indirect double negation
3185                 }
3186             }
3187             else
3188             {
3189                 nStringScanSign = -1;
3190             }
3191         }
3192         else    // > 0
3193         {
3194             nStringScanSign = -1;
3195         }
3196     }
3197     nStringScanNumFor = nSub;
3198     return true;
3199 }
3200 
3201 
3202 /**
3203  * Recognizes types of number, exponential, fraction, percent, currency, date, time.
3204  * Else text => return false
3205  */
3206 bool ImpSvNumberInputScan::IsNumberFormatMain( const OUString& rString,        // string to be analyzed
3207                                                const SvNumberformat* pFormat ) // maybe number format set to match against
3208 {
3209     Reset();
3210     mpFormat = pFormat;
3211     NumberStringDivision( rString );             // breakdown into strings and numbers
3212     if (nStringsCnt >= SV_MAX_COUNT_INPUT_STRINGS) // too many elements
3213     {
3214         return false;                            // Njet, Nope, ...
3215     }
3216     if (nNumericsCnt == 0)                           // no number in input
3217     {
3218         if ( nStringsCnt > 0 )
3219         {
3220             // Here we may change the original, we don't need it anymore.
3221             // This saves copies and ToUpper() in GetLogical() and is faster.
3222             sStrArray[0] = comphelper::string::strip(sStrArray[0], ' ');
3223             OUString& rStrArray = sStrArray[0];
3224             nLogical = GetLogical( rStrArray );
3225             if ( nLogical )
3226             {
3227                 eScannedType = SvNumFormatType::LOGICAL; // !!! it's a BOOLEAN
3228                 nMatchedAllStrings &= ~nMatchedVirgin;
3229                 return true;
3230             }
3231             else
3232             {
3233                 return false;                   // simple text
3234             }
3235         }
3236         else
3237         {
3238             return false;                       // simple text
3239         }
3240     }
3241 
3242     sal_uInt16 i = 0;                           // mark any symbol
3243     sal_uInt16 j = 0;                           // mark only numbers
3244 
3245     switch ( nNumericsCnt )
3246     {
3247     case 1 :                                // Exactly 1 number in input
3248         // nStringsCnt >= 1
3249         if (GetNextNumber(i,j)) // i=1,0
3250         {   // Number at start
3251             if (eSetType == SvNumFormatType::FRACTION)  // Fraction 1 = 1/1
3252             {
3253                 if (i >= nStringsCnt || // no end string nor decimal separator
3254                     pFormatter->IsDecimalSep( sStrArray[i]))
3255                 {
3256                     eScannedType = SvNumFormatType::FRACTION;
3257                     nMatchedAllStrings &= ~nMatchedVirgin;
3258                     return true;
3259                 }
3260             }
3261         }
3262         else
3263         {                                   // Analyze start string
3264             if (!ScanStartString( sStrArray[i] ))  // i=0
3265             {
3266                 return false;               // already an error
3267             }
3268             i++;                            // next symbol, i=1
3269         }
3270         GetNextNumber(i,j);                 // i=1,2
3271         if (eSetType == SvNumFormatType::FRACTION)  // Fraction -1 = -1/1
3272         {
3273             if (nSign && !bNegCheck &&      // Sign +, -
3274                 eScannedType == SvNumFormatType::UNDEFINED &&   // not date or currency
3275                 nDecPos == 0 &&             // no previous decimal separator
3276                 (i >= nStringsCnt ||        // no end string nor decimal separator
3277                  pFormatter->IsDecimalSep( sStrArray[i]))
3278                 )
3279             {
3280                 eScannedType = SvNumFormatType::FRACTION;
3281                 nMatchedAllStrings &= ~nMatchedVirgin;
3282                 return true;
3283             }
3284         }
3285         if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3286         {
3287             return false;
3288         }
3289         break;
3290     case 2 :                                // Exactly 2 numbers in input
3291                                             // nStringsCnt >= 3
3292         if (!GetNextNumber(i,j))            // i=1,0
3293         {                                   // Analyze start string
3294             if (!ScanStartString( sStrArray[i] ))
3295                 return false;               // already an error
3296             i++;                            // i=1
3297         }
3298         GetNextNumber(i,j);                 // i=1,2
3299         if ( !ScanMidString( sStrArray[i], i ) )
3300         {
3301             return false;
3302         }
3303         i++;                                // next symbol, i=2,3
3304         GetNextNumber(i,j);                 // i=3,4
3305         if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3306         {
3307             return false;
3308         }
3309         if (eSetType == SvNumFormatType::FRACTION)  // -1,200. as fraction
3310         {
3311             if (!bNegCheck  &&                  // no sign '('
3312                 eScannedType == SvNumFormatType::UNDEFINED &&
3313                 (nDecPos == 0 || nDecPos == 3)  // no decimal separator or at end
3314                 )
3315             {
3316                 eScannedType = SvNumFormatType::FRACTION;
3317                 nMatchedAllStrings &= ~nMatchedVirgin;
3318                 return true;
3319             }
3320         }
3321         break;
3322     case 3 :                                // Exactly 3 numbers in input
3323                                             // nStringsCnt >= 5
3324         if (!GetNextNumber(i,j))            // i=1,0
3325         {                                   // Analyze start string
3326             if (!ScanStartString( sStrArray[i] ))
3327             {
3328                 return false;               // already an error
3329             }
3330             i++;                            // i=1
3331             if (nDecPos == 1)               // decimal separator at start => error
3332             {
3333                 return false;
3334             }
3335         }
3336         GetNextNumber(i,j);                 // i=1,2
3337         if ( !ScanMidString( sStrArray[i], i ) )
3338         {
3339             return false;
3340         }
3341         i++;                                // i=2,3
3342         if (eScannedType == SvNumFormatType::SCIENTIFIC)    // E only at end
3343         {
3344             return false;
3345         }
3346         GetNextNumber(i,j);                 // i=3,4
3347         if ( !ScanMidString( sStrArray[i], i ) )
3348         {
3349             return false;
3350         }
3351         i++;                                // i=4,5
3352         GetNextNumber(i,j);                 // i=5,6
3353         if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3354         {
3355             return false;
3356         }
3357         if (eSetType == SvNumFormatType::FRACTION)  // -1,200,100. as fraction
3358         {
3359             if (!bNegCheck  &&                  // no sign '('
3360                 eScannedType == SvNumFormatType::UNDEFINED &&
3361                 (nDecPos == 0 || nDecPos == 3)  // no decimal separator or at end
3362                 )
3363             {
3364                 eScannedType = SvNumFormatType::FRACTION;
3365                 nMatchedAllStrings &= ~nMatchedVirgin;
3366                 return true;
3367             }
3368         }
3369         if ( eScannedType == SvNumFormatType::FRACTION && nDecPos )
3370         {
3371             return false;                   // #36857# not a real fraction
3372         }
3373         break;
3374     default:                                // More than 3 numbers in input
3375                                             // nStringsCnt >= 7
3376         if (!GetNextNumber(i,j))            // i=1,0
3377         {                                   // Analyze startstring
3378             if (!ScanStartString( sStrArray[i] ))
3379                 return false;               // already an error
3380             i++;                            // i=1
3381             if (nDecPos == 1)               // decimal separator at start => error
3382                 return false;
3383         }
3384         GetNextNumber(i,j);                 // i=1,2
3385         if ( !ScanMidString( sStrArray[i], i ) )
3386         {
3387             return false;
3388         }
3389         i++;                                // i=2,3
3390         {
3391             sal_uInt16 nThOld = 10;                 // just not 0 or 1
3392             while (nThOld != nThousand && j < nNumericsCnt-1) // Execute at least one time
3393                                                           // but leave one number.
3394             {                                             // Loop over group separators
3395                 nThOld = nThousand;
3396                 if (eScannedType == SvNumFormatType::SCIENTIFIC)    // E only at end
3397                 {
3398                     return false;
3399                 }
3400                 GetNextNumber(i,j);
3401                 if ( i < nStringsCnt && !ScanMidString( sStrArray[i], i ) )
3402                 {
3403                     return false;
3404                 }
3405                 i++;
3406             }
3407         }
3408         if (eScannedType == SvNumFormatType::DATE ||    // long date or
3409             eScannedType == SvNumFormatType::TIME ||    // long time or
3410             eScannedType == SvNumFormatType::UNDEFINED) // long number
3411         {
3412             for (sal_uInt16 k = j; k < nNumericsCnt-1; k++)
3413             {
3414                 if (eScannedType == SvNumFormatType::SCIENTIFIC)    // E only at endd
3415                 {
3416                     return false;
3417                 }
3418                 GetNextNumber(i,j);
3419                 if ( i < nStringsCnt && !ScanMidString( sStrArray[i], i ) )
3420                 {
3421                     return false;
3422                 }
3423                 i++;
3424             }
3425         }
3426         GetNextNumber(i,j);
3427         if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3428         {
3429             return false;
3430         }
3431         if (eSetType == SvNumFormatType::FRACTION)  // -1,200,100. as fraction
3432         {
3433             if (!bNegCheck  &&                  // no sign '('
3434                 eScannedType == SvNumFormatType::UNDEFINED &&
3435                 (nDecPos == 0 || nDecPos == 3)  // no decimal separator or at end
3436                 )
3437             {
3438                 eScannedType = SvNumFormatType::FRACTION;
3439                 nMatchedAllStrings &= ~nMatchedVirgin;
3440                 return true;
3441             }
3442         }
3443         if ( eScannedType == SvNumFormatType::FRACTION && nDecPos )
3444         {
3445             return false;                       // #36857# not a real fraction
3446         }
3447         break;
3448     }
3449 
3450     if (eScannedType == SvNumFormatType::UNDEFINED)
3451     {
3452         nMatchedAllStrings &= ~nMatchedVirgin;
3453         // did match including nMatchedUsedAsReturn
3454         bool bDidMatch = (nMatchedAllStrings != 0);
3455         if ( nMatchedAllStrings )
3456         {
3457             bool bMatch = mpFormat && mpFormat->IsNumForStringElementCountEqual(
3458                                nStringScanNumFor, nStringsCnt, nNumericsCnt );
3459             if ( !bMatch )
3460             {
3461                 nMatchedAllStrings = 0;
3462             }
3463         }
3464         if ( nMatchedAllStrings )
3465         {
3466             // A type DEFINED means that no category could be assigned to the
3467             // overall format because of mixed type subformats. Use the scan
3468             // matched subformat's type if any.
3469             SvNumFormatType eForType = eSetType;
3470             if ((eForType == SvNumFormatType::UNDEFINED || eForType == SvNumFormatType::DEFINED) && mpFormat)
3471                 eForType = mpFormat->GetNumForInfoScannedType( nStringScanNumFor);
3472             if (eForType != SvNumFormatType::UNDEFINED && eForType != SvNumFormatType::DEFINED)
3473                 eScannedType = eForType;
3474             else
3475                 eScannedType = SvNumFormatType::NUMBER;
3476         }
3477         else if ( bDidMatch )
3478         {
3479             return false;
3480         }
3481         else
3482         {
3483             eScannedType = SvNumFormatType::NUMBER;
3484             // everything else should have been recognized by now
3485         }
3486     }
3487     else if ( eScannedType == SvNumFormatType::DATE )
3488     {
3489         // the very relaxed date input checks may interfere with a preset format
3490         nMatchedAllStrings &= ~nMatchedVirgin;
3491         bool bWasReturn = ((nMatchedAllStrings & nMatchedUsedAsReturn) != 0);
3492         if ( nMatchedAllStrings )
3493         {
3494             bool bMatch = mpFormat && mpFormat->IsNumForStringElementCountEqual(
3495                                nStringScanNumFor, nStringsCnt, nNumericsCnt );
3496             if ( !bMatch )
3497             {
3498                 nMatchedAllStrings = 0;
3499             }
3500         }
3501         if ( nMatchedAllStrings )
3502         {
3503             // A type DEFINED means that no category could be assigned to the
3504             // overall format because of mixed type subformats. Do not override
3505             // the scanned type in this case. Otherwise in IsNumberFormat() the
3506             // first numeric particle would be accepted as number.
3507             SvNumFormatType eForType = eSetType;
3508             if ((eForType == SvNumFormatType::UNDEFINED || eForType == SvNumFormatType::DEFINED) && mpFormat)
3509                 eForType = mpFormat->GetNumForInfoScannedType( nStringScanNumFor);
3510             if (eForType != SvNumFormatType::UNDEFINED && eForType != SvNumFormatType::DEFINED)
3511                 eScannedType = eForType;
3512         }
3513         else if ( bWasReturn )
3514         {
3515             return false;
3516         }
3517     }
3518     else
3519     {
3520         nMatchedAllStrings = 0; // reset flag to no substrings matched
3521     }
3522     return true;
3523 }
3524 
3525 
3526 /**
3527  * Return true or false depending on the nMatched... state and remember usage
3528  */
3529 bool ImpSvNumberInputScan::MatchedReturn()
3530 {
3531     if ( nMatchedAllStrings & ~nMatchedVirgin )
3532     {
3533         nMatchedAllStrings |= nMatchedUsedAsReturn;
3534         return true;
3535     }
3536     return false;
3537 }
3538 
3539 
3540 /**
3541  * Initialize uppercase months and weekdays
3542  */
3543 void ImpSvNumberInputScan::InitText()
3544 {
3545     sal_Int32 j, nElems;
3546     const CharClass* pChrCls = pFormatter->GetCharClass();
3547     const CalendarWrapper* pCal = pFormatter->GetCalendar();
3548 
3549     pUpperMonthText.reset();
3550     pUpperAbbrevMonthText.reset();
3551     css::uno::Sequence< css::i18n::CalendarItem2 > xElems = pCal->getMonths();
3552     nElems = xElems.getLength();
3553     pUpperMonthText.reset( new OUString[nElems] );
3554     pUpperAbbrevMonthText.reset( new OUString[nElems] );
3555     for ( j = 0; j < nElems; j++ )
3556     {
3557         pUpperMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3558         pUpperAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3559     }
3560 
3561     pUpperGenitiveMonthText.reset();
3562     pUpperGenitiveAbbrevMonthText.reset();
3563     xElems = pCal->getGenitiveMonths();
3564     bScanGenitiveMonths = (nElems != xElems.getLength());
3565     nElems = xElems.getLength();
3566     pUpperGenitiveMonthText.reset( new OUString[nElems] );
3567     pUpperGenitiveAbbrevMonthText.reset( new OUString[nElems] );
3568     for ( j = 0; j < nElems; j++ )
3569     {
3570         pUpperGenitiveMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3571         pUpperGenitiveAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3572         if (!bScanGenitiveMonths &&
3573             (pUpperGenitiveMonthText[j] != pUpperMonthText[j] ||
3574              pUpperGenitiveAbbrevMonthText[j] != pUpperAbbrevMonthText[j]))
3575         {
3576             bScanGenitiveMonths = true;
3577         }
3578     }
3579 
3580     pUpperPartitiveMonthText.reset();
3581     pUpperPartitiveAbbrevMonthText.reset();
3582     xElems = pCal->getPartitiveMonths();
3583     bScanPartitiveMonths = (nElems != xElems.getLength());
3584     nElems = xElems.getLength();
3585     pUpperPartitiveMonthText.reset( new OUString[nElems] );
3586     pUpperPartitiveAbbrevMonthText.reset( new OUString[nElems] );
3587     for ( j = 0; j < nElems; j++ )
3588     {
3589         pUpperPartitiveMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3590         pUpperPartitiveAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3591         if (!bScanPartitiveMonths &&
3592             (pUpperPartitiveMonthText[j] != pUpperGenitiveMonthText[j] ||
3593              pUpperPartitiveAbbrevMonthText[j] != pUpperGenitiveAbbrevMonthText[j]))
3594         {
3595             bScanPartitiveMonths = true;
3596         }
3597     }
3598 
3599     pUpperDayText.reset();
3600     pUpperAbbrevDayText.reset();
3601     xElems = pCal->getDays();
3602     nElems = xElems.getLength();
3603     pUpperDayText.reset( new OUString[nElems] );
3604     pUpperAbbrevDayText.reset( new OUString[nElems] );
3605     for ( j = 0; j < nElems; j++ )
3606     {
3607         pUpperDayText[j] = pChrCls->uppercase( xElems[j].FullName );
3608         pUpperAbbrevDayText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3609     }
3610 
3611     bTextInitialized = true;
3612 }
3613 
3614 
3615 /**
3616  * MUST be called if International/Locale is changed
3617  */
3618 void ImpSvNumberInputScan::ChangeIntl()
3619 {
3620     sal_Unicode cDecSep = pFormatter->GetNumDecimalSep()[0];
3621     bDecSepInDateSeps = ( cDecSep == '-' ||
3622                           cDecSep == pFormatter->GetDateSep()[0] );
3623     if (!bDecSepInDateSeps)
3624     {
3625         sal_Unicode cDecSepAlt = pFormatter->GetNumDecimalSepAlt().toChar();
3626         bDecSepInDateSeps = cDecSepAlt && (cDecSepAlt == '-' || cDecSepAlt == pFormatter->GetDateSep()[0]);
3627     }
3628     bTextInitialized = false;
3629     aUpperCurrSymbol.clear();
3630     InvalidateDateAcceptancePatterns();
3631 }
3632 
3633 
3634 void ImpSvNumberInputScan::InvalidateDateAcceptancePatterns()
3635 {
3636     if (sDateAcceptancePatterns.hasElements())
3637     {
3638         sDateAcceptancePatterns = css::uno::Sequence< OUString >();
3639     }
3640 }
3641 
3642 
3643 void ImpSvNumberInputScan::ChangeNullDate( const sal_uInt16 Day,
3644                                            const sal_uInt16 Month,
3645                                            const sal_Int16 Year )
3646 {
3647     if ( pNullDate )
3648     {
3649         *pNullDate = Date(Day, Month, Year);
3650     }
3651     else
3652     {
3653         pNullDate.reset(new Date(Day, Month, Year));
3654     }
3655 }
3656 
3657 
3658 /**
3659  * Does rString represent a number (also date, time et al)
3660  */
3661 bool ImpSvNumberInputScan::IsNumberFormat( const OUString& rString,         // string to be analyzed
3662                                            SvNumFormatType& F_Type,                   // IN: old type, OUT: new type
3663                                            double& fOutNumber,              // OUT: number if convertible
3664                                            const SvNumberformat* pFormat )  // maybe a number format to match against
3665 {
3666     OUString aString;
3667     bool res; // return value
3668     sal_uInt16 k;
3669     eSetType = F_Type; // old type set
3670 
3671     if ( !rString.getLength() )
3672     {
3673         res = false;
3674     }
3675     else if (rString.getLength() > 308) // arbitrary
3676     {
3677         res = false;
3678     }
3679     else
3680     {
3681         // NoMoreUpperNeeded, all comparisons on UpperCase
3682         aString = pFormatter->GetCharClass()->uppercase( rString );
3683         // convert native number to ASCII if necessary
3684         TransformInput(pFormatter, aString);
3685         res = IsNumberFormatMain( aString, pFormat );
3686     }
3687 
3688     if (res)
3689     {
3690         // Accept signed date only for ISO date with at least four digits in
3691         // year to not have an input of -M-D-Y arbitrarily recognized. The
3692         // final order is only determined in GetDateRef().
3693         // Also accept for Y/M/D date pattern match, i.e. if the first number
3694         // is year.
3695         // Accept only if the year immediately follows the sign character with
3696         // no space in between.
3697         if (nSign && (eScannedType == SvNumFormatType::DATE ||
3698                       eScannedType == SvNumFormatType::DATETIME) && mbEraCE == kDefaultEra &&
3699                 (IsDatePatternNumberOfType(0,'Y') || (MayBeIso8601() && sStrArray[nNums[0]].getLength() >= 4)))
3700         {
3701             const sal_Unicode c = sStrArray[0][sStrArray[0].getLength()-1];
3702             if (c == '-' || c == '+')
3703             {
3704                 // A '+' sign doesn't change the era.
3705                 if (nSign < 0)
3706                     mbEraCE = false;  // BCE
3707                 nSign = 0;
3708             }
3709         }
3710         if ( bNegCheck ||                             // ')' not found for '('
3711              (nSign && (eScannedType == SvNumFormatType::DATE ||
3712                         eScannedType == SvNumFormatType::DATETIME))) // signed date/datetime
3713         {
3714             res = false;
3715         }
3716         else
3717         {                                           // check count of partial number strings
3718             switch (eScannedType)
3719             {
3720             case SvNumFormatType::PERCENT:
3721             case SvNumFormatType::CURRENCY:
3722             case SvNumFormatType::NUMBER:
3723                 if (nDecPos == 1)               // .05
3724                 {
3725                     // matched MidStrings function like group separators
3726                     if ( nMatchedAllStrings )
3727                     {
3728                         nThousand = nNumericsCnt - 1;
3729                     }
3730                     else if ( nNumericsCnt != 1 )
3731                     {
3732                         res = false;
3733                     }
3734                 }
3735                 else if (nDecPos == 2)          // 1.05
3736                 {
3737                     // matched MidStrings function like group separators
3738                     if ( nMatchedAllStrings )
3739                     {
3740                         nThousand = nNumericsCnt - 1;
3741                     }
3742                     else if ( nNumericsCnt != nThousand+2 )
3743                     {
3744                         res = false;
3745                     }
3746                 }
3747                 else                            // 1,100 or 1,100.
3748                 {
3749                     // matched MidStrings function like group separators
3750                     if ( nMatchedAllStrings )
3751                     {
3752                         nThousand = nNumericsCnt - 1;
3753                     }
3754                     else if ( nNumericsCnt != nThousand+1 )
3755                     {
3756                         res = false;
3757                     }
3758                 }
3759                 break;
3760 
3761             case SvNumFormatType::SCIENTIFIC:       // 1.0e-2
3762                 if (nDecPos == 1)               // .05
3763                 {
3764                     if (nNumericsCnt != 2)
3765                     {
3766                         res = false;
3767                     }
3768                 }
3769                 else if (nDecPos == 2)          // 1.05
3770                 {
3771                     if (nNumericsCnt != nThousand+3)
3772                     {
3773                         res = false;
3774                     }
3775                 }
3776                 else                            // 1,100 or 1,100.
3777                 {
3778                     if (nNumericsCnt != nThousand+2)
3779                     {
3780                         res = false;
3781                     }
3782                 }
3783                 break;
3784 
3785             case SvNumFormatType::DATE:
3786                 if (nMonth < 0 && nDayOfWeek < 0 && nNumericsCnt == 3)
3787                 {
3788                     // If both, short month name and day of week name were
3789                     // detected, and also numbers for full date, assume that we
3790                     // have a day of week instead of month name.
3791                     nMonth = 0;
3792                     nMonthPos = 0;
3793                 }
3794                 if (nMonth)
3795                 {                               // month name and numbers
3796                     if (nNumericsCnt > 2)
3797                     {
3798                         res = false;
3799                     }
3800                 }
3801                 else
3802                 {
3803                     if (nNumericsCnt > 3)
3804                     {
3805                         res = false;
3806                     }
3807                     else
3808                     {
3809                         // Even if a date pattern was matched, for abbreviated
3810                         // pattern like "D.M." an input of "D.M. #" was
3811                         // accepted because # could had been a time. Here we do
3812                         // not have a combined date/time input though and #
3813                         // would be taken as Year in this example, which it is
3814                         // not. The count of numbers in pattern must match the
3815                         // count of numbers in input.
3816                         res = (GetDatePatternNumbers() == nNumericsCnt)
3817                             || IsAcceptableIso8601() || nMatchedAllStrings;
3818                     }
3819                 }
3820                 break;
3821 
3822             case SvNumFormatType::TIME:
3823                 if (nDecPos)
3824                 {                               // hundredth seconds included
3825                     if (nNumericsCnt > 4)
3826                     {
3827                         res = false;
3828                     }
3829                 }
3830                 else
3831                 {
3832                     if (nNumericsCnt > 3)
3833                     {
3834                         res = false;
3835                     }
3836                 }
3837                 break;
3838 
3839             case SvNumFormatType::DATETIME:
3840                 if (nMonth < 0 && nDayOfWeek < 0 && nNumericsCnt >= 5)
3841                 {
3842                     // If both, abbreviated month name and day of week name
3843                     // were detected, and also at least numbers for full date
3844                     // plus time including minutes, assume that we have a day
3845                     // of week instead of month name.
3846                     nMonth = 0;
3847                     nMonthPos = 0;
3848                 }
3849                 if (nMonth)
3850                 {                               // month name and numbers
3851                     if (nDecPos)
3852                     {                           // hundredth seconds included
3853                         if (nNumericsCnt > 6)
3854                         {
3855                             res = false;
3856                         }
3857                     }
3858                     else
3859                     {
3860                         if (nNumericsCnt > 5)
3861                         {
3862                             res = false;
3863                         }
3864                     }
3865                 }
3866                 else
3867                 {
3868                     if (nDecPos)
3869                     {                           // hundredth seconds included
3870                         if (nNumericsCnt > 7)
3871                         {
3872                             res = false;
3873                         }
3874                     }
3875                     else
3876                     {
3877                         if (nNumericsCnt > 6)
3878                         {
3879                             res = false;
3880                         }
3881                     }
3882                     if (res)
3883                     {
3884                         res = IsAcceptedDatePattern( nNums[0]) || MayBeIso8601() || nMatchedAllStrings;
3885                     }
3886                 }
3887                 break;
3888 
3889             default:
3890                 break;
3891             }   // switch
3892         }   // else
3893     }   // if (res)
3894 
3895     OUStringBuffer sResString;
3896 
3897     if (res)
3898     {                                       // we finally have a number
3899         switch (eScannedType)
3900         {
3901         case SvNumFormatType::LOGICAL:
3902             if (nLogical ==  1)
3903             {
3904                 fOutNumber = 1.0;           // True
3905             }
3906             else if (nLogical == -1)
3907             {
3908                 fOutNumber = 0.0;           // False
3909             }
3910             else
3911             {
3912                 res = false;                // Oops
3913             }
3914             break;
3915 
3916         case SvNumFormatType::PERCENT:
3917         case SvNumFormatType::CURRENCY:
3918         case SvNumFormatType::NUMBER:
3919         case SvNumFormatType::SCIENTIFIC:
3920         case SvNumFormatType::DEFINED:          // if no category detected handle as number
3921             if ( nDecPos == 1 )             // . at start
3922             {
3923                 sResString.append("0.");
3924             }
3925 
3926             for ( k = 0; k <= nThousand; k++)
3927             {
3928                 sResString.append(sStrArray[nNums[k]]);  // integer part
3929             }
3930             if ( nDecPos == 2 && k < nNumericsCnt )     // . somewhere
3931             {
3932                 sResString.append('.');
3933                 sal_uInt16 nStop = (eScannedType == SvNumFormatType::SCIENTIFIC ?
3934                                     nNumericsCnt-1 : nNumericsCnt);
3935                 for ( ; k < nStop; k++)
3936                 {
3937                     sResString.append(sStrArray[nNums[k]]);  // fractional part
3938                 }
3939             }
3940 
3941             if (eScannedType != SvNumFormatType::SCIENTIFIC)
3942             {
3943                 fOutNumber = StringToDouble(sResString.makeStringAndClear());
3944             }
3945             else
3946             {                                           // append exponent
3947                 sResString.append('E');
3948                 if ( nESign == -1 )
3949                 {
3950                     sResString.append('-');
3951                 }
3952                 sResString.append(sStrArray[nNums[nNumericsCnt-1]]);
3953                 rtl_math_ConversionStatus eStatus;
3954                 fOutNumber = ::rtl::math::stringToDouble( sResString.makeStringAndClear(), '.', ',', &eStatus );
3955                 if ( eStatus == rtl_math_ConversionStatus_OutOfRange )
3956                 {
3957                     F_Type = SvNumFormatType::TEXT;         // overflow/underflow -> Text
3958                     if (nESign == -1)
3959                     {
3960                         fOutNumber = 0.0;
3961                     }
3962                     else
3963                     {
3964                         fOutNumber = DBL_MAX;
3965                     }
3966                     return true;
3967                 }
3968             }
3969 
3970             if ( nStringScanSign )
3971             {
3972                 if ( nSign )
3973                 {
3974                     nSign *= nStringScanSign;
3975                 }
3976                 else
3977                 {
3978                     nSign = nStringScanSign;
3979                 }
3980             }
3981             if ( nSign < 0 )
3982             {
3983                 fOutNumber = -fOutNumber;
3984             }
3985 
3986             if (eScannedType == SvNumFormatType::PERCENT)
3987             {
3988                 fOutNumber/= 100.0;
3989             }
3990             break;
3991 
3992         case SvNumFormatType::FRACTION:
3993             if (nNumericsCnt == 1)
3994             {
3995                 fOutNumber = StringToDouble(sStrArray[nNums[0]]);
3996             }
3997             else if (nNumericsCnt == 2)
3998             {
3999                 if (nThousand == 1)
4000                 {
4001                     sResString = sStrArray[nNums[0]];
4002                     sResString.append(sStrArray[nNums[1]]); // integer part
4003                     fOutNumber = StringToDouble(sResString.makeStringAndClear());
4004                 }
4005                 else
4006                 {
4007                     double fNumerator = StringToDouble(sStrArray[nNums[0]]);
4008                     double fDenominator = StringToDouble(sStrArray[nNums[1]]);
4009                     if (fDenominator != 0.0)
4010                     {
4011                         fOutNumber = fNumerator/fDenominator;
4012                     }
4013                     else
4014                     {
4015                         res = false;
4016                     }
4017                 }
4018             }
4019             else // nNumericsCnt > 2
4020             {
4021                 k = 1;
4022                 sResString = sStrArray[nNums[0]];
4023                 if (nThousand > 0)
4024                 {
4025                     for (; k <= nThousand; k++)
4026                     {
4027                         sResString.append(sStrArray[nNums[k]]);
4028                     }
4029                 }
4030                 fOutNumber = StringToDouble(sResString.makeStringAndClear());
4031 
4032                 if (k == nNumericsCnt-2)
4033                 {
4034                     double fNumerator = StringToDouble(sStrArray[nNums[k]]);
4035                     double fDenominator = StringToDouble(sStrArray[nNums[k + 1]]);
4036                     if (fDenominator != 0.0)
4037                     {
4038                         fOutNumber += fNumerator/fDenominator;
4039                     }
4040                     else
4041                     {
4042                         res = false;
4043                     }
4044                 }
4045             }
4046 
4047             if ( nStringScanSign )
4048             {
4049                 if ( nSign )
4050                 {
4051                     nSign *= nStringScanSign;
4052                 }
4053                 else
4054                 {
4055                     nSign = nStringScanSign;
4056                 }
4057             }
4058             if ( nSign < 0 )
4059             {
4060                 fOutNumber = -fOutNumber;
4061             }
4062             break;
4063 
4064         case SvNumFormatType::TIME:
4065             res = GetTimeRef(fOutNumber, 0, nNumericsCnt);
4066             if ( nSign < 0 )
4067             {
4068                 fOutNumber = -fOutNumber;
4069             }
4070             break;
4071 
4072         case SvNumFormatType::DATE:
4073             res = GetDateRef( fOutNumber, k );
4074             break;
4075 
4076         case SvNumFormatType::DATETIME:
4077             res = GetDateRef( fOutNumber, k );
4078             if ( res )
4079             {
4080                 double fTime;
4081                 res = GetTimeRef( fTime, k, nNumericsCnt - k );
4082                 fOutNumber += fTime;
4083             }
4084             break;
4085 
4086         default:
4087             SAL_WARN( "svl.numbers", "Some number recognized but what's it?" );
4088             fOutNumber = 0.0;
4089             break;
4090         }
4091     }
4092 
4093     if (res) // overflow/underflow -> Text
4094     {
4095         if (fOutNumber < -DBL_MAX) // -1.7E308
4096         {
4097             F_Type = SvNumFormatType::TEXT;
4098             fOutNumber = -DBL_MAX;
4099             return true;
4100         }
4101         else if (fOutNumber >  DBL_MAX) // 1.7E308
4102         {
4103             F_Type = SvNumFormatType::TEXT;
4104             fOutNumber = DBL_MAX;
4105             return true;
4106         }
4107     }
4108 
4109     if (!res)
4110     {
4111         eScannedType = SvNumFormatType::TEXT;
4112         fOutNumber = 0.0;
4113     }
4114 
4115     F_Type = eScannedType;
4116     return res;
4117 }
4118 
4119 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
4120