lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / svl / source / numbers / zforfind.cxx
blobdfb66e3de2359a188e1817b4b3074cd8acf7a74b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <cstdlib>
21 #include <float.h>
22 #include <errno.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>
36 #include <svl/zforlist.hxx>
37 #include "zforscan.hxx"
38 #include <svl/zformat.hxx>
40 #include "zforfind.hxx"
42 #ifndef DBG_UTIL
43 #define NF_TEST_CALENDAR 0
44 #else
45 #define NF_TEST_CALENDAR 0
46 #endif
47 #if NF_TEST_CALENDAR
48 #include <comphelper/processfactory.hxx>
49 #include <com/sun/star/i18n/XCalendar4.hpp>
50 #endif
53 const sal_uInt8 ImpSvNumberInputScan::nMatchedEndString = 0x01;
54 const sal_uInt8 ImpSvNumberInputScan::nMatchedMidString = 0x02;
55 const sal_uInt8 ImpSvNumberInputScan::nMatchedStartString = 0x04;
56 const sal_uInt8 ImpSvNumberInputScan::nMatchedVirgin = 0x08;
57 const sal_uInt8 ImpSvNumberInputScan::nMatchedUsedAsReturn = 0x10;
59 /* It is not clear how we want timezones to be handled. Convert them to local
60 * time isn't wanted, as it isn't done in any other place and timezone
61 * information isn't stored anywhere. Ignoring them and pretending local time
62 * may be wrong too and might not be what the user expects. Keep the input as
63 * string so that no information is lost.
64 * Anyway, defining NF_RECOGNIZE_ISO8601_TIMEZONES to 1 would be the way how it
65 * would work, together with the nTimezonePos handling in GetTimeRef(). */
66 #define NF_RECOGNIZE_ISO8601_TIMEZONES 0
68 static const sal_Unicode cNoBreakSpace = 0xA0;
69 static const sal_Unicode cNarrowNoBreakSpace = 0x202F;
70 static const bool kDefaultEra = true; // Gregorian CE, positive year
72 ImpSvNumberInputScan::ImpSvNumberInputScan( SvNumberFormatter* pFormatterP )
74 bTextInitialized( false ),
75 bScanGenitiveMonths( false ),
76 bScanPartitiveMonths( false ),
77 eScannedType( SvNumFormatType::UNDEFINED ),
78 eSetType( SvNumFormatType::UNDEFINED )
80 pFormatter = pFormatterP;
81 pNullDate.reset( new Date(30,12,1899) );
82 nYear2000 = SvNumberFormatter::GetYear2000Default();
83 Reset();
84 ChangeIntl();
88 ImpSvNumberInputScan::~ImpSvNumberInputScan()
93 void ImpSvNumberInputScan::Reset()
95 mpFormat = nullptr;
96 nMonth = 0;
97 nMonthPos = 0;
98 nDayOfWeek = 0;
99 nTimePos = 0;
100 nSign = 0;
101 nESign = 0;
102 nDecPos = 0;
103 bNegCheck = false;
104 nStringsCnt = 0;
105 nNumericsCnt = 0;
106 nThousand = 0;
107 eScannedType = SvNumFormatType::UNDEFINED;
108 nAmPm = 0;
109 nPosThousandString = 0;
110 nLogical = 0;
111 mbEraCE = kDefaultEra;
112 nStringScanNumFor = 0;
113 nStringScanSign = 0;
114 nMatchedAllStrings = nMatchedVirgin;
115 nMayBeIso8601 = 0;
116 bIso8601Tsep = false;
117 nMayBeMonthDate = 0;
118 nAcceptedDatePattern = -2;
119 nDatePatternStart = 0;
120 nDatePatternNumbers = 0;
122 for (sal_uInt32 i = 0; i < SV_MAX_COUNT_INPUT_STRINGS; i++)
124 IsNum[i] = false;
125 nNums[i] = 0;
129 // native number transliteration if necessary
130 static void TransformInput( SvNumberFormatter const * pFormatter, OUString& rStr )
132 sal_Int32 nPos, nLen;
133 for ( nPos = 0, nLen = rStr.getLength(); nPos < nLen; ++nPos )
135 if ( 256 <= rStr[ nPos ] &&
136 pFormatter->GetCharClass()->isDigit( rStr, nPos ) )
138 break;
141 if ( nPos < nLen )
143 rStr = pFormatter->GetNatNum()->getNativeNumberString( rStr,
144 pFormatter->GetLanguageTag().getLocale(), 0 );
150 * Only simple unsigned floating point values without any error detection,
151 * decimal separator has to be '.'
153 double ImpSvNumberInputScan::StringToDouble( const OUString& rStr, bool bForceFraction )
155 double fNum = 0.0;
156 double fFrac = 0.0;
157 int nExp = 0;
158 sal_Int32 nPos = 0;
159 sal_Int32 nLen = rStr.getLength();
160 bool bPreSep = !bForceFraction;
162 while (nPos < nLen)
164 if (rStr[nPos] == '.')
166 bPreSep = false;
168 else if (bPreSep)
170 fNum = fNum * 10.0 + static_cast<double>(rStr[nPos] - '0');
172 else
174 fFrac = fFrac * 10.0 + static_cast<double>(rStr[nPos] - '0');
175 --nExp;
177 nPos++;
179 if ( fFrac )
181 return fNum + ::rtl::math::pow10Exp( fFrac, nExp );
183 return fNum;
188 * Splits up the input into numbers and strings for further processing
189 * (by the Turing machine).
191 * Starting state = GetChar
192 * ---------------+-------------------+-----------------------------+---------------
193 * Old State | Character read | Event | New state
194 * ---------------+-------------------+-----------------------------+---------------
195 * GetChar | Number | Symbol = Character | GetValue
196 * | Else | Symbol = Character | GetString
197 * ---------------|-------------------+-----------------------------+---------------
198 * GetValue | Number | Symbol = Symbol + Character | GetValue
199 * | Else | Dec(CharPos) | Stop
200 * ---------------+-------------------+-----------------------------+---------------
201 * GetString | Number | Dec(CharPos) | Stop
202 * | Else | Symbol = Symbol + Character | GetString
203 * ---------------+-------------------+-----------------------------+---------------
205 enum ScanState // States of the Turing machine
207 SsStop = 0,
208 SsStart = 1,
209 SsGetValue = 2,
210 SsGetString = 3
213 bool ImpSvNumberInputScan::NextNumberStringSymbol( const sal_Unicode*& pStr,
214 OUString& rSymbol )
216 bool isNumber = false;
217 sal_Unicode cToken;
218 ScanState eState = SsStart;
219 const sal_Unicode* pHere = pStr;
220 sal_Int32 nChars = 0;
222 while ( ((cToken = *pHere) != 0) && eState != SsStop)
224 pHere++;
225 switch (eState)
227 case SsStart:
228 if ( rtl::isAsciiDigit( cToken ) )
230 eState = SsGetValue;
231 isNumber = true;
233 else
235 eState = SsGetString;
237 nChars++;
238 break;
239 case SsGetValue:
240 if ( rtl::isAsciiDigit( cToken ) )
242 nChars++;
244 else
246 eState = SsStop;
247 pHere--;
249 break;
250 case SsGetString:
251 if ( !rtl::isAsciiDigit( cToken ) )
253 nChars++;
255 else
257 eState = SsStop;
258 pHere--;
260 break;
261 default:
262 break;
263 } // switch
264 } // while
266 if ( nChars )
268 rSymbol = OUString( pStr, nChars );
270 else
272 rSymbol.clear();
275 pStr = pHere;
277 return isNumber;
281 // FIXME: should be grouping; it is only used though in case nStringsCnt is
282 // near SV_MAX_COUNT_INPUT_STRINGS, in NumberStringDivision().
284 bool ImpSvNumberInputScan::SkipThousands( const sal_Unicode*& pStr,
285 OUString& rSymbol ) const
287 bool res = false;
288 OUStringBuffer sBuff(rSymbol);
289 sal_Unicode cToken;
290 const OUString& rThSep = pFormatter->GetNumThousandSep();
291 const sal_Unicode* pHere = pStr;
292 ScanState eState = SsStart;
293 sal_Int32 nCounter = 0; // counts 3 digits
295 while ( ((cToken = *pHere) != 0) && eState != SsStop)
297 pHere++;
298 switch (eState)
300 case SsStart:
301 if ( StringPtrContains( rThSep, pHere-1, 0 ) )
303 nCounter = 0;
304 eState = SsGetValue;
305 pHere += rThSep.getLength() - 1;
307 else
309 eState = SsStop;
310 pHere--;
312 break;
313 case SsGetValue:
314 if ( rtl::isAsciiDigit( cToken ) )
316 sBuff.append(cToken);
317 nCounter++;
318 if (nCounter == 3)
320 eState = SsStart;
321 res = true; // .000 combination found
324 else
326 eState = SsStop;
327 pHere--;
329 break;
330 default:
331 break;
332 } // switch
333 } // while
335 if (eState == SsGetValue) // break with less than 3 digits
337 if ( nCounter )
339 sBuff.remove( sBuff.getLength() - nCounter, nCounter );
341 pHere -= nCounter + rThSep.getLength(); // put back ThSep also
343 rSymbol = sBuff.makeStringAndClear();
344 pStr = pHere;
346 return res;
350 void ImpSvNumberInputScan::NumberStringDivision( const OUString& rString )
352 const sal_Unicode* pStr = rString.getStr();
353 const sal_Unicode* const pEnd = pStr + rString.getLength();
354 while ( pStr < pEnd && nStringsCnt < SV_MAX_COUNT_INPUT_STRINGS )
356 if ( NextNumberStringSymbol( pStr, sStrArray[nStringsCnt] ) )
357 { // Number
358 IsNum[nStringsCnt] = true;
359 nNums[nNumericsCnt] = nStringsCnt;
360 nNumericsCnt++;
361 if (nStringsCnt >= SV_MAX_COUNT_INPUT_STRINGS - 7 &&
362 nPosThousandString == 0) // Only once
364 if ( SkipThousands( pStr, sStrArray[nStringsCnt] ) )
366 nPosThousandString = nStringsCnt;
370 else
372 IsNum[nStringsCnt] = false;
374 nStringsCnt++;
380 * Whether rString contains rWhat at nPos
382 bool ImpSvNumberInputScan::StringContainsImpl( const OUString& rWhat,
383 const OUString& rString, sal_Int32 nPos )
385 if ( nPos + rWhat.getLength() <= rString.getLength() )
387 return StringPtrContainsImpl( rWhat, rString.getStr(), nPos );
389 return false;
394 * Whether pString contains rWhat at nPos
396 bool ImpSvNumberInputScan::StringPtrContainsImpl( const OUString& rWhat,
397 const sal_Unicode* pString, sal_Int32 nPos )
399 if ( rWhat.isEmpty() )
401 return false;
403 const sal_Unicode* pWhat = rWhat.getStr();
404 const sal_Unicode* const pEnd = pWhat + rWhat.getLength();
405 const sal_Unicode* pStr = pString + nPos;
406 while ( pWhat < pEnd )
408 if ( *pWhat != *pStr )
410 return false;
412 pWhat++;
413 pStr++;
415 return true;
420 * Whether rString contains word rWhat at nPos
422 bool ImpSvNumberInputScan::StringContainsWord( const OUString& rWhat,
423 const OUString& rString, sal_Int32 nPos ) const
425 if (rWhat.isEmpty() || rString.getLength() < nPos + rWhat.getLength())
426 return false;
428 if (StringPtrContainsImpl( rWhat, rString.getStr(), nPos))
430 nPos += rWhat.getLength();
431 if (nPos == rString.getLength())
432 return true; // word at end of string
434 /* TODO: we COULD invoke bells and whistles word break iterator to find
435 * the next boundary, but really ... this is called for date input, so
436 * how many languages do not separate the day and month names in some
437 * form? */
439 // Check simple ASCII first before invoking i18n or anything else.
440 const sal_Unicode c = rString[nPos];
442 // Common separating ASCII characters in date context.
443 switch (c)
445 case ' ':
446 case '-':
447 case '.':
448 case '/':
449 return true;
450 default:
451 ; // nothing
454 if (rtl::isAsciiAlphanumeric( c ))
455 return false; // Alpha or numeric is not word gap.
457 sal_Int32 nIndex = nPos;
458 rString.iterateCodePoints( &nIndex);
459 if (nPos+1 < nIndex)
460 return true; // Surrogate, assume these to be new words.
462 const sal_Int32 nType = pFormatter->GetCharClass()->getCharacterType( rString, nPos);
463 using namespace ::com::sun::star::i18n;
465 if ((nType & (KCharacterType::UPPER | KCharacterType::LOWER | KCharacterType::DIGIT)) != 0)
466 return false; // Alpha or numeric is not word gap.
468 if (nType & KCharacterType::LETTER)
469 return true; // Letter other than alpha is new word. (Is it?)
471 return true; // Catch all remaining as gap until we know better.
474 return false;
479 * Skips the supplied char
481 inline bool ImpSvNumberInputScan::SkipChar( sal_Unicode c, const OUString& rString,
482 sal_Int32& nPos )
484 if ((nPos < rString.getLength()) && (rString[nPos] == c))
486 nPos++;
487 return true;
489 return false;
494 * Skips blanks
496 inline void ImpSvNumberInputScan::SkipBlanks( const OUString& rString,
497 sal_Int32& nPos )
499 if ( nPos < rString.getLength() )
501 const sal_Unicode* p = rString.getStr() + nPos;
502 while ( *p == ' ' || *p == cNoBreakSpace || *p == cNarrowNoBreakSpace )
504 nPos++;
505 p++;
512 * jump over rWhat in rString at nPos
514 inline bool ImpSvNumberInputScan::SkipString( const OUString& rWhat,
515 const OUString& rString, sal_Int32& nPos )
517 if ( StringContains( rWhat, rString, nPos ) )
519 nPos = nPos + rWhat.getLength();
520 return true;
522 return false;
527 * Recognizes exactly ,111 in {3} and {3,2} or ,11 in {3,2} grouping
529 inline bool ImpSvNumberInputScan::GetThousandSep( const OUString& rString,
530 sal_Int32& nPos,
531 sal_uInt16 nStringPos ) const
533 const OUString& rSep = pFormatter->GetNumThousandSep();
534 // Is it an ordinary space instead of a no-break space?
535 bool bSpaceBreak = (rSep[0] == cNoBreakSpace || rSep[0] == cNarrowNoBreakSpace) &&
536 rString[0] == u' ' &&
537 rSep.getLength() == 1 && rString.getLength() == 1;
538 if (!((rString == rSep || bSpaceBreak) && // nothing else
539 nStringPos < nStringsCnt - 1 && // safety first!
540 IsNum[ nStringPos + 1 ] )) // number follows
542 return false; // no? => out
545 utl::DigitGroupingIterator aGrouping( pFormatter->GetLocaleData()->getDigitGrouping());
546 // Match ,### in {3} or ,## in {3,2}
547 /* FIXME: this could be refined to match ,## in {3,2} only if ,##,## or
548 * ,##,### and to match ,### in {3,2} only if it's the last. However,
549 * currently there is no track kept where group separators occur. In {3,2}
550 * #,###,### and #,##,## would be valid input, which maybe isn't even bad
551 * for #,###,###. Other combinations such as #,###,## maybe not. */
552 sal_Int32 nLen = sStrArray[ nStringPos + 1 ].getLength();
553 if (nLen == aGrouping.get() || // with 3 (or so) digits
554 nLen == aGrouping.advance().get() || // or with 2 (or 3 or so) digits
555 nPosThousandString == nStringPos + 1 ) // or concatenated
557 nPos = nPos + rSep.getLength();
558 return true;
560 return false;
565 * Conversion of text to logical value
566 * "true" => 1:
567 * "false"=> -1:
568 * else => 0:
570 short ImpSvNumberInputScan::GetLogical( const OUString& rString ) const
572 short res;
574 const ImpSvNumberformatScan* pFS = pFormatter->GetFormatScanner();
575 if ( rString == pFS->GetTrueString() )
577 res = 1;
579 else if ( rString == pFS->GetFalseString() )
581 res = -1;
583 else
585 res = 0;
587 return res;
592 * Converts a string containing a month name (JAN, January) at nPos into the
593 * month number (negative if abbreviated), returns 0 if nothing found
595 short ImpSvNumberInputScan::GetMonth( const OUString& rString, sal_Int32& nPos )
597 short res = 0; // no month found
599 if (rString.getLength() > nPos) // only if needed
601 if ( !bTextInitialized )
603 InitText();
605 sal_Int16 nMonths = pFormatter->GetCalendar()->getNumberOfMonthsInYear();
606 for ( sal_Int16 i = 0; i < nMonths; i++ )
608 if ( bScanGenitiveMonths && StringContainsWord( pUpperGenitiveMonthText[i], rString, nPos ) )
609 { // genitive full names first
610 nPos = nPos + pUpperGenitiveMonthText[i].getLength();
611 res = i + 1;
612 break; // for
614 else if ( bScanGenitiveMonths && StringContainsWord( pUpperGenitiveAbbrevMonthText[i], rString, nPos ) )
615 { // genitive abbreviated
616 nPos = nPos + pUpperGenitiveAbbrevMonthText[i].getLength();
617 res = sal::static_int_cast< short >(-(i+1)); // negative
618 break; // for
620 else if ( bScanPartitiveMonths && StringContainsWord( pUpperPartitiveMonthText[i], rString, nPos ) )
621 { // partitive full names
622 nPos = nPos + pUpperPartitiveMonthText[i].getLength();
623 res = i+1;
624 break; // for
626 else if ( bScanPartitiveMonths && StringContainsWord( pUpperPartitiveAbbrevMonthText[i], rString, nPos ) )
627 { // partitive abbreviated
628 nPos = nPos + pUpperPartitiveAbbrevMonthText[i].getLength();
629 res = sal::static_int_cast< short >(-(i+1)); // negative
630 break; // for
632 else if ( StringContainsWord( pUpperMonthText[i], rString, nPos ) )
633 { // noun full names
634 nPos = nPos + pUpperMonthText[i].getLength();
635 res = i+1;
636 break; // for
638 else if ( StringContainsWord( pUpperAbbrevMonthText[i], rString, nPos ) )
639 { // noun abbreviated
640 nPos = nPos + pUpperAbbrevMonthText[i].getLength();
641 res = sal::static_int_cast< short >(-(i+1)); // negative
642 break; // for
644 else if ( i == 8 && pUpperAbbrevMonthText[i] == "SEPT" &&
645 StringContainsWord( "SEP", rString, nPos ) )
646 { // #102136# The correct English form of month September abbreviated is
647 // SEPT, but almost every data contains SEP instead.
648 nPos = nPos + 3;
649 res = sal::static_int_cast< short >(-(i+1)); // negative
650 break; // for
653 if (!res)
655 // Brutal hack for German locales that know "Januar" or "Jänner".
656 /* TODO: add alternative month names to locale data? if there are
657 * more languages.. */
658 const LanguageTag& rLanguageTag = pFormatter->GetLanguageTag();
659 if (rLanguageTag.getLanguage() == "de")
661 if (rLanguageTag.getCountry() == "AT")
663 // Locale data has Jänner/Jän
664 assert(pUpperMonthText[0] == u"J\u00C4NNER");
665 if (StringContainsWord( "JANUAR", rString, nPos))
667 nPos += 6;
668 res = 1;
670 else if (StringContainsWord( "JAN", rString, nPos))
672 nPos += 3;
673 res = -1;
676 else
678 // Locale data has Januar/Jan
679 assert(pUpperMonthText[0] == "JANUAR");
680 if (StringContainsWord( u"J\u00C4NNER", rString, nPos))
682 nPos += 6;
683 res = 1;
685 else if (StringContainsWord( u"J\u00C4N", rString, nPos))
687 nPos += 3;
688 res = -1;
695 return res;
700 * Converts a string containing a DayOfWeek name (Mon, Monday) at nPos into the
701 * DayOfWeek number + 1 (negative if abbreviated), returns 0 if nothing found
703 int ImpSvNumberInputScan::GetDayOfWeek( const OUString& rString, sal_Int32& nPos )
705 int res = 0; // no day found
707 if (rString.getLength() > nPos) // only if needed
709 if ( !bTextInitialized )
711 InitText();
713 sal_Int16 nDays = pFormatter->GetCalendar()->getNumberOfDaysInWeek();
714 for ( sal_Int16 i = 0; i < nDays; i++ )
716 if ( StringContainsWord( pUpperDayText[i], rString, nPos ) )
717 { // full names first
718 nPos = nPos + pUpperDayText[i].getLength();
719 res = i + 1;
720 break; // for
722 if ( StringContainsWord( pUpperAbbrevDayText[i], rString, nPos ) )
723 { // abbreviated
724 nPos = nPos + pUpperAbbrevDayText[i].getLength();
725 res = -(i + 1); // negative
726 break; // for
731 return res;
736 * Reading a currency symbol
737 * '$' => true
738 * else => false
740 bool ImpSvNumberInputScan::GetCurrency( const OUString& rString, sal_Int32& nPos )
742 if ( rString.getLength() > nPos )
744 if ( !aUpperCurrSymbol.getLength() )
745 { // If no format specified the currency of the currently active locale.
746 LanguageType eLang = (mpFormat ? mpFormat->GetLanguage() :
747 pFormatter->GetLocaleData()->getLanguageTag().getLanguageType());
748 aUpperCurrSymbol = pFormatter->GetCharClass()->uppercase(
749 SvNumberFormatter::GetCurrencyEntry( eLang ).GetSymbol() );
751 if ( StringContains( aUpperCurrSymbol, rString, nPos ) )
753 nPos = nPos + aUpperCurrSymbol.getLength();
754 return true;
756 if ( mpFormat )
758 OUString aSymbol, aExtension;
759 if ( mpFormat->GetNewCurrencySymbol( aSymbol, aExtension ) )
761 if ( aSymbol.getLength() <= rString.getLength() - nPos )
763 aSymbol = pFormatter->GetCharClass()->uppercase(aSymbol);
764 if ( StringContains( aSymbol, rString, nPos ) )
766 nPos = nPos + aSymbol.getLength();
767 return true;
774 return false;
779 * Reading the time period specifier (AM/PM) for the 12 hour clock
781 * Returns:
782 * "AM" or "PM" => true
783 * else => false
785 * nAmPos:
786 * "AM" => 1
787 * "PM" => -1
788 * else => 0
790 bool ImpSvNumberInputScan::GetTimeAmPm( const OUString& rString, sal_Int32& nPos )
793 if ( rString.getLength() > nPos )
795 const CharClass* pChr = pFormatter->GetCharClass();
796 const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
797 if ( StringContains( pChr->uppercase( pLoc->getTimeAM() ), rString, nPos ) )
799 nAmPm = 1;
800 nPos = nPos + pLoc->getTimeAM().getLength();
801 return true;
803 else if ( StringContains( pChr->uppercase( pLoc->getTimePM() ), rString, nPos ) )
805 nAmPm = -1;
806 nPos = nPos + pLoc->getTimePM().getLength();
807 return true;
811 return false;
816 * Read a decimal separator (',')
817 * ',' => true
818 * else => false
820 inline bool ImpSvNumberInputScan::GetDecSep( const OUString& rString, sal_Int32& nPos ) const
822 if ( rString.getLength() > nPos )
824 const OUString& rSep = pFormatter->GetNumDecimalSep();
825 if ( rString.match( rSep, nPos) )
827 nPos = nPos + rSep.getLength();
828 return true;
830 const OUString& rSepAlt = pFormatter->GetNumDecimalSepAlt();
831 if ( !rSepAlt.isEmpty() && rString.match( rSepAlt, nPos) )
833 nPos = nPos + rSepAlt.getLength();
834 return true;
837 return false;
842 * Reading a hundredth seconds separator
844 inline bool ImpSvNumberInputScan::GetTime100SecSep( const OUString& rString, sal_Int32& nPos ) const
846 if ( rString.getLength() > nPos )
848 if (bIso8601Tsep)
850 // ISO 8601 specifies both '.' dot and ',' comma as fractional
851 // separator.
852 if (rString[nPos] == '.' || rString[nPos] == ',')
854 ++nPos;
855 return true;
858 // Even in an otherwise ISO 8601 string be lenient and accept the
859 // locale defined separator.
860 const OUString& rSep = pFormatter->GetLocaleData()->getTime100SecSep();
861 if ( rString.match( rSep, nPos ))
863 nPos = nPos + rSep.getLength();
864 return true;
867 return false;
872 * Read a sign including brackets
873 * '+' => 1
874 * '-' => -1
875 * '(' => -1, bNegCheck = 1
876 * else => 0
878 int ImpSvNumberInputScan::GetSign( const OUString& rString, sal_Int32& nPos )
880 if (rString.getLength() > nPos)
881 switch (rString[ nPos ])
883 case '+':
884 nPos++;
885 return 1;
886 case '(': // '(' similar to '-' ?!?
887 bNegCheck = true;
888 SAL_FALLTHROUGH;
889 case '-':
890 nPos++;
891 return -1;
892 default:
893 break;
896 return 0;
901 * Read a sign with an exponent
902 * '+' => 1
903 * '-' => -1
904 * else => 0
906 short ImpSvNumberInputScan::GetESign( const OUString& rString, sal_Int32& nPos )
908 if (rString.getLength() > nPos)
910 switch (rString[nPos])
912 case '+':
913 nPos++;
914 return 1;
915 case '-':
916 nPos++;
917 return -1;
918 default:
919 break;
922 return 0;
927 * i counts string portions, j counts numbers thereof.
928 * It should had been called SkipNumber instead.
930 inline bool ImpSvNumberInputScan::GetNextNumber( sal_uInt16& i, sal_uInt16& j ) const
932 if ( i < nStringsCnt && IsNum[i] )
934 j++;
935 i++;
936 return true;
938 return false;
942 bool ImpSvNumberInputScan::GetTimeRef( double& fOutNumber,
943 sal_uInt16 nIndex, // j-value of the first numeric time part of input, default 0
944 sal_uInt16 nCnt ) const // count of numeric time parts
946 bool bRet = true;
947 sal_uInt16 nHour;
948 sal_uInt16 nMinute = 0;
949 sal_uInt16 nSecond = 0;
950 double fSecond100 = 0.0;
951 sal_uInt16 nStartIndex = nIndex;
953 if (nDecPos == 2 && (nCnt == 3 || nCnt == 2)) // 20:45.5 or 45.5
955 nHour = 0;
957 else if (nIndex - nStartIndex < nCnt)
959 nHour = static_cast<sal_uInt16>(sStrArray[nNums[nIndex++]].toInt32());
961 else
963 nHour = 0;
964 bRet = false;
965 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetTimeRef: bad number index");
967 if (nDecPos == 2 && nCnt == 2) // 45.5
969 nMinute = 0;
971 else if (nIndex - nStartIndex < nCnt)
973 nMinute = static_cast<sal_uInt16>(sStrArray[nNums[nIndex++]].toInt32());
975 if (nIndex - nStartIndex < nCnt)
977 nSecond = static_cast<sal_uInt16>(sStrArray[nNums[nIndex++]].toInt32());
979 if (nIndex - nStartIndex < nCnt)
981 fSecond100 = StringToDouble( sStrArray[nNums[nIndex]], true );
983 if (nAmPm && nHour > 12) // not a valid AM/PM clock time
985 bRet = false;
987 else if (nAmPm == -1 && nHour != 12) // PM
989 nHour += 12;
991 else if (nAmPm == 1 && nHour == 12) // 12 AM
993 nHour = 0;
995 fOutNumber = (static_cast<double>(nHour)*3600 +
996 static_cast<double>(nMinute)*60 +
997 static_cast<double>(nSecond) +
998 fSecond100)/86400.0;
999 return bRet;
1003 sal_uInt16 ImpSvNumberInputScan::ImplGetDay( sal_uInt16 nIndex ) const
1005 sal_uInt16 nRes = 0;
1007 if (sStrArray[nNums[nIndex]].getLength() <= 2)
1009 sal_uInt16 nNum = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1010 if (nNum <= 31)
1012 nRes = nNum;
1016 return nRes;
1020 sal_uInt16 ImpSvNumberInputScan::ImplGetMonth( sal_uInt16 nIndex ) const
1022 // Preset invalid month number
1023 sal_uInt16 nRes = pFormatter->GetCalendar()->getNumberOfMonthsInYear();
1025 if (sStrArray[nNums[nIndex]].getLength() <= 2)
1027 sal_uInt16 nNum = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1028 if ( 0 < nNum && nNum <= nRes )
1030 nRes = nNum - 1; // zero based for CalendarFieldIndex::MONTH
1034 return nRes;
1039 * 30 -> 1930, 29 -> 2029, or 56 -> 1756, 55 -> 1855, ...
1041 sal_uInt16 ImpSvNumberInputScan::ImplGetYear( sal_uInt16 nIndex )
1043 sal_uInt16 nYear = 0;
1045 sal_Int32 nLen = sStrArray[nNums[nIndex]].getLength();
1046 // 16-bit integer year width can have 5 digits, allow for one additional
1047 // leading zero as convention.
1048 if (nLen <= 6)
1050 nYear = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1051 // A year in another, not Gregorian CE era is never expanded.
1052 // A year < 100 entered with at least 3 digits with leading 0 is taken
1053 // as is without expansion.
1054 if (mbEraCE == kDefaultEra && nYear < 100 && nLen < 3)
1056 nYear = SvNumberFormatter::ExpandTwoDigitYear( nYear, nYear2000 );
1060 return nYear;
1064 bool ImpSvNumberInputScan::MayBeIso8601()
1066 if (nMayBeIso8601 == 0)
1068 nMayBeIso8601 = 1;
1069 sal_Int32 nLen = ((nNumericsCnt >= 1 && nNums[0] < nStringsCnt) ? sStrArray[nNums[0]].getLength() : 0);
1070 if (nLen)
1072 sal_Int32 n;
1073 if (nNumericsCnt >= 3 && nNums[2] < nStringsCnt &&
1074 sStrArray[nNums[0]+1] == "-" && // separator year-month
1075 (n = sStrArray[nNums[1]].toInt32()) >= 1 && n <= 12 && // month
1076 sStrArray[nNums[1]+1] == "-" && // separator month-day
1077 (n = sStrArray[nNums[2]].toInt32()) >= 1 && n <= 31) // day
1079 // Year (nNums[0]) value not checked, may be anything, but
1080 // length (number of digits) is checked.
1081 nMayBeIso8601 = (nLen >= 4 ? 4 : (nLen == 3 ? 3 : (nLen > 0 ? 2 : 1)));
1085 return nMayBeIso8601 > 1;
1089 bool ImpSvNumberInputScan::CanForceToIso8601( DateOrder eDateOrder )
1091 int nCanForceToIso8601 = 0;
1092 if (!MayBeIso8601())
1094 return false;
1096 else if (nMayBeIso8601 >= 3)
1098 return true; // at least 3 digits in year
1100 else
1102 if (eDateOrder == DateOrder::Invalid)
1104 // As if any of the cases below can be applied, but only if a
1105 // locale dependent date pattern was not matched.
1106 if ((GetDatePatternNumbers() == nNumericsCnt) && IsDatePatternNumberOfType(0,'Y'))
1107 return false;
1108 eDateOrder = GetDateOrder();
1111 nCanForceToIso8601 = 1;
1114 sal_Int32 n;
1115 switch (eDateOrder)
1117 case DateOrder::DMY: // "day" value out of range => ISO 8601 year
1118 if ((n = sStrArray[nNums[0]].toInt32()) < 1 || n > 31)
1120 nCanForceToIso8601 = 2;
1122 break;
1123 case DateOrder::MDY: // "month" value out of range => ISO 8601 year
1124 if ((n = sStrArray[nNums[0]].toInt32()) < 1 || n > 12)
1126 nCanForceToIso8601 = 2;
1128 break;
1129 case DateOrder::YMD: // always possible
1130 nCanForceToIso8601 = 2;
1131 break;
1132 default: break;
1134 return nCanForceToIso8601 > 1;
1138 bool ImpSvNumberInputScan::IsAcceptableIso8601()
1140 if (mpFormat && (mpFormat->GetType() & SvNumFormatType::DATE))
1142 switch (pFormatter->GetEvalDateFormat())
1144 case NF_EVALDATEFORMAT_INTL:
1145 return CanForceToIso8601( GetDateOrder());
1146 case NF_EVALDATEFORMAT_FORMAT:
1147 return CanForceToIso8601( mpFormat->GetDateOrder());
1148 default:
1149 return CanForceToIso8601( GetDateOrder()) || CanForceToIso8601( mpFormat->GetDateOrder());
1152 return CanForceToIso8601( GetDateOrder());
1156 bool ImpSvNumberInputScan::MayBeMonthDate()
1158 if (nMayBeMonthDate == 0)
1160 nMayBeMonthDate = 1;
1161 if (nNumericsCnt >= 2 && nNums[1] < nStringsCnt)
1163 // "-Jan-"
1164 const OUString& rM = sStrArray[ nNums[ 0 ] + 1 ];
1165 if (rM.getLength() >= 3 && rM[0] == '-' && rM[ rM.getLength() - 1] == '-')
1167 // Check year length assuming at least 3 digits (including
1168 // leading zero). Two digit years 1..31 are out of luck here
1169 // and may be taken as day of month.
1170 bool bYear1 = (sStrArray[nNums[0]].getLength() >= 3);
1171 bool bYear2 = (sStrArray[nNums[1]].getLength() >= 3);
1172 sal_Int32 n;
1173 bool bDay1 = !bYear1;
1174 if (bDay1)
1176 n = sStrArray[nNums[0]].toInt32();
1177 bDay1 = n >= 1 && n <= 31;
1179 bool bDay2 = !bYear2;
1180 if (bDay2)
1182 n = sStrArray[nNums[1]].toInt32();
1183 bDay2 = n >= 1 && n <= 31;
1186 if (bDay1 && !bDay2)
1188 nMayBeMonthDate = 2; // dd-month-yy
1190 else if (!bDay1 && bDay2)
1192 nMayBeMonthDate = 3; // yy-month-dd
1194 else if (bDay1 && bDay2)
1196 // Ambiguous ##-MMM-## date, but some big vendor's database
1197 // reports write this crap, assume this always to be
1198 nMayBeMonthDate = 2; // dd-month-yy
1203 return nMayBeMonthDate > 1;
1207 /** If a string is a separator plus '-' minus sign preceding a 'Y' year in
1208 a date pattern at position nPat.
1210 static bool lcl_IsSignedYearSep( const OUString& rStr, const OUString& rPat, sal_Int32 nPat )
1212 bool bOk = false;
1213 sal_Int32 nLen = rStr.getLength();
1214 if (nLen > 1 && rStr[nLen-1] == '-')
1216 --nLen;
1217 if (nPat + nLen < rPat.getLength() && rPat[nPat+nLen] == 'Y')
1219 // Signed year is possible.
1220 bOk = (rPat.indexOf( rStr.copy( 0, nLen), nPat) == nPat);
1223 return bOk;
1227 bool ImpSvNumberInputScan::IsAcceptedDatePattern( sal_uInt16 nStartPatternAt )
1229 if (nAcceptedDatePattern >= -1)
1231 return (nAcceptedDatePattern >= 0);
1233 if (!nNumericsCnt)
1235 nAcceptedDatePattern = -1;
1237 else if (!sDateAcceptancePatterns.getLength())
1239 // The current locale is the format's locale, if a format is present.
1240 const NfEvalDateFormat eEDF = pFormatter->GetEvalDateFormat();
1241 if (!mpFormat || eEDF == NF_EVALDATEFORMAT_FORMAT || mpFormat->GetLanguage() == pFormatter->GetLanguage())
1243 sDateAcceptancePatterns = pFormatter->GetLocaleData()->getDateAcceptancePatterns();
1245 else
1247 OnDemandLocaleDataWrapper& xLocaleData = pFormatter->GetOnDemandLocaleDataWrapper(
1248 SvNumberFormatter::InputScannerPrivateAccess());
1249 const LanguageTag aSaveLocale( xLocaleData->getLanguageTag() );
1250 assert(mpFormat->GetLanguage() == aSaveLocale.getLanguageType()); // prerequisite
1251 // Obtain formatter's locale's (e.g. system) patterns.
1252 xLocaleData.changeLocale( LanguageTag( pFormatter->GetLanguage()));
1253 const css::uno::Sequence<OUString> aLocalePatterns( xLocaleData->getDateAcceptancePatterns());
1254 // Reset to format's locale.
1255 xLocaleData.changeLocale( aSaveLocale);
1256 // When concatenating don't care about duplicates, combining
1257 // weeding those out reallocs yet another time and probably doesn't
1258 // take less time than looping over two additional patterns below..
1259 switch (eEDF)
1261 case NF_EVALDATEFORMAT_FORMAT:
1262 assert(!"shouldn't reach here");
1263 break;
1264 case NF_EVALDATEFORMAT_INTL:
1265 sDateAcceptancePatterns = aLocalePatterns;
1266 break;
1267 case NF_EVALDATEFORMAT_INTL_FORMAT:
1268 sDateAcceptancePatterns = comphelper::concatSequences(
1269 aLocalePatterns,
1270 xLocaleData->getDateAcceptancePatterns());
1271 break;
1272 case NF_EVALDATEFORMAT_FORMAT_INTL:
1273 sDateAcceptancePatterns = comphelper::concatSequences(
1274 xLocaleData->getDateAcceptancePatterns(),
1275 aLocalePatterns);
1276 break;
1279 SAL_WARN_IF( !sDateAcceptancePatterns.getLength(), "svl.numbers", "ImpSvNumberInputScan::IsAcceptedDatePattern: no date acceptance patterns");
1280 nAcceptedDatePattern = (sDateAcceptancePatterns.getLength() ? -2 : -1);
1283 if (nAcceptedDatePattern == -1)
1285 return false;
1287 nDatePatternStart = nStartPatternAt; // remember start particle
1289 const sal_Int32 nMonthsInYear = pFormatter->GetCalendar()->getNumberOfMonthsInYear();
1291 for (sal_Int32 nPattern=0; nPattern < sDateAcceptancePatterns.getLength(); ++nPattern)
1293 sal_uInt16 nNext = nDatePatternStart;
1294 nDatePatternNumbers = 0;
1295 bool bOk = true;
1296 const OUString& rPat = sDateAcceptancePatterns[nPattern];
1297 sal_Int32 nPat = 0;
1298 for ( ; nPat < rPat.getLength() && bOk && nNext < nStringsCnt; ++nPat, ++nNext)
1300 const sal_Unicode c = rPat[nPat];
1301 switch (c)
1303 case 'Y':
1304 case 'M':
1305 case 'D':
1306 bOk = IsNum[nNext];
1307 if (bOk && (c == 'M' || c == 'D'))
1309 // Check the D and M cases for plausibility. This also
1310 // prevents recognition of date instead of number with a
1311 // numeric group input if date separator is identical to
1312 // group separator, for example with D.M as a pattern and
1313 // #.### as a group.
1314 sal_Int32 nMaxLen, nMaxVal;
1315 switch (c)
1317 case 'M':
1318 nMaxLen = 2;
1319 nMaxVal = nMonthsInYear;
1320 break;
1321 case 'D':
1322 nMaxLen = 2;
1323 nMaxVal = 31;
1324 break;
1325 default:
1326 // This merely exists against
1327 // -Werror=maybe-uninitialized, which is nonsense
1328 // after the (c == 'M' || c == 'D') check above,
1329 // but ...
1330 nMaxLen = 2;
1331 nMaxVal = 31;
1333 bOk = (sStrArray[nNext].getLength() <= nMaxLen);
1334 if (bOk)
1336 sal_Int32 nNum = sStrArray[nNext].toInt32();
1337 bOk = (1 <= nNum && nNum <= nMaxVal);
1340 if (bOk)
1341 ++nDatePatternNumbers;
1342 break;
1343 default:
1344 bOk = !IsNum[nNext];
1345 if (bOk)
1347 const sal_Int32 nLen = sStrArray[nNext].getLength();
1348 bOk = (rPat.indexOf( sStrArray[nNext], nPat) == nPat);
1349 if (bOk)
1351 nPat += nLen - 1;
1353 else if ((bOk = lcl_IsSignedYearSep( sStrArray[nNext], rPat, nPat)))
1355 nPat += nLen - 2;
1357 else if (nPat + nLen > rPat.getLength() && sStrArray[nNext][ nLen - 1 ] == ' ')
1359 using namespace comphelper::string;
1360 // Trailing blanks in input.
1361 OUStringBuffer aBuf(sStrArray[nNext]);
1362 aBuf.stripEnd();
1363 // Expand again in case of pattern "M. D. " and
1364 // input "M. D. ", maybe fetched far, but..
1365 padToLength(aBuf, rPat.getLength() - nPat, ' ');
1366 OUString aStr = aBuf.makeStringAndClear();
1367 bOk = (rPat.indexOf( aStr, nPat) == nPat);
1368 if (bOk)
1370 nPat += aStr.getLength() - 1;
1374 break;
1377 if (bOk)
1379 // Check for trailing characters mismatch.
1380 if (nNext < nStringsCnt)
1382 // Pattern end but not input end.
1383 // A trailing blank may be part of the current pattern input,
1384 // if pattern is "D.M." and input is "D.M. hh:mm" last was
1385 // ". ", or may be following the current pattern input, if
1386 // pattern is "D.M" and input is "D.M hh:mm" last was "M".
1387 sal_Int32 nPos = 0;
1388 sal_uInt16 nCheck;
1389 if (nPat > 0 && nNext > 0)
1391 // nPat is one behind after the for loop.
1392 sal_Int32 nPatCheck = nPat - 1;
1393 switch (rPat[nPatCheck])
1395 case 'Y':
1396 case 'M':
1397 case 'D':
1398 nCheck = nNext;
1399 break;
1400 default:
1402 nCheck = nNext - 1;
1403 // Advance position in input to match length of
1404 // non-YMD (separator) characters in pattern.
1405 sal_Unicode c;
1408 ++nPos;
1409 } while ((c = rPat[--nPatCheck]) != 'Y' && c != 'M' && c != 'D');
1413 else
1415 nCheck = nNext;
1417 if (!IsNum[nCheck])
1419 // Trailing (or separating if time follows) blanks are ok.
1420 SkipBlanks( sStrArray[nCheck], nPos);
1421 if (nPos == sStrArray[nCheck].getLength())
1423 nAcceptedDatePattern = nPattern;
1424 return true;
1428 else if (nPat == rPat.getLength())
1430 // Input end and pattern end => match.
1431 nAcceptedDatePattern = nPattern;
1432 return true;
1434 // else Input end but not pattern end, no match.
1437 nAcceptedDatePattern = -1;
1438 return false;
1442 bool ImpSvNumberInputScan::SkipDatePatternSeparator( sal_uInt16 nParticle, sal_Int32 & rPos, bool & rSignedYear )
1444 // If not initialized yet start with first number, if any.
1445 if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1447 return false;
1449 if (nParticle < nDatePatternStart || nParticle >= nStringsCnt || IsNum[nParticle])
1451 return false;
1453 sal_uInt16 nNext = nDatePatternStart;
1454 const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1455 for (sal_Int32 nPat = 0; nPat < rPat.getLength() && nNext < nStringsCnt; ++nPat, ++nNext)
1457 switch (rPat[nPat])
1459 case 'Y':
1460 case 'M':
1461 case 'D':
1462 break;
1463 default:
1464 if (nNext == nParticle)
1466 const sal_Int32 nLen = sStrArray[nNext].getLength();
1467 bool bOk = (rPat.indexOf( sStrArray[nNext], nPat) == nPat);
1468 if (!bOk)
1470 bOk = lcl_IsSignedYearSep( sStrArray[nNext], rPat, nPat);
1471 if (bOk)
1472 rSignedYear = true;
1474 if (!bOk && (nPat + nLen > rPat.getLength() && sStrArray[nNext][ nLen - 1 ] == ' '))
1476 // The same ugly trailing blanks check as in
1477 // IsAcceptedDatePattern().
1478 using namespace comphelper::string;
1479 OUStringBuffer aBuf(sStrArray[nNext]);
1480 aBuf.stripEnd();
1481 padToLength(aBuf, rPat.getLength() - nPat, ' ');
1482 bOk = (rPat.indexOf( aBuf.makeStringAndClear(), nPat) == nPat);
1484 if (bOk)
1486 rPos = nLen; // yes, set, not add!
1487 return true;
1489 else
1490 return false;
1492 nPat += sStrArray[nNext].getLength() - 1;
1493 break;
1496 return false;
1500 sal_uInt16 ImpSvNumberInputScan::GetDatePatternNumbers()
1502 // If not initialized yet start with first number, if any.
1503 if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1505 return 0;
1507 return nDatePatternNumbers;
1511 bool ImpSvNumberInputScan::IsDatePatternNumberOfType( sal_uInt16 nNumber, sal_Unicode cType )
1513 if (GetDatePatternNumbers() <= nNumber)
1514 return false;
1516 sal_uInt16 nNum = 0;
1517 const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1518 for (sal_Int32 nPat = 0; nPat < rPat.getLength(); ++nPat)
1520 switch (rPat[nPat])
1522 case 'Y':
1523 case 'M':
1524 case 'D':
1525 if (nNum == nNumber)
1526 return rPat[nPat] == cType;
1527 ++nNum;
1528 break;
1531 return false;
1535 sal_uInt32 ImpSvNumberInputScan::GetDatePatternOrder()
1537 // If not initialized yet start with first number, if any.
1538 if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1540 return 0;
1542 sal_uInt32 nOrder = 0;
1543 const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1544 for (sal_Int32 nPat = 0; nPat < rPat.getLength() && !(nOrder & 0xff0000); ++nPat)
1546 switch (rPat[nPat])
1548 case 'Y':
1549 case 'M':
1550 case 'D':
1551 nOrder = (nOrder << 8) | rPat[nPat];
1552 break;
1555 return nOrder;
1559 DateOrder ImpSvNumberInputScan::GetDateOrder()
1561 sal_uInt32 nOrder = GetDatePatternOrder();
1562 if (!nOrder)
1564 return pFormatter->GetLocaleData()->getDateOrder();
1566 switch ((nOrder & 0xff0000) >> 16)
1568 case 'Y':
1569 if ((((nOrder & 0xff00) >> 8) == 'M') && ((nOrder & 0xff) == 'D'))
1571 return DateOrder::YMD;
1573 break;
1574 case 'M':
1575 if ((((nOrder & 0xff00) >> 8) == 'D') && ((nOrder & 0xff) == 'Y'))
1577 return DateOrder::MDY;
1579 break;
1580 case 'D':
1581 if ((((nOrder & 0xff00) >> 8) == 'M') && ((nOrder & 0xff) == 'Y'))
1583 return DateOrder::DMY;
1585 break;
1586 default:
1587 case 0:
1588 switch ((nOrder & 0xff00) >> 8)
1590 case 'Y':
1591 switch (nOrder & 0xff)
1593 case 'M':
1594 return DateOrder::YMD;
1596 break;
1597 case 'M':
1598 switch (nOrder & 0xff)
1600 case 'Y':
1601 return DateOrder::DMY;
1602 case 'D':
1603 return DateOrder::MDY;
1605 break;
1606 case 'D':
1607 switch (nOrder & 0xff)
1609 case 'Y':
1610 return DateOrder::MDY;
1611 case 'M':
1612 return DateOrder::DMY;
1614 break;
1615 default:
1616 case 0:
1617 switch (nOrder & 0xff)
1619 case 'Y':
1620 return DateOrder::YMD;
1621 case 'M':
1622 return DateOrder::MDY;
1623 case 'D':
1624 return DateOrder::DMY;
1626 break;
1629 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateOrder: undefined, falling back to locale's default");
1630 return pFormatter->GetLocaleData()->getDateOrder();
1633 bool ImpSvNumberInputScan::GetDateRef( double& fDays, sal_uInt16& nCounter )
1635 using namespace ::com::sun::star::i18n;
1636 NfEvalDateFormat eEDF;
1637 int nFormatOrder;
1638 if ( mpFormat && (mpFormat->GetType() & SvNumFormatType::DATE) )
1640 eEDF = pFormatter->GetEvalDateFormat();
1641 switch ( eEDF )
1643 case NF_EVALDATEFORMAT_INTL :
1644 case NF_EVALDATEFORMAT_FORMAT :
1645 nFormatOrder = 1; // only one loop
1646 break;
1647 default:
1648 nFormatOrder = 2;
1649 if ( nMatchedAllStrings )
1651 eEDF = NF_EVALDATEFORMAT_FORMAT_INTL;
1652 // we have a complete match, use it
1656 else
1658 eEDF = NF_EVALDATEFORMAT_INTL;
1659 nFormatOrder = 1;
1661 bool res = true;
1663 const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
1664 CalendarWrapper* pCal = pFormatter->GetCalendar();
1665 for ( int nTryOrder = 1; nTryOrder <= nFormatOrder; nTryOrder++ )
1667 pCal->setGregorianDateTime( Date( Date::SYSTEM ) ); // today
1668 OUString aOrgCalendar; // empty => not changed yet
1669 DateOrder DateFmt;
1670 bool bFormatTurn;
1671 switch ( eEDF )
1673 case NF_EVALDATEFORMAT_INTL :
1674 bFormatTurn = false;
1675 DateFmt = GetDateOrder();
1676 break;
1677 case NF_EVALDATEFORMAT_FORMAT :
1678 bFormatTurn = true;
1679 DateFmt = mpFormat->GetDateOrder();
1680 break;
1681 case NF_EVALDATEFORMAT_INTL_FORMAT :
1682 if ( nTryOrder == 1 )
1684 bFormatTurn = false;
1685 DateFmt = GetDateOrder();
1687 else
1689 bFormatTurn = true;
1690 DateFmt = mpFormat->GetDateOrder();
1692 break;
1693 case NF_EVALDATEFORMAT_FORMAT_INTL :
1694 if ( nTryOrder == 2 )
1696 bFormatTurn = false;
1697 DateFmt = GetDateOrder();
1699 else
1701 bFormatTurn = true;
1702 // Even if the format pattern is to be preferred, the input may
1703 // have matched a pattern of the current locale, which then
1704 // again is to be preferred. Both date orders can be different
1705 // so we need to obtain the actual match. For example ISO
1706 // YYYY-MM-DD format vs locale's DD.MM.YY input.
1707 if (!GetDatePatternOrder())
1709 // No pattern match => format match.
1710 DateFmt = mpFormat->GetDateOrder();
1712 else
1714 // Pattern match. Note that patterns may have been
1715 // constructed from the format's locale and prepended to
1716 // the current locale's patterns, it doesn't necessarily
1717 // mean a current locale's pattern was matched, but may if
1718 // the format's locale's patterns didn't match, which were
1719 // tried first.
1720 DateFmt = GetDateOrder();
1723 break;
1724 default:
1725 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateRef: unknown NfEvalDateFormat" );
1726 DateFmt = DateOrder::YMD;
1727 bFormatTurn = false;
1729 if ( bFormatTurn )
1731 /* TODO:
1732 We are currently not able to fully support a switch to another calendar during
1733 input for the following reasons:
1734 1. We do have a problem if both (locale's default and format's) calendars
1735 define the same YMD order and use the same date separator, there is no way
1736 to distinguish between them if the input results in valid calendar input for
1737 both calendars. How to solve? Would NfEvalDateFormat be sufficient? Should
1738 it always be set to NF_EVALDATEFORMAT_FORMAT_INTL and thus the format's
1739 calendar be preferred? This could be confusing if a Calc cell was formatted
1740 different to the locale's default and has no content yet, then the user has
1741 no clue about the format or calendar being set.
1742 2. In Calc cell edit mode a date is always displayed and edited using the
1743 default edit format of the default calendar (normally being Gregorian). If
1744 input was ambiguous due to issue #1 we'd need a mechanism to tell that a
1745 date was edited and not newly entered. Not feasible. Otherwise we'd need a
1746 mechanism to use a specific edit format with a specific calendar according
1747 to the format set.
1748 3. For some calendars like Japanese Gengou we'd need era input, which isn't
1749 implemented at all. Though this is a rare and special case, forcing a
1750 calendar dependent edit format as suggested in item #2 might require era
1751 input, if it shouldn't result in a fallback to Gregorian calendar.
1752 4. Last and least: the GetMonth() method currently only matches month names of
1753 the default calendar. Alternating month names of the actual format's
1754 calendar would have to be implemented. No problem.
1757 #ifdef THE_FUTURE
1758 if ( mpFormat->IsOtherCalendar( nStringScanNumFor ) )
1760 mpFormat->SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
1762 else
1764 mpFormat->SwitchToSpecifiedCalendar( aOrgCalendar, fOrgDateTime,
1765 nStringScanNumFor );
1767 #endif
1770 res = true;
1771 nCounter = 0;
1772 // For incomplete dates, always assume first day of month if not specified.
1773 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
1775 switch (nNumericsCnt) // count of numbers in string
1777 case 0: // none
1778 if (nMonthPos) // only month (Jan)
1780 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1782 else
1784 res = false;
1786 break;
1788 case 1: // only one number
1789 nCounter = 1;
1790 switch (nMonthPos) // where is the month
1792 case 0: // not found
1794 // If input matched a date pattern, use the pattern
1795 // to determine if it is a day, month or year. The
1796 // pattern should have only one single value then,
1797 // 'D-', 'M-' or 'Y-'. If input did not match a
1798 // pattern assume the usual day of current month.
1799 sal_uInt32 nDateOrder = (bFormatTurn ?
1800 mpFormat->GetExactDateOrder() :
1801 GetDatePatternOrder());
1802 switch (nDateOrder)
1804 case 'Y':
1805 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1806 break;
1807 case 'M':
1808 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1809 break;
1810 case 'D':
1811 default:
1812 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1813 break;
1815 break;
1817 case 1: // month at the beginning (Jan 01)
1818 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1819 switch (DateFmt)
1821 case DateOrder::MDY:
1822 case DateOrder::YMD:
1824 sal_uInt16 nDay = ImplGetDay(0);
1825 sal_uInt16 nYear = ImplGetYear(0);
1826 if (nDay == 0 || nDay > 32)
1828 pCal->setValue( CalendarFieldIndex::YEAR, nYear);
1830 else
1832 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1834 break;
1836 case DateOrder::DMY:
1837 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1838 break;
1839 default:
1840 res = false;
1841 break;
1843 break;
1844 case 3: // month at the end (10 Jan)
1845 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1846 switch (DateFmt)
1848 case DateOrder::DMY:
1849 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1850 break;
1851 case DateOrder::YMD:
1852 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1853 break;
1854 default:
1855 res = false;
1856 break;
1858 break;
1859 default:
1860 res = false;
1861 break;
1862 } // switch (nMonthPos)
1863 break;
1865 case 2: // 2 numbers
1866 nCounter = 2;
1867 switch (nMonthPos) // where is the month
1869 case 0: // not found
1871 sal_uInt32 nExactDateOrder = (bFormatTurn ?
1872 mpFormat->GetExactDateOrder() :
1873 GetDatePatternOrder());
1874 bool bIsExact = (0xff < nExactDateOrder && nExactDateOrder <= 0xffff);
1875 if (!bIsExact && bFormatTurn && IsAcceptedDatePattern( nNums[0]))
1877 // If input does not match format but pattern, use pattern
1878 // instead, even if eEDF==NF_EVALDATEFORMAT_FORMAT_INTL.
1879 // For example, format has "Y-M-D" and pattern is "D.M.",
1880 // input with 2 numbers can't match format and 31.12. would
1881 // lead to 1931-12-01 (fdo#54344)
1882 nExactDateOrder = GetDatePatternOrder();
1883 bIsExact = (0xff < nExactDateOrder && nExactDateOrder <= 0xffff);
1885 bool bHadExact;
1886 if (bIsExact)
1888 // formatted as date and exactly 2 parts
1889 bHadExact = true;
1890 switch ( (nExactDateOrder >> 8) & 0xff )
1892 case 'Y':
1893 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1894 break;
1895 case 'M':
1896 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1897 break;
1898 case 'D':
1899 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1900 break;
1901 default:
1902 bHadExact = false;
1904 switch ( nExactDateOrder & 0xff )
1906 case 'Y':
1907 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
1908 break;
1909 case 'M':
1910 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
1911 break;
1912 case 'D':
1913 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
1914 break;
1915 default:
1916 bHadExact = false;
1918 SAL_WARN_IF( !bHadExact, "svl.numbers", "ImpSvNumberInputScan::GetDateRef: error in exact date order");
1920 else
1922 bHadExact = false;
1924 // If input matched against a date acceptance pattern
1925 // do not attempt to mess around with guessing the
1926 // order, either it matches or it doesn't.
1927 if ((bFormatTurn || !bIsExact) && (!bHadExact || !pCal->isValid()))
1929 if ( !bHadExact && nExactDateOrder )
1931 pCal->setGregorianDateTime( Date( Date::SYSTEM ) ); // reset today
1933 switch (DateFmt)
1935 case DateOrder::MDY:
1936 // M D
1937 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
1938 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1939 if ( !pCal->isValid() ) // 2nd try
1940 { // M Y
1941 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
1942 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1943 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
1945 break;
1946 case DateOrder::DMY:
1947 // D M
1948 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1949 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
1950 if ( !pCal->isValid() ) // 2nd try
1951 { // M Y
1952 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
1953 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1954 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
1956 break;
1957 case DateOrder::YMD:
1958 // M D
1959 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
1960 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1961 if ( !pCal->isValid() ) // 2nd try
1962 { // Y M
1963 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
1964 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
1965 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1967 break;
1968 default:
1969 res = false;
1970 break;
1974 break;
1975 case 1: // month at the beginning (Jan 01 01)
1977 // The input is valid as MDY in almost any
1978 // constellation, there is no date order (M)YD except if
1979 // set in a format applied.
1980 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1981 sal_uInt32 nExactDateOrder = (bFormatTurn ? mpFormat->GetExactDateOrder() : 0);
1982 if ((((nExactDateOrder >> 8) & 0xff) == 'Y') && ((nExactDateOrder & 0xff) == 'D'))
1984 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
1985 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1987 else
1989 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1990 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
1992 break;
1994 case 2: // month in the middle (10 Jan 94)
1996 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1997 DateOrder eDF = (MayBeMonthDate() ? (nMayBeMonthDate == 2 ? DateOrder::DMY : DateOrder::YMD) : DateFmt);
1998 switch (eDF)
2000 case DateOrder::DMY:
2001 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2002 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2003 break;
2004 case DateOrder::YMD:
2005 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2006 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2007 break;
2008 default:
2009 res = false;
2010 break;
2012 break;
2014 default: // else, e.g. month at the end (94 10 Jan)
2015 res = false;
2016 break;
2017 } // switch (nMonthPos)
2018 break;
2020 default: // more than two numbers (31.12.94 8:23) (31.12. 8:23)
2021 switch (nMonthPos) // where is the month
2023 case 0: // not found
2025 nCounter = 3;
2026 if ( nTimePos > 1 )
2027 { // find first time number index (should only be 3 or 2 anyway)
2028 for ( sal_uInt16 j = 0; j < nNumericsCnt; j++ )
2030 if ( nNums[j] == nTimePos - 2 )
2032 nCounter = j;
2033 break; // for
2037 // ISO 8601 yyyy-mm-dd forced recognition
2038 DateOrder eDF = (CanForceToIso8601( DateFmt) ? DateOrder::YMD : DateFmt);
2039 switch (eDF)
2041 case DateOrder::MDY:
2042 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2043 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2044 if ( nCounter > 2 )
2045 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(2) );
2046 break;
2047 case DateOrder::DMY:
2048 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2049 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2050 if ( nCounter > 2 )
2051 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(2) );
2052 break;
2053 case DateOrder::YMD:
2054 if ( nCounter > 2 )
2055 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(2) );
2056 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2057 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2058 break;
2059 default:
2060 res = false;
2061 break;
2063 break;
2065 case 1: // month at the beginning (Jan 01 01 8:23)
2066 nCounter = 2;
2067 switch (DateFmt)
2069 case DateOrder::MDY:
2070 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2071 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2072 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2073 break;
2074 default:
2075 res = false;
2076 break;
2078 break;
2079 case 2: // month in the middle (10 Jan 94 8:23)
2080 nCounter = 2;
2081 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2082 switch (DateFmt)
2084 case DateOrder::DMY:
2085 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2086 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2087 break;
2088 case DateOrder::YMD:
2089 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2090 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2091 break;
2092 default:
2093 res = false;
2094 break;
2096 break;
2097 default: // else, e.g. month at the end (94 10 Jan 8:23)
2098 nCounter = 2;
2099 res = false;
2100 break;
2101 } // switch (nMonthPos)
2102 break;
2103 } // switch (nNumericsCnt)
2105 if (mbEraCE != kDefaultEra)
2106 pCal->setValue( CalendarFieldIndex::ERA, mbEraCE ? 1 : 0);
2108 if ( res && pCal->isValid() )
2110 double fDiff = DateTime(*pNullDate) - pCal->getEpochStart();
2111 fDays = ::rtl::math::approxFloor( pCal->getLocalDateTime() );
2112 fDays -= fDiff;
2113 nTryOrder = nFormatOrder; // break for
2115 else
2117 res = false;
2119 if ( aOrgCalendar.getLength() )
2121 pCal->loadCalendar( aOrgCalendar, pLoc->getLanguageTag().getLocale() ); // restore calendar
2123 #if NF_TEST_CALENDAR
2125 using namespace ::com::sun::star;
2126 struct entry { const char* lan; const char* cou; const char* cal; };
2127 const entry cals[] = {
2128 { "en", "US", "gregorian" },
2129 { "ar", "TN", "hijri" },
2130 { "he", "IL", "jewish" },
2131 { "ja", "JP", "gengou" },
2132 { "ko", "KR", "hanja_yoil" },
2133 { "th", "TH", "buddhist" },
2134 { "zh", "TW", "ROC" },
2135 {0,0,0}
2137 lang::Locale aLocale;
2138 bool bValid;
2139 sal_Int16 nDay, nMyMonth, nYear, nHour, nMinute, nSecond;
2140 sal_Int16 nDaySet, nMonthSet, nYearSet, nHourSet, nMinuteSet, nSecondSet;
2141 sal_Int16 nZO, nDST1, nDST2, nDST, nZOmillis, nDST1millis, nDST2millis, nDSTmillis;
2142 sal_Int32 nZoneInMillis, nDST1InMillis, nDST2InMillis;
2143 uno::Reference< uno::XComponentContext > xContext =
2144 ::comphelper::getProcessComponentContext();
2145 uno::Reference< i18n::XCalendar4 > xCal = i18n::LocaleCalendar2::create(xContext);
2146 for ( const entry* p = cals; p->lan; ++p )
2148 aLocale.Language = OUString::createFromAscii( p->lan );
2149 aLocale.Country = OUString::createFromAscii( p->cou );
2150 xCal->loadCalendar( OUString::createFromAscii( p->cal ),
2151 aLocale );
2152 double nDateTime = 0.0; // 1-Jan-1970 00:00:00
2153 nZO = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET );
2154 nZOmillis = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS );
2155 nZoneInMillis = static_cast<sal_Int32>(nZO) * 60000 +
2156 (nZO < 0 ? -1 : 1) * static_cast<sal_uInt16>(nZOmillis);
2157 nDST1 = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2158 nDST1millis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2159 nDST1InMillis = static_cast<sal_Int32>(nDST1) * 60000 +
2160 (nDST1 < 0 ? -1 : 1) * static_cast<sal_uInt16>(nDST1millis);
2161 nDateTime -= (double)(nZoneInMillis + nDST1InMillis) / 1000.0 / 60.0 / 60.0 / 24.0;
2162 xCal->setDateTime( nDateTime );
2163 nDST2 = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2164 nDST2millis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2165 nDST2InMillis = static_cast<sal_Int32>(nDST2) * 60000 +
2166 (nDST2 < 0 ? -1 : 1) * static_cast<sal_uInt16>(nDST2millis);
2167 if ( nDST1InMillis != nDST2InMillis )
2169 nDateTime = 0.0 - (double)(nZoneInMillis + nDST2InMillis) / 1000.0 / 60.0 / 60.0 / 24.0;
2170 xCal->setDateTime( nDateTime );
2172 nDaySet = xCal->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH );
2173 nMonthSet = xCal->getValue( i18n::CalendarFieldIndex::MONTH );
2174 nYearSet = xCal->getValue( i18n::CalendarFieldIndex::YEAR );
2175 nHourSet = xCal->getValue( i18n::CalendarFieldIndex::HOUR );
2176 nMinuteSet = xCal->getValue( i18n::CalendarFieldIndex::MINUTE );
2177 nSecondSet = xCal->getValue( i18n::CalendarFieldIndex::SECOND );
2178 nZO = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET );
2179 nZOmillis = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS );
2180 nDST = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2181 nDSTmillis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2182 xCal->setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH, nDaySet );
2183 xCal->setValue( i18n::CalendarFieldIndex::MONTH, nMonthSet );
2184 xCal->setValue( i18n::CalendarFieldIndex::YEAR, nYearSet );
2185 xCal->setValue( i18n::CalendarFieldIndex::HOUR, nHourSet );
2186 xCal->setValue( i18n::CalendarFieldIndex::MINUTE, nMinuteSet );
2187 xCal->setValue( i18n::CalendarFieldIndex::SECOND, nSecondSet );
2188 bValid = xCal->isValid();
2189 nDay = xCal->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH );
2190 nMyMonth= xCal->getValue( i18n::CalendarFieldIndex::MONTH );
2191 nYear = xCal->getValue( i18n::CalendarFieldIndex::YEAR );
2192 nHour = xCal->getValue( i18n::CalendarFieldIndex::HOUR );
2193 nMinute = xCal->getValue( i18n::CalendarFieldIndex::MINUTE );
2194 nSecond = xCal->getValue( i18n::CalendarFieldIndex::SECOND );
2195 bValid = bValid && nDay == nDaySet && nMyMonth == nMonthSet && nYear ==
2196 nYearSet && nHour == nHourSet && nMinute == nMinuteSet && nSecond
2197 == nSecondSet;
2200 #endif // NF_TEST_CALENDAR
2204 return res;
2209 * Analyze first string
2210 * All gone => true
2211 * else => false
2213 bool ImpSvNumberInputScan::ScanStartString( const OUString& rString )
2215 sal_Int32 nPos = 0;
2217 // First of all, eat leading blanks
2218 SkipBlanks(rString, nPos);
2220 // Yes, nMatchedAllStrings should know about the sign position
2221 nSign = GetSign(rString, nPos);
2222 if ( nSign ) // sign?
2224 SkipBlanks(rString, nPos);
2226 // #102371# match against format string only if start string is not a sign character
2227 if ( nMatchedAllStrings && !(nSign && rString.getLength() == 1) )
2229 // Match against format in any case, so later on for a "x1-2-3" input
2230 // we may distinguish between a xy-m-d (or similar) date and a x0-0-0
2231 // format. No sign detection here!
2232 if ( ScanStringNumFor( rString, nPos, 0, true ) )
2234 nMatchedAllStrings |= nMatchedStartString;
2236 else
2238 nMatchedAllStrings = 0;
2242 // Bail out early for just a sign.
2243 if (nSign && nPos == rString.getLength())
2244 return true;
2246 if ( GetDecSep(rString, nPos) ) // decimal separator in start string
2248 nDecPos = 1;
2249 SkipBlanks(rString, nPos);
2251 else if ( GetCurrency(rString, nPos) ) // currency (DM 1)?
2253 eScannedType = SvNumFormatType::CURRENCY; // !!! it IS currency !!!
2254 SkipBlanks(rString, nPos);
2255 if (nSign == 0) // no sign yet
2257 nSign = GetSign(rString, nPos);
2258 if ( nSign ) // DM -1
2260 SkipBlanks(rString, nPos);
2263 if ( GetDecSep(rString, nPos) ) // decimal separator follows currency
2265 nDecPos = 1;
2266 SkipBlanks(rString, nPos);
2269 else
2271 const sal_Int32 nMonthStart = nPos;
2272 short nTempMonth = GetMonth(rString, nPos);
2273 if (nTempMonth < 0)
2275 // Short month and day names may be identical in some locales, e.g.
2276 // "mar" for "martes" or "marzo" in Spanish.
2277 // Do not let a month name immediately take precedence if a day
2278 // name was meant instead. Assume that both could be valid, until
2279 // encountered differently or the final evaluation in
2280 // IsNumberFormat() checks, but continue with weighing the month
2281 // name higher unless we have both day of week and month name here.
2282 sal_Int32 nTempPos = nMonthStart;
2283 nDayOfWeek = GetDayOfWeek( rString, nTempPos);
2284 if (nDayOfWeek < 0)
2286 SkipChar( '.', rString, nTempPos ); // abbreviated
2287 SkipString( pFormatter->GetLocaleData()->getLongDateDayOfWeekSep(), rString, nTempPos );
2288 SkipBlanks( rString, nTempPos);
2289 short nTempTempMonth = GetMonth( rString, nTempPos);
2290 if (nTempTempMonth)
2292 // Fall into the else branch below that handles both.
2293 nTempMonth = 0;
2294 nPos = nMonthStart;
2295 nDayOfWeek = 0;
2296 // Do not set nDayOfWeek hereafter, anywhere.
2300 if ( nTempMonth ) // month (Jan 1)?
2302 // Jan1 without separator is not a date, unless it is followed by a
2303 // separator and a (year) number.
2304 if (nPos < rString.getLength() || (nStringsCnt >= 4 && nNumericsCnt >= 2))
2306 eScannedType = SvNumFormatType::DATE; // !!! it IS a date !!!
2307 nMonth = nTempMonth;
2308 nMonthPos = 1; // month at the beginning
2309 if ( nMonth < 0 )
2311 SkipChar( '.', rString, nPos ); // abbreviated
2313 SkipBlanks(rString, nPos);
2315 else
2317 nPos = nMonthStart; // rewind month
2320 else
2322 int nTempDayOfWeek = GetDayOfWeek( rString, nPos );
2323 if ( nTempDayOfWeek )
2325 // day of week is just parsed away
2326 eScannedType = SvNumFormatType::DATE; // !!! it IS a date !!!
2327 if ( nPos < rString.getLength() )
2329 if ( nTempDayOfWeek < 0 )
2331 // abbreviated
2332 if ( rString[ nPos ] == '.' )
2334 ++nPos;
2337 else
2339 // full long name
2340 SkipBlanks(rString, nPos);
2341 SkipString( pFormatter->GetLocaleData()->getLongDateDayOfWeekSep(), rString, nPos );
2343 SkipBlanks(rString, nPos);
2344 nTempMonth = GetMonth(rString, nPos);
2345 if ( nTempMonth ) // month (Jan 1)?
2347 // Jan1 without separator is not a date, unless it is followed by a
2348 // separator and a (year) number.
2349 if (nPos < rString.getLength() || (nStringsCnt >= 4 && nNumericsCnt >= 2))
2351 nMonth = nTempMonth;
2352 nMonthPos = 1; // month a the beginning
2353 if ( nMonth < 0 )
2355 SkipChar( '.', rString, nPos ); // abbreviated
2357 SkipBlanks(rString, nPos);
2359 else
2361 nPos = nMonthStart; // rewind month
2365 if (!nMonth)
2367 // Determine and remember following date pattern, if any.
2368 IsAcceptedDatePattern( 1);
2374 // skip any trailing '-' or '/' chars
2375 if (nPos < rString.getLength())
2377 while (SkipChar ('-', rString, nPos) || SkipChar ('/', rString, nPos))
2378 ; // do nothing
2380 if (nPos < rString.getLength()) // not everything consumed
2382 // Does input StartString equal StartString of format?
2383 // This time with sign detection!
2384 if ( !ScanStringNumFor( rString, nPos, 0 ) )
2386 return MatchedReturn();
2390 return true;
2395 * Analyze string in the middle
2396 * All gone => true
2397 * else => false
2399 bool ImpSvNumberInputScan::ScanMidString( const OUString& rString, sal_uInt16 nStringPos )
2401 sal_Int32 nPos = 0;
2402 SvNumFormatType eOldScannedType = eScannedType;
2404 if ( nMatchedAllStrings )
2405 { // Match against format in any case, so later on for a "1-2-3-4" input
2406 // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2407 // format.
2408 if ( ScanStringNumFor( rString, 0, nStringPos ) )
2410 nMatchedAllStrings |= nMatchedMidString;
2412 else
2414 nMatchedAllStrings = 0;
2418 SkipBlanks(rString, nPos);
2419 if (GetDecSep(rString, nPos)) // decimal separator?
2421 if (nDecPos == 1 || nDecPos == 3) // .12.4 or 1.E2.1
2423 return MatchedReturn();
2425 else if (nDecPos == 2) // . dup: 12.4.
2427 bool bSignedYear = false;
2428 if (bDecSepInDateSeps || // . also date separator
2429 SkipDatePatternSeparator( nStringPos, nPos, bSignedYear))
2431 if ( eScannedType != SvNumFormatType::UNDEFINED &&
2432 eScannedType != SvNumFormatType::DATE &&
2433 eScannedType != SvNumFormatType::DATETIME) // already another type
2435 return MatchedReturn();
2437 if (eScannedType == SvNumFormatType::UNDEFINED)
2439 eScannedType = SvNumFormatType::DATE; // !!! it IS a date
2441 SkipBlanks(rString, nPos);
2443 else
2445 return MatchedReturn();
2448 else
2450 nDecPos = 2; // . in mid string
2451 SkipBlanks(rString, nPos);
2454 else if ( (eScannedType & SvNumFormatType::TIME) &&
2455 GetTime100SecSep( rString, nPos ) )
2456 { // hundredth seconds separator
2457 if ( nDecPos )
2459 return MatchedReturn();
2461 nDecPos = 2; // . in mid string
2463 // If this is exactly an ISO 8601 fractional seconds separator, bail
2464 // out early to not get confused by later checks for group separator or
2465 // other.
2466 if (bIso8601Tsep && nPos == rString.getLength() &&
2467 eScannedType == SvNumFormatType::DATETIME && (rString == "." || rString == ","))
2468 return true;
2470 SkipBlanks(rString, nPos);
2473 if (SkipChar('/', rString, nPos)) // fraction?
2475 if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2476 eScannedType != SvNumFormatType::DATE) // except date
2478 return MatchedReturn(); // => jan/31/1994
2480 else if (eScannedType != SvNumFormatType::DATE && // analyzed no date until now
2481 ( eSetType == SvNumFormatType::FRACTION || // and preset was fraction
2482 (nNumericsCnt == 3 && // or 3 numbers
2483 (nStringPos == 3 || // and 3rd string particle
2484 (nStringPos == 4 && nSign))))) // or 4th if signed
2486 SkipBlanks(rString, nPos);
2487 if (nPos == rString.getLength())
2489 eScannedType = SvNumFormatType::FRACTION; // !!! it IS a fraction (so far)
2490 if (eSetType == SvNumFormatType::FRACTION &&
2491 nNumericsCnt == 2 &&
2492 (nStringPos == 1 || // for 4/5
2493 (nStringPos == 2 && nSign))) // or signed -4/5
2495 return true; // don't fall into date trap
2499 else
2501 nPos--; // put '/' back
2505 if (GetThousandSep(rString, nPos, nStringPos)) // 1,000
2507 if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2508 eScannedType != SvNumFormatType::CURRENCY) // except currency
2510 return MatchedReturn();
2512 nThousand++;
2515 const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
2516 bool bSignedYear = false;
2517 bool bDate = SkipDatePatternSeparator( nStringPos, nPos, bSignedYear); // 12/31 31.12. 12/31/1999 31.12.1999
2518 if (!bDate)
2520 const OUString& rDate = pFormatter->GetDateSep();
2521 SkipBlanks(rString, nPos);
2522 bDate = SkipString( rDate, rString, nPos); // 10. 10- 10/
2524 if (bDate || ((MayBeIso8601() || MayBeMonthDate()) && // 1999-12-31 31-Dec-1999
2525 SkipChar( '-', rString, nPos)))
2527 if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2528 eScannedType != SvNumFormatType::DATE) // except date
2530 return MatchedReturn();
2532 SkipBlanks(rString, nPos);
2533 eScannedType = SvNumFormatType::DATE; // !!! it IS a date
2534 short nTmpMonth = GetMonth(rString, nPos); // 10. Jan 94
2535 if (nMonth && nTmpMonth) // month dup
2537 return MatchedReturn();
2539 if (nTmpMonth)
2541 nMonth = nTmpMonth;
2542 nMonthPos = 2; // month in the middle
2543 if ( nMonth < 0 && SkipChar( '.', rString, nPos ) )
2544 ; // short month may be abbreviated Jan.
2545 else if ( SkipChar( '-', rString, nPos ) )
2546 ; // #79632# recognize 17-Jan-2001 to be a date
2547 // #99065# short and long month name
2548 else
2550 SkipString( pLoc->getLongDateMonthSep(), rString, nPos );
2552 SkipBlanks(rString, nPos);
2554 if (bSignedYear)
2556 if (mbEraCE != kDefaultEra) // signed year twice?
2557 return MatchedReturn();
2559 mbEraCE = false; // BCE
2563 const sal_Int32 nMonthStart = nPos;
2564 short nTempMonth = GetMonth(rString, nPos); // month in the middle (10 Jan 94)
2565 if (nTempMonth)
2567 if (nMonth != 0) // month dup
2569 return MatchedReturn();
2571 if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2572 eScannedType != SvNumFormatType::DATE) // except date
2574 return MatchedReturn();
2576 if (nMonthStart > 0 && nPos < rString.getLength()) // 10Jan or Jan94 without separator are not dates
2578 eScannedType = SvNumFormatType::DATE; // !!! it IS a date
2579 nMonth = nTempMonth;
2580 nMonthPos = 2; // month in the middle
2581 if ( nMonth < 0 )
2583 SkipChar( '.', rString, nPos ); // abbreviated
2585 SkipString( pLoc->getLongDateMonthSep(), rString, nPos );
2586 SkipBlanks(rString, nPos);
2588 else
2590 nPos = nMonthStart; // rewind month
2594 if ( SkipChar('E', rString, nPos) || // 10E, 10e, 10,Ee
2595 SkipChar('e', rString, nPos) )
2597 if (eScannedType != SvNumFormatType::UNDEFINED) // already another type
2599 return MatchedReturn();
2601 else
2603 SkipBlanks(rString, nPos);
2604 eScannedType = SvNumFormatType::SCIENTIFIC; // !!! it IS scientific
2605 if ( nThousand+2 == nNumericsCnt && nDecPos == 2 ) // special case 1.E2
2607 nDecPos = 3; // 1,100.E2 1,100,100.E3
2610 nESign = GetESign(rString, nPos); // signed exponent?
2611 SkipBlanks(rString, nPos);
2614 const OUString& rTime = pLoc->getTimeSep();
2615 if ( SkipString(rTime, rString, nPos) ) // time separator?
2617 if (nDecPos) // already . => maybe error
2619 if (bDecSepInDateSeps) // . also date sep
2621 if ( eScannedType != SvNumFormatType::DATE && // already another type than date
2622 eScannedType != SvNumFormatType::DATETIME) // or date time
2624 return MatchedReturn();
2626 if (eScannedType == SvNumFormatType::DATE)
2628 nDecPos = 0; // reset for time transition
2631 else
2633 return MatchedReturn();
2636 if ((eScannedType == SvNumFormatType::DATE || // already date type
2637 eScannedType == SvNumFormatType::DATETIME) && // or date time
2638 nNumericsCnt > 3) // and more than 3 numbers? (31.Dez.94 8:23)
2640 SkipBlanks(rString, nPos);
2641 eScannedType = SvNumFormatType::DATETIME; // !!! it IS date with time
2643 else if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2644 eScannedType != SvNumFormatType::TIME) // except time
2646 return MatchedReturn();
2648 else
2650 SkipBlanks(rString, nPos);
2651 eScannedType = SvNumFormatType::TIME; // !!! it IS a time
2653 if ( !nTimePos )
2655 nTimePos = nStringPos + 1;
2659 if (nPos < rString.getLength())
2661 switch (eScannedType)
2663 case SvNumFormatType::DATE:
2664 if (nMonthPos == 1 && pLoc->getLongDateOrder() == DateOrder::MDY)
2666 // #68232# recognize long date separators like ", " in "September 5, 1999"
2667 if (SkipString( pLoc->getLongDateDaySep(), rString, nPos ))
2669 SkipBlanks( rString, nPos );
2672 else if (nPos == 0 && rString.getLength() == 1 && MayBeIso8601())
2674 if ( (nStringPos == 5 && rString[0] == 'T') ||
2675 (nStringPos == 6 && rString[0] == 'T' && sStrArray[0] == "-"))
2677 // ISO 8601 combined date and time, yyyy-mm-ddThh:mm or -yyyy-mm-ddThh:mm
2678 ++nPos;
2679 bIso8601Tsep = true;
2681 else if (nStringPos == 7 && rString[0] == ':')
2683 // ISO 8601 combined date and time, the time part; we reach
2684 // here if the locale's separator is not ':' so it couldn't
2685 // be detected above in the time block.
2686 if (nNumericsCnt >= 5)
2687 eScannedType = SvNumFormatType::DATETIME;
2688 ++nPos;
2691 break;
2692 case SvNumFormatType::DATETIME:
2693 if (nPos == 0 && rString.getLength() == 1 && MayBeIso8601())
2695 if (nStringPos == 9 && rString[0] == ':')
2697 // ISO 8601 combined date and time, the time part continued.
2698 ++nPos;
2701 #if NF_RECOGNIZE_ISO8601_TIMEZONES
2702 else if (nPos == 0 && rString.getLength() == 1 && nStringPos >= 9 && MayBeIso8601())
2704 // ISO 8601 timezone offset
2705 switch (rString[ 0 ])
2707 case '+':
2708 case '-':
2709 if (nStringPos == nStringsCnt - 2 ||
2710 nStringPos == nStringsCnt - 4)
2712 ++nPos; // yyyy-mm-ddThh:mm[:ss]+xx[[:]yy]
2713 // nTimezonePos needed for GetTimeRef()
2714 if (!nTimezonePos)
2716 nTimezonePos = nStringPos + 1;
2719 break;
2720 case ':':
2721 if (nTimezonePos && nStringPos >= 11 &&
2722 nStringPos == nStringsCnt - 2)
2724 ++nPos; // yyyy-mm-ddThh:mm[:ss]+xx:yy
2726 break;
2729 #endif
2730 break;
2731 default: break;
2735 if (nPos < rString.getLength()) // not everything consumed?
2737 if ( nMatchedAllStrings & ~nMatchedVirgin )
2739 eScannedType = eOldScannedType;
2741 else
2743 return false;
2747 return true;
2752 * Analyze the end
2753 * All gone => true
2754 * else => false
2756 bool ImpSvNumberInputScan::ScanEndString( const OUString& rString )
2758 sal_Int32 nPos = 0;
2760 if ( nMatchedAllStrings )
2761 { // Match against format in any case, so later on for a "1-2-3-4" input
2762 // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2763 // format.
2764 if ( ScanStringNumFor( rString, 0, 0xFFFF ) )
2766 nMatchedAllStrings |= nMatchedEndString;
2768 else
2770 nMatchedAllStrings = 0;
2774 SkipBlanks(rString, nPos);
2775 if (GetDecSep(rString, nPos)) // decimal separator?
2777 if (nDecPos == 1 || nDecPos == 3) // .12.4 or 12.E4.
2779 return MatchedReturn();
2781 else if (nDecPos == 2) // . dup: 12.4.
2783 bool bSignedYear = false;
2784 if (bDecSepInDateSeps || // . also date separator
2785 SkipDatePatternSeparator( nStringsCnt-1, nPos, bSignedYear))
2787 if ( eScannedType != SvNumFormatType::UNDEFINED &&
2788 eScannedType != SvNumFormatType::DATE &&
2789 eScannedType != SvNumFormatType::DATETIME) // already another type
2791 return MatchedReturn();
2793 if (eScannedType == SvNumFormatType::UNDEFINED)
2795 eScannedType = SvNumFormatType::DATE; // !!! it IS a date
2797 SkipBlanks(rString, nPos);
2799 else
2801 return MatchedReturn();
2804 else
2806 nDecPos = 3; // . in end string
2807 SkipBlanks(rString, nPos);
2811 bool bSignDetectedHere = false;
2812 if ( nSign == 0 && // conflict - not signed
2813 eScannedType != SvNumFormatType::DATE) // and not date
2814 //!? catch time too?
2815 { // not signed yet
2816 nSign = GetSign(rString, nPos); // 1- DM
2817 if (bNegCheck) // '(' as sign
2819 return MatchedReturn();
2821 if (nSign)
2823 bSignDetectedHere = true;
2827 SkipBlanks(rString, nPos);
2828 if (bNegCheck && SkipChar(')', rString, nPos)) // skip ')' if appropriate
2830 bNegCheck = false;
2831 SkipBlanks(rString, nPos);
2834 if ( GetCurrency(rString, nPos) ) // currency symbol?
2836 if (eScannedType != SvNumFormatType::UNDEFINED) // currency dup
2838 return MatchedReturn();
2840 else
2842 SkipBlanks(rString, nPos);
2843 eScannedType = SvNumFormatType::CURRENCY;
2844 } // behind currency a '-' is allowed
2845 if (nSign == 0) // not signed yet
2847 nSign = GetSign(rString, nPos); // DM -
2848 SkipBlanks(rString, nPos);
2849 if (bNegCheck) // 3 DM (
2851 return MatchedReturn();
2854 if ( bNegCheck && eScannedType == SvNumFormatType::CURRENCY &&
2855 SkipChar(')', rString, nPos) )
2857 bNegCheck = false; // ')' skipped
2858 SkipBlanks(rString, nPos); // only if currency
2862 if ( SkipChar('%', rString, nPos) ) // 1%
2864 if (eScannedType != SvNumFormatType::UNDEFINED) // already another type
2866 return MatchedReturn();
2868 SkipBlanks(rString, nPos);
2869 eScannedType = SvNumFormatType::PERCENT;
2872 const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
2873 const OUString& rTime = pLoc->getTimeSep();
2874 if ( SkipString(rTime, rString, nPos) ) // 10:
2876 if (nDecPos) // already , => error
2878 return MatchedReturn();
2880 if (eScannedType == SvNumFormatType::DATE && nNumericsCnt > 2) // 31.Dez.94 8:
2882 SkipBlanks(rString, nPos);
2883 eScannedType = SvNumFormatType::DATETIME;
2885 else if (eScannedType != SvNumFormatType::UNDEFINED &&
2886 eScannedType != SvNumFormatType::TIME) // already another type
2888 return MatchedReturn();
2890 else
2892 SkipBlanks(rString, nPos);
2893 eScannedType = SvNumFormatType::TIME;
2895 if ( !nTimePos )
2897 nTimePos = nStringsCnt;
2901 bool bSignedYear = false;
2902 bool bDate = SkipDatePatternSeparator( nStringsCnt-1, nPos, bSignedYear); // 12/31 31.12. 12/31/1999 31.12.1999
2903 if (!bDate)
2905 const OUString& rDate = pFormatter->GetDateSep();
2906 bDate = SkipString( rDate, rString, nPos); // 10. 10- 10/
2908 if (bDate && bSignDetectedHere)
2910 nSign = 0; // 'D-' takes precedence over signed date
2912 if (bDate || ((MayBeIso8601() || MayBeMonthDate())
2913 && SkipChar( '-', rString, nPos)))
2915 if (eScannedType != SvNumFormatType::UNDEFINED &&
2916 eScannedType != SvNumFormatType::DATE) // already another type
2918 return MatchedReturn();
2920 else
2922 SkipBlanks(rString, nPos);
2923 eScannedType = SvNumFormatType::DATE;
2925 short nTmpMonth = GetMonth(rString, nPos); // 10. Jan
2926 if (nMonth && nTmpMonth) // month dup
2928 return MatchedReturn();
2930 if (nTmpMonth)
2932 nMonth = nTmpMonth;
2933 nMonthPos = 3; // month at end
2934 if ( nMonth < 0 )
2936 SkipChar( '.', rString, nPos ); // abbreviated
2938 SkipBlanks(rString, nPos);
2942 const sal_Int32 nMonthStart = nPos;
2943 short nTempMonth = GetMonth(rString, nPos); // 10 Jan
2944 if (nTempMonth)
2946 if (nMonth) // month dup
2948 return MatchedReturn();
2950 if (eScannedType != SvNumFormatType::UNDEFINED &&
2951 eScannedType != SvNumFormatType::DATE) // already another type
2953 return MatchedReturn();
2955 if (nMonthStart > 0) // 10Jan without separator is not a date
2957 eScannedType = SvNumFormatType::DATE;
2958 nMonth = nTempMonth;
2959 nMonthPos = 3; // month at end
2960 if ( nMonth < 0 )
2962 SkipChar( '.', rString, nPos ); // abbreviated
2964 SkipBlanks(rString, nPos);
2966 else
2968 nPos = nMonthStart; // rewind month
2972 sal_Int32 nOrigPos = nPos;
2973 if (GetTimeAmPm(rString, nPos))
2975 if (eScannedType != SvNumFormatType::UNDEFINED &&
2976 eScannedType != SvNumFormatType::TIME &&
2977 eScannedType != SvNumFormatType::DATETIME) // already another type
2979 return MatchedReturn();
2981 else
2983 // If not already scanned as time, 6.78am does not result in 6
2984 // seconds and 78 hundredths in the morning. Keep as suffix.
2985 if (eScannedType != SvNumFormatType::TIME && nDecPos == 2 && nNumericsCnt == 2)
2987 nPos = nOrigPos; // rewind am/pm
2989 else
2991 SkipBlanks(rString, nPos);
2992 if ( eScannedType != SvNumFormatType::DATETIME )
2994 eScannedType = SvNumFormatType::TIME;
3000 if ( bNegCheck && SkipChar(')', rString, nPos) )
3002 if (eScannedType == SvNumFormatType::CURRENCY) // only if currency
3004 bNegCheck = false; // skip ')'
3005 SkipBlanks(rString, nPos);
3007 else
3009 return MatchedReturn();
3013 if ( nPos < rString.getLength() &&
3014 (eScannedType == SvNumFormatType::DATE ||
3015 eScannedType == SvNumFormatType::DATETIME) )
3017 // day of week is just parsed away
3018 sal_Int32 nOldPos = nPos;
3019 const OUString& rSep = pFormatter->GetLocaleData()->getLongDateDayOfWeekSep();
3020 if ( StringContains( rSep, rString, nPos ) )
3022 nPos = nPos + rSep.getLength();
3023 SkipBlanks(rString, nPos);
3025 int nTempDayOfWeek = GetDayOfWeek( rString, nPos );
3026 if ( nTempDayOfWeek )
3028 if ( nPos < rString.getLength() )
3030 if ( nTempDayOfWeek < 0 )
3031 { // short
3032 if ( rString[ nPos ] == '.' )
3034 ++nPos;
3037 SkipBlanks(rString, nPos);
3040 else
3042 nPos = nOldPos;
3046 #if NF_RECOGNIZE_ISO8601_TIMEZONES
3047 if (nPos == 0 && eScannedType == SvNumFormatType::DATETIME &&
3048 rString.getLength() == 1 && rString[ 0 ] == 'Z' && MayBeIso8601())
3050 // ISO 8601 timezone UTC yyyy-mm-ddThh:mmZ
3051 ++nPos;
3053 #endif
3055 if (nPos < rString.getLength()) // everything consumed?
3057 // does input EndString equal EndString in Format?
3058 if ( !ScanStringNumFor( rString, nPos, 0xFFFF ) )
3060 return false;
3064 return true;
3068 bool ImpSvNumberInputScan::ScanStringNumFor( const OUString& rString, // String to scan
3069 sal_Int32 nPos, // Position until which was consumed
3070 sal_uInt16 nString, // Substring of format, 0xFFFF => last
3071 bool bDontDetectNegation) // Suppress sign detection
3073 if ( !mpFormat )
3075 return false;
3077 const ::utl::TransliterationWrapper* pTransliteration = pFormatter->GetTransliteration();
3078 const OUString* pStr;
3079 OUString aString( rString );
3080 bool bFound = false;
3081 bool bFirst = true;
3082 bool bContinue = true;
3083 sal_uInt16 nSub;
3086 // Don't try "lower" subformats ff the very first match was the second
3087 // or third subformat.
3088 nSub = nStringScanNumFor;
3090 { // Step through subformats, first positive, then negative, then
3091 // other, but not the last (text) subformat.
3092 pStr = mpFormat->GetNumForString( nSub, nString, true );
3093 if ( pStr && pTransliteration->isEqual( aString, *pStr ) )
3095 bFound = true;
3096 bContinue = false;
3098 else if ( nSub < 2 )
3100 ++nSub;
3102 else
3104 bContinue = false;
3107 while ( bContinue );
3108 if ( !bFound && bFirst && nPos )
3110 // try remaining substring
3111 bFirst = false;
3112 aString = aString.copy(nPos);
3113 bContinue = true;
3116 while ( bContinue );
3118 if ( !bFound )
3120 if ( !bDontDetectNegation && (nString == 0) &&
3121 !bFirst && (nSign < 0) && mpFormat->IsSecondSubformatRealNegative() )
3123 // simply negated twice? --1
3124 aString = aString.replaceAll(" ", "");
3125 if ( (aString.getLength() == 1) && (aString[0] == '-') )
3127 bFound = true;
3128 nStringScanSign = -1;
3129 nSub = 0; //! not 1
3132 if ( !bFound )
3134 return false;
3137 else if ( !bDontDetectNegation && (nSub == 1) &&
3138 mpFormat->IsSecondSubformatRealNegative() )
3140 // negative
3141 if ( nStringScanSign < 0 )
3143 if ( (nSign < 0) && (nStringScanNumFor != 1) )
3145 nStringScanSign = 1; // triple negated --1 yyy
3148 else if ( nStringScanSign == 0 )
3150 if ( nSign < 0 )
3151 { // nSign and nStringScanSign will be combined later,
3152 // flip sign if doubly negated
3153 if ( (nString == 0) && !bFirst &&
3154 SvNumberformat::HasStringNegativeSign( aString ) )
3156 nStringScanSign = -1; // direct double negation
3158 else if ( mpFormat->IsNegativeWithoutSign() )
3160 nStringScanSign = -1; // indirect double negation
3163 else
3165 nStringScanSign = -1;
3168 else // > 0
3170 nStringScanSign = -1;
3173 nStringScanNumFor = nSub;
3174 return true;
3179 * Recognizes types of number, exponential, fraction, percent, currency, date, time.
3180 * Else text => return false
3182 bool ImpSvNumberInputScan::IsNumberFormatMain( const OUString& rString, // string to be analyzed
3183 const SvNumberformat* pFormat ) // maybe number format set to match against
3185 Reset();
3186 mpFormat = pFormat;
3187 NumberStringDivision( rString ); // breakdown into strings and numbers
3188 if (nStringsCnt >= SV_MAX_COUNT_INPUT_STRINGS) // too many elements
3190 return false; // Njet, Nope, ...
3192 if (nNumericsCnt == 0) // no number in input
3194 if ( nStringsCnt > 0 )
3196 // Here we may change the original, we don't need it anymore.
3197 // This saves copies and ToUpper() in GetLogical() and is faster.
3198 sStrArray[0] = comphelper::string::strip(sStrArray[0], ' ');
3199 OUString& rStrArray = sStrArray[0];
3200 nLogical = GetLogical( rStrArray );
3201 if ( nLogical )
3203 eScannedType = SvNumFormatType::LOGICAL; // !!! it's a BOOLEAN
3204 nMatchedAllStrings &= ~nMatchedVirgin;
3205 return true;
3207 else
3209 return false; // simple text
3212 else
3214 return false; // simple text
3218 sal_uInt16 i = 0; // mark any symbol
3219 sal_uInt16 j = 0; // mark only numbers
3221 switch ( nNumericsCnt )
3223 case 1 : // Exactly 1 number in input
3224 // nStringsCnt >= 1
3225 if (GetNextNumber(i,j)) // i=1,0
3226 { // Number at start
3227 if (eSetType == SvNumFormatType::FRACTION) // Fraction 1 = 1/1
3229 if (i >= nStringsCnt || // no end string nor decimal separator
3230 pFormatter->IsDecimalSep( sStrArray[i]))
3232 eScannedType = SvNumFormatType::FRACTION;
3233 nMatchedAllStrings &= ~nMatchedVirgin;
3234 return true;
3238 else
3239 { // Analyze start string
3240 if (!ScanStartString( sStrArray[i] )) // i=0
3242 return false; // already an error
3244 i++; // next symbol, i=1
3246 GetNextNumber(i,j); // i=1,2
3247 if (eSetType == SvNumFormatType::FRACTION) // Fraction -1 = -1/1
3249 if (nSign && !bNegCheck && // Sign +, -
3250 eScannedType == SvNumFormatType::UNDEFINED && // not date or currency
3251 nDecPos == 0 && // no previous decimal separator
3252 (i >= nStringsCnt || // no end string nor decimal separator
3253 pFormatter->IsDecimalSep( sStrArray[i]))
3256 eScannedType = SvNumFormatType::FRACTION;
3257 nMatchedAllStrings &= ~nMatchedVirgin;
3258 return true;
3261 if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3263 return false;
3265 break;
3266 case 2 : // Exactly 2 numbers in input
3267 // nStringsCnt >= 3
3268 if (!GetNextNumber(i,j)) // i=1,0
3269 { // Analyze start string
3270 if (!ScanStartString( sStrArray[i] ))
3271 return false; // already an error
3272 i++; // i=1
3274 GetNextNumber(i,j); // i=1,2
3275 if ( !ScanMidString( sStrArray[i], i ) )
3277 return false;
3279 i++; // next symbol, i=2,3
3280 GetNextNumber(i,j); // i=3,4
3281 if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3283 return false;
3285 if (eSetType == SvNumFormatType::FRACTION) // -1,200. as fraction
3287 if (!bNegCheck && // no sign '('
3288 eScannedType == SvNumFormatType::UNDEFINED &&
3289 (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end
3292 eScannedType = SvNumFormatType::FRACTION;
3293 nMatchedAllStrings &= ~nMatchedVirgin;
3294 return true;
3297 break;
3298 case 3 : // Exactly 3 numbers in input
3299 // nStringsCnt >= 5
3300 if (!GetNextNumber(i,j)) // i=1,0
3301 { // Analyze start string
3302 if (!ScanStartString( sStrArray[i] ))
3304 return false; // already an error
3306 i++; // i=1
3307 if (nDecPos == 1) // decimal separator at start => error
3309 return false;
3312 GetNextNumber(i,j); // i=1,2
3313 if ( !ScanMidString( sStrArray[i], i ) )
3315 return false;
3317 i++; // i=2,3
3318 if (eScannedType == SvNumFormatType::SCIENTIFIC) // E only at end
3320 return false;
3322 GetNextNumber(i,j); // i=3,4
3323 if ( !ScanMidString( sStrArray[i], i ) )
3325 return false;
3327 i++; // i=4,5
3328 GetNextNumber(i,j); // i=5,6
3329 if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3331 return false;
3333 if (eSetType == SvNumFormatType::FRACTION) // -1,200,100. as fraction
3335 if (!bNegCheck && // no sign '('
3336 eScannedType == SvNumFormatType::UNDEFINED &&
3337 (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end
3340 eScannedType = SvNumFormatType::FRACTION;
3341 nMatchedAllStrings &= ~nMatchedVirgin;
3342 return true;
3345 if ( eScannedType == SvNumFormatType::FRACTION && nDecPos )
3347 return false; // #36857# not a real fraction
3349 break;
3350 default: // More than 3 numbers in input
3351 // nStringsCnt >= 7
3352 if (!GetNextNumber(i,j)) // i=1,0
3353 { // Analyze startstring
3354 if (!ScanStartString( sStrArray[i] ))
3355 return false; // already an error
3356 i++; // i=1
3357 if (nDecPos == 1) // decimal separator at start => error
3358 return false;
3360 GetNextNumber(i,j); // i=1,2
3361 if ( !ScanMidString( sStrArray[i], i ) )
3363 return false;
3365 i++; // i=2,3
3367 sal_uInt16 nThOld = 10; // just not 0 or 1
3368 while (nThOld != nThousand && j < nNumericsCnt-1) // Execute at least one time
3369 // but leave one number.
3370 { // Loop over group separators
3371 nThOld = nThousand;
3372 if (eScannedType == SvNumFormatType::SCIENTIFIC) // E only at end
3374 return false;
3376 GetNextNumber(i,j);
3377 if ( i < nStringsCnt && !ScanMidString( sStrArray[i], i ) )
3379 return false;
3381 i++;
3384 if (eScannedType == SvNumFormatType::DATE || // long date or
3385 eScannedType == SvNumFormatType::TIME || // long time or
3386 eScannedType == SvNumFormatType::UNDEFINED) // long number
3388 for (sal_uInt16 k = j; k < nNumericsCnt-1; k++)
3390 if (eScannedType == SvNumFormatType::SCIENTIFIC) // E only at endd
3392 return false;
3394 GetNextNumber(i,j);
3395 if ( i < nStringsCnt && !ScanMidString( sStrArray[i], i ) )
3397 return false;
3399 i++;
3402 GetNextNumber(i,j);
3403 if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3405 return false;
3407 if (eSetType == SvNumFormatType::FRACTION) // -1,200,100. as fraction
3409 if (!bNegCheck && // no sign '('
3410 eScannedType == SvNumFormatType::UNDEFINED &&
3411 (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end
3414 eScannedType = SvNumFormatType::FRACTION;
3415 nMatchedAllStrings &= ~nMatchedVirgin;
3416 return true;
3419 if ( eScannedType == SvNumFormatType::FRACTION && nDecPos )
3421 return false; // #36857# not a real fraction
3423 break;
3426 if (eScannedType == SvNumFormatType::UNDEFINED)
3428 nMatchedAllStrings &= ~nMatchedVirgin;
3429 // did match including nMatchedUsedAsReturn
3430 bool bDidMatch = (nMatchedAllStrings != 0);
3431 if ( nMatchedAllStrings )
3433 bool bMatch = mpFormat && mpFormat->IsNumForStringElementCountEqual(
3434 nStringScanNumFor, nStringsCnt, nNumericsCnt );
3435 if ( !bMatch )
3437 nMatchedAllStrings = 0;
3440 if ( nMatchedAllStrings )
3442 // A type DEFINED means that no category could be assigned to the
3443 // overall format because of mixed type subformats. Use the scan
3444 // matched subformat's type if any.
3445 SvNumFormatType eForType = eSetType;
3446 if ((eForType == SvNumFormatType::UNDEFINED || eForType == SvNumFormatType::DEFINED) && mpFormat)
3447 eForType = mpFormat->GetNumForInfoScannedType( nStringScanNumFor);
3448 if (eForType != SvNumFormatType::UNDEFINED && eForType != SvNumFormatType::DEFINED)
3449 eScannedType = eForType;
3450 else
3451 eScannedType = SvNumFormatType::NUMBER;
3453 else if ( bDidMatch )
3455 return false;
3457 else
3459 eScannedType = SvNumFormatType::NUMBER;
3460 // everything else should have been recognized by now
3463 else if ( eScannedType == SvNumFormatType::DATE )
3465 // the very relaxed date input checks may interfere with a preset format
3466 nMatchedAllStrings &= ~nMatchedVirgin;
3467 bool bWasReturn = ((nMatchedAllStrings & nMatchedUsedAsReturn) != 0);
3468 if ( nMatchedAllStrings )
3470 bool bMatch = mpFormat && mpFormat->IsNumForStringElementCountEqual(
3471 nStringScanNumFor, nStringsCnt, nNumericsCnt );
3472 if ( !bMatch )
3474 nMatchedAllStrings = 0;
3477 if ( nMatchedAllStrings )
3479 // A type DEFINED means that no category could be assigned to the
3480 // overall format because of mixed type subformats. Do not override
3481 // the scanned type in this case. Otherwise in IsNumberFormat() the
3482 // first numeric particle would be accepted as number.
3483 SvNumFormatType eForType = eSetType;
3484 if ((eForType == SvNumFormatType::UNDEFINED || eForType == SvNumFormatType::DEFINED) && mpFormat)
3485 eForType = mpFormat->GetNumForInfoScannedType( nStringScanNumFor);
3486 if (eForType != SvNumFormatType::UNDEFINED && eForType != SvNumFormatType::DEFINED)
3487 eScannedType = eForType;
3489 else if ( bWasReturn )
3491 return false;
3494 else
3496 nMatchedAllStrings = 0; // reset flag to no substrings matched
3498 return true;
3503 * Return true or false depending on the nMatched... state and remember usage
3505 bool ImpSvNumberInputScan::MatchedReturn()
3507 if ( nMatchedAllStrings & ~nMatchedVirgin )
3509 nMatchedAllStrings |= nMatchedUsedAsReturn;
3510 return true;
3512 return false;
3517 * Initialize uppercase months and weekdays
3519 void ImpSvNumberInputScan::InitText()
3521 sal_Int32 j, nElems;
3522 const CharClass* pChrCls = pFormatter->GetCharClass();
3523 const CalendarWrapper* pCal = pFormatter->GetCalendar();
3525 pUpperMonthText.reset();
3526 pUpperAbbrevMonthText.reset();
3527 css::uno::Sequence< css::i18n::CalendarItem2 > xElems = pCal->getMonths();
3528 nElems = xElems.getLength();
3529 pUpperMonthText.reset( new OUString[nElems] );
3530 pUpperAbbrevMonthText.reset( new OUString[nElems] );
3531 for ( j = 0; j < nElems; j++ )
3533 pUpperMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3534 pUpperAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3537 pUpperGenitiveMonthText.reset();
3538 pUpperGenitiveAbbrevMonthText.reset();
3539 xElems = pCal->getGenitiveMonths();
3540 bScanGenitiveMonths = (nElems != xElems.getLength());
3541 nElems = xElems.getLength();
3542 pUpperGenitiveMonthText.reset( new OUString[nElems] );
3543 pUpperGenitiveAbbrevMonthText.reset( new OUString[nElems] );
3544 for ( j = 0; j < nElems; j++ )
3546 pUpperGenitiveMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3547 pUpperGenitiveAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3548 if (!bScanGenitiveMonths &&
3549 (pUpperGenitiveMonthText[j] != pUpperMonthText[j] ||
3550 pUpperGenitiveAbbrevMonthText[j] != pUpperAbbrevMonthText[j]))
3552 bScanGenitiveMonths = true;
3556 pUpperPartitiveMonthText.reset();
3557 pUpperPartitiveAbbrevMonthText.reset();
3558 xElems = pCal->getPartitiveMonths();
3559 bScanPartitiveMonths = (nElems != xElems.getLength());
3560 nElems = xElems.getLength();
3561 pUpperPartitiveMonthText.reset( new OUString[nElems] );
3562 pUpperPartitiveAbbrevMonthText.reset( new OUString[nElems] );
3563 for ( j = 0; j < nElems; j++ )
3565 pUpperPartitiveMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3566 pUpperPartitiveAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3567 if (!bScanPartitiveMonths &&
3568 (pUpperPartitiveMonthText[j] != pUpperGenitiveMonthText[j] ||
3569 pUpperPartitiveAbbrevMonthText[j] != pUpperGenitiveAbbrevMonthText[j]))
3571 bScanPartitiveMonths = true;
3575 pUpperDayText.reset();
3576 pUpperAbbrevDayText.reset();
3577 xElems = pCal->getDays();
3578 nElems = xElems.getLength();
3579 pUpperDayText.reset( new OUString[nElems] );
3580 pUpperAbbrevDayText.reset( new OUString[nElems] );
3581 for ( j = 0; j < nElems; j++ )
3583 pUpperDayText[j] = pChrCls->uppercase( xElems[j].FullName );
3584 pUpperAbbrevDayText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3587 bTextInitialized = true;
3592 * MUST be called if International/Locale is changed
3594 void ImpSvNumberInputScan::ChangeIntl()
3596 sal_Unicode cDecSep = pFormatter->GetNumDecimalSep()[0];
3597 bDecSepInDateSeps = ( cDecSep == '-' ||
3598 cDecSep == pFormatter->GetDateSep()[0] );
3599 if (!bDecSepInDateSeps)
3601 sal_Unicode cDecSepAlt = pFormatter->GetNumDecimalSepAlt().toChar();
3602 bDecSepInDateSeps = cDecSepAlt && (cDecSepAlt == '-' || cDecSepAlt == pFormatter->GetDateSep()[0]);
3604 bTextInitialized = false;
3605 aUpperCurrSymbol.clear();
3606 InvalidateDateAcceptancePatterns();
3610 void ImpSvNumberInputScan::InvalidateDateAcceptancePatterns()
3612 if (sDateAcceptancePatterns.getLength())
3614 sDateAcceptancePatterns = css::uno::Sequence< OUString >();
3619 void ImpSvNumberInputScan::ChangeNullDate( const sal_uInt16 Day,
3620 const sal_uInt16 Month,
3621 const sal_Int16 Year )
3623 if ( pNullDate )
3625 *pNullDate = Date(Day, Month, Year);
3627 else
3629 pNullDate.reset(new Date(Day, Month, Year));
3635 * Does rString represent a number (also date, time et al)
3637 bool ImpSvNumberInputScan::IsNumberFormat( const OUString& rString, // string to be analyzed
3638 SvNumFormatType& F_Type, // IN: old type, OUT: new type
3639 double& fOutNumber, // OUT: number if convertible
3640 const SvNumberformat* pFormat ) // maybe a number format to match against
3642 OUString aString;
3643 bool res; // return value
3644 sal_uInt16 k;
3645 eSetType = F_Type; // old type set
3647 if ( !rString.getLength() )
3649 res = false;
3651 else if (rString.getLength() > 308) // arbitrary
3653 res = false;
3655 else
3657 // NoMoreUpperNeeded, all comparisons on UpperCase
3658 aString = pFormatter->GetCharClass()->uppercase( rString );
3659 // convert native number to ASCII if necessary
3660 TransformInput(pFormatter, aString);
3661 res = IsNumberFormatMain( aString, pFormat );
3664 if (res)
3666 // Accept signed date only for ISO date with at least four digits in
3667 // year to not have an input of -M-D-Y arbitrarily recognized. The
3668 // final order is only determined in GetDateRef().
3669 // Also accept for Y/M/D date pattern match, i.e. if the first number
3670 // is year.
3671 // Accept only if the year immediately follows the sign character with
3672 // no space in between.
3673 if (nSign && (eScannedType == SvNumFormatType::DATE ||
3674 eScannedType == SvNumFormatType::DATETIME) && mbEraCE == kDefaultEra &&
3675 (IsDatePatternNumberOfType(0,'Y') || (MayBeIso8601() && sStrArray[nNums[0]].getLength() >= 4)))
3677 const sal_Unicode c = sStrArray[0][sStrArray[0].getLength()-1];
3678 if (c == '-' || c == '+')
3680 // A '+' sign doesn't change the era.
3681 if (nSign < 0)
3682 mbEraCE = false; // BCE
3683 nSign = 0;
3686 if ( bNegCheck || // ')' not found for '('
3687 (nSign && (eScannedType == SvNumFormatType::DATE ||
3688 eScannedType == SvNumFormatType::DATETIME))) // signed date/datetime
3690 res = false;
3692 else
3693 { // check count of partial number strings
3694 switch (eScannedType)
3696 case SvNumFormatType::PERCENT:
3697 case SvNumFormatType::CURRENCY:
3698 case SvNumFormatType::NUMBER:
3699 if (nDecPos == 1) // .05
3701 // matched MidStrings function like group separators
3702 if ( nMatchedAllStrings )
3704 nThousand = nNumericsCnt - 1;
3706 else if ( nNumericsCnt != 1 )
3708 res = false;
3711 else if (nDecPos == 2) // 1.05
3713 // matched MidStrings function like group separators
3714 if ( nMatchedAllStrings )
3716 nThousand = nNumericsCnt - 1;
3718 else if ( nNumericsCnt != nThousand+2 )
3720 res = false;
3723 else // 1,100 or 1,100.
3725 // matched MidStrings function like group separators
3726 if ( nMatchedAllStrings )
3728 nThousand = nNumericsCnt - 1;
3730 else if ( nNumericsCnt != nThousand+1 )
3732 res = false;
3735 break;
3737 case SvNumFormatType::SCIENTIFIC: // 1.0e-2
3738 if (nDecPos == 1) // .05
3740 if (nNumericsCnt != 2)
3742 res = false;
3745 else if (nDecPos == 2) // 1.05
3747 if (nNumericsCnt != nThousand+3)
3749 res = false;
3752 else // 1,100 or 1,100.
3754 if (nNumericsCnt != nThousand+2)
3756 res = false;
3759 break;
3761 case SvNumFormatType::DATE:
3762 if (nMonth < 0 && nDayOfWeek < 0 && nNumericsCnt == 3)
3764 // If both, short month name and day of week name were
3765 // detected, and also numbers for full date, assume that we
3766 // have a day of week instead of month name.
3767 nMonth = 0;
3768 nMonthPos = 0;
3770 if (nMonth)
3771 { // month name and numbers
3772 if (nNumericsCnt > 2)
3774 res = false;
3777 else
3779 if (nNumericsCnt > 3)
3781 res = false;
3783 else
3785 // Even if a date pattern was matched, for abbreviated
3786 // pattern like "D.M." an input of "D.M. #" was
3787 // accepted because # could had been a time. Here we do
3788 // not have a combined date/time input though and #
3789 // would be taken as Year in this example, which it is
3790 // not. The count of numbers in pattern must match the
3791 // count of numbers in input.
3792 res = (GetDatePatternNumbers() == nNumericsCnt)
3793 || IsAcceptableIso8601() || nMatchedAllStrings;
3796 break;
3798 case SvNumFormatType::TIME:
3799 if (nDecPos)
3800 { // hundredth seconds included
3801 if (nNumericsCnt > 4)
3803 res = false;
3806 else
3808 if (nNumericsCnt > 3)
3810 res = false;
3813 break;
3815 case SvNumFormatType::DATETIME:
3816 if (nMonth < 0 && nDayOfWeek < 0 && nNumericsCnt >= 5)
3818 // If both, abbreviated month name and day of week name
3819 // were detected, and also at least numbers for full date
3820 // plus time including minutes, assume that we have a day
3821 // of week instead of month name.
3822 nMonth = 0;
3823 nMonthPos = 0;
3825 if (nMonth)
3826 { // month name and numbers
3827 if (nDecPos)
3828 { // hundredth seconds included
3829 if (nNumericsCnt > 6)
3831 res = false;
3834 else
3836 if (nNumericsCnt > 5)
3838 res = false;
3842 else
3844 if (nDecPos)
3845 { // hundredth seconds included
3846 if (nNumericsCnt > 7)
3848 res = false;
3851 else
3853 if (nNumericsCnt > 6)
3855 res = false;
3858 if (res)
3860 res = IsAcceptedDatePattern( nNums[0]) || MayBeIso8601() || nMatchedAllStrings;
3863 break;
3865 default:
3866 break;
3867 } // switch
3868 } // else
3869 } // if (res)
3871 OUStringBuffer sResString;
3873 if (res)
3874 { // we finally have a number
3875 switch (eScannedType)
3877 case SvNumFormatType::LOGICAL:
3878 if (nLogical == 1)
3880 fOutNumber = 1.0; // True
3882 else if (nLogical == -1)
3884 fOutNumber = 0.0; // False
3886 else
3888 res = false; // Oops
3890 break;
3892 case SvNumFormatType::PERCENT:
3893 case SvNumFormatType::CURRENCY:
3894 case SvNumFormatType::NUMBER:
3895 case SvNumFormatType::SCIENTIFIC:
3896 case SvNumFormatType::DEFINED: // if no category detected handle as number
3897 if ( nDecPos == 1 ) // . at start
3899 sResString.append("0.");
3902 for ( k = 0; k <= nThousand; k++)
3904 sResString.append(sStrArray[nNums[k]]); // integer part
3906 if ( nDecPos == 2 && k < nNumericsCnt ) // . somewhere
3908 sResString.append('.');
3909 sal_uInt16 nStop = (eScannedType == SvNumFormatType::SCIENTIFIC ?
3910 nNumericsCnt-1 : nNumericsCnt);
3911 for ( ; k < nStop; k++)
3913 sResString.append(sStrArray[nNums[k]]); // fractional part
3917 if (eScannedType != SvNumFormatType::SCIENTIFIC)
3919 fOutNumber = StringToDouble(sResString.makeStringAndClear());
3921 else
3922 { // append exponent
3923 sResString.append('E');
3924 if ( nESign == -1 )
3926 sResString.append('-');
3928 sResString.append(sStrArray[nNums[nNumericsCnt-1]]);
3929 rtl_math_ConversionStatus eStatus;
3930 fOutNumber = ::rtl::math::stringToDouble( sResString.makeStringAndClear(), '.', ',', &eStatus );
3931 if ( eStatus == rtl_math_ConversionStatus_OutOfRange )
3933 F_Type = SvNumFormatType::TEXT; // overflow/underflow -> Text
3934 if (nESign == -1)
3936 fOutNumber = 0.0;
3938 else
3940 fOutNumber = DBL_MAX;
3942 return true;
3946 if ( nStringScanSign )
3948 if ( nSign )
3950 nSign *= nStringScanSign;
3952 else
3954 nSign = nStringScanSign;
3957 if ( nSign < 0 )
3959 fOutNumber = -fOutNumber;
3962 if (eScannedType == SvNumFormatType::PERCENT)
3964 fOutNumber/= 100.0;
3966 break;
3968 case SvNumFormatType::FRACTION:
3969 if (nNumericsCnt == 1)
3971 fOutNumber = StringToDouble(sStrArray[nNums[0]]);
3973 else if (nNumericsCnt == 2)
3975 if (nThousand == 1)
3977 sResString = sStrArray[nNums[0]];
3978 sResString.append(sStrArray[nNums[1]]); // integer part
3979 fOutNumber = StringToDouble(sResString.makeStringAndClear());
3981 else
3983 double fNumerator = StringToDouble(sStrArray[nNums[0]]);
3984 double fDenominator = StringToDouble(sStrArray[nNums[1]]);
3985 if (fDenominator != 0.0)
3987 fOutNumber = fNumerator/fDenominator;
3989 else
3991 res = false;
3995 else // nNumericsCnt > 2
3997 k = 1;
3998 sResString = sStrArray[nNums[0]];
3999 if (nThousand > 0)
4001 for (; k <= nThousand; k++)
4003 sResString.append(sStrArray[nNums[k]]);
4006 fOutNumber = StringToDouble(sResString.makeStringAndClear());
4008 if (k == nNumericsCnt-2)
4010 double fNumerator = StringToDouble(sStrArray[nNums[k]]);
4011 double fDenominator = StringToDouble(sStrArray[nNums[k + 1]]);
4012 if (fDenominator != 0.0)
4014 fOutNumber += fNumerator/fDenominator;
4016 else
4018 res = false;
4023 if ( nStringScanSign )
4025 if ( nSign )
4027 nSign *= nStringScanSign;
4029 else
4031 nSign = nStringScanSign;
4034 if ( nSign < 0 )
4036 fOutNumber = -fOutNumber;
4038 break;
4040 case SvNumFormatType::TIME:
4041 res = GetTimeRef(fOutNumber, 0, nNumericsCnt);
4042 if ( nSign < 0 )
4044 fOutNumber = -fOutNumber;
4046 break;
4048 case SvNumFormatType::DATE:
4049 res = GetDateRef( fOutNumber, k );
4050 break;
4052 case SvNumFormatType::DATETIME:
4053 res = GetDateRef( fOutNumber, k );
4054 if ( res )
4056 double fTime;
4057 res = GetTimeRef( fTime, k, nNumericsCnt - k );
4058 fOutNumber += fTime;
4060 break;
4062 default:
4063 SAL_WARN( "svl.numbers", "Some number recognized but what's it?" );
4064 fOutNumber = 0.0;
4065 break;
4069 if (res) // overflow/underflow -> Text
4071 if (fOutNumber < -DBL_MAX) // -1.7E308
4073 F_Type = SvNumFormatType::TEXT;
4074 fOutNumber = -DBL_MAX;
4075 return true;
4077 else if (fOutNumber > DBL_MAX) // 1.7E308
4079 F_Type = SvNumFormatType::TEXT;
4080 fOutNumber = DBL_MAX;
4081 return true;
4085 if (!res)
4087 eScannedType = SvNumFormatType::TEXT;
4088 fOutNumber = 0.0;
4091 F_Type = eScannedType;
4092 return res;
4095 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */