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