nss: upgrade to release 3.73
[LibreOffice.git] / svl / source / numbers / zforfind.cxx
blob9f90dd28b73217b7744a9f90b15879c27811f044
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 <dtoa.h>
22 #include <float.h>
23 #include <comphelper/string.hxx>
24 #include <sal/log.hxx>
25 #include <tools/date.hxx>
26 #include <rtl/math.hxx>
27 #include <rtl/character.hxx>
28 #include <unotools/charclass.hxx>
29 #include <unotools/calendarwrapper.hxx>
30 #include <unotools/localedatawrapper.hxx>
31 #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
32 #include <com/sun/star/i18n/LocaleCalendar2.hpp>
33 #include <unotools/digitgroupingiterator.hxx>
34 #include <comphelper/sequence.hxx>
36 #include <svl/zforlist.hxx>
37 #include "zforscan.hxx"
38 #include <svl/zformat.hxx>
40 #include <memory>
42 #include "zforfind.hxx"
44 #ifndef DBG_UTIL
45 #define NF_TEST_CALENDAR 0
46 #else
47 #define NF_TEST_CALENDAR 0
48 #endif
49 #if NF_TEST_CALENDAR
50 #include <comphelper/processfactory.hxx>
51 #include <com/sun/star/i18n/XCalendar4.hpp>
52 #endif
55 const sal_uInt8 ImpSvNumberInputScan::nMatchedEndString = 0x01;
56 const sal_uInt8 ImpSvNumberInputScan::nMatchedMidString = 0x02;
57 const sal_uInt8 ImpSvNumberInputScan::nMatchedStartString = 0x04;
58 const sal_uInt8 ImpSvNumberInputScan::nMatchedVirgin = 0x08;
59 const sal_uInt8 ImpSvNumberInputScan::nMatchedUsedAsReturn = 0x10;
61 /* It is not clear how we want timezones to be handled. Convert them to local
62 * time isn't wanted, as it isn't done in any other place and timezone
63 * information isn't stored anywhere. Ignoring them and pretending local time
64 * may be wrong too and might not be what the user expects. Keep the input as
65 * string so that no information is lost.
66 * Anyway, defining NF_RECOGNIZE_ISO8601_TIMEZONES to 1 would be the way how it
67 * would work, together with the nTimezonePos handling in GetTimeRef(). */
68 #define NF_RECOGNIZE_ISO8601_TIMEZONES 0
70 const sal_Unicode cNoBreakSpace = 0xA0;
71 const sal_Unicode cNarrowNoBreakSpace = 0x202F;
72 const bool kDefaultEra = true; // Gregorian CE, positive year
74 ImpSvNumberInputScan::ImpSvNumberInputScan( SvNumberFormatter* pFormatterP )
76 bTextInitialized( false ),
77 bScanGenitiveMonths( false ),
78 bScanPartitiveMonths( false ),
79 eScannedType( SvNumFormatType::UNDEFINED ),
80 eSetType( SvNumFormatType::UNDEFINED )
82 pFormatter = pFormatterP;
83 pNullDate.reset( new Date(30,12,1899) );
84 nYear2000 = SvNumberFormatter::GetYear2000Default();
85 Reset();
86 ChangeIntl();
90 ImpSvNumberInputScan::~ImpSvNumberInputScan()
95 void ImpSvNumberInputScan::Reset()
97 mpFormat = nullptr;
98 nMonth = 0;
99 nMonthPos = 0;
100 nDayOfWeek = 0;
101 nTimePos = 0;
102 nSign = 0;
103 nESign = 0;
104 nDecPos = 0;
105 bNegCheck = false;
106 nStringsCnt = 0;
107 nNumericsCnt = 0;
108 nThousand = 0;
109 eScannedType = SvNumFormatType::UNDEFINED;
110 nAmPm = 0;
111 nPosThousandString = 0;
112 nLogical = 0;
113 mbEraCE = kDefaultEra;
114 nStringScanNumFor = 0;
115 nStringScanSign = 0;
116 nMatchedAllStrings = nMatchedVirgin;
117 nMayBeIso8601 = 0;
118 bIso8601Tsep = false;
119 nMayBeMonthDate = 0;
120 nAcceptedDatePattern = -2;
121 nDatePatternStart = 0;
122 nDatePatternNumbers = 0;
124 for (sal_uInt32 i = 0; i < SV_MAX_COUNT_INPUT_STRINGS; i++)
126 IsNum[i] = false;
127 nNums[i] = 0;
131 // native number transliteration if necessary
132 static void TransformInput( SvNumberFormatter const * pFormatter, OUString& rStr )
134 sal_Int32 nPos, nLen;
135 for ( nPos = 0, nLen = rStr.getLength(); nPos < nLen; ++nPos )
137 if ( 256 <= rStr[ nPos ] &&
138 pFormatter->GetCharClass()->isDigit( rStr, nPos ) )
140 break;
143 if ( nPos < nLen )
145 rStr = pFormatter->GetNatNum()->getNativeNumberString( rStr,
146 pFormatter->GetLanguageTag().getLocale(), 0 );
152 * Only simple unsigned floating point values without any error detection,
153 * decimal separator has to be '.'
155 double ImpSvNumberInputScan::StringToDouble( const OUString& rStr, bool bForceFraction )
157 std::unique_ptr<char[]> bufInHeap;
158 constexpr int bufOnStackSize = 256;
159 char bufOnStack[bufOnStackSize];
160 char* buf = bufOnStack;
161 const sal_Int32 bufsize = rStr.getLength() + (bForceFraction ? 2 : 1);
162 if (bufsize > bufOnStackSize)
164 bufInHeap = std::make_unique<char[]>(bufsize);
165 buf = bufInHeap.get();
167 char* p = buf;
168 if (bForceFraction)
169 *p++ = '.';
170 for (sal_Int32 nPos = 0; nPos < rStr.getLength(); ++nPos)
172 sal_Unicode c = rStr[nPos];
173 if (c == '.' || (c >= '0' && c <= '9'))
174 *p++ = static_cast<char>(c);
175 else
176 break;
178 *p = '\0';
180 return strtod_nolocale(buf, nullptr);
183 namespace {
186 * Splits up the input into numbers and strings for further processing
187 * (by the Turing machine).
189 * Starting state = GetChar
190 * ---------------+-------------------+-----------------------------+---------------
191 * Old State | Character read | Event | New state
192 * ---------------+-------------------+-----------------------------+---------------
193 * GetChar | Number | Symbol = Character | GetValue
194 * | Else | Symbol = Character | GetString
195 * ---------------|-------------------+-----------------------------+---------------
196 * GetValue | Number | Symbol = Symbol + Character | GetValue
197 * | Else | Dec(CharPos) | Stop
198 * ---------------+-------------------+-----------------------------+---------------
199 * GetString | Number | Dec(CharPos) | Stop
200 * | Else | Symbol = Symbol + Character | GetString
201 * ---------------+-------------------+-----------------------------+---------------
203 enum ScanState // States of the Turing machine
205 SsStop = 0,
206 SsStart = 1,
207 SsGetValue = 2,
208 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 for (;;)
224 cToken = *pHere;
225 if (cToken == 0 || eState == SsStop)
226 break;
227 pHere++;
228 switch (eState)
230 case SsStart:
231 if ( rtl::isAsciiDigit( cToken ) )
233 eState = SsGetValue;
234 isNumber = true;
236 else
238 eState = SsGetString;
240 nChars++;
241 break;
242 case SsGetValue:
243 if ( rtl::isAsciiDigit( cToken ) )
245 nChars++;
247 else
249 eState = SsStop;
250 pHere--;
252 break;
253 case SsGetString:
254 if ( !rtl::isAsciiDigit( cToken ) )
256 nChars++;
258 else
260 eState = SsStop;
261 pHere--;
263 break;
264 default:
265 break;
266 } // switch
267 } // while
269 if ( nChars )
271 rSymbol = OUString( pStr, nChars );
273 else
275 rSymbol.clear();
278 pStr = pHere;
280 return isNumber;
284 // FIXME: should be grouping; it is only used though in case nStringsCnt is
285 // near SV_MAX_COUNT_INPUT_STRINGS, in NumberStringDivision().
287 bool ImpSvNumberInputScan::SkipThousands( const sal_Unicode*& pStr,
288 OUString& rSymbol ) const
290 bool res = false;
291 OUStringBuffer sBuff(rSymbol);
292 sal_Unicode cToken;
293 const OUString& rThSep = pFormatter->GetNumThousandSep();
294 const sal_Unicode* pHere = pStr;
295 ScanState eState = SsStart;
296 sal_Int32 nCounter = 0; // counts 3 digits
298 for (;;)
300 cToken = *pHere;
301 if (cToken == 0 || eState == SsStop)
302 break;
303 pHere++;
304 switch (eState)
306 case SsStart:
307 if ( StringPtrContains( rThSep, pHere-1, 0 ) )
309 nCounter = 0;
310 eState = SsGetValue;
311 pHere += rThSep.getLength() - 1;
313 else
315 eState = SsStop;
316 pHere--;
318 break;
319 case SsGetValue:
320 if ( rtl::isAsciiDigit( cToken ) )
322 sBuff.append(cToken);
323 nCounter++;
324 if (nCounter == 3)
326 eState = SsStart;
327 res = true; // .000 combination found
330 else
332 eState = SsStop;
333 pHere--;
335 break;
336 default:
337 break;
338 } // switch
339 } // while
341 if (eState == SsGetValue) // break with less than 3 digits
343 if ( nCounter )
345 sBuff.remove( sBuff.getLength() - nCounter, nCounter );
347 pHere -= nCounter + rThSep.getLength(); // put back ThSep also
349 rSymbol = sBuff.makeStringAndClear();
350 pStr = pHere;
352 return res;
356 void ImpSvNumberInputScan::NumberStringDivision( const OUString& rString )
358 const sal_Unicode* pStr = rString.getStr();
359 const sal_Unicode* const pEnd = pStr + rString.getLength();
360 while ( pStr < pEnd && nStringsCnt < SV_MAX_COUNT_INPUT_STRINGS )
362 if ( NextNumberStringSymbol( pStr, sStrArray[nStringsCnt] ) )
363 { // Number
364 IsNum[nStringsCnt] = true;
365 nNums[nNumericsCnt] = nStringsCnt;
366 nNumericsCnt++;
367 if (nStringsCnt >= SV_MAX_COUNT_INPUT_STRINGS - 7 &&
368 nPosThousandString == 0) // Only once
370 if ( SkipThousands( pStr, sStrArray[nStringsCnt] ) )
372 nPosThousandString = nStringsCnt;
376 else
378 IsNum[nStringsCnt] = false;
380 nStringsCnt++;
386 * Whether rString contains rWhat at nPos
388 bool ImpSvNumberInputScan::StringContainsImpl( const OUString& rWhat,
389 const OUString& rString, sal_Int32 nPos )
391 if ( nPos + rWhat.getLength() <= rString.getLength() )
393 return StringPtrContainsImpl( rWhat, rString.getStr(), nPos );
395 return false;
400 * Whether pString contains rWhat at nPos
402 bool ImpSvNumberInputScan::StringPtrContainsImpl( const OUString& rWhat,
403 const sal_Unicode* pString, sal_Int32 nPos )
405 if ( rWhat.isEmpty() )
407 return false;
409 const sal_Unicode* pWhat = rWhat.getStr();
410 const sal_Unicode* const pEnd = pWhat + rWhat.getLength();
411 const sal_Unicode* pStr = pString + nPos;
412 while ( pWhat < pEnd )
414 if ( *pWhat != *pStr )
416 return false;
418 pWhat++;
419 pStr++;
421 return true;
426 * Whether rString contains word rWhat at nPos
428 bool ImpSvNumberInputScan::StringContainsWord( const OUString& rWhat,
429 const OUString& rString, sal_Int32 nPos ) const
431 if (rWhat.isEmpty() || rString.getLength() < nPos + rWhat.getLength())
432 return false;
434 if (StringPtrContainsImpl( rWhat, rString.getStr(), nPos))
436 nPos += rWhat.getLength();
437 if (nPos == rString.getLength())
438 return true; // word at end of string
440 /* TODO: we COULD invoke bells and whistles word break iterator to find
441 * the next boundary, but really ... this is called for date input, so
442 * how many languages do not separate the day and month names in some
443 * form? */
445 // Check simple ASCII first before invoking i18n or anything else.
446 const sal_Unicode c = rString[nPos];
448 // Common separating ASCII characters in date context.
449 switch (c)
451 case ' ':
452 case '-':
453 case '.':
454 case '/':
455 return true;
456 default:
457 ; // nothing
460 if (rtl::isAsciiAlphanumeric( c ))
461 return false; // Alpha or numeric is not word gap.
463 sal_Int32 nIndex = nPos;
464 rString.iterateCodePoints( &nIndex);
465 if (nPos+1 < nIndex)
466 return true; // Surrogate, assume these to be new words.
468 const sal_Int32 nType = pFormatter->GetCharClass()->getCharacterType( rString, nPos);
469 using namespace ::com::sun::star::i18n;
471 if ((nType & (KCharacterType::UPPER | KCharacterType::LOWER | KCharacterType::DIGIT)) != 0)
472 return false; // Alpha or numeric is not word gap.
474 if (nType & KCharacterType::LETTER)
475 return true; // Letter other than alpha is new word. (Is it?)
477 return true; // Catch all remaining as gap until we know better.
480 return false;
485 * Skips the supplied char
487 inline bool ImpSvNumberInputScan::SkipChar( sal_Unicode c, const OUString& rString,
488 sal_Int32& nPos )
490 if ((nPos < rString.getLength()) && (rString[nPos] == c))
492 nPos++;
493 return true;
495 return false;
500 * Skips blanks
502 inline bool ImpSvNumberInputScan::SkipBlanks( const OUString& rString,
503 sal_Int32& nPos )
505 sal_Int32 nHere = nPos;
506 if ( nPos < rString.getLength() )
508 const sal_Unicode* p = rString.getStr() + nPos;
509 while ( *p == ' ' || *p == cNoBreakSpace || *p == cNarrowNoBreakSpace )
511 nPos++;
512 p++;
515 return nHere < nPos;
520 * jump over rWhat in rString at nPos
522 inline bool ImpSvNumberInputScan::SkipString( const OUString& rWhat,
523 const OUString& rString, sal_Int32& nPos )
525 if ( StringContains( rWhat, rString, nPos ) )
527 nPos = nPos + rWhat.getLength();
528 return true;
530 return false;
535 * Recognizes exactly ,111 in {3} and {3,2} or ,11 in {3,2} grouping
537 inline bool ImpSvNumberInputScan::GetThousandSep( const OUString& rString,
538 sal_Int32& nPos,
539 sal_uInt16 nStringPos ) const
541 const OUString& rSep = pFormatter->GetNumThousandSep();
542 // Is it an ordinary space instead of a no-break space?
543 bool bSpaceBreak = (rSep[0] == cNoBreakSpace || rSep[0] == cNarrowNoBreakSpace) &&
544 rString[0] == u' ' &&
545 rSep.getLength() == 1 && rString.getLength() == 1;
546 if (!((rString == rSep || bSpaceBreak) && // nothing else
547 nStringPos < nStringsCnt - 1 && // safety first!
548 IsNum[ nStringPos + 1 ] )) // number follows
550 return false; // no? => out
553 utl::DigitGroupingIterator aGrouping( pFormatter->GetLocaleData()->getDigitGrouping());
554 // Match ,### in {3} or ,## in {3,2}
555 /* FIXME: this could be refined to match ,## in {3,2} only if ,##,## or
556 * ,##,### and to match ,### in {3,2} only if it's the last. However,
557 * currently there is no track kept where group separators occur. In {3,2}
558 * #,###,### and #,##,## would be valid input, which maybe isn't even bad
559 * for #,###,###. Other combinations such as #,###,## maybe not. */
560 sal_Int32 nLen = sStrArray[ nStringPos + 1 ].getLength();
561 if (nLen == aGrouping.get() || // with 3 (or so) digits
562 nLen == aGrouping.advance().get() || // or with 2 (or 3 or so) digits
563 nPosThousandString == nStringPos + 1 ) // or concatenated
565 nPos = nPos + rSep.getLength();
566 return true;
568 return false;
573 * Conversion of text to logical value
574 * "true" => 1:
575 * "false"=> -1:
576 * else => 0:
578 short ImpSvNumberInputScan::GetLogical( const OUString& rString ) const
580 short res;
582 const ImpSvNumberformatScan* pFS = pFormatter->GetFormatScanner();
583 if ( rString == pFS->GetTrueString() )
585 res = 1;
587 else if ( rString == pFS->GetFalseString() )
589 res = -1;
591 else
593 res = 0;
595 return res;
600 * Converts a string containing a month name (JAN, January) at nPos into the
601 * month number (negative if abbreviated), returns 0 if nothing found
603 short ImpSvNumberInputScan::GetMonth( const OUString& rString, sal_Int32& nPos )
605 short res = 0; // no month found
607 if (rString.getLength() > nPos) // only if needed
609 if ( !bTextInitialized )
611 InitText();
613 sal_Int16 nMonths = pFormatter->GetCalendar()->getNumberOfMonthsInYear();
614 for ( sal_Int16 i = 0; i < nMonths; i++ )
616 if ( bScanGenitiveMonths && StringContainsWord( pUpperGenitiveMonthText[i], rString, nPos ) )
617 { // genitive full names first
618 nPos = nPos + pUpperGenitiveMonthText[i].getLength();
619 res = i + 1;
620 break; // for
622 else if ( bScanGenitiveMonths && StringContainsWord( pUpperGenitiveAbbrevMonthText[i], rString, nPos ) )
623 { // genitive abbreviated
624 nPos = nPos + pUpperGenitiveAbbrevMonthText[i].getLength();
625 res = sal::static_int_cast< short >(-(i+1)); // negative
626 break; // for
628 else if ( bScanPartitiveMonths && StringContainsWord( pUpperPartitiveMonthText[i], rString, nPos ) )
629 { // partitive full names
630 nPos = nPos + pUpperPartitiveMonthText[i].getLength();
631 res = i+1;
632 break; // for
634 else if ( bScanPartitiveMonths && StringContainsWord( pUpperPartitiveAbbrevMonthText[i], rString, nPos ) )
635 { // partitive abbreviated
636 nPos = nPos + pUpperPartitiveAbbrevMonthText[i].getLength();
637 res = sal::static_int_cast< short >(-(i+1)); // negative
638 break; // for
640 else if ( StringContainsWord( pUpperMonthText[i], rString, nPos ) )
641 { // noun full names
642 nPos = nPos + pUpperMonthText[i].getLength();
643 res = i+1;
644 break; // for
646 else if ( StringContainsWord( pUpperAbbrevMonthText[i], rString, nPos ) )
647 { // noun abbreviated
648 nPos = nPos + pUpperAbbrevMonthText[i].getLength();
649 res = sal::static_int_cast< short >(-(i+1)); // negative
650 break; // for
652 else if (i == 2 && pFormatter->GetLanguageTag().getLanguage() == "de")
654 if (pUpperAbbrevMonthText[i] == u"M\u00C4R" && StringContainsWord( "MRZ", rString, nPos))
655 { // Accept MRZ for MÄR
656 nPos = nPos + 3;
657 res = sal::static_int_cast< short >(-(i+1)); // negative
658 break; // for
660 else if (pUpperAbbrevMonthText[i] == "MRZ" && StringContainsWord( u"M\u00C4R", rString, nPos))
661 { // And vice versa, accept MÄR for MRZ
662 nPos = nPos + 3;
663 res = sal::static_int_cast< short >(-(i+1)); // negative
664 break; // for
667 else if (i == 8)
669 // This assumes the weirdness is applicable to all locales.
670 // It is the case for at least en-* and de-* locales.
671 if (pUpperAbbrevMonthText[i] == "SEPT" && StringContainsWord( "SEP", rString, nPos))
672 { // #102136# The correct English form of month September abbreviated is
673 // SEPT, but almost every data contains SEP instead.
674 nPos = nPos + 3;
675 res = sal::static_int_cast< short >(-(i+1)); // negative
676 break; // for
678 else if (pUpperAbbrevMonthText[i] == "SEP" && StringContainsWord( "SEPT", rString, nPos))
679 { // And vice versa, accept SEPT for SEP
680 nPos = nPos + 4;
681 res = sal::static_int_cast< short >(-(i+1)); // negative
682 break; // for
686 if (!res)
688 // Brutal hack for German locales that know "Januar" or "Jänner".
689 /* TODO: add alternative month names to locale data? if there are
690 * more languages... */
691 const LanguageTag& rLanguageTag = pFormatter->GetLanguageTag();
692 if (rLanguageTag.getLanguage() == "de")
694 if (rLanguageTag.getCountry() == "AT")
696 // Locale data has Jänner/Jän
697 assert(pUpperMonthText[0] == u"J\u00C4NNER");
698 if (StringContainsWord( "JANUAR", rString, nPos))
700 nPos += 6;
701 res = 1;
703 else if (StringContainsWord( "JAN", rString, nPos))
705 nPos += 3;
706 res = -1;
709 else
711 // Locale data has Januar/Jan
712 assert(pUpperMonthText[0] == "JANUAR");
713 if (StringContainsWord( u"J\u00C4NNER", rString, nPos))
715 nPos += 6;
716 res = 1;
718 else if (StringContainsWord( u"J\u00C4N", rString, nPos))
720 nPos += 3;
721 res = -1;
728 return res;
733 * Converts a string containing a DayOfWeek name (Mon, Monday) at nPos into the
734 * DayOfWeek number + 1 (negative if abbreviated), returns 0 if nothing found
736 int ImpSvNumberInputScan::GetDayOfWeek( const OUString& rString, sal_Int32& nPos )
738 int res = 0; // no day found
740 if (rString.getLength() > nPos) // only if needed
742 if ( !bTextInitialized )
744 InitText();
746 sal_Int16 nDays = pFormatter->GetCalendar()->getNumberOfDaysInWeek();
747 for ( sal_Int16 i = 0; i < nDays; i++ )
749 if ( StringContainsWord( pUpperDayText[i], rString, nPos ) )
750 { // full names first
751 nPos = nPos + pUpperDayText[i].getLength();
752 res = i + 1;
753 break; // for
755 if ( StringContainsWord( pUpperAbbrevDayText[i], rString, nPos ) )
756 { // abbreviated
757 nPos = nPos + pUpperAbbrevDayText[i].getLength();
758 res = -(i + 1); // negative
759 break; // for
764 return res;
769 * Reading a currency symbol
770 * '$' => true
771 * else => false
773 bool ImpSvNumberInputScan::GetCurrency( const OUString& rString, sal_Int32& nPos )
775 if ( rString.getLength() > nPos )
777 if ( !aUpperCurrSymbol.getLength() )
778 { // If no format specified the currency of the currently active locale.
779 LanguageType eLang = (mpFormat ? mpFormat->GetLanguage() :
780 pFormatter->GetLocaleData()->getLanguageTag().getLanguageType());
781 aUpperCurrSymbol = pFormatter->GetCharClass()->uppercase(
782 SvNumberFormatter::GetCurrencyEntry( eLang ).GetSymbol() );
784 if ( StringContains( aUpperCurrSymbol, rString, nPos ) )
786 nPos = nPos + aUpperCurrSymbol.getLength();
787 return true;
789 if ( mpFormat )
791 OUString aSymbol, aExtension;
792 if ( mpFormat->GetNewCurrencySymbol( aSymbol, aExtension ) )
794 if ( aSymbol.getLength() <= rString.getLength() - nPos )
796 aSymbol = pFormatter->GetCharClass()->uppercase(aSymbol);
797 if ( StringContains( aSymbol, rString, nPos ) )
799 nPos = nPos + aSymbol.getLength();
800 return true;
807 return false;
812 * Reading the time period specifier (AM/PM) for the 12 hour clock
814 * Returns:
815 * "AM" or "PM" => true
816 * else => false
818 * nAmPos:
819 * "AM" => 1
820 * "PM" => -1
821 * else => 0
823 bool ImpSvNumberInputScan::GetTimeAmPm( const OUString& rString, sal_Int32& nPos )
826 if ( rString.getLength() > nPos )
828 const CharClass* pChr = pFormatter->GetCharClass();
829 const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
830 if ( StringContains( pChr->uppercase( pLoc->getTimeAM() ), rString, nPos ) )
832 nAmPm = 1;
833 nPos = nPos + pLoc->getTimeAM().getLength();
834 return true;
836 else if ( StringContains( pChr->uppercase( pLoc->getTimePM() ), rString, nPos ) )
838 nAmPm = -1;
839 nPos = nPos + pLoc->getTimePM().getLength();
840 return true;
844 return false;
849 * Read a decimal separator (',')
850 * ',' => true
851 * else => false
853 inline bool ImpSvNumberInputScan::GetDecSep( const OUString& rString, sal_Int32& nPos ) const
855 if ( rString.getLength() > nPos )
857 const OUString& rSep = pFormatter->GetNumDecimalSep();
858 if ( rString.match( rSep, nPos) )
860 nPos = nPos + rSep.getLength();
861 return true;
863 const OUString& rSepAlt = pFormatter->GetNumDecimalSepAlt();
864 if ( !rSepAlt.isEmpty() && rString.match( rSepAlt, nPos) )
866 nPos = nPos + rSepAlt.getLength();
867 return true;
870 return false;
875 * Reading a hundredth seconds separator
877 inline bool ImpSvNumberInputScan::GetTime100SecSep( const OUString& rString, sal_Int32& nPos ) const
879 if ( rString.getLength() > nPos )
881 if (bIso8601Tsep)
883 // ISO 8601 specifies both '.' dot and ',' comma as fractional
884 // separator.
885 if (rString[nPos] == '.' || rString[nPos] == ',')
887 ++nPos;
888 return true;
891 // Even in an otherwise ISO 8601 string be lenient and accept the
892 // locale defined separator.
893 const OUString& rSep = pFormatter->GetLocaleData()->getTime100SecSep();
894 if ( rString.match( rSep, nPos ))
896 nPos = nPos + rSep.getLength();
897 return true;
900 return false;
905 * Read a sign including brackets
906 * '+' => 1
907 * '-' => -1
908 * '(' => -1, bNegCheck = 1
909 * else => 0
911 int ImpSvNumberInputScan::GetSign( const OUString& rString, sal_Int32& nPos )
913 if (rString.getLength() > nPos)
914 switch (rString[ nPos ])
916 case '+':
917 nPos++;
918 return 1;
919 case '(': // '(' similar to '-' ?!?
920 bNegCheck = true;
921 [[fallthrough]];
922 case '-':
923 nPos++;
924 return -1;
925 default:
926 break;
929 return 0;
934 * Read a sign with an exponent
935 * '+' => 1
936 * '-' => -1
937 * else => 0
939 short ImpSvNumberInputScan::GetESign( const OUString& rString, sal_Int32& nPos )
941 if (rString.getLength() > nPos)
943 switch (rString[nPos])
945 case '+':
946 nPos++;
947 return 1;
948 case '-':
949 nPos++;
950 return -1;
951 default:
952 break;
955 return 0;
960 * i counts string portions, j counts numbers thereof.
961 * It should had been called SkipNumber instead.
963 inline bool ImpSvNumberInputScan::GetNextNumber( sal_uInt16& i, sal_uInt16& j ) const
965 if ( i < nStringsCnt && IsNum[i] )
967 j++;
968 i++;
969 return true;
971 return false;
975 bool ImpSvNumberInputScan::GetTimeRef( double& fOutNumber,
976 sal_uInt16 nIndex, // j-value of the first numeric time part of input, default 0
977 sal_uInt16 nCnt, // count of numeric time parts
978 SvNumInputOptions eInputOptions
979 ) const
981 bool bRet = true;
982 sal_uInt16 nHour;
983 sal_uInt16 nMinute = 0;
984 sal_uInt16 nSecond = 0;
985 double fSecond100 = 0.0;
986 sal_uInt16 nStartIndex = nIndex;
988 if (nDecPos == 2 && (nCnt == 3 || nCnt == 2)) // 20:45.5 or 45.5
990 nHour = 0;
992 else if (mpFormat && nDecPos == 0 && nCnt == 2 && mpFormat->IsMinuteSecondFormat())
994 // Input on MM:SS format, instead of doing HH:MM:00
995 nHour = 0;
997 else if (nIndex - nStartIndex < nCnt)
999 nHour = static_cast<sal_uInt16>(sStrArray[nNums[nIndex++]].toInt32());
1001 else
1003 nHour = 0;
1004 bRet = false;
1005 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetTimeRef: bad number index");
1008 // 0:123 or 0:0:123 or 0:123:59 is valid
1009 bool bAllowDuration = (nHour == 0 && !nAmPm);
1011 if (nAmPm && nHour > 12) // not a valid AM/PM clock time
1013 bRet = false;
1015 else if (nAmPm == -1 && nHour != 12) // PM
1017 nHour += 12;
1019 else if (nAmPm == 1 && nHour == 12) // 12 AM
1021 nHour = 0;
1024 if (nDecPos == 2 && nCnt == 2) // 45.5
1026 nMinute = 0;
1028 else if (nIndex - nStartIndex < nCnt)
1030 nMinute = static_cast<sal_uInt16>(sStrArray[nNums[nIndex++]].toInt32());
1031 if (!(eInputOptions & SvNumInputOptions::LAX_TIME) && !bAllowDuration
1032 && nIndex > 1 && nMinute > 59)
1033 bRet = false; // 1:60 or 1:123 is invalid, 123:1 or 0:123 is valid
1034 if (bAllowDuration)
1035 bAllowDuration = (nMinute == 0);
1037 if (nIndex - nStartIndex < nCnt)
1039 nSecond = static_cast<sal_uInt16>(sStrArray[nNums[nIndex++]].toInt32());
1040 if (!(eInputOptions & SvNumInputOptions::LAX_TIME) && !bAllowDuration
1041 && nIndex > 1 && nSecond > 59 && !(nHour == 23 && nMinute == 59 && nSecond == 60))
1042 bRet = false; // 1:60 or 1:123 or 1:1:123 is invalid, 123:1 or 123:1:1 or 0:0:123 is valid, or leap second
1044 if (nIndex - nStartIndex < nCnt)
1046 fSecond100 = StringToDouble( sStrArray[nNums[nIndex]], true );
1048 fOutNumber = (static_cast<double>(nHour)*3600 +
1049 static_cast<double>(nMinute)*60 +
1050 static_cast<double>(nSecond) +
1051 fSecond100)/86400.0;
1052 return bRet;
1056 sal_uInt16 ImpSvNumberInputScan::ImplGetDay( sal_uInt16 nIndex ) const
1058 sal_uInt16 nRes = 0;
1060 if (sStrArray[nNums[nIndex]].getLength() <= 2)
1062 sal_uInt16 nNum = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1063 if (nNum <= 31)
1065 nRes = nNum;
1069 return nRes;
1073 sal_uInt16 ImpSvNumberInputScan::ImplGetMonth( sal_uInt16 nIndex ) const
1075 // Preset invalid month number
1076 sal_uInt16 nRes = pFormatter->GetCalendar()->getNumberOfMonthsInYear();
1078 if (sStrArray[nNums[nIndex]].getLength() <= 2)
1080 sal_uInt16 nNum = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1081 if ( 0 < nNum && nNum <= nRes )
1083 nRes = nNum - 1; // zero based for CalendarFieldIndex::MONTH
1087 return nRes;
1092 * 30 -> 1930, 29 -> 2029, or 56 -> 1756, 55 -> 1855, ...
1094 sal_uInt16 ImpSvNumberInputScan::ImplGetYear( sal_uInt16 nIndex )
1096 sal_uInt16 nYear = 0;
1098 sal_Int32 nLen = sStrArray[nNums[nIndex]].getLength();
1099 // 16-bit integer year width can have 5 digits, allow for one additional
1100 // leading zero as convention.
1101 if (nLen <= 6)
1103 nYear = static_cast<sal_uInt16>(sStrArray[nNums[nIndex]].toInt32());
1104 // A year in another, not Gregorian CE era is never expanded.
1105 // A year < 100 entered with at least 3 digits with leading 0 is taken
1106 // as is without expansion.
1107 if (mbEraCE == kDefaultEra && nYear < 100 && nLen < 3)
1109 nYear = SvNumberFormatter::ExpandTwoDigitYear( nYear, nYear2000 );
1113 return nYear;
1117 bool ImpSvNumberInputScan::MayBeIso8601()
1119 if (nMayBeIso8601 == 0)
1121 nMayBeIso8601 = 1;
1122 sal_Int32 nLen = ((nNumericsCnt >= 1 && nNums[0] < nStringsCnt) ? sStrArray[nNums[0]].getLength() : 0);
1123 if (nLen)
1125 sal_Int32 n;
1126 if (nNumericsCnt >= 3 && nNums[2] < nStringsCnt &&
1127 sStrArray[nNums[0]+1] == "-" && // separator year-month
1128 (n = sStrArray[nNums[1]].toInt32()) >= 1 && n <= 12 && // month
1129 sStrArray[nNums[1]+1] == "-" && // separator month-day
1130 (n = sStrArray[nNums[2]].toInt32()) >= 1 && n <= 31) // day
1132 // Year (nNums[0]) value not checked, may be anything, but
1133 // length (number of digits) is checked.
1134 nMayBeIso8601 = (nLen >= 4 ? 4 : (nLen == 3 ? 3 : (nLen > 0 ? 2 : 1)));
1138 return nMayBeIso8601 > 1;
1142 bool ImpSvNumberInputScan::CanForceToIso8601( DateOrder eDateOrder )
1144 int nCanForceToIso8601 = 0;
1145 if (!MayBeIso8601())
1147 return false;
1149 else if (nMayBeIso8601 >= 3)
1151 return true; // at least 3 digits in year
1153 else
1155 if (eDateOrder == DateOrder::Invalid)
1157 // As if any of the cases below can be applied, but only if a
1158 // locale dependent date pattern was not matched.
1159 if ((GetDatePatternNumbers() == nNumericsCnt) && IsDatePatternNumberOfType(0,'Y'))
1160 return false;
1161 eDateOrder = GetDateOrder();
1164 nCanForceToIso8601 = 1;
1167 sal_Int32 n;
1168 switch (eDateOrder)
1170 case DateOrder::DMY: // "day" value out of range => ISO 8601 year
1171 n = sStrArray[nNums[0]].toInt32();
1172 if (n < 1 || n > 31)
1174 nCanForceToIso8601 = 2;
1176 break;
1177 case DateOrder::MDY: // "month" value out of range => ISO 8601 year
1178 n = sStrArray[nNums[0]].toInt32();
1179 if (n < 1 || n > 12)
1181 nCanForceToIso8601 = 2;
1183 break;
1184 case DateOrder::YMD: // always possible
1185 nCanForceToIso8601 = 2;
1186 break;
1187 default: break;
1189 return nCanForceToIso8601 > 1;
1193 bool ImpSvNumberInputScan::IsAcceptableIso8601()
1195 if (mpFormat && (mpFormat->GetType() & SvNumFormatType::DATE))
1197 switch (pFormatter->GetEvalDateFormat())
1199 case NF_EVALDATEFORMAT_INTL:
1200 return CanForceToIso8601( GetDateOrder());
1201 case NF_EVALDATEFORMAT_FORMAT:
1202 return CanForceToIso8601( mpFormat->GetDateOrder());
1203 default:
1204 return CanForceToIso8601( GetDateOrder()) || CanForceToIso8601( mpFormat->GetDateOrder());
1207 return CanForceToIso8601( GetDateOrder());
1211 bool ImpSvNumberInputScan::MayBeMonthDate()
1213 if (nMayBeMonthDate == 0)
1215 nMayBeMonthDate = 1;
1216 if (nNumericsCnt >= 2 && nNums[1] < nStringsCnt)
1218 // "-Jan-"
1219 const OUString& rM = sStrArray[ nNums[ 0 ] + 1 ];
1220 if (rM.getLength() >= 3 && rM[0] == '-' && rM[ rM.getLength() - 1] == '-')
1222 // Check year length assuming at least 3 digits (including
1223 // leading zero). Two digit years 1..31 are out of luck here
1224 // and may be taken as day of month.
1225 bool bYear1 = (sStrArray[nNums[0]].getLength() >= 3);
1226 bool bYear2 = (sStrArray[nNums[1]].getLength() >= 3);
1227 sal_Int32 n;
1228 bool bDay1 = !bYear1;
1229 if (bDay1)
1231 n = sStrArray[nNums[0]].toInt32();
1232 bDay1 = n >= 1 && n <= 31;
1234 bool bDay2 = !bYear2;
1235 if (bDay2)
1237 n = sStrArray[nNums[1]].toInt32();
1238 bDay2 = n >= 1 && n <= 31;
1241 if (bDay1 && !bDay2)
1243 nMayBeMonthDate = 2; // dd-month-yy
1245 else if (!bDay1 && bDay2)
1247 nMayBeMonthDate = 3; // yy-month-dd
1249 else if (bDay1 && bDay2)
1251 // Ambiguous ##-MMM-## date, but some big vendor's database
1252 // reports write this crap, assume this always to be
1253 nMayBeMonthDate = 2; // dd-month-yy
1258 return nMayBeMonthDate > 1;
1262 /** If a string is a separator plus '-' minus sign preceding a 'Y' year in
1263 a date pattern at position nPat.
1265 static bool lcl_IsSignedYearSep( const OUString& rStr, const OUString& rPat, sal_Int32 nPat )
1267 bool bOk = false;
1268 sal_Int32 nLen = rStr.getLength();
1269 if (nLen > 1 && rStr[nLen-1] == '-')
1271 --nLen;
1272 if (nPat + nLen < rPat.getLength() && rPat[nPat+nLen] == 'Y')
1274 // Signed year is possible.
1275 bOk = (rPat.indexOf( rStr.subView( 0, nLen), nPat) == nPat);
1278 return bOk;
1282 /** Length of separator usually is 1 but theoretically could be anything. */
1283 static sal_Int32 lcl_getPatternSeparatorLength( const OUString& rPat, sal_Int32 nPat )
1285 sal_Int32 nSep = nPat;
1286 sal_Unicode c;
1287 while (nSep < rPat.getLength() && (c = rPat[nSep]) != 'D' && c != 'M' && c != 'Y')
1288 ++nSep;
1289 return nSep - nPat;
1293 bool ImpSvNumberInputScan::IsAcceptedDatePattern( sal_uInt16 nStartPatternAt )
1295 if (nAcceptedDatePattern >= -1)
1297 return (nAcceptedDatePattern >= 0);
1299 if (!nNumericsCnt)
1301 nAcceptedDatePattern = -1;
1303 else if (!sDateAcceptancePatterns.hasElements())
1305 // The current locale is the format's locale, if a format is present.
1306 const NfEvalDateFormat eEDF = pFormatter->GetEvalDateFormat();
1307 if (!mpFormat || eEDF == NF_EVALDATEFORMAT_FORMAT || mpFormat->GetLanguage() == pFormatter->GetLanguage())
1309 sDateAcceptancePatterns = pFormatter->GetLocaleData()->getDateAcceptancePatterns();
1311 else
1313 OnDemandLocaleDataWrapper& xLocaleData = pFormatter->GetOnDemandLocaleDataWrapper(
1314 SvNumberFormatter::InputScannerPrivateAccess());
1315 const LanguageTag aSaveLocale( xLocaleData->getLanguageTag() );
1316 assert(mpFormat->GetLanguage() == aSaveLocale.getLanguageType()); // prerequisite
1317 // Obtain formatter's locale's (e.g. system) patterns.
1318 xLocaleData.changeLocale( LanguageTag( pFormatter->GetLanguage()));
1319 const css::uno::Sequence<OUString> aLocalePatterns( xLocaleData->getDateAcceptancePatterns());
1320 // Reset to format's locale.
1321 xLocaleData.changeLocale( aSaveLocale);
1322 // When concatenating don't care about duplicates, combining
1323 // weeding those out reallocs yet another time and probably doesn't
1324 // take less time than looping over two additional patterns below...
1325 switch (eEDF)
1327 case NF_EVALDATEFORMAT_FORMAT:
1328 assert(!"shouldn't reach here");
1329 break;
1330 case NF_EVALDATEFORMAT_INTL:
1331 sDateAcceptancePatterns = aLocalePatterns;
1332 break;
1333 case NF_EVALDATEFORMAT_INTL_FORMAT:
1334 sDateAcceptancePatterns = comphelper::concatSequences(
1335 aLocalePatterns,
1336 xLocaleData->getDateAcceptancePatterns());
1337 break;
1338 case NF_EVALDATEFORMAT_FORMAT_INTL:
1339 sDateAcceptancePatterns = comphelper::concatSequences(
1340 xLocaleData->getDateAcceptancePatterns(),
1341 aLocalePatterns);
1342 break;
1345 SAL_WARN_IF( !sDateAcceptancePatterns.hasElements(), "svl.numbers", "ImpSvNumberInputScan::IsAcceptedDatePattern: no date acceptance patterns");
1346 nAcceptedDatePattern = (sDateAcceptancePatterns.hasElements() ? -2 : -1);
1349 if (nAcceptedDatePattern == -1)
1351 return false;
1353 nDatePatternStart = nStartPatternAt; // remember start particle
1355 const sal_Int32 nMonthsInYear = pFormatter->GetCalendar()->getNumberOfMonthsInYear();
1357 for (sal_Int32 nPattern=0; nPattern < sDateAcceptancePatterns.getLength(); ++nPattern)
1359 sal_uInt16 nNext = nDatePatternStart;
1360 nDatePatternNumbers = 0;
1361 bool bOk = true;
1362 const OUString& rPat = sDateAcceptancePatterns[nPattern];
1363 sal_Int32 nPat = 0;
1364 for ( ; nPat < rPat.getLength() && bOk && nNext < nStringsCnt; ++nPat, ++nNext)
1366 const sal_Unicode c = rPat[nPat];
1367 switch (c)
1369 case 'Y':
1370 case 'M':
1371 case 'D':
1372 bOk = IsNum[nNext];
1373 if (bOk && (c == 'M' || c == 'D'))
1375 // Check the D and M cases for plausibility. This also
1376 // prevents recognition of date instead of number with a
1377 // numeric group input if date separator is identical to
1378 // group separator, for example with D.M as a pattern and
1379 // #.### as a group.
1380 sal_Int32 nMaxLen, nMaxVal;
1381 switch (c)
1383 case 'M':
1384 nMaxLen = 2;
1385 nMaxVal = nMonthsInYear;
1386 break;
1387 case 'D':
1388 nMaxLen = 2;
1389 nMaxVal = 31;
1390 break;
1391 default:
1392 // This merely exists against
1393 // -Werror=maybe-uninitialized, which is nonsense
1394 // after the (c == 'M' || c == 'D') check above,
1395 // but ...
1396 nMaxLen = 2;
1397 nMaxVal = 31;
1399 bOk = (sStrArray[nNext].getLength() <= nMaxLen);
1400 if (bOk)
1402 sal_Int32 nNum = sStrArray[nNext].toInt32();
1403 bOk = (1 <= nNum && nNum <= nMaxVal);
1406 if (bOk)
1407 ++nDatePatternNumbers;
1408 break;
1409 default:
1410 bOk = !IsNum[nNext];
1411 if (bOk)
1413 const sal_Int32 nSepLen = lcl_getPatternSeparatorLength( rPat, nPat);
1414 // Non-numeric input must match separator exactly to be
1415 // accepted as such.
1416 const sal_Int32 nLen = sStrArray[nNext].getLength();
1417 bOk = (nLen == nSepLen && rPat.indexOf( sStrArray[nNext], nPat) == nPat);
1418 if (bOk)
1420 nPat += nLen - 1;
1422 else if ((bOk = lcl_IsSignedYearSep( sStrArray[nNext], rPat, nPat)))
1424 nPat += nLen - 2;
1426 else if (nPat + nLen > rPat.getLength() && sStrArray[nNext][ nLen - 1 ] == ' ')
1428 using namespace comphelper::string;
1429 // Trailing blanks in input.
1430 OUStringBuffer aBuf(sStrArray[nNext]);
1431 aBuf.stripEnd();
1432 // Expand again in case of pattern "M. D. " and
1433 // input "M. D. ", maybe fetched far, but...
1434 padToLength(aBuf, rPat.getLength() - nPat, ' ');
1435 OUString aStr = aBuf.makeStringAndClear();
1436 bOk = (rPat.indexOf( aStr, nPat) == nPat);
1437 if (bOk)
1439 nPat += aStr.getLength() - 1;
1443 break;
1446 if (bOk)
1448 // Check for trailing characters mismatch.
1449 if (nNext < nStringsCnt)
1451 // Pattern end but not input end.
1452 // A trailing blank may be part of the current pattern input,
1453 // if pattern is "D.M." and input is "D.M. hh:mm" last was
1454 // ". ", or may be following the current pattern input, if
1455 // pattern is "D.M" and input is "D.M hh:mm" last was "M".
1456 sal_Int32 nPos = 0;
1457 sal_uInt16 nCheck;
1458 if (nPat > 0 && nNext > 0)
1460 // nPat is one behind after the for loop.
1461 sal_Int32 nPatCheck = nPat - 1;
1462 switch (rPat[nPatCheck])
1464 case 'Y':
1465 case 'M':
1466 case 'D':
1467 nCheck = nNext;
1468 break;
1469 default:
1471 nCheck = nNext - 1;
1472 // Advance position in input to match length of
1473 // non-YMD (separator) characters in pattern.
1474 sal_Unicode c;
1477 ++nPos;
1478 c = rPat[--nPatCheck];
1479 } while (c != 'Y' && c != 'M' && c != 'D');
1483 else
1485 nCheck = nNext;
1487 if (!IsNum[nCheck])
1489 // Trailing (or separating if time follows) blanks are ok.
1490 // No blank and a following number is not.
1491 const bool bBlanks = SkipBlanks( sStrArray[nCheck], nPos);
1492 if (nPos == sStrArray[nCheck].getLength() && (bBlanks || !IsNum[nNext]))
1494 nAcceptedDatePattern = nPattern;
1495 return true;
1499 else if (nPat == rPat.getLength())
1501 // Input end and pattern end => match.
1502 nAcceptedDatePattern = nPattern;
1503 return true;
1505 // else Input end but not pattern end, no match.
1508 nAcceptedDatePattern = -1;
1509 return false;
1513 bool ImpSvNumberInputScan::SkipDatePatternSeparator( sal_uInt16 nParticle, sal_Int32 & rPos, bool & rSignedYear )
1515 // If not initialized yet start with first number, if any.
1516 if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1518 return false;
1520 if (nParticle < nDatePatternStart || nParticle >= nStringsCnt || IsNum[nParticle])
1522 return false;
1524 sal_uInt16 nNext = nDatePatternStart;
1525 const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1526 for (sal_Int32 nPat = 0; nPat < rPat.getLength() && nNext < nStringsCnt; ++nPat, ++nNext)
1528 switch (rPat[nPat])
1530 case 'Y':
1531 case 'M':
1532 case 'D':
1533 break;
1534 default:
1535 if (nNext == nParticle)
1537 const sal_Int32 nSepLen = lcl_getPatternSeparatorLength( rPat, nPat);
1538 const sal_Int32 nLen = sStrArray[nNext].getLength();
1539 bool bOk = (nLen == nSepLen && rPat.indexOf( sStrArray[nNext], nPat) == nPat);
1540 if (!bOk)
1542 bOk = lcl_IsSignedYearSep( sStrArray[nNext], rPat, nPat);
1543 if (bOk)
1544 rSignedYear = true;
1546 if (!bOk && (nPat + nLen > rPat.getLength() && sStrArray[nNext][ nLen - 1 ] == ' '))
1548 // The same ugly trailing blanks check as in
1549 // IsAcceptedDatePattern().
1550 using namespace comphelper::string;
1551 OUStringBuffer aBuf(sStrArray[nNext]);
1552 aBuf.stripEnd();
1553 padToLength(aBuf, rPat.getLength() - nPat, ' ');
1554 bOk = (rPat.indexOf( aBuf.makeStringAndClear(), nPat) == nPat);
1556 if (bOk)
1558 rPos = nLen; // yes, set, not add!
1559 return true;
1561 else
1562 return false;
1564 nPat += sStrArray[nNext].getLength() - 1;
1565 break;
1568 return false;
1572 sal_uInt16 ImpSvNumberInputScan::GetDatePatternNumbers()
1574 // If not initialized yet start with first number, if any.
1575 if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1577 return 0;
1579 return nDatePatternNumbers;
1583 bool ImpSvNumberInputScan::IsDatePatternNumberOfType( sal_uInt16 nNumber, sal_Unicode cType )
1585 if (GetDatePatternNumbers() <= nNumber)
1586 return false;
1588 sal_uInt16 nNum = 0;
1589 const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1590 for (sal_Int32 nPat = 0; nPat < rPat.getLength(); ++nPat)
1592 switch (rPat[nPat])
1594 case 'Y':
1595 case 'M':
1596 case 'D':
1597 if (nNum == nNumber)
1598 return rPat[nPat] == cType;
1599 ++nNum;
1600 break;
1603 return false;
1607 sal_uInt32 ImpSvNumberInputScan::GetDatePatternOrder()
1609 // If not initialized yet start with first number, if any.
1610 if (!IsAcceptedDatePattern( nNumericsCnt ? nNums[0] : 0 ))
1612 return 0;
1614 sal_uInt32 nOrder = 0;
1615 const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
1616 for (sal_Int32 nPat = 0; nPat < rPat.getLength() && !(nOrder & 0xff0000); ++nPat)
1618 switch (rPat[nPat])
1620 case 'Y':
1621 case 'M':
1622 case 'D':
1623 nOrder = (nOrder << 8) | rPat[nPat];
1624 break;
1627 return nOrder;
1631 DateOrder ImpSvNumberInputScan::GetDateOrder( bool bFromFormatIfNoPattern )
1633 sal_uInt32 nOrder = GetDatePatternOrder();
1634 if (!nOrder)
1636 if (bFromFormatIfNoPattern && mpFormat)
1637 return mpFormat->GetDateOrder();
1638 else
1639 return pFormatter->GetLocaleData()->getDateOrder();
1641 switch ((nOrder & 0xff0000) >> 16)
1643 case 'Y':
1644 if ((((nOrder & 0xff00) >> 8) == 'M') && ((nOrder & 0xff) == 'D'))
1646 return DateOrder::YMD;
1648 break;
1649 case 'M':
1650 if ((((nOrder & 0xff00) >> 8) == 'D') && ((nOrder & 0xff) == 'Y'))
1652 return DateOrder::MDY;
1654 break;
1655 case 'D':
1656 if ((((nOrder & 0xff00) >> 8) == 'M') && ((nOrder & 0xff) == 'Y'))
1658 return DateOrder::DMY;
1660 break;
1661 default:
1662 case 0:
1663 switch ((nOrder & 0xff00) >> 8)
1665 case 'Y':
1666 switch (nOrder & 0xff)
1668 case 'M':
1669 return DateOrder::YMD;
1671 break;
1672 case 'M':
1673 switch (nOrder & 0xff)
1675 case 'Y':
1676 return DateOrder::DMY;
1677 case 'D':
1678 return DateOrder::MDY;
1680 break;
1681 case 'D':
1682 switch (nOrder & 0xff)
1684 case 'Y':
1685 return DateOrder::MDY;
1686 case 'M':
1687 return DateOrder::DMY;
1689 break;
1690 default:
1691 case 0:
1692 switch (nOrder & 0xff)
1694 case 'Y':
1695 return DateOrder::YMD;
1696 case 'M':
1697 return DateOrder::MDY;
1698 case 'D':
1699 return DateOrder::DMY;
1701 break;
1704 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateOrder: undefined, falling back to locale's default");
1705 return pFormatter->GetLocaleData()->getDateOrder();
1708 bool ImpSvNumberInputScan::GetDateRef( double& fDays, sal_uInt16& nCounter )
1710 using namespace ::com::sun::star::i18n;
1711 NfEvalDateFormat eEDF;
1712 int nFormatOrder;
1713 if ( mpFormat && (mpFormat->GetType() & SvNumFormatType::DATE) )
1715 eEDF = pFormatter->GetEvalDateFormat();
1716 switch ( eEDF )
1718 case NF_EVALDATEFORMAT_INTL :
1719 case NF_EVALDATEFORMAT_FORMAT :
1720 nFormatOrder = 1; // only one loop
1721 break;
1722 default:
1723 nFormatOrder = 2;
1724 if ( nMatchedAllStrings )
1726 eEDF = NF_EVALDATEFORMAT_FORMAT_INTL;
1727 // we have a complete match, use it
1731 else
1733 eEDF = NF_EVALDATEFORMAT_INTL;
1734 nFormatOrder = 1;
1736 bool res = true;
1738 const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
1739 CalendarWrapper* pCal = pFormatter->GetCalendar();
1740 for ( int nTryOrder = 1; nTryOrder <= nFormatOrder; nTryOrder++ )
1742 pCal->setGregorianDateTime( Date( Date::SYSTEM ) ); // today
1743 OUString aOrgCalendar; // empty => not changed yet
1744 DateOrder DateFmt;
1745 bool bFormatTurn;
1746 switch ( eEDF )
1748 case NF_EVALDATEFORMAT_INTL :
1749 bFormatTurn = false;
1750 DateFmt = GetDateOrder();
1751 break;
1752 case NF_EVALDATEFORMAT_FORMAT :
1753 bFormatTurn = true;
1754 DateFmt = mpFormat->GetDateOrder();
1755 break;
1756 case NF_EVALDATEFORMAT_INTL_FORMAT :
1757 if ( nTryOrder == 1 )
1759 bFormatTurn = false;
1760 DateFmt = GetDateOrder();
1762 else
1764 bFormatTurn = true;
1765 DateFmt = mpFormat->GetDateOrder();
1767 break;
1768 case NF_EVALDATEFORMAT_FORMAT_INTL :
1769 if ( nTryOrder == 2 )
1771 bFormatTurn = false;
1772 DateFmt = GetDateOrder();
1774 else
1776 bFormatTurn = true;
1777 // Even if the format pattern is to be preferred, the input may
1778 // have matched a pattern of the current locale, which then
1779 // again is to be preferred. Both date orders can be different
1780 // so we need to obtain the actual match. For example ISO
1781 // YYYY-MM-DD format vs locale's DD.MM.YY input.
1782 // If no pattern was matched, obtain from format.
1783 // Note that patterns may have been constructed from the
1784 // format's locale and prepended to the current locale's
1785 // patterns, it doesn't necessarily mean a current locale's
1786 // pattern was matched, but may if the format's locale's
1787 // patterns didn't match, which were tried first.
1788 DateFmt = GetDateOrder(true);
1790 break;
1791 default:
1792 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateRef: unknown NfEvalDateFormat" );
1793 DateFmt = DateOrder::YMD;
1794 bFormatTurn = false;
1796 if ( bFormatTurn )
1798 /* TODO:
1799 We are currently not able to fully support a switch to another calendar during
1800 input for the following reasons:
1801 1. We do have a problem if both (locale's default and format's) calendars
1802 define the same YMD order and use the same date separator, there is no way
1803 to distinguish between them if the input results in valid calendar input for
1804 both calendars. How to solve? Would NfEvalDateFormat be sufficient? Should
1805 it always be set to NF_EVALDATEFORMAT_FORMAT_INTL and thus the format's
1806 calendar be preferred? This could be confusing if a Calc cell was formatted
1807 different to the locale's default and has no content yet, then the user has
1808 no clue about the format or calendar being set.
1809 2. In Calc cell edit mode a date is always displayed and edited using the
1810 default edit format of the default calendar (normally being Gregorian). If
1811 input was ambiguous due to issue #1 we'd need a mechanism to tell that a
1812 date was edited and not newly entered. Not feasible. Otherwise we'd need a
1813 mechanism to use a specific edit format with a specific calendar according
1814 to the format set.
1815 3. For some calendars like Japanese Gengou we'd need era input, which isn't
1816 implemented at all. Though this is a rare and special case, forcing a
1817 calendar dependent edit format as suggested in item #2 might require era
1818 input, if it shouldn't result in a fallback to Gregorian calendar.
1819 4. Last and least: the GetMonth() method currently only matches month names of
1820 the default calendar. Alternating month names of the actual format's
1821 calendar would have to be implemented. No problem.
1824 #ifdef THE_FUTURE
1825 if ( mpFormat->IsOtherCalendar( nStringScanNumFor ) )
1827 mpFormat->SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
1829 else
1831 mpFormat->SwitchToSpecifiedCalendar( aOrgCalendar, fOrgDateTime,
1832 nStringScanNumFor );
1834 #endif
1837 res = true;
1838 nCounter = 0;
1839 // For incomplete dates, always assume first day of month if not specified.
1840 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
1842 switch (nNumericsCnt) // count of numbers in string
1844 case 0: // none
1845 if (nMonthPos) // only month (Jan)
1847 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1849 else
1851 res = false;
1853 break;
1855 case 1: // only one number
1856 nCounter = 1;
1857 switch (nMonthPos) // where is the month
1859 case 0: // not found
1861 // If input matched a date pattern, use the pattern
1862 // to determine if it is a day, month or year. The
1863 // pattern should have only one single value then,
1864 // 'D-', 'M-' or 'Y-'. If input did not match a
1865 // pattern assume the usual day of current month.
1866 sal_uInt32 nDateOrder = (bFormatTurn ?
1867 mpFormat->GetExactDateOrder() :
1868 GetDatePatternOrder());
1869 switch (nDateOrder)
1871 case 'Y':
1872 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1873 break;
1874 case 'M':
1875 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1876 break;
1877 case 'D':
1878 default:
1879 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1880 break;
1882 break;
1884 case 1: // month at the beginning (Jan 01)
1885 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1886 switch (DateFmt)
1888 case DateOrder::MDY:
1889 case DateOrder::YMD:
1891 sal_uInt16 nDay = ImplGetDay(0);
1892 sal_uInt16 nYear = ImplGetYear(0);
1893 if (nDay == 0 || nDay > 32)
1895 pCal->setValue( CalendarFieldIndex::YEAR, nYear);
1897 else
1899 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1901 break;
1903 case DateOrder::DMY:
1904 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1905 break;
1906 default:
1907 res = false;
1908 break;
1910 break;
1911 case 3: // month at the end (10 Jan)
1912 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
1913 switch (DateFmt)
1915 case DateOrder::DMY:
1916 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1917 break;
1918 case DateOrder::YMD:
1919 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1920 break;
1921 default:
1922 res = false;
1923 break;
1925 break;
1926 default:
1927 res = false;
1928 break;
1929 } // switch (nMonthPos)
1930 break;
1932 case 2: // 2 numbers
1933 nCounter = 2;
1934 switch (nMonthPos) // where is the month
1936 case 0: // not found
1938 sal_uInt32 nExactDateOrder = (bFormatTurn ?
1939 mpFormat->GetExactDateOrder() :
1940 GetDatePatternOrder());
1941 bool bIsExact = (0xff < nExactDateOrder && nExactDateOrder <= 0xffff);
1942 if (!bIsExact && bFormatTurn && IsAcceptedDatePattern( nNums[0]))
1944 // If input does not match format but pattern, use pattern
1945 // instead, even if eEDF==NF_EVALDATEFORMAT_FORMAT_INTL.
1946 // For example, format has "Y-M-D" and pattern is "D.M.",
1947 // input with 2 numbers can't match format and 31.12. would
1948 // lead to 1931-12-01 (fdo#54344)
1949 nExactDateOrder = GetDatePatternOrder();
1950 bIsExact = (0xff < nExactDateOrder && nExactDateOrder <= 0xffff);
1952 bool bHadExact;
1953 if (bIsExact)
1955 // formatted as date and exactly 2 parts
1956 bHadExact = true;
1957 switch ( (nExactDateOrder >> 8) & 0xff )
1959 case 'Y':
1960 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
1961 break;
1962 case 'M':
1963 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
1964 break;
1965 case 'D':
1966 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
1967 break;
1968 default:
1969 bHadExact = false;
1971 switch ( nExactDateOrder & 0xff )
1973 case 'Y':
1974 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
1975 break;
1976 case 'M':
1977 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
1978 break;
1979 case 'D':
1980 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
1981 break;
1982 default:
1983 bHadExact = false;
1985 SAL_WARN_IF( !bHadExact, "svl.numbers", "ImpSvNumberInputScan::GetDateRef: error in exact date order");
1987 else
1989 bHadExact = false;
1991 // If input matched against a date acceptance pattern
1992 // do not attempt to mess around with guessing the
1993 // order, either it matches or it doesn't.
1994 if ((bFormatTurn || !bIsExact) && (!bHadExact || !pCal->isValid()))
1996 if ( !bHadExact && nExactDateOrder )
1998 pCal->setGregorianDateTime( Date( Date::SYSTEM ) ); // reset today
2000 switch (DateFmt)
2002 case DateOrder::MDY:
2003 // M D
2004 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2005 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2006 if ( !pCal->isValid() ) // 2nd try
2007 { // M Y
2008 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
2009 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2010 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2012 break;
2013 case DateOrder::DMY:
2014 // D M
2015 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2016 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2017 if ( !pCal->isValid() ) // 2nd try
2018 { // M Y
2019 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
2020 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2021 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2023 break;
2024 case DateOrder::YMD:
2025 // M D
2026 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2027 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2028 if ( !pCal->isValid() ) // 2nd try
2029 { // Y M
2030 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, 1 );
2031 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2032 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2034 break;
2035 default:
2036 res = false;
2037 break;
2041 break;
2042 case 1: // month at the beginning (Jan 01 01)
2044 // The input is valid as MDY in almost any
2045 // constellation, there is no date order (M)YD except if
2046 // set in a format applied.
2047 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2048 sal_uInt32 nExactDateOrder = (bFormatTurn ? mpFormat->GetExactDateOrder() : 0);
2049 if ((((nExactDateOrder >> 8) & 0xff) == 'Y') && ((nExactDateOrder & 0xff) == 'D'))
2051 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2052 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2054 else
2056 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2057 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2059 break;
2061 case 2: // month in the middle (10 Jan 94)
2063 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2064 DateOrder eDF = (MayBeMonthDate() ? (nMayBeMonthDate == 2 ? DateOrder::DMY : DateOrder::YMD) : DateFmt);
2065 switch (eDF)
2067 case DateOrder::DMY:
2068 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2069 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2070 break;
2071 case DateOrder::YMD:
2072 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2073 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2074 break;
2075 default:
2076 res = false;
2077 break;
2079 break;
2081 default: // else, e.g. month at the end (94 10 Jan)
2082 res = false;
2083 break;
2084 } // switch (nMonthPos)
2085 break;
2087 default: // more than two numbers (31.12.94 8:23) (31.12. 8:23)
2088 switch (nMonthPos) // where is the month
2090 case 0: // not found
2092 nCounter = 3;
2093 if ( nTimePos > 1 )
2094 { // find first time number index (should only be 3 or 2 anyway)
2095 for ( sal_uInt16 j = 0; j < nNumericsCnt; j++ )
2097 if ( nNums[j] == nTimePos - 2 )
2099 nCounter = j;
2100 break; // for
2104 // ISO 8601 yyyy-mm-dd forced recognition
2105 DateOrder eDF = (CanForceToIso8601( DateFmt) ? DateOrder::YMD : DateFmt);
2106 switch (eDF)
2108 case DateOrder::MDY:
2109 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2110 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(0) );
2111 if ( nCounter > 2 )
2112 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(2) );
2113 break;
2114 case DateOrder::DMY:
2115 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2116 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2117 if ( nCounter > 2 )
2118 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(2) );
2119 break;
2120 case DateOrder::YMD:
2121 if ( nCounter > 2 )
2122 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(2) );
2123 pCal->setValue( CalendarFieldIndex::MONTH, ImplGetMonth(1) );
2124 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2125 break;
2126 default:
2127 res = false;
2128 break;
2130 break;
2132 case 1: // month at the beginning (Jan 01 01 8:23)
2133 nCounter = 2;
2134 switch (DateFmt)
2136 case DateOrder::MDY:
2137 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2138 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2139 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2140 break;
2141 default:
2142 res = false;
2143 break;
2145 break;
2146 case 2: // month in the middle (10 Jan 94 8:23)
2147 nCounter = 2;
2148 pCal->setValue( CalendarFieldIndex::MONTH, std::abs(nMonth)-1 );
2149 switch (DateFmt)
2151 case DateOrder::DMY:
2152 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
2153 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
2154 break;
2155 case DateOrder::YMD:
2156 pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(1) );
2157 pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(0) );
2158 break;
2159 default:
2160 res = false;
2161 break;
2163 break;
2164 default: // else, e.g. month at the end (94 10 Jan 8:23)
2165 nCounter = 2;
2166 res = false;
2167 break;
2168 } // switch (nMonthPos)
2169 break;
2170 } // switch (nNumericsCnt)
2172 if (mbEraCE != kDefaultEra)
2173 pCal->setValue( CalendarFieldIndex::ERA, mbEraCE ? 1 : 0);
2175 if ( res && pCal->isValid() )
2177 double fDiff = DateTime(*pNullDate) - pCal->getEpochStart();
2178 fDays = ::rtl::math::approxFloor( pCal->getLocalDateTime() );
2179 fDays -= fDiff;
2180 nTryOrder = nFormatOrder; // break for
2182 else
2184 res = false;
2186 if ( aOrgCalendar.getLength() )
2188 pCal->loadCalendar( aOrgCalendar, pLoc->getLanguageTag().getLocale() ); // restore calendar
2190 #if NF_TEST_CALENDAR
2192 using namespace ::com::sun::star;
2193 struct entry { const char* lan; const char* cou; const char* cal; };
2194 const entry cals[] = {
2195 { "en", "US", "gregorian" },
2196 { "ar", "TN", "hijri" },
2197 { "he", "IL", "jewish" },
2198 { "ja", "JP", "gengou" },
2199 { "ko", "KR", "hanja_yoil" },
2200 { "th", "TH", "buddhist" },
2201 { "zh", "TW", "ROC" },
2202 {0,0,0}
2204 lang::Locale aLocale;
2205 bool bValid;
2206 sal_Int16 nDay, nMyMonth, nYear, nHour, nMinute, nSecond;
2207 sal_Int16 nDaySet, nMonthSet, nYearSet, nHourSet, nMinuteSet, nSecondSet;
2208 sal_Int16 nZO, nDST1, nDST2, nDST, nZOmillis, nDST1millis, nDST2millis, nDSTmillis;
2209 sal_Int32 nZoneInMillis, nDST1InMillis, nDST2InMillis;
2210 uno::Reference< uno::XComponentContext > xContext =
2211 ::comphelper::getProcessComponentContext();
2212 uno::Reference< i18n::XCalendar4 > xCal = i18n::LocaleCalendar2::create(xContext);
2213 for ( const entry* p = cals; p->lan; ++p )
2215 aLocale.Language = OUString::createFromAscii( p->lan );
2216 aLocale.Country = OUString::createFromAscii( p->cou );
2217 xCal->loadCalendar( OUString::createFromAscii( p->cal ),
2218 aLocale );
2219 double nDateTime = 0.0; // 1-Jan-1970 00:00:00
2220 nZO = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET );
2221 nZOmillis = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS );
2222 nZoneInMillis = static_cast<sal_Int32>(nZO) * 60000 +
2223 (nZO < 0 ? -1 : 1) * static_cast<sal_uInt16>(nZOmillis);
2224 nDST1 = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2225 nDST1millis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2226 nDST1InMillis = static_cast<sal_Int32>(nDST1) * 60000 +
2227 (nDST1 < 0 ? -1 : 1) * static_cast<sal_uInt16>(nDST1millis);
2228 nDateTime -= (double)(nZoneInMillis + nDST1InMillis) / 1000.0 / 60.0 / 60.0 / 24.0;
2229 xCal->setDateTime( nDateTime );
2230 nDST2 = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2231 nDST2millis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2232 nDST2InMillis = static_cast<sal_Int32>(nDST2) * 60000 +
2233 (nDST2 < 0 ? -1 : 1) * static_cast<sal_uInt16>(nDST2millis);
2234 if ( nDST1InMillis != nDST2InMillis )
2236 nDateTime = 0.0 - (double)(nZoneInMillis + nDST2InMillis) / 1000.0 / 60.0 / 60.0 / 24.0;
2237 xCal->setDateTime( nDateTime );
2239 nDaySet = xCal->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH );
2240 nMonthSet = xCal->getValue( i18n::CalendarFieldIndex::MONTH );
2241 nYearSet = xCal->getValue( i18n::CalendarFieldIndex::YEAR );
2242 nHourSet = xCal->getValue( i18n::CalendarFieldIndex::HOUR );
2243 nMinuteSet = xCal->getValue( i18n::CalendarFieldIndex::MINUTE );
2244 nSecondSet = xCal->getValue( i18n::CalendarFieldIndex::SECOND );
2245 nZO = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET );
2246 nZOmillis = xCal->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS );
2247 nDST = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET );
2248 nDSTmillis = xCal->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS );
2249 xCal->setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH, nDaySet );
2250 xCal->setValue( i18n::CalendarFieldIndex::MONTH, nMonthSet );
2251 xCal->setValue( i18n::CalendarFieldIndex::YEAR, nYearSet );
2252 xCal->setValue( i18n::CalendarFieldIndex::HOUR, nHourSet );
2253 xCal->setValue( i18n::CalendarFieldIndex::MINUTE, nMinuteSet );
2254 xCal->setValue( i18n::CalendarFieldIndex::SECOND, nSecondSet );
2255 bValid = xCal->isValid();
2256 nDay = xCal->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH );
2257 nMyMonth= xCal->getValue( i18n::CalendarFieldIndex::MONTH );
2258 nYear = xCal->getValue( i18n::CalendarFieldIndex::YEAR );
2259 nHour = xCal->getValue( i18n::CalendarFieldIndex::HOUR );
2260 nMinute = xCal->getValue( i18n::CalendarFieldIndex::MINUTE );
2261 nSecond = xCal->getValue( i18n::CalendarFieldIndex::SECOND );
2262 bValid = bValid && nDay == nDaySet && nMyMonth == nMonthSet && nYear ==
2263 nYearSet && nHour == nHourSet && nMinute == nMinuteSet && nSecond
2264 == nSecondSet;
2267 #endif // NF_TEST_CALENDAR
2271 return res;
2276 * Analyze first string
2277 * All gone => true
2278 * else => false
2280 bool ImpSvNumberInputScan::ScanStartString( const OUString& rString )
2282 sal_Int32 nPos = 0;
2284 // First of all, eat leading blanks
2285 SkipBlanks(rString, nPos);
2287 // Yes, nMatchedAllStrings should know about the sign position
2288 nSign = GetSign(rString, nPos);
2289 if ( nSign ) // sign?
2291 SkipBlanks(rString, nPos);
2293 // #102371# match against format string only if start string is not a sign character
2294 if ( nMatchedAllStrings && !(nSign && rString.getLength() == 1) )
2296 // Match against format in any case, so later on for a "x1-2-3" input
2297 // we may distinguish between a xy-m-d (or similar) date and a x0-0-0
2298 // format. No sign detection here!
2299 if ( ScanStringNumFor( rString, nPos, 0, true ) )
2301 nMatchedAllStrings |= nMatchedStartString;
2303 else
2305 nMatchedAllStrings = 0;
2309 // Bail out early for just a sign.
2310 if (nSign && nPos == rString.getLength())
2311 return true;
2313 const sal_Int32 nStartBlanks = nPos;
2314 if ( GetDecSep(rString, nPos) ) // decimal separator in start string
2316 if (SkipBlanks(rString, nPos))
2317 nPos = nStartBlanks; // `. 2` not a decimal separator
2318 else
2319 nDecPos = 1; // leading decimal separator
2321 else if ( GetCurrency(rString, nPos) ) // currency (DM 1)?
2323 eScannedType = SvNumFormatType::CURRENCY; // !!! it IS currency !!!
2324 SkipBlanks(rString, nPos);
2325 if (nSign == 0) // no sign yet
2327 nSign = GetSign(rString, nPos);
2328 if ( nSign ) // DM -1
2330 SkipBlanks(rString, nPos);
2333 if ( GetDecSep(rString, nPos) ) // decimal separator follows currency
2335 if (SkipBlanks(rString, nPos))
2337 nPos = nStartBlanks; // `DM . 2` not a decimal separator
2338 eScannedType = SvNumFormatType::UNDEFINED; // !!! it is NOT currency !!!
2340 else
2341 nDecPos = 1; // leading decimal separator
2344 else
2346 const sal_Int32 nMonthStart = nPos;
2347 short nTempMonth = GetMonth(rString, nPos);
2348 if (nTempMonth < 0)
2350 // Short month and day names may be identical in some locales, e.g.
2351 // "mar" for "martes" or "marzo" in Spanish.
2352 // Do not let a month name immediately take precedence if a day
2353 // name was meant instead. Assume that both could be valid, until
2354 // encountered differently or the final evaluation in
2355 // IsNumberFormat() checks, but continue with weighing the month
2356 // name higher unless we have both day of week and month name here.
2357 sal_Int32 nTempPos = nMonthStart;
2358 nDayOfWeek = GetDayOfWeek( rString, nTempPos);
2359 if (nDayOfWeek < 0)
2361 SkipChar( '.', rString, nTempPos ); // abbreviated
2362 SkipString( pFormatter->GetLocaleData()->getLongDateDayOfWeekSep(), rString, nTempPos );
2363 SkipBlanks( rString, nTempPos);
2364 short nTempTempMonth = GetMonth( rString, nTempPos);
2365 if (nTempTempMonth)
2367 // Fall into the else branch below that handles both.
2368 nTempMonth = 0;
2369 nPos = nMonthStart;
2370 nDayOfWeek = 0;
2371 // Do not set nDayOfWeek hereafter, anywhere.
2375 if ( nTempMonth ) // month (Jan 1)?
2377 // Jan1 without separator is not a date, unless it is followed by a
2378 // separator and a (year) number.
2379 if (nPos < rString.getLength() || (nStringsCnt >= 4 && nNumericsCnt >= 2))
2381 eScannedType = SvNumFormatType::DATE; // !!! it IS a date !!!
2382 nMonth = nTempMonth;
2383 nMonthPos = 1; // month at the beginning
2384 if ( nMonth < 0 )
2386 SkipChar( '.', rString, nPos ); // abbreviated
2388 SkipBlanks(rString, nPos);
2390 else
2392 nPos = nMonthStart; // rewind month
2395 else
2397 int nTempDayOfWeek = GetDayOfWeek( rString, nPos );
2398 if ( nTempDayOfWeek )
2400 // day of week is just parsed away
2401 eScannedType = SvNumFormatType::DATE; // !!! it IS a date !!!
2402 if ( nPos < rString.getLength() )
2404 if ( nTempDayOfWeek < 0 )
2406 // abbreviated
2407 if ( rString[ nPos ] == '.' )
2409 ++nPos;
2412 else
2414 // full long name
2415 SkipBlanks(rString, nPos);
2416 SkipString( pFormatter->GetLocaleData()->getLongDateDayOfWeekSep(), rString, nPos );
2418 SkipBlanks(rString, nPos);
2419 nTempMonth = GetMonth(rString, nPos);
2420 if ( nTempMonth ) // month (Jan 1)?
2422 // Jan1 without separator is not a date, unless it is followed by a
2423 // separator and a (year) number.
2424 if (nPos < rString.getLength() || (nStringsCnt >= 4 && nNumericsCnt >= 2))
2426 nMonth = nTempMonth;
2427 nMonthPos = 1; // month at the beginning
2428 if ( nMonth < 0 )
2430 SkipChar( '.', rString, nPos ); // abbreviated
2432 SkipBlanks(rString, nPos);
2434 else
2436 nPos = nMonthStart; // rewind month
2440 if (!nMonth)
2442 // Determine and remember following date pattern, if any.
2443 IsAcceptedDatePattern( 1);
2447 // Skip one trailing '-' or '/' character to recognize June-2007
2448 if (nMonth && nPos + 1 == rString.getLength())
2450 SkipChar('-', rString, nPos) || SkipChar('/', rString, nPos);
2454 if (nPos < rString.getLength()) // not everything consumed
2456 // Does input StartString equal StartString of format?
2457 // This time with sign detection!
2458 if ( !ScanStringNumFor( rString, nPos, 0 ) )
2460 return MatchedReturn();
2464 return true;
2469 * Analyze string in the middle
2470 * All gone => true
2471 * else => false
2473 bool ImpSvNumberInputScan::ScanMidString( const OUString& rString, sal_uInt16 nStringPos )
2475 sal_Int32 nPos = 0;
2476 SvNumFormatType eOldScannedType = eScannedType;
2478 if ( nMatchedAllStrings )
2479 { // Match against format in any case, so later on for a "1-2-3-4" input
2480 // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2481 // format.
2482 if ( ScanStringNumFor( rString, 0, nStringPos ) )
2484 nMatchedAllStrings |= nMatchedMidString;
2486 else
2488 nMatchedAllStrings = 0;
2492 const sal_Int32 nStartBlanks = nPos;
2493 const bool bBlanks = SkipBlanks(rString, nPos);
2494 if (GetDecSep(rString, nPos)) // decimal separator?
2496 if (nDecPos == 1 || nDecPos == 3) // .12.4 or 1.E2.1
2498 return MatchedReturn();
2500 else if (nDecPos == 2) // . dup: 12.4.
2502 bool bSignedYear = false;
2503 if (bDecSepInDateSeps || // . also date separator
2504 SkipDatePatternSeparator( nStringPos, nPos, bSignedYear))
2506 if ( eScannedType != SvNumFormatType::UNDEFINED &&
2507 eScannedType != SvNumFormatType::DATE &&
2508 eScannedType != SvNumFormatType::DATETIME) // already another type
2510 return MatchedReturn();
2512 if (eScannedType == SvNumFormatType::UNDEFINED)
2514 eScannedType = SvNumFormatType::DATE; // !!! it IS a date
2516 SkipBlanks(rString, nPos);
2518 else
2520 return MatchedReturn();
2523 else if (bBlanks)
2525 // `1 .2` or `1 . 2` not a decimal separator, reset
2526 nPos = nStartBlanks;
2528 else if (SkipBlanks(rString, nPos))
2530 // `1. 2` not a decimal separator, reset
2531 nPos = nStartBlanks;
2533 else
2535 nDecPos = 2; // . in mid string
2538 else if ( (eScannedType & SvNumFormatType::TIME) &&
2539 GetTime100SecSep( rString, nPos ) )
2540 { // hundredth seconds separator
2541 if ( nDecPos )
2543 return MatchedReturn();
2545 nDecPos = 2; // . in mid string
2547 // If this is exactly an ISO 8601 fractional seconds separator, bail
2548 // out early to not get confused by later checks for group separator or
2549 // other.
2550 if (bIso8601Tsep && nPos == rString.getLength() &&
2551 eScannedType == SvNumFormatType::DATETIME && (rString == "." || rString == ","))
2552 return true;
2554 SkipBlanks(rString, nPos);
2557 if (SkipChar('/', rString, nPos)) // fraction?
2559 if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2560 eScannedType != SvNumFormatType::DATE) // except date
2562 return MatchedReturn(); // => jan/31/1994
2564 else if (eScannedType != SvNumFormatType::DATE && // analyzed no date until now
2565 ( eSetType == SvNumFormatType::FRACTION || // and preset was fraction
2566 (nNumericsCnt == 3 && // or 3 numbers
2567 (nStringPos == 3 || // and 3rd string particle
2568 (nStringPos == 4 && nSign))))) // or 4th if signed
2570 SkipBlanks(rString, nPos);
2571 if (nPos == rString.getLength())
2573 eScannedType = SvNumFormatType::FRACTION; // !!! it IS a fraction (so far)
2574 if (eSetType == SvNumFormatType::FRACTION &&
2575 nNumericsCnt == 2 &&
2576 (nStringPos == 1 || // for 4/5
2577 (nStringPos == 2 && nSign))) // or signed -4/5
2579 return true; // don't fall into date trap
2583 else
2585 nPos--; // put '/' back
2589 if (GetThousandSep(rString, nPos, nStringPos)) // 1,000
2591 if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2592 eScannedType != SvNumFormatType::CURRENCY) // except currency
2594 return MatchedReturn();
2596 nThousand++;
2599 const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
2600 bool bSignedYear = false;
2601 bool bDate = SkipDatePatternSeparator( nStringPos, nPos, bSignedYear); // 12/31 31.12. 12/31/1999 31.12.1999
2602 if (!bDate)
2604 const OUString& rDate = pFormatter->GetDateSep();
2605 SkipBlanks(rString, nPos);
2606 bDate = SkipString( rDate, rString, nPos); // 10. 10- 10/
2608 if (bDate || ((MayBeIso8601() || MayBeMonthDate()) && // 1999-12-31 31-Dec-1999
2609 SkipChar( '-', rString, nPos)))
2611 if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2612 eScannedType != SvNumFormatType::DATE) // except date
2614 return MatchedReturn();
2616 SkipBlanks(rString, nPos);
2617 eScannedType = SvNumFormatType::DATE; // !!! it IS a date
2618 short nTmpMonth = GetMonth(rString, nPos); // 10. Jan 94
2619 if (nMonth && nTmpMonth) // month dup
2621 return MatchedReturn();
2623 if (nTmpMonth)
2625 nMonth = nTmpMonth;
2626 nMonthPos = 2; // month in the middle
2627 if ( nMonth < 0 && SkipChar( '.', rString, nPos ) )
2628 ; // short month may be abbreviated Jan.
2629 else if ( SkipChar( '-', rString, nPos ) )
2630 ; // #79632# recognize 17-Jan-2001 to be a date
2631 // #99065# short and long month name
2632 else
2634 SkipString( pLoc->getLongDateMonthSep(), rString, nPos );
2636 SkipBlanks(rString, nPos);
2638 if (bSignedYear)
2640 if (mbEraCE != kDefaultEra) // signed year twice?
2641 return MatchedReturn();
2643 mbEraCE = false; // BCE
2647 const sal_Int32 nMonthStart = nPos;
2648 short nTempMonth = GetMonth(rString, nPos); // month in the middle (10 Jan 94)
2649 if (nTempMonth)
2651 if (nMonth != 0) // month dup
2653 return MatchedReturn();
2655 if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2656 eScannedType != SvNumFormatType::DATE) // except date
2658 return MatchedReturn();
2660 if (nMonthStart > 0 && nPos < rString.getLength()) // 10Jan or Jan94 without separator are not dates
2662 eScannedType = SvNumFormatType::DATE; // !!! it IS a date
2663 nMonth = nTempMonth;
2664 nMonthPos = 2; // month in the middle
2665 if ( nMonth < 0 )
2667 SkipChar( '.', rString, nPos ); // abbreviated
2669 SkipString( pLoc->getLongDateMonthSep(), rString, nPos );
2670 SkipBlanks(rString, nPos);
2672 else
2674 nPos = nMonthStart; // rewind month
2678 if ( SkipChar('E', rString, nPos) || // 10E, 10e, 10,Ee
2679 SkipChar('e', rString, nPos) )
2681 if (eScannedType != SvNumFormatType::UNDEFINED) // already another type
2683 return MatchedReturn();
2685 else
2687 SkipBlanks(rString, nPos);
2688 eScannedType = SvNumFormatType::SCIENTIFIC; // !!! it IS scientific
2689 if ( nThousand+2 == nNumericsCnt && nDecPos == 2 ) // special case 1.E2
2691 nDecPos = 3; // 1,100.E2 1,100,100.E3
2694 nESign = GetESign(rString, nPos); // signed exponent?
2695 SkipBlanks(rString, nPos);
2698 const OUString& rTime = pLoc->getTimeSep();
2699 if ( SkipString(rTime, rString, nPos) ) // time separator?
2701 if (nDecPos) // already . => maybe error
2703 if (bDecSepInDateSeps) // . also date sep
2705 if ( eScannedType != SvNumFormatType::DATE && // already another type than date
2706 eScannedType != SvNumFormatType::DATETIME) // or date time
2708 return MatchedReturn();
2710 if (eScannedType == SvNumFormatType::DATE)
2712 nDecPos = 0; // reset for time transition
2715 else
2717 return MatchedReturn();
2720 if ((eScannedType == SvNumFormatType::DATE || // already date type
2721 eScannedType == SvNumFormatType::DATETIME) && // or date time
2722 nNumericsCnt > 3) // and more than 3 numbers? (31.Dez.94 8:23)
2724 SkipBlanks(rString, nPos);
2725 eScannedType = SvNumFormatType::DATETIME; // !!! it IS date with time
2727 else if ( eScannedType != SvNumFormatType::UNDEFINED && // already another type
2728 eScannedType != SvNumFormatType::TIME) // except time
2730 return MatchedReturn();
2732 else
2734 SkipBlanks(rString, nPos);
2735 eScannedType = SvNumFormatType::TIME; // !!! it IS a time
2737 if ( !nTimePos )
2739 nTimePos = nStringPos + 1;
2743 if (nPos < rString.getLength())
2745 switch (eScannedType)
2747 case SvNumFormatType::DATE:
2748 if (nMonthPos == 1 && pLoc->getLongDateOrder() == DateOrder::MDY)
2750 // #68232# recognize long date separators like ", " in "September 5, 1999"
2751 if (SkipString( pLoc->getLongDateDaySep(), rString, nPos ))
2753 SkipBlanks( rString, nPos );
2756 else if (nPos == 0 && rString.getLength() == 1 && MayBeIso8601())
2758 if ( (nStringPos == 5 && rString[0] == 'T') ||
2759 (nStringPos == 6 && rString[0] == 'T' && sStrArray[0] == "-"))
2761 // ISO 8601 combined date and time, yyyy-mm-ddThh:mm or -yyyy-mm-ddThh:mm
2762 ++nPos;
2763 bIso8601Tsep = true;
2765 else if (nStringPos == 7 && rString[0] == ':')
2767 // ISO 8601 combined date and time, the time part; we reach
2768 // here if the locale's separator is not ':' so it couldn't
2769 // be detected above in the time block.
2770 if (nNumericsCnt >= 5)
2771 eScannedType = SvNumFormatType::DATETIME;
2772 ++nPos;
2775 break;
2776 case SvNumFormatType::DATETIME:
2777 if (nPos == 0 && rString.getLength() == 1 && MayBeIso8601())
2779 if (nStringPos == 9 && rString[0] == ':')
2781 // ISO 8601 combined date and time, the time part continued.
2782 ++nPos;
2785 #if NF_RECOGNIZE_ISO8601_TIMEZONES
2786 else if (nPos == 0 && rString.getLength() == 1 && nStringPos >= 9 && MayBeIso8601())
2788 // ISO 8601 timezone offset
2789 switch (rString[ 0 ])
2791 case '+':
2792 case '-':
2793 if (nStringPos == nStringsCnt - 2 ||
2794 nStringPos == nStringsCnt - 4)
2796 ++nPos; // yyyy-mm-ddThh:mm[:ss]+xx[[:]yy]
2797 // nTimezonePos needed for GetTimeRef()
2798 if (!nTimezonePos)
2800 nTimezonePos = nStringPos + 1;
2803 break;
2804 case ':':
2805 if (nTimezonePos && nStringPos >= 11 &&
2806 nStringPos == nStringsCnt - 2)
2808 ++nPos; // yyyy-mm-ddThh:mm[:ss]+xx:yy
2810 break;
2813 #endif
2814 break;
2815 default: break;
2819 if (nPos < rString.getLength()) // not everything consumed?
2821 if ( nMatchedAllStrings & ~nMatchedVirgin )
2823 eScannedType = eOldScannedType;
2825 else
2827 return false;
2831 return true;
2836 * Analyze the end
2837 * All gone => true
2838 * else => false
2840 bool ImpSvNumberInputScan::ScanEndString( const OUString& rString )
2842 sal_Int32 nPos = 0;
2844 if ( nMatchedAllStrings )
2845 { // Match against format in any case, so later on for a "1-2-3-4" input
2846 // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2847 // format.
2848 if ( ScanStringNumFor( rString, 0, 0xFFFF ) )
2850 nMatchedAllStrings |= nMatchedEndString;
2852 else
2854 nMatchedAllStrings = 0;
2858 const sal_Int32 nStartBlanks = nPos;
2859 const bool bBlanks = SkipBlanks(rString, nPos);
2860 if (GetDecSep(rString, nPos)) // decimal separator?
2862 if (nDecPos == 1 || nDecPos == 3) // .12.4 or 12.E4.
2864 return MatchedReturn();
2866 else if (nDecPos == 2) // . dup: 12.4.
2868 bool bSignedYear = false;
2869 if (bDecSepInDateSeps || // . also date separator
2870 SkipDatePatternSeparator( nStringsCnt-1, nPos, bSignedYear))
2872 if ( eScannedType != SvNumFormatType::UNDEFINED &&
2873 eScannedType != SvNumFormatType::DATE &&
2874 eScannedType != SvNumFormatType::DATETIME) // already another type
2876 return MatchedReturn();
2878 if (eScannedType == SvNumFormatType::UNDEFINED)
2880 eScannedType = SvNumFormatType::DATE; // !!! it IS a date
2882 SkipBlanks(rString, nPos);
2884 else
2886 return MatchedReturn();
2889 else if (bBlanks)
2891 // not a decimal separator, reset
2892 nPos = nStartBlanks;
2894 else
2896 nDecPos = 3; // . in end string
2897 SkipBlanks(rString, nPos);
2901 bool bSignDetectedHere = false;
2902 if ( nSign == 0 && // conflict - not signed
2903 eScannedType != SvNumFormatType::DATE) // and not date
2904 //!? catch time too?
2905 { // not signed yet
2906 nSign = GetSign(rString, nPos); // 1- DM
2907 if (bNegCheck) // '(' as sign
2909 return MatchedReturn();
2911 if (nSign)
2913 bSignDetectedHere = true;
2917 SkipBlanks(rString, nPos);
2918 if (bNegCheck && SkipChar(')', rString, nPos)) // skip ')' if appropriate
2920 bNegCheck = false;
2921 SkipBlanks(rString, nPos);
2924 if ( GetCurrency(rString, nPos) ) // currency symbol?
2926 if (eScannedType != SvNumFormatType::UNDEFINED) // currency dup
2928 return MatchedReturn();
2930 else
2932 SkipBlanks(rString, nPos);
2933 eScannedType = SvNumFormatType::CURRENCY;
2934 } // behind currency a '-' is allowed
2935 if (nSign == 0) // not signed yet
2937 nSign = GetSign(rString, nPos); // DM -
2938 SkipBlanks(rString, nPos);
2939 if (bNegCheck) // 3 DM (
2941 return MatchedReturn();
2944 if ( bNegCheck && eScannedType == SvNumFormatType::CURRENCY &&
2945 SkipChar(')', rString, nPos) )
2947 bNegCheck = false; // ')' skipped
2948 SkipBlanks(rString, nPos); // only if currency
2952 if ( SkipChar('%', rString, nPos) ) // 1%
2954 if (eScannedType != SvNumFormatType::UNDEFINED) // already another type
2956 return MatchedReturn();
2958 SkipBlanks(rString, nPos);
2959 eScannedType = SvNumFormatType::PERCENT;
2962 const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
2963 const OUString& rTime = pLoc->getTimeSep();
2964 if ( SkipString(rTime, rString, nPos) ) // 10:
2966 if (nDecPos) // already , => error
2968 return MatchedReturn();
2970 if (eScannedType == SvNumFormatType::DATE && nNumericsCnt > 2) // 31.Dez.94 8:
2972 SkipBlanks(rString, nPos);
2973 eScannedType = SvNumFormatType::DATETIME;
2975 else if (eScannedType != SvNumFormatType::UNDEFINED &&
2976 eScannedType != SvNumFormatType::TIME) // already another type
2978 return MatchedReturn();
2980 else
2982 SkipBlanks(rString, nPos);
2983 eScannedType = SvNumFormatType::TIME;
2985 if ( !nTimePos )
2987 nTimePos = nStringsCnt;
2991 bool bSignedYear = false;
2992 bool bDate = SkipDatePatternSeparator( nStringsCnt-1, nPos, bSignedYear); // 12/31 31.12. 12/31/1999 31.12.1999
2993 if (!bDate)
2995 const OUString& rDate = pFormatter->GetDateSep();
2996 bDate = SkipString( rDate, rString, nPos); // 10. 10- 10/
2998 if (bDate && bSignDetectedHere)
3000 nSign = 0; // 'D-' takes precedence over signed date
3002 if (bDate || ((MayBeIso8601() || MayBeMonthDate())
3003 && SkipChar( '-', rString, nPos)))
3005 if (eScannedType != SvNumFormatType::UNDEFINED &&
3006 eScannedType != SvNumFormatType::DATE) // already another type
3008 return MatchedReturn();
3010 else
3012 SkipBlanks(rString, nPos);
3013 eScannedType = SvNumFormatType::DATE;
3015 short nTmpMonth = GetMonth(rString, nPos); // 10. Jan
3016 if (nMonth && nTmpMonth) // month dup
3018 return MatchedReturn();
3020 if (nTmpMonth)
3022 nMonth = nTmpMonth;
3023 nMonthPos = 3; // month at end
3024 if ( nMonth < 0 )
3026 SkipChar( '.', rString, nPos ); // abbreviated
3028 SkipBlanks(rString, nPos);
3032 const sal_Int32 nMonthStart = nPos;
3033 short nTempMonth = GetMonth(rString, nPos); // 10 Jan
3034 if (nTempMonth)
3036 if (nMonth) // month dup
3038 return MatchedReturn();
3040 if (eScannedType != SvNumFormatType::UNDEFINED &&
3041 eScannedType != SvNumFormatType::DATE) // already another type
3043 return MatchedReturn();
3045 if (nMonthStart > 0) // 10Jan without separator is not a date
3047 eScannedType = SvNumFormatType::DATE;
3048 nMonth = nTempMonth;
3049 nMonthPos = 3; // month at end
3050 if ( nMonth < 0 )
3052 SkipChar( '.', rString, nPos ); // abbreviated
3054 SkipBlanks(rString, nPos);
3056 else
3058 nPos = nMonthStart; // rewind month
3062 sal_Int32 nOrigPos = nPos;
3063 if (GetTimeAmPm(rString, nPos))
3065 if (eScannedType != SvNumFormatType::UNDEFINED &&
3066 eScannedType != SvNumFormatType::TIME &&
3067 eScannedType != SvNumFormatType::DATETIME) // already another type
3069 return MatchedReturn();
3071 else
3073 // If not already scanned as time, 6.78am does not result in 6
3074 // seconds and 78 hundredths in the morning. Keep as suffix.
3075 if (eScannedType != SvNumFormatType::TIME && nDecPos == 2 && nNumericsCnt == 2)
3077 nPos = nOrigPos; // rewind am/pm
3079 else
3081 SkipBlanks(rString, nPos);
3082 if ( eScannedType != SvNumFormatType::DATETIME )
3084 eScannedType = SvNumFormatType::TIME;
3090 if ( bNegCheck && SkipChar(')', rString, nPos) )
3092 if (eScannedType == SvNumFormatType::CURRENCY) // only if currency
3094 bNegCheck = false; // skip ')'
3095 SkipBlanks(rString, nPos);
3097 else
3099 return MatchedReturn();
3103 if ( nPos < rString.getLength() &&
3104 (eScannedType == SvNumFormatType::DATE ||
3105 eScannedType == SvNumFormatType::DATETIME) )
3107 // day of week is just parsed away
3108 sal_Int32 nOldPos = nPos;
3109 const OUString& rSep = pFormatter->GetLocaleData()->getLongDateDayOfWeekSep();
3110 if ( StringContains( rSep, rString, nPos ) )
3112 nPos = nPos + rSep.getLength();
3113 SkipBlanks(rString, nPos);
3115 int nTempDayOfWeek = GetDayOfWeek( rString, nPos );
3116 if ( nTempDayOfWeek )
3118 if ( nPos < rString.getLength() )
3120 if ( nTempDayOfWeek < 0 )
3121 { // short
3122 if ( rString[ nPos ] == '.' )
3124 ++nPos;
3127 SkipBlanks(rString, nPos);
3130 else
3132 nPos = nOldPos;
3136 #if NF_RECOGNIZE_ISO8601_TIMEZONES
3137 if (nPos == 0 && eScannedType == SvNumFormatType::DATETIME &&
3138 rString.getLength() == 1 && rString[ 0 ] == 'Z' && MayBeIso8601())
3140 // ISO 8601 timezone UTC yyyy-mm-ddThh:mmZ
3141 ++nPos;
3143 #endif
3145 if (nPos < rString.getLength()) // everything consumed?
3147 // does input EndString equal EndString in Format?
3148 if ( !ScanStringNumFor( rString, nPos, 0xFFFF ) )
3150 return false;
3154 return true;
3158 bool ImpSvNumberInputScan::ScanStringNumFor( const OUString& rString, // String to scan
3159 sal_Int32 nPos, // Position until which was consumed
3160 sal_uInt16 nString, // Substring of format, 0xFFFF => last
3161 bool bDontDetectNegation) // Suppress sign detection
3163 if ( !mpFormat )
3165 return false;
3167 const ::utl::TransliterationWrapper* pTransliteration = pFormatter->GetTransliteration();
3168 const OUString* pStr;
3169 OUString aString( rString );
3170 bool bFound = false;
3171 bool bFirst = true;
3172 bool bContinue = true;
3173 sal_uInt16 nSub;
3176 // Don't try "lower" subformats ff the very first match was the second
3177 // or third subformat.
3178 nSub = nStringScanNumFor;
3180 { // Step through subformats, first positive, then negative, then
3181 // other, but not the last (text) subformat.
3182 pStr = mpFormat->GetNumForString( nSub, nString, true );
3183 if ( pStr && pTransliteration->isEqual( aString, *pStr ) )
3185 bFound = true;
3186 bContinue = false;
3188 else if ( nSub < 2 )
3190 ++nSub;
3192 else
3194 bContinue = false;
3197 while ( bContinue );
3198 if ( !bFound && bFirst && nPos )
3200 // try remaining substring
3201 bFirst = false;
3202 aString = aString.copy(nPos);
3203 bContinue = true;
3206 while ( bContinue );
3208 if ( !bFound )
3210 if ( !bDontDetectNegation && (nString == 0) &&
3211 !bFirst && (nSign < 0) && mpFormat->IsSecondSubformatRealNegative() )
3213 // simply negated twice? --1
3214 aString = aString.replaceAll(" ", "");
3215 if ( (aString.getLength() == 1) && (aString[0] == '-') )
3217 bFound = true;
3218 nStringScanSign = -1;
3219 nSub = 0; //! not 1
3222 if ( !bFound )
3224 return false;
3227 else if ( !bDontDetectNegation && (nSub == 1) &&
3228 mpFormat->IsSecondSubformatRealNegative() )
3230 // negative
3231 if ( nStringScanSign < 0 )
3233 if ( (nSign < 0) && (nStringScanNumFor != 1) )
3235 nStringScanSign = 1; // triple negated --1 yyy
3238 else if ( nStringScanSign == 0 )
3240 if ( nSign < 0 )
3241 { // nSign and nStringScanSign will be combined later,
3242 // flip sign if doubly negated
3243 if ( (nString == 0) && !bFirst &&
3244 SvNumberformat::HasStringNegativeSign( aString ) )
3246 nStringScanSign = -1; // direct double negation
3248 else if ( mpFormat->IsNegativeWithoutSign() )
3250 nStringScanSign = -1; // indirect double negation
3253 else
3255 nStringScanSign = -1;
3258 else // > 0
3260 nStringScanSign = -1;
3263 nStringScanNumFor = nSub;
3264 return true;
3269 * Recognizes types of number, exponential, fraction, percent, currency, date, time.
3270 * Else text => return false
3272 bool ImpSvNumberInputScan::IsNumberFormatMain( const OUString& rString, // string to be analyzed
3273 const SvNumberformat* pFormat ) // maybe number format set to match against
3275 Reset();
3276 mpFormat = pFormat;
3277 NumberStringDivision( rString ); // breakdown into strings and numbers
3278 if (nStringsCnt >= SV_MAX_COUNT_INPUT_STRINGS) // too many elements
3280 return false; // Njet, Nope, ...
3282 if (nNumericsCnt == 0) // no number in input
3284 if ( nStringsCnt > 0 )
3286 // Here we may change the original, we don't need it anymore.
3287 // This saves copies and ToUpper() in GetLogical() and is faster.
3288 sStrArray[0] = comphelper::string::strip(sStrArray[0], ' ');
3289 OUString& rStrArray = sStrArray[0];
3290 nLogical = GetLogical( rStrArray );
3291 if ( nLogical )
3293 eScannedType = SvNumFormatType::LOGICAL; // !!! it's a BOOLEAN
3294 nMatchedAllStrings &= ~nMatchedVirgin;
3295 return true;
3297 else
3299 return false; // simple text
3302 else
3304 return false; // simple text
3308 sal_uInt16 i = 0; // mark any symbol
3309 sal_uInt16 j = 0; // mark only numbers
3311 switch ( nNumericsCnt )
3313 case 1 : // Exactly 1 number in input
3314 // nStringsCnt >= 1
3315 if (GetNextNumber(i,j)) // i=1,0
3316 { // Number at start
3317 if (eSetType == SvNumFormatType::FRACTION) // Fraction 1 = 1/1
3319 if (i >= nStringsCnt || // no end string nor decimal separator
3320 pFormatter->IsDecimalSep( sStrArray[i]))
3322 eScannedType = SvNumFormatType::FRACTION;
3323 nMatchedAllStrings &= ~nMatchedVirgin;
3324 return true;
3328 else
3329 { // Analyze start string
3330 if (!ScanStartString( sStrArray[i] )) // i=0
3332 return false; // already an error
3334 i++; // next symbol, i=1
3336 GetNextNumber(i,j); // i=1,2
3337 if (eSetType == SvNumFormatType::FRACTION) // Fraction -1 = -1/1
3339 if (nSign && !bNegCheck && // Sign +, -
3340 eScannedType == SvNumFormatType::UNDEFINED && // not date or currency
3341 nDecPos == 0 && // no previous decimal separator
3342 (i >= nStringsCnt || // no end string nor decimal separator
3343 pFormatter->IsDecimalSep( sStrArray[i]))
3346 eScannedType = SvNumFormatType::FRACTION;
3347 nMatchedAllStrings &= ~nMatchedVirgin;
3348 return true;
3351 if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3353 return false;
3355 break;
3356 case 2 : // Exactly 2 numbers in input
3357 // nStringsCnt >= 3
3358 if (!GetNextNumber(i,j)) // i=1,0
3359 { // Analyze start string
3360 if (!ScanStartString( sStrArray[i] ))
3361 return false; // already an error
3362 i++; // i=1
3364 GetNextNumber(i,j); // i=1,2
3365 if ( !ScanMidString( sStrArray[i], i ) )
3367 return false;
3369 i++; // next symbol, i=2,3
3370 GetNextNumber(i,j); // i=3,4
3371 if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3373 return false;
3375 if (eSetType == SvNumFormatType::FRACTION) // -1,200. as fraction
3377 if (!bNegCheck && // no sign '('
3378 eScannedType == SvNumFormatType::UNDEFINED &&
3379 (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end
3382 eScannedType = SvNumFormatType::FRACTION;
3383 nMatchedAllStrings &= ~nMatchedVirgin;
3384 return true;
3387 break;
3388 case 3 : // Exactly 3 numbers in input
3389 // nStringsCnt >= 5
3390 if (!GetNextNumber(i,j)) // i=1,0
3391 { // Analyze start string
3392 if (!ScanStartString( sStrArray[i] ))
3394 return false; // already an error
3396 i++; // i=1
3397 if (nDecPos == 1) // decimal separator at start => error
3399 return false;
3402 GetNextNumber(i,j); // i=1,2
3403 if ( !ScanMidString( sStrArray[i], i ) )
3405 return false;
3407 i++; // i=2,3
3408 if (eScannedType == SvNumFormatType::SCIENTIFIC) // E only at end
3410 return false;
3412 GetNextNumber(i,j); // i=3,4
3413 if ( !ScanMidString( sStrArray[i], i ) )
3415 return false;
3417 i++; // i=4,5
3418 GetNextNumber(i,j); // i=5,6
3419 if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3421 return false;
3423 if (eSetType == SvNumFormatType::FRACTION) // -1,200,100. as fraction
3425 if (!bNegCheck && // no sign '('
3426 eScannedType == SvNumFormatType::UNDEFINED &&
3427 (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end
3430 eScannedType = SvNumFormatType::FRACTION;
3431 nMatchedAllStrings &= ~nMatchedVirgin;
3432 return true;
3435 if ( eScannedType == SvNumFormatType::FRACTION && nDecPos )
3437 return false; // #36857# not a real fraction
3439 break;
3440 default: // More than 3 numbers in input
3441 // nStringsCnt >= 7
3442 if (!GetNextNumber(i,j)) // i=1,0
3443 { // Analyze startstring
3444 if (!ScanStartString( sStrArray[i] ))
3445 return false; // already an error
3446 i++; // i=1
3447 if (nDecPos == 1) // decimal separator at start => error
3448 return false;
3450 GetNextNumber(i,j); // i=1,2
3451 if ( !ScanMidString( sStrArray[i], i ) )
3453 return false;
3455 i++; // i=2,3
3457 sal_uInt16 nThOld = 10; // just not 0 or 1
3458 while (nThOld != nThousand && j < nNumericsCnt-1) // Execute at least one time
3459 // but leave one number.
3460 { // Loop over group separators
3461 nThOld = nThousand;
3462 if (eScannedType == SvNumFormatType::SCIENTIFIC) // E only at end
3464 return false;
3466 GetNextNumber(i,j);
3467 if ( i < nStringsCnt && !ScanMidString( sStrArray[i], i ) )
3469 return false;
3471 i++;
3474 if (eScannedType == SvNumFormatType::DATE || // long date or
3475 eScannedType == SvNumFormatType::TIME || // long time or
3476 eScannedType == SvNumFormatType::UNDEFINED) // long number
3478 for (sal_uInt16 k = j; k < nNumericsCnt-1; k++)
3480 if (eScannedType == SvNumFormatType::SCIENTIFIC) // E only at endd
3482 return false;
3484 GetNextNumber(i,j);
3485 if ( i < nStringsCnt && !ScanMidString( sStrArray[i], i ) )
3487 return false;
3489 i++;
3492 GetNextNumber(i,j);
3493 if (i < nStringsCnt && !ScanEndString( sStrArray[i] ))
3495 return false;
3497 if (eSetType == SvNumFormatType::FRACTION) // -1,200,100. as fraction
3499 if (!bNegCheck && // no sign '('
3500 eScannedType == SvNumFormatType::UNDEFINED &&
3501 (nDecPos == 0 || nDecPos == 3) // no decimal separator or at end
3504 eScannedType = SvNumFormatType::FRACTION;
3505 nMatchedAllStrings &= ~nMatchedVirgin;
3506 return true;
3509 if ( eScannedType == SvNumFormatType::FRACTION && nDecPos )
3511 return false; // #36857# not a real fraction
3513 break;
3516 if (eScannedType == SvNumFormatType::UNDEFINED)
3518 nMatchedAllStrings &= ~nMatchedVirgin;
3519 // did match including nMatchedUsedAsReturn
3520 bool bDidMatch = (nMatchedAllStrings != 0);
3521 if ( nMatchedAllStrings )
3523 bool bMatch = mpFormat && mpFormat->IsNumForStringElementCountEqual(
3524 nStringScanNumFor, nStringsCnt, nNumericsCnt );
3525 if ( !bMatch )
3527 nMatchedAllStrings = 0;
3530 if ( nMatchedAllStrings )
3532 // A type DEFINED means that no category could be assigned to the
3533 // overall format because of mixed type subformats. Use the scan
3534 // matched subformat's type if any.
3535 SvNumFormatType eForType = eSetType;
3536 if ((eForType == SvNumFormatType::UNDEFINED || eForType == SvNumFormatType::DEFINED) && mpFormat)
3537 eForType = mpFormat->GetNumForInfoScannedType( nStringScanNumFor);
3538 if (eForType != SvNumFormatType::UNDEFINED && eForType != SvNumFormatType::DEFINED)
3539 eScannedType = eForType;
3540 else
3541 eScannedType = SvNumFormatType::NUMBER;
3543 else if ( bDidMatch )
3545 // Accept a plain fractional number like 123.45 as there may be a
3546 // decimal separator also present as literal like in a 0"."0 weirdo
3547 // format.
3548 if (nDecPos != 2 || nNumericsCnt != 2)
3549 return false;
3550 eScannedType = SvNumFormatType::NUMBER;
3552 else
3554 eScannedType = SvNumFormatType::NUMBER;
3555 // everything else should have been recognized by now
3558 else if ( eScannedType == SvNumFormatType::DATE )
3560 // the very relaxed date input checks may interfere with a preset format
3561 nMatchedAllStrings &= ~nMatchedVirgin;
3562 bool bWasReturn = ((nMatchedAllStrings & nMatchedUsedAsReturn) != 0);
3563 if ( nMatchedAllStrings )
3565 bool bMatch = mpFormat && mpFormat->IsNumForStringElementCountEqual(
3566 nStringScanNumFor, nStringsCnt, nNumericsCnt );
3567 if ( !bMatch )
3569 nMatchedAllStrings = 0;
3572 if ( nMatchedAllStrings )
3574 // A type DEFINED means that no category could be assigned to the
3575 // overall format because of mixed type subformats. Do not override
3576 // the scanned type in this case. Otherwise in IsNumberFormat() the
3577 // first numeric particle would be accepted as number.
3578 SvNumFormatType eForType = eSetType;
3579 if ((eForType == SvNumFormatType::UNDEFINED || eForType == SvNumFormatType::DEFINED) && mpFormat)
3580 eForType = mpFormat->GetNumForInfoScannedType( nStringScanNumFor);
3581 if (eForType != SvNumFormatType::UNDEFINED && eForType != SvNumFormatType::DEFINED)
3582 eScannedType = eForType;
3584 else if ( bWasReturn )
3586 return false;
3589 else
3591 nMatchedAllStrings = 0; // reset flag to no substrings matched
3593 return true;
3598 * Return true or false depending on the nMatched... state and remember usage
3600 bool ImpSvNumberInputScan::MatchedReturn()
3602 if ( nMatchedAllStrings & ~nMatchedVirgin )
3604 nMatchedAllStrings |= nMatchedUsedAsReturn;
3605 return true;
3607 return false;
3612 * Initialize uppercase months and weekdays
3614 void ImpSvNumberInputScan::InitText()
3616 sal_Int32 j, nElems;
3617 const CharClass* pChrCls = pFormatter->GetCharClass();
3618 const CalendarWrapper* pCal = pFormatter->GetCalendar();
3620 pUpperMonthText.reset();
3621 pUpperAbbrevMonthText.reset();
3622 css::uno::Sequence< css::i18n::CalendarItem2 > xElems = pCal->getMonths();
3623 nElems = xElems.getLength();
3624 pUpperMonthText.reset( new OUString[nElems] );
3625 pUpperAbbrevMonthText.reset( new OUString[nElems] );
3626 for ( j = 0; j < nElems; j++ )
3628 pUpperMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3629 pUpperAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3632 pUpperGenitiveMonthText.reset();
3633 pUpperGenitiveAbbrevMonthText.reset();
3634 xElems = pCal->getGenitiveMonths();
3635 bScanGenitiveMonths = (nElems != xElems.getLength());
3636 nElems = xElems.getLength();
3637 pUpperGenitiveMonthText.reset( new OUString[nElems] );
3638 pUpperGenitiveAbbrevMonthText.reset( new OUString[nElems] );
3639 for ( j = 0; j < nElems; j++ )
3641 pUpperGenitiveMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3642 pUpperGenitiveAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3643 if (!bScanGenitiveMonths &&
3644 (pUpperGenitiveMonthText[j] != pUpperMonthText[j] ||
3645 pUpperGenitiveAbbrevMonthText[j] != pUpperAbbrevMonthText[j]))
3647 bScanGenitiveMonths = true;
3651 pUpperPartitiveMonthText.reset();
3652 pUpperPartitiveAbbrevMonthText.reset();
3653 xElems = pCal->getPartitiveMonths();
3654 bScanPartitiveMonths = (nElems != xElems.getLength());
3655 nElems = xElems.getLength();
3656 pUpperPartitiveMonthText.reset( new OUString[nElems] );
3657 pUpperPartitiveAbbrevMonthText.reset( new OUString[nElems] );
3658 for ( j = 0; j < nElems; j++ )
3660 pUpperPartitiveMonthText[j] = pChrCls->uppercase( xElems[j].FullName );
3661 pUpperPartitiveAbbrevMonthText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3662 if (!bScanPartitiveMonths &&
3663 (pUpperPartitiveMonthText[j] != pUpperGenitiveMonthText[j] ||
3664 pUpperPartitiveAbbrevMonthText[j] != pUpperGenitiveAbbrevMonthText[j]))
3666 bScanPartitiveMonths = true;
3670 pUpperDayText.reset();
3671 pUpperAbbrevDayText.reset();
3672 xElems = pCal->getDays();
3673 nElems = xElems.getLength();
3674 pUpperDayText.reset( new OUString[nElems] );
3675 pUpperAbbrevDayText.reset( new OUString[nElems] );
3676 for ( j = 0; j < nElems; j++ )
3678 pUpperDayText[j] = pChrCls->uppercase( xElems[j].FullName );
3679 pUpperAbbrevDayText[j] = pChrCls->uppercase( xElems[j].AbbrevName );
3682 bTextInitialized = true;
3687 * MUST be called if International/Locale is changed
3689 void ImpSvNumberInputScan::ChangeIntl()
3691 sal_Unicode cDecSep = pFormatter->GetNumDecimalSep()[0];
3692 bDecSepInDateSeps = ( cDecSep == '-' ||
3693 cDecSep == pFormatter->GetDateSep()[0] );
3694 if (!bDecSepInDateSeps)
3696 sal_Unicode cDecSepAlt = pFormatter->GetNumDecimalSepAlt().toChar();
3697 bDecSepInDateSeps = cDecSepAlt && (cDecSepAlt == '-' || cDecSepAlt == pFormatter->GetDateSep()[0]);
3699 bTextInitialized = false;
3700 aUpperCurrSymbol.clear();
3701 InvalidateDateAcceptancePatterns();
3705 void ImpSvNumberInputScan::InvalidateDateAcceptancePatterns()
3707 if (sDateAcceptancePatterns.hasElements())
3709 sDateAcceptancePatterns = css::uno::Sequence< OUString >();
3714 void ImpSvNumberInputScan::ChangeNullDate( const sal_uInt16 Day,
3715 const sal_uInt16 Month,
3716 const sal_Int16 Year )
3718 if ( pNullDate )
3720 *pNullDate = Date(Day, Month, Year);
3722 else
3724 pNullDate.reset(new Date(Day, Month, Year));
3730 * Does rString represent a number (also date, time et al)
3732 bool ImpSvNumberInputScan::IsNumberFormat( const OUString& rString, // string to be analyzed
3733 SvNumFormatType& F_Type, // IN: old type, OUT: new type
3734 double& fOutNumber, // OUT: number if convertible
3735 const SvNumberformat* pFormat, // maybe a number format to match against
3736 SvNumInputOptions eInputOptions )
3738 bool res; // return value
3739 sal_uInt16 k;
3740 eSetType = F_Type; // old type set
3742 if ( !rString.getLength() )
3744 res = false;
3746 else if (rString.getLength() > 308) // arbitrary
3748 res = false;
3750 else
3752 // NoMoreUpperNeeded, all comparisons on UpperCase
3753 OUString aString = pFormatter->GetCharClass()->uppercase( rString );
3754 // convert native number to ASCII if necessary
3755 TransformInput(pFormatter, aString);
3756 res = IsNumberFormatMain( aString, pFormat );
3759 if (res)
3761 // Accept signed date only for ISO date with at least four digits in
3762 // year to not have an input of -M-D-Y arbitrarily recognized. The
3763 // final order is only determined in GetDateRef().
3764 // Also accept for Y/M/D date pattern match, i.e. if the first number
3765 // is year.
3766 // Accept only if the year immediately follows the sign character with
3767 // no space in between.
3768 if (nSign && (eScannedType == SvNumFormatType::DATE ||
3769 eScannedType == SvNumFormatType::DATETIME) && mbEraCE == kDefaultEra &&
3770 (IsDatePatternNumberOfType(0,'Y') || (MayBeIso8601() && sStrArray[nNums[0]].getLength() >= 4)))
3772 const sal_Unicode c = sStrArray[0][sStrArray[0].getLength()-1];
3773 if (c == '-' || c == '+')
3775 // A '+' sign doesn't change the era.
3776 if (nSign < 0)
3777 mbEraCE = false; // BCE
3778 nSign = 0;
3781 if ( bNegCheck || // ')' not found for '('
3782 (nSign && (eScannedType == SvNumFormatType::DATE ||
3783 eScannedType == SvNumFormatType::DATETIME))) // signed date/datetime
3785 res = false;
3787 else
3788 { // check count of partial number strings
3789 switch (eScannedType)
3791 case SvNumFormatType::PERCENT:
3792 case SvNumFormatType::CURRENCY:
3793 case SvNumFormatType::NUMBER:
3794 if (nDecPos == 1) // .05
3796 // Matched MidStrings function like group separators, but
3797 // there can't be an integer part numeric input, so
3798 // effectively 0 thousands groups.
3799 if ( nMatchedAllStrings )
3801 nThousand = 0;
3803 else if ( nNumericsCnt != 1 )
3805 res = false;
3808 else if (nDecPos == 2) // 1.05
3810 // Matched MidStrings function like group separators, but
3811 // let a decimal separator override a literal separator
3812 // string; like 0"." with input 123.45
3813 if ( nMatchedAllStrings )
3815 if (nNumericsCnt == 2)
3816 nThousand = 0;
3817 else
3819 // Assume that if there was a decimal separator
3820 // matching also a literal string then it was the
3821 // last. We could find the last possible match to
3822 // support literals in fractions, but really..
3823 nThousand = nNumericsCnt - 1;
3826 else if ( nNumericsCnt != nThousand+2 )
3828 res = false;
3831 else // 1,100 or 1,100.
3833 // matched MidStrings function like group separators
3834 if ( nMatchedAllStrings )
3836 nThousand = nNumericsCnt - 1;
3838 else if ( nNumericsCnt != nThousand+1 )
3840 res = false;
3843 break;
3845 case SvNumFormatType::SCIENTIFIC: // 1.0e-2
3846 if (nDecPos == 1) // .05
3848 if (nNumericsCnt != 2)
3850 res = false;
3853 else if (nDecPos == 2) // 1.05
3855 if (nNumericsCnt != nThousand+3)
3857 res = false;
3860 else // 1,100 or 1,100.
3862 if (nNumericsCnt != nThousand+2)
3864 res = false;
3867 break;
3869 case SvNumFormatType::DATE:
3870 if (nMonth < 0 && nDayOfWeek < 0 && nNumericsCnt == 3)
3872 // If both, short month name and day of week name were
3873 // detected, and also numbers for full date, assume that we
3874 // have a day of week instead of month name.
3875 nMonth = 0;
3876 nMonthPos = 0;
3878 if (nMonth)
3879 { // month name and numbers
3880 if (nNumericsCnt > 2)
3882 res = false;
3885 else
3887 if (nNumericsCnt > 3)
3889 res = false;
3891 else
3893 // Even if a date pattern was matched, for abbreviated
3894 // pattern like "D.M." an input of "D.M. #" was
3895 // accepted because # could had been a time. Here we do
3896 // not have a combined date/time input though and #
3897 // would be taken as Year in this example, which it is
3898 // not. The count of numbers in pattern must match the
3899 // count of numbers in input.
3900 res = (GetDatePatternNumbers() == nNumericsCnt)
3901 || IsAcceptableIso8601() || nMatchedAllStrings;
3904 break;
3906 case SvNumFormatType::TIME:
3907 if (nDecPos)
3908 { // hundredth seconds included
3909 if (nNumericsCnt > 4)
3911 res = false;
3914 else
3916 if (nNumericsCnt > 3)
3918 res = false;
3921 break;
3923 case SvNumFormatType::DATETIME:
3924 if (nMonth < 0 && nDayOfWeek < 0 && nNumericsCnt >= 5)
3926 // If both, abbreviated month name and day of week name
3927 // were detected, and also at least numbers for full date
3928 // plus time including minutes, assume that we have a day
3929 // of week instead of month name.
3930 nMonth = 0;
3931 nMonthPos = 0;
3933 if (nMonth)
3934 { // month name and numbers
3935 if (nDecPos)
3936 { // hundredth seconds included
3937 if (nNumericsCnt > 6)
3939 res = false;
3942 else
3944 if (nNumericsCnt > 5)
3946 res = false;
3950 else
3952 if (nDecPos)
3953 { // hundredth seconds included
3954 if (nNumericsCnt > 7)
3956 res = false;
3959 else
3961 if (nNumericsCnt > 6)
3963 res = false;
3966 if (res)
3968 res = IsAcceptedDatePattern( nNums[0]) || MayBeIso8601() || nMatchedAllStrings;
3971 break;
3973 default:
3974 break;
3975 } // switch
3976 } // else
3977 } // if (res)
3979 OUStringBuffer sResString;
3981 if (res)
3982 { // we finally have a number
3983 switch (eScannedType)
3985 case SvNumFormatType::LOGICAL:
3986 if (nLogical == 1)
3988 fOutNumber = 1.0; // True
3990 else if (nLogical == -1)
3992 fOutNumber = 0.0; // False
3994 else
3996 res = false; // Oops
3998 break;
4000 case SvNumFormatType::PERCENT:
4001 case SvNumFormatType::CURRENCY:
4002 case SvNumFormatType::NUMBER:
4003 case SvNumFormatType::SCIENTIFIC:
4004 case SvNumFormatType::DEFINED: // if no category detected handle as number
4005 if ( nDecPos == 1 ) // . at start
4007 sResString.append("0.");
4010 for ( k = 0; k <= nThousand; k++)
4012 sResString.append(sStrArray[nNums[k]]); // integer part
4014 if ( nDecPos == 2 && k < nNumericsCnt ) // . somewhere
4016 sResString.append('.');
4017 sal_uInt16 nStop = (eScannedType == SvNumFormatType::SCIENTIFIC ?
4018 nNumericsCnt-1 : nNumericsCnt);
4019 for ( ; k < nStop; k++)
4021 sResString.append(sStrArray[nNums[k]]); // fractional part
4025 if (eScannedType != SvNumFormatType::SCIENTIFIC)
4027 fOutNumber = StringToDouble(sResString.makeStringAndClear());
4029 else
4030 { // append exponent
4031 sResString.append('E');
4032 if ( nESign == -1 )
4034 sResString.append('-');
4036 sResString.append(sStrArray[nNums[nNumericsCnt-1]]);
4037 rtl_math_ConversionStatus eStatus;
4038 fOutNumber = ::rtl::math::stringToDouble( sResString.makeStringAndClear(), '.', ',', &eStatus );
4039 if ( eStatus == rtl_math_ConversionStatus_OutOfRange )
4041 F_Type = SvNumFormatType::TEXT; // overflow/underflow -> Text
4042 if (nESign == -1)
4044 fOutNumber = 0.0;
4046 else
4048 fOutNumber = DBL_MAX;
4050 return true;
4054 if ( nStringScanSign )
4056 if ( nSign )
4058 nSign *= nStringScanSign;
4060 else
4062 nSign = nStringScanSign;
4065 if ( nSign < 0 )
4067 fOutNumber = -fOutNumber;
4070 if (eScannedType == SvNumFormatType::PERCENT)
4072 fOutNumber/= 100.0;
4074 break;
4076 case SvNumFormatType::FRACTION:
4077 if (nNumericsCnt == 1)
4079 fOutNumber = StringToDouble(sStrArray[nNums[0]]);
4081 else if (nNumericsCnt == 2)
4083 if (nThousand == 1)
4085 sResString = sStrArray[nNums[0]];
4086 sResString.append(sStrArray[nNums[1]]); // integer part
4087 fOutNumber = StringToDouble(sResString.makeStringAndClear());
4089 else
4091 double fNumerator = StringToDouble(sStrArray[nNums[0]]);
4092 double fDenominator = StringToDouble(sStrArray[nNums[1]]);
4093 if (fDenominator != 0.0)
4095 fOutNumber = fNumerator/fDenominator;
4097 else
4099 res = false;
4103 else // nNumericsCnt > 2
4105 k = 1;
4106 sResString = sStrArray[nNums[0]];
4107 if (nThousand > 0)
4109 for (; k <= nThousand; k++)
4111 sResString.append(sStrArray[nNums[k]]);
4114 fOutNumber = StringToDouble(sResString.makeStringAndClear());
4116 if (k == nNumericsCnt-2)
4118 double fNumerator = StringToDouble(sStrArray[nNums[k]]);
4119 double fDenominator = StringToDouble(sStrArray[nNums[k + 1]]);
4120 if (fDenominator != 0.0)
4122 fOutNumber += fNumerator/fDenominator;
4124 else
4126 res = false;
4131 if ( nStringScanSign )
4133 if ( nSign )
4135 nSign *= nStringScanSign;
4137 else
4139 nSign = nStringScanSign;
4142 if ( nSign < 0 )
4144 fOutNumber = -fOutNumber;
4146 break;
4148 case SvNumFormatType::TIME:
4149 res = GetTimeRef(fOutNumber, 0, nNumericsCnt, eInputOptions);
4150 if ( nSign < 0 )
4152 fOutNumber = -fOutNumber;
4154 break;
4156 case SvNumFormatType::DATE:
4157 res = GetDateRef( fOutNumber, k );
4158 break;
4160 case SvNumFormatType::DATETIME:
4161 res = GetDateRef( fOutNumber, k );
4162 if ( res )
4164 double fTime;
4165 res = GetTimeRef( fTime, k, nNumericsCnt - k, eInputOptions);
4166 fOutNumber += fTime;
4168 break;
4170 default:
4171 SAL_WARN( "svl.numbers", "Some number recognized but what's it?" );
4172 fOutNumber = 0.0;
4173 break;
4177 if (res) // overflow/underflow -> Text
4179 if (fOutNumber < -DBL_MAX) // -1.7E308
4181 F_Type = SvNumFormatType::TEXT;
4182 fOutNumber = -DBL_MAX;
4183 return true;
4185 else if (fOutNumber > DBL_MAX) // 1.7E308
4187 F_Type = SvNumFormatType::TEXT;
4188 fOutNumber = DBL_MAX;
4189 return true;
4193 if (!res)
4195 eScannedType = SvNumFormatType::TEXT;
4196 fOutNumber = 0.0;
4199 F_Type = eScannedType;
4200 return res;
4203 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */