1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
23 #include <comphelper/string.hxx>
24 #include <sal/log.hxx>
25 #include <tools/date.hxx>
26 #include <rtl/math.hxx>
27 #include <rtl/character.hxx>
28 #include <unotools/charclass.hxx>
29 #include <unotools/calendarwrapper.hxx>
30 #include <unotools/localedatawrapper.hxx>
31 #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
32 #include <com/sun/star/i18n/LocaleCalendar2.hpp>
33 #include <unotools/digitgroupingiterator.hxx>
34 #include <comphelper/sequence.hxx>
36 #include <svl/zforlist.hxx>
37 #include "zforscan.hxx"
38 #include <svl/zformat.hxx>
42 #include "zforfind.hxx"
45 #define NF_TEST_CALENDAR 0
47 #define NF_TEST_CALENDAR 0
50 #include <comphelper/processfactory.hxx>
51 #include <com/sun/star/i18n/XCalendar4.hpp>
55 const sal_uInt8
ImpSvNumberInputScan::nMatchedEndString
= 0x01;
56 const sal_uInt8
ImpSvNumberInputScan::nMatchedMidString
= 0x02;
57 const sal_uInt8
ImpSvNumberInputScan::nMatchedStartString
= 0x04;
58 const sal_uInt8
ImpSvNumberInputScan::nMatchedVirgin
= 0x08;
59 const sal_uInt8
ImpSvNumberInputScan::nMatchedUsedAsReturn
= 0x10;
61 /* It is not clear how we want timezones to be handled. Convert them to local
62 * time isn't wanted, as it isn't done in any other place and timezone
63 * information isn't stored anywhere. Ignoring them and pretending local time
64 * may be wrong too and might not be what the user expects. Keep the input as
65 * string so that no information is lost.
66 * Anyway, defining NF_RECOGNIZE_ISO8601_TIMEZONES to 1 would be the way how it
67 * would work, together with the nTimezonePos handling in GetTimeRef(). */
68 #define NF_RECOGNIZE_ISO8601_TIMEZONES 0
70 const sal_Unicode cNoBreakSpace
= 0xA0;
71 const sal_Unicode cNarrowNoBreakSpace
= 0x202F;
72 const bool kDefaultEra
= true; // Gregorian CE, positive year
74 ImpSvNumberInputScan::ImpSvNumberInputScan( SvNumberFormatter
* pFormatterP
)
76 bTextInitialized( false ),
77 bScanGenitiveMonths( false ),
78 bScanPartitiveMonths( false ),
79 eScannedType( SvNumFormatType::UNDEFINED
),
80 eSetType( SvNumFormatType::UNDEFINED
)
82 pFormatter
= pFormatterP
;
83 pNullDate
.reset( new Date(30,12,1899) );
84 nYear2000
= SvNumberFormatter::GetYear2000Default();
90 ImpSvNumberInputScan::~ImpSvNumberInputScan()
95 void ImpSvNumberInputScan::Reset()
109 eScannedType
= SvNumFormatType::UNDEFINED
;
111 nPosThousandString
= 0;
113 mbEraCE
= kDefaultEra
;
114 nStringScanNumFor
= 0;
116 nMatchedAllStrings
= nMatchedVirgin
;
118 bIso8601Tsep
= false;
120 nAcceptedDatePattern
= -2;
121 nDatePatternStart
= 0;
122 nDatePatternNumbers
= 0;
124 for (sal_uInt32 i
= 0; i
< SV_MAX_COUNT_INPUT_STRINGS
; i
++)
131 // native number transliteration if necessary
132 static void TransformInput( SvNumberFormatter
const * pFormatter
, OUString
& rStr
)
134 sal_Int32 nPos
, nLen
;
135 for ( nPos
= 0, nLen
= rStr
.getLength(); nPos
< nLen
; ++nPos
)
137 if ( 256 <= rStr
[ nPos
] &&
138 pFormatter
->GetCharClass()->isDigit( rStr
, nPos
) )
145 rStr
= pFormatter
->GetNatNum()->getNativeNumberString( rStr
,
146 pFormatter
->GetLanguageTag().getLocale(), 0 );
152 * Only simple unsigned floating point values without any error detection,
153 * decimal separator has to be '.'
155 double ImpSvNumberInputScan::StringToDouble( const OUString
& rStr
, bool bForceFraction
)
157 std::unique_ptr
<char[]> bufInHeap
;
158 constexpr int bufOnStackSize
= 256;
159 char bufOnStack
[bufOnStackSize
];
160 char* buf
= bufOnStack
;
161 const sal_Int32 bufsize
= rStr
.getLength() + (bForceFraction
? 2 : 1);
162 if (bufsize
> bufOnStackSize
)
164 bufInHeap
= std::make_unique
<char[]>(bufsize
);
165 buf
= bufInHeap
.get();
170 for (sal_Int32 nPos
= 0; nPos
< rStr
.getLength(); ++nPos
)
172 sal_Unicode c
= rStr
[nPos
];
173 if (c
== '.' || (c
>= '0' && c
<= '9'))
174 *p
++ = static_cast<char>(c
);
180 return strtod_nolocale(buf
, nullptr);
186 * Splits up the input into numbers and strings for further processing
187 * (by the Turing machine).
189 * Starting state = GetChar
190 * ---------------+-------------------+-----------------------------+---------------
191 * Old State | Character read | Event | New state
192 * ---------------+-------------------+-----------------------------+---------------
193 * GetChar | Number | Symbol = Character | GetValue
194 * | Else | Symbol = Character | GetString
195 * ---------------|-------------------+-----------------------------+---------------
196 * GetValue | Number | Symbol = Symbol + Character | GetValue
197 * | Else | Dec(CharPos) | Stop
198 * ---------------+-------------------+-----------------------------+---------------
199 * GetString | Number | Dec(CharPos) | Stop
200 * | Else | Symbol = Symbol + Character | GetString
201 * ---------------+-------------------+-----------------------------+---------------
203 enum ScanState
// States of the Turing machine
213 bool ImpSvNumberInputScan::NextNumberStringSymbol( const sal_Unicode
*& pStr
,
216 bool isNumber
= false;
218 ScanState eState
= SsStart
;
219 const sal_Unicode
* pHere
= pStr
;
220 sal_Int32 nChars
= 0;
225 if (cToken
== 0 || eState
== SsStop
)
231 if ( rtl::isAsciiDigit( cToken
) )
238 eState
= SsGetString
;
243 if ( rtl::isAsciiDigit( cToken
) )
254 if ( !rtl::isAsciiDigit( cToken
) )
271 rSymbol
= OUString( pStr
, nChars
);
284 // FIXME: should be grouping; it is only used though in case nStringsCnt is
285 // near SV_MAX_COUNT_INPUT_STRINGS, in NumberStringDivision().
287 bool ImpSvNumberInputScan::SkipThousands( const sal_Unicode
*& pStr
,
288 OUString
& rSymbol
) const
291 OUStringBuffer
sBuff(rSymbol
);
293 const OUString
& rThSep
= pFormatter
->GetNumThousandSep();
294 const sal_Unicode
* pHere
= pStr
;
295 ScanState eState
= SsStart
;
296 sal_Int32 nCounter
= 0; // counts 3 digits
301 if (cToken
== 0 || eState
== SsStop
)
307 if ( StringPtrContains( rThSep
, pHere
-1, 0 ) )
311 pHere
+= rThSep
.getLength() - 1;
320 if ( rtl::isAsciiDigit( cToken
) )
322 sBuff
.append(cToken
);
327 res
= true; // .000 combination found
341 if (eState
== SsGetValue
) // break with less than 3 digits
345 sBuff
.remove( sBuff
.getLength() - nCounter
, nCounter
);
347 pHere
-= nCounter
+ rThSep
.getLength(); // put back ThSep also
349 rSymbol
= sBuff
.makeStringAndClear();
356 void ImpSvNumberInputScan::NumberStringDivision( const OUString
& rString
)
358 const sal_Unicode
* pStr
= rString
.getStr();
359 const sal_Unicode
* const pEnd
= pStr
+ rString
.getLength();
360 while ( pStr
< pEnd
&& nStringsCnt
< SV_MAX_COUNT_INPUT_STRINGS
)
362 if ( NextNumberStringSymbol( pStr
, sStrArray
[nStringsCnt
] ) )
364 IsNum
[nStringsCnt
] = true;
365 nNums
[nNumericsCnt
] = nStringsCnt
;
367 if (nStringsCnt
>= SV_MAX_COUNT_INPUT_STRINGS
- 7 &&
368 nPosThousandString
== 0) // Only once
370 if ( SkipThousands( pStr
, sStrArray
[nStringsCnt
] ) )
372 nPosThousandString
= nStringsCnt
;
378 IsNum
[nStringsCnt
] = false;
386 * Whether rString contains rWhat at nPos
388 bool ImpSvNumberInputScan::StringContainsImpl( const OUString
& rWhat
,
389 const OUString
& rString
, sal_Int32 nPos
)
391 if ( nPos
+ rWhat
.getLength() <= rString
.getLength() )
393 return StringPtrContainsImpl( rWhat
, rString
.getStr(), nPos
);
400 * Whether pString contains rWhat at nPos
402 bool ImpSvNumberInputScan::StringPtrContainsImpl( const OUString
& rWhat
,
403 const sal_Unicode
* pString
, sal_Int32 nPos
)
405 if ( rWhat
.isEmpty() )
409 const sal_Unicode
* pWhat
= rWhat
.getStr();
410 const sal_Unicode
* const pEnd
= pWhat
+ rWhat
.getLength();
411 const sal_Unicode
* pStr
= pString
+ nPos
;
412 while ( pWhat
< pEnd
)
414 if ( *pWhat
!= *pStr
)
426 * Whether rString contains word rWhat at nPos
428 bool ImpSvNumberInputScan::StringContainsWord( const OUString
& rWhat
,
429 const OUString
& rString
, sal_Int32 nPos
) const
431 if (rWhat
.isEmpty() || rString
.getLength() < nPos
+ rWhat
.getLength())
434 if (StringPtrContainsImpl( rWhat
, rString
.getStr(), nPos
))
436 nPos
+= rWhat
.getLength();
437 if (nPos
== rString
.getLength())
438 return true; // word at end of string
440 /* TODO: we COULD invoke bells and whistles word break iterator to find
441 * the next boundary, but really ... this is called for date input, so
442 * how many languages do not separate the day and month names in some
445 // Check simple ASCII first before invoking i18n or anything else.
446 const sal_Unicode c
= rString
[nPos
];
448 // Common separating ASCII characters in date context.
460 if (rtl::isAsciiAlphanumeric( c
))
461 return false; // Alpha or numeric is not word gap.
463 sal_Int32 nIndex
= nPos
;
464 rString
.iterateCodePoints( &nIndex
);
466 return true; // Surrogate, assume these to be new words.
468 const sal_Int32 nType
= pFormatter
->GetCharClass()->getCharacterType( rString
, nPos
);
469 using namespace ::com::sun::star::i18n
;
471 if ((nType
& (KCharacterType::UPPER
| KCharacterType::LOWER
| KCharacterType::DIGIT
)) != 0)
472 return false; // Alpha or numeric is not word gap.
474 if (nType
& KCharacterType::LETTER
)
475 return true; // Letter other than alpha is new word. (Is it?)
477 return true; // Catch all remaining as gap until we know better.
485 * Skips the supplied char
487 inline bool ImpSvNumberInputScan::SkipChar( sal_Unicode c
, const OUString
& rString
,
490 if ((nPos
< rString
.getLength()) && (rString
[nPos
] == c
))
502 inline bool ImpSvNumberInputScan::SkipBlanks( const OUString
& rString
,
505 sal_Int32 nHere
= nPos
;
506 if ( nPos
< rString
.getLength() )
508 const sal_Unicode
* p
= rString
.getStr() + nPos
;
509 while ( *p
== ' ' || *p
== cNoBreakSpace
|| *p
== cNarrowNoBreakSpace
)
520 * jump over rWhat in rString at nPos
522 inline bool ImpSvNumberInputScan::SkipString( const OUString
& rWhat
,
523 const OUString
& rString
, sal_Int32
& nPos
)
525 if ( StringContains( rWhat
, rString
, nPos
) )
527 nPos
= nPos
+ rWhat
.getLength();
535 * Recognizes exactly ,111 in {3} and {3,2} or ,11 in {3,2} grouping
537 inline bool ImpSvNumberInputScan::GetThousandSep( const OUString
& rString
,
539 sal_uInt16 nStringPos
) const
541 const OUString
& rSep
= pFormatter
->GetNumThousandSep();
542 // Is it an ordinary space instead of a no-break space?
543 bool bSpaceBreak
= (rSep
[0] == cNoBreakSpace
|| rSep
[0] == cNarrowNoBreakSpace
) &&
544 rString
[0] == u
' ' &&
545 rSep
.getLength() == 1 && rString
.getLength() == 1;
546 if (!((rString
== rSep
|| bSpaceBreak
) && // nothing else
547 nStringPos
< nStringsCnt
- 1 && // safety first!
548 IsNum
[ nStringPos
+ 1 ] )) // number follows
550 return false; // no? => out
553 utl::DigitGroupingIterator
aGrouping( pFormatter
->GetLocaleData()->getDigitGrouping());
554 // Match ,### in {3} or ,## in {3,2}
555 /* FIXME: this could be refined to match ,## in {3,2} only if ,##,## or
556 * ,##,### and to match ,### in {3,2} only if it's the last. However,
557 * currently there is no track kept where group separators occur. In {3,2}
558 * #,###,### and #,##,## would be valid input, which maybe isn't even bad
559 * for #,###,###. Other combinations such as #,###,## maybe not. */
560 sal_Int32 nLen
= sStrArray
[ nStringPos
+ 1 ].getLength();
561 if (nLen
== aGrouping
.get() || // with 3 (or so) digits
562 nLen
== aGrouping
.advance().get() || // or with 2 (or 3 or so) digits
563 nPosThousandString
== nStringPos
+ 1 ) // or concatenated
565 nPos
= nPos
+ rSep
.getLength();
573 * Conversion of text to logical value
578 short ImpSvNumberInputScan::GetLogical( const OUString
& rString
) const
582 const ImpSvNumberformatScan
* pFS
= pFormatter
->GetFormatScanner();
583 if ( rString
== pFS
->GetTrueString() )
587 else if ( rString
== pFS
->GetFalseString() )
600 * Converts a string containing a month name (JAN, January) at nPos into the
601 * month number (negative if abbreviated), returns 0 if nothing found
603 short ImpSvNumberInputScan::GetMonth( const OUString
& rString
, sal_Int32
& nPos
)
605 short res
= 0; // no month found
607 if (rString
.getLength() > nPos
) // only if needed
609 if ( !bTextInitialized
)
613 sal_Int16 nMonths
= pFormatter
->GetCalendar()->getNumberOfMonthsInYear();
614 for ( sal_Int16 i
= 0; i
< nMonths
; i
++ )
616 if ( bScanGenitiveMonths
&& StringContainsWord( pUpperGenitiveMonthText
[i
], rString
, nPos
) )
617 { // genitive full names first
618 nPos
= nPos
+ pUpperGenitiveMonthText
[i
].getLength();
622 else if ( bScanGenitiveMonths
&& StringContainsWord( pUpperGenitiveAbbrevMonthText
[i
], rString
, nPos
) )
623 { // genitive abbreviated
624 nPos
= nPos
+ pUpperGenitiveAbbrevMonthText
[i
].getLength();
625 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
628 else if ( bScanPartitiveMonths
&& StringContainsWord( pUpperPartitiveMonthText
[i
], rString
, nPos
) )
629 { // partitive full names
630 nPos
= nPos
+ pUpperPartitiveMonthText
[i
].getLength();
634 else if ( bScanPartitiveMonths
&& StringContainsWord( pUpperPartitiveAbbrevMonthText
[i
], rString
, nPos
) )
635 { // partitive abbreviated
636 nPos
= nPos
+ pUpperPartitiveAbbrevMonthText
[i
].getLength();
637 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
640 else if ( StringContainsWord( pUpperMonthText
[i
], rString
, nPos
) )
642 nPos
= nPos
+ pUpperMonthText
[i
].getLength();
646 else if ( StringContainsWord( pUpperAbbrevMonthText
[i
], rString
, nPos
) )
647 { // noun abbreviated
648 nPos
= nPos
+ pUpperAbbrevMonthText
[i
].getLength();
649 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
652 else if (i
== 2 && pFormatter
->GetLanguageTag().getLanguage() == "de")
654 if (pUpperAbbrevMonthText
[i
] == u
"M\u00C4R" && StringContainsWord( "MRZ", rString
, nPos
))
655 { // Accept MRZ for MÄR
657 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
660 else if (pUpperAbbrevMonthText
[i
] == "MRZ" && StringContainsWord( u
"M\u00C4R", rString
, nPos
))
661 { // And vice versa, accept MÄR for MRZ
663 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
669 // This assumes the weirdness is applicable to all locales.
670 // It is the case for at least en-* and de-* locales.
671 if (pUpperAbbrevMonthText
[i
] == "SEPT" && StringContainsWord( "SEP", rString
, nPos
))
672 { // #102136# The correct English form of month September abbreviated is
673 // SEPT, but almost every data contains SEP instead.
675 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
678 else if (pUpperAbbrevMonthText
[i
] == "SEP" && StringContainsWord( "SEPT", rString
, nPos
))
679 { // And vice versa, accept SEPT for SEP
681 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
688 // Brutal hack for German locales that know "Januar" or "Jänner".
689 /* TODO: add alternative month names to locale data? if there are
690 * more languages... */
691 const LanguageTag
& rLanguageTag
= pFormatter
->GetLanguageTag();
692 if (rLanguageTag
.getLanguage() == "de")
694 if (rLanguageTag
.getCountry() == "AT")
696 // Locale data has Jänner/Jän
697 assert(pUpperMonthText
[0] == u
"J\u00C4NNER");
698 if (StringContainsWord( "JANUAR", rString
, nPos
))
703 else if (StringContainsWord( "JAN", rString
, nPos
))
711 // Locale data has Januar/Jan
712 assert(pUpperMonthText
[0] == "JANUAR");
713 if (StringContainsWord( u
"J\u00C4NNER", rString
, nPos
))
718 else if (StringContainsWord( u
"J\u00C4N", rString
, nPos
))
733 * Converts a string containing a DayOfWeek name (Mon, Monday) at nPos into the
734 * DayOfWeek number + 1 (negative if abbreviated), returns 0 if nothing found
736 int ImpSvNumberInputScan::GetDayOfWeek( const OUString
& rString
, sal_Int32
& nPos
)
738 int res
= 0; // no day found
740 if (rString
.getLength() > nPos
) // only if needed
742 if ( !bTextInitialized
)
746 sal_Int16 nDays
= pFormatter
->GetCalendar()->getNumberOfDaysInWeek();
747 for ( sal_Int16 i
= 0; i
< nDays
; i
++ )
749 if ( StringContainsWord( pUpperDayText
[i
], rString
, nPos
) )
750 { // full names first
751 nPos
= nPos
+ pUpperDayText
[i
].getLength();
755 if ( StringContainsWord( pUpperAbbrevDayText
[i
], rString
, nPos
) )
757 nPos
= nPos
+ pUpperAbbrevDayText
[i
].getLength();
758 res
= -(i
+ 1); // negative
769 * Reading a currency symbol
773 bool ImpSvNumberInputScan::GetCurrency( const OUString
& rString
, sal_Int32
& nPos
)
775 if ( rString
.getLength() > nPos
)
777 if ( !aUpperCurrSymbol
.getLength() )
778 { // If no format specified the currency of the currently active locale.
779 LanguageType eLang
= (mpFormat
? mpFormat
->GetLanguage() :
780 pFormatter
->GetLocaleData()->getLanguageTag().getLanguageType());
781 aUpperCurrSymbol
= pFormatter
->GetCharClass()->uppercase(
782 SvNumberFormatter::GetCurrencyEntry( eLang
).GetSymbol() );
784 if ( StringContains( aUpperCurrSymbol
, rString
, nPos
) )
786 nPos
= nPos
+ aUpperCurrSymbol
.getLength();
791 OUString aSymbol
, aExtension
;
792 if ( mpFormat
->GetNewCurrencySymbol( aSymbol
, aExtension
) )
794 if ( aSymbol
.getLength() <= rString
.getLength() - nPos
)
796 aSymbol
= pFormatter
->GetCharClass()->uppercase(aSymbol
);
797 if ( StringContains( aSymbol
, rString
, nPos
) )
799 nPos
= nPos
+ aSymbol
.getLength();
812 * Reading the time period specifier (AM/PM) for the 12 hour clock
815 * "AM" or "PM" => true
823 bool ImpSvNumberInputScan::GetTimeAmPm( const OUString
& rString
, sal_Int32
& nPos
)
826 if ( rString
.getLength() > nPos
)
828 const CharClass
* pChr
= pFormatter
->GetCharClass();
829 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
830 if ( StringContains( pChr
->uppercase( pLoc
->getTimeAM() ), rString
, nPos
) )
833 nPos
= nPos
+ pLoc
->getTimeAM().getLength();
836 else if ( StringContains( pChr
->uppercase( pLoc
->getTimePM() ), rString
, nPos
) )
839 nPos
= nPos
+ pLoc
->getTimePM().getLength();
849 * Read a decimal separator (',')
853 inline bool ImpSvNumberInputScan::GetDecSep( const OUString
& rString
, sal_Int32
& nPos
) const
855 if ( rString
.getLength() > nPos
)
857 const OUString
& rSep
= pFormatter
->GetNumDecimalSep();
858 if ( rString
.match( rSep
, nPos
) )
860 nPos
= nPos
+ rSep
.getLength();
863 const OUString
& rSepAlt
= pFormatter
->GetNumDecimalSepAlt();
864 if ( !rSepAlt
.isEmpty() && rString
.match( rSepAlt
, nPos
) )
866 nPos
= nPos
+ rSepAlt
.getLength();
875 * Reading a hundredth seconds separator
877 inline bool ImpSvNumberInputScan::GetTime100SecSep( const OUString
& rString
, sal_Int32
& nPos
) const
879 if ( rString
.getLength() > nPos
)
883 // ISO 8601 specifies both '.' dot and ',' comma as fractional
885 if (rString
[nPos
] == '.' || rString
[nPos
] == ',')
891 // Even in an otherwise ISO 8601 string be lenient and accept the
892 // locale defined separator.
893 const OUString
& rSep
= pFormatter
->GetLocaleData()->getTime100SecSep();
894 if ( rString
.match( rSep
, nPos
))
896 nPos
= nPos
+ rSep
.getLength();
905 * Read a sign including brackets
908 * '(' => -1, bNegCheck = 1
911 int ImpSvNumberInputScan::GetSign( const OUString
& rString
, sal_Int32
& nPos
)
913 if (rString
.getLength() > nPos
)
914 switch (rString
[ nPos
])
919 case '(': // '(' similar to '-' ?!?
934 * Read a sign with an exponent
939 short ImpSvNumberInputScan::GetESign( const OUString
& rString
, sal_Int32
& nPos
)
941 if (rString
.getLength() > nPos
)
943 switch (rString
[nPos
])
960 * i counts string portions, j counts numbers thereof.
961 * It should had been called SkipNumber instead.
963 inline bool ImpSvNumberInputScan::GetNextNumber( sal_uInt16
& i
, sal_uInt16
& j
) const
965 if ( i
< nStringsCnt
&& IsNum
[i
] )
975 bool ImpSvNumberInputScan::GetTimeRef( double& fOutNumber
,
976 sal_uInt16 nIndex
, // j-value of the first numeric time part of input, default 0
977 sal_uInt16 nCnt
, // count of numeric time parts
978 SvNumInputOptions eInputOptions
983 sal_uInt16 nMinute
= 0;
984 sal_uInt16 nSecond
= 0;
985 double fSecond100
= 0.0;
986 sal_uInt16 nStartIndex
= nIndex
;
988 if (nDecPos
== 2 && (nCnt
== 3 || nCnt
== 2)) // 20:45.5 or 45.5
992 else if (mpFormat
&& nDecPos
== 0 && nCnt
== 2 && mpFormat
->IsMinuteSecondFormat())
994 // Input on MM:SS format, instead of doing HH:MM:00
997 else if (nIndex
- nStartIndex
< nCnt
)
999 nHour
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
++]].toInt32());
1005 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetTimeRef: bad number index");
1008 // 0:123 or 0:0:123 or 0:123:59 is valid
1009 bool bAllowDuration
= (nHour
== 0 && !nAmPm
);
1011 if (nAmPm
&& nHour
> 12) // not a valid AM/PM clock time
1015 else if (nAmPm
== -1 && nHour
!= 12) // PM
1019 else if (nAmPm
== 1 && nHour
== 12) // 12 AM
1024 if (nDecPos
== 2 && nCnt
== 2) // 45.5
1028 else if (nIndex
- nStartIndex
< nCnt
)
1030 nMinute
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
++]].toInt32());
1031 if (!(eInputOptions
& SvNumInputOptions::LAX_TIME
) && !bAllowDuration
1032 && nIndex
> 1 && nMinute
> 59)
1033 bRet
= false; // 1:60 or 1:123 is invalid, 123:1 or 0:123 is valid
1035 bAllowDuration
= (nMinute
== 0);
1037 if (nIndex
- nStartIndex
< nCnt
)
1039 nSecond
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
++]].toInt32());
1040 if (!(eInputOptions
& SvNumInputOptions::LAX_TIME
) && !bAllowDuration
1041 && nIndex
> 1 && nSecond
> 59 && !(nHour
== 23 && nMinute
== 59 && nSecond
== 60))
1042 bRet
= false; // 1:60 or 1:123 or 1:1:123 is invalid, 123:1 or 123:1:1 or 0:0:123 is valid, or leap second
1044 if (nIndex
- nStartIndex
< nCnt
)
1046 fSecond100
= StringToDouble( sStrArray
[nNums
[nIndex
]], true );
1048 fOutNumber
= (static_cast<double>(nHour
)*3600 +
1049 static_cast<double>(nMinute
)*60 +
1050 static_cast<double>(nSecond
) +
1051 fSecond100
)/86400.0;
1056 sal_uInt16
ImpSvNumberInputScan::ImplGetDay( sal_uInt16 nIndex
) const
1058 sal_uInt16 nRes
= 0;
1060 if (sStrArray
[nNums
[nIndex
]].getLength() <= 2)
1062 sal_uInt16 nNum
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
]].toInt32());
1073 sal_uInt16
ImpSvNumberInputScan::ImplGetMonth( sal_uInt16 nIndex
) const
1075 // Preset invalid month number
1076 sal_uInt16 nRes
= pFormatter
->GetCalendar()->getNumberOfMonthsInYear();
1078 if (sStrArray
[nNums
[nIndex
]].getLength() <= 2)
1080 sal_uInt16 nNum
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
]].toInt32());
1081 if ( 0 < nNum
&& nNum
<= nRes
)
1083 nRes
= nNum
- 1; // zero based for CalendarFieldIndex::MONTH
1092 * 30 -> 1930, 29 -> 2029, or 56 -> 1756, 55 -> 1855, ...
1094 sal_uInt16
ImpSvNumberInputScan::ImplGetYear( sal_uInt16 nIndex
)
1096 sal_uInt16 nYear
= 0;
1098 sal_Int32 nLen
= sStrArray
[nNums
[nIndex
]].getLength();
1099 // 16-bit integer year width can have 5 digits, allow for one additional
1100 // leading zero as convention.
1103 nYear
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
]].toInt32());
1104 // A year in another, not Gregorian CE era is never expanded.
1105 // A year < 100 entered with at least 3 digits with leading 0 is taken
1106 // as is without expansion.
1107 if (mbEraCE
== kDefaultEra
&& nYear
< 100 && nLen
< 3)
1109 nYear
= SvNumberFormatter::ExpandTwoDigitYear( nYear
, nYear2000
);
1117 bool ImpSvNumberInputScan::MayBeIso8601()
1119 if (nMayBeIso8601
== 0)
1122 sal_Int32 nLen
= ((nNumericsCnt
>= 1 && nNums
[0] < nStringsCnt
) ? sStrArray
[nNums
[0]].getLength() : 0);
1126 if (nNumericsCnt
>= 3 && nNums
[2] < nStringsCnt
&&
1127 sStrArray
[nNums
[0]+1] == "-" && // separator year-month
1128 (n
= sStrArray
[nNums
[1]].toInt32()) >= 1 && n
<= 12 && // month
1129 sStrArray
[nNums
[1]+1] == "-" && // separator month-day
1130 (n
= sStrArray
[nNums
[2]].toInt32()) >= 1 && n
<= 31) // day
1132 // Year (nNums[0]) value not checked, may be anything, but
1133 // length (number of digits) is checked.
1134 nMayBeIso8601
= (nLen
>= 4 ? 4 : (nLen
== 3 ? 3 : (nLen
> 0 ? 2 : 1)));
1138 return nMayBeIso8601
> 1;
1142 bool ImpSvNumberInputScan::CanForceToIso8601( DateOrder eDateOrder
)
1144 int nCanForceToIso8601
= 0;
1145 if (!MayBeIso8601())
1149 else if (nMayBeIso8601
>= 3)
1151 return true; // at least 3 digits in year
1155 if (eDateOrder
== DateOrder::Invalid
)
1157 // As if any of the cases below can be applied, but only if a
1158 // locale dependent date pattern was not matched.
1159 if ((GetDatePatternNumbers() == nNumericsCnt
) && IsDatePatternNumberOfType(0,'Y'))
1161 eDateOrder
= GetDateOrder();
1164 nCanForceToIso8601
= 1;
1170 case DateOrder::DMY
: // "day" value out of range => ISO 8601 year
1171 n
= sStrArray
[nNums
[0]].toInt32();
1172 if (n
< 1 || n
> 31)
1174 nCanForceToIso8601
= 2;
1177 case DateOrder::MDY
: // "month" value out of range => ISO 8601 year
1178 n
= sStrArray
[nNums
[0]].toInt32();
1179 if (n
< 1 || n
> 12)
1181 nCanForceToIso8601
= 2;
1184 case DateOrder::YMD
: // always possible
1185 nCanForceToIso8601
= 2;
1189 return nCanForceToIso8601
> 1;
1193 bool ImpSvNumberInputScan::IsAcceptableIso8601()
1195 if (mpFormat
&& (mpFormat
->GetType() & SvNumFormatType::DATE
))
1197 switch (pFormatter
->GetEvalDateFormat())
1199 case NF_EVALDATEFORMAT_INTL
:
1200 return CanForceToIso8601( GetDateOrder());
1201 case NF_EVALDATEFORMAT_FORMAT
:
1202 return CanForceToIso8601( mpFormat
->GetDateOrder());
1204 return CanForceToIso8601( GetDateOrder()) || CanForceToIso8601( mpFormat
->GetDateOrder());
1207 return CanForceToIso8601( GetDateOrder());
1211 bool ImpSvNumberInputScan::MayBeMonthDate()
1213 if (nMayBeMonthDate
== 0)
1215 nMayBeMonthDate
= 1;
1216 if (nNumericsCnt
>= 2 && nNums
[1] < nStringsCnt
)
1219 const OUString
& rM
= sStrArray
[ nNums
[ 0 ] + 1 ];
1220 if (rM
.getLength() >= 3 && rM
[0] == '-' && rM
[ rM
.getLength() - 1] == '-')
1222 // Check year length assuming at least 3 digits (including
1223 // leading zero). Two digit years 1..31 are out of luck here
1224 // and may be taken as day of month.
1225 bool bYear1
= (sStrArray
[nNums
[0]].getLength() >= 3);
1226 bool bYear2
= (sStrArray
[nNums
[1]].getLength() >= 3);
1228 bool bDay1
= !bYear1
;
1231 n
= sStrArray
[nNums
[0]].toInt32();
1232 bDay1
= n
>= 1 && n
<= 31;
1234 bool bDay2
= !bYear2
;
1237 n
= sStrArray
[nNums
[1]].toInt32();
1238 bDay2
= n
>= 1 && n
<= 31;
1241 if (bDay1
&& !bDay2
)
1243 nMayBeMonthDate
= 2; // dd-month-yy
1245 else if (!bDay1
&& bDay2
)
1247 nMayBeMonthDate
= 3; // yy-month-dd
1249 else if (bDay1
&& bDay2
)
1251 // Ambiguous ##-MMM-## date, but some big vendor's database
1252 // reports write this crap, assume this always to be
1253 nMayBeMonthDate
= 2; // dd-month-yy
1258 return nMayBeMonthDate
> 1;
1262 /** If a string is a separator plus '-' minus sign preceding a 'Y' year in
1263 a date pattern at position nPat.
1265 static bool lcl_IsSignedYearSep( const OUString
& rStr
, const OUString
& rPat
, sal_Int32 nPat
)
1268 sal_Int32 nLen
= rStr
.getLength();
1269 if (nLen
> 1 && rStr
[nLen
-1] == '-')
1272 if (nPat
+ nLen
< rPat
.getLength() && rPat
[nPat
+nLen
] == 'Y')
1274 // Signed year is possible.
1275 bOk
= (rPat
.indexOf( rStr
.subView( 0, nLen
), nPat
) == nPat
);
1282 /** Length of separator usually is 1 but theoretically could be anything. */
1283 static sal_Int32
lcl_getPatternSeparatorLength( const OUString
& rPat
, sal_Int32 nPat
)
1285 sal_Int32 nSep
= nPat
;
1287 while (nSep
< rPat
.getLength() && (c
= rPat
[nSep
]) != 'D' && c
!= 'M' && c
!= 'Y')
1293 bool ImpSvNumberInputScan::IsAcceptedDatePattern( sal_uInt16 nStartPatternAt
)
1295 if (nAcceptedDatePattern
>= -1)
1297 return (nAcceptedDatePattern
>= 0);
1301 nAcceptedDatePattern
= -1;
1303 else if (!sDateAcceptancePatterns
.hasElements())
1305 // The current locale is the format's locale, if a format is present.
1306 const NfEvalDateFormat eEDF
= pFormatter
->GetEvalDateFormat();
1307 if (!mpFormat
|| eEDF
== NF_EVALDATEFORMAT_FORMAT
|| mpFormat
->GetLanguage() == pFormatter
->GetLanguage())
1309 sDateAcceptancePatterns
= pFormatter
->GetLocaleData()->getDateAcceptancePatterns();
1313 OnDemandLocaleDataWrapper
& xLocaleData
= pFormatter
->GetOnDemandLocaleDataWrapper(
1314 SvNumberFormatter::InputScannerPrivateAccess());
1315 const LanguageTag
aSaveLocale( xLocaleData
->getLanguageTag() );
1316 assert(mpFormat
->GetLanguage() == aSaveLocale
.getLanguageType()); // prerequisite
1317 // Obtain formatter's locale's (e.g. system) patterns.
1318 xLocaleData
.changeLocale( LanguageTag( pFormatter
->GetLanguage()));
1319 const css::uno::Sequence
<OUString
> aLocalePatterns( xLocaleData
->getDateAcceptancePatterns());
1320 // Reset to format's locale.
1321 xLocaleData
.changeLocale( aSaveLocale
);
1322 // When concatenating don't care about duplicates, combining
1323 // weeding those out reallocs yet another time and probably doesn't
1324 // take less time than looping over two additional patterns below...
1327 case NF_EVALDATEFORMAT_FORMAT
:
1328 assert(!"shouldn't reach here");
1330 case NF_EVALDATEFORMAT_INTL
:
1331 sDateAcceptancePatterns
= aLocalePatterns
;
1333 case NF_EVALDATEFORMAT_INTL_FORMAT
:
1334 sDateAcceptancePatterns
= comphelper::concatSequences(
1336 xLocaleData
->getDateAcceptancePatterns());
1338 case NF_EVALDATEFORMAT_FORMAT_INTL
:
1339 sDateAcceptancePatterns
= comphelper::concatSequences(
1340 xLocaleData
->getDateAcceptancePatterns(),
1345 SAL_WARN_IF( !sDateAcceptancePatterns
.hasElements(), "svl.numbers", "ImpSvNumberInputScan::IsAcceptedDatePattern: no date acceptance patterns");
1346 nAcceptedDatePattern
= (sDateAcceptancePatterns
.hasElements() ? -2 : -1);
1349 if (nAcceptedDatePattern
== -1)
1353 nDatePatternStart
= nStartPatternAt
; // remember start particle
1355 const sal_Int32 nMonthsInYear
= pFormatter
->GetCalendar()->getNumberOfMonthsInYear();
1357 for (sal_Int32 nPattern
=0; nPattern
< sDateAcceptancePatterns
.getLength(); ++nPattern
)
1359 sal_uInt16 nNext
= nDatePatternStart
;
1360 nDatePatternNumbers
= 0;
1362 const OUString
& rPat
= sDateAcceptancePatterns
[nPattern
];
1364 for ( ; nPat
< rPat
.getLength() && bOk
&& nNext
< nStringsCnt
; ++nPat
, ++nNext
)
1366 const sal_Unicode c
= rPat
[nPat
];
1373 if (bOk
&& (c
== 'M' || c
== 'D'))
1375 // Check the D and M cases for plausibility. This also
1376 // prevents recognition of date instead of number with a
1377 // numeric group input if date separator is identical to
1378 // group separator, for example with D.M as a pattern and
1379 // #.### as a group.
1380 sal_Int32 nMaxLen
, nMaxVal
;
1385 nMaxVal
= nMonthsInYear
;
1392 // This merely exists against
1393 // -Werror=maybe-uninitialized, which is nonsense
1394 // after the (c == 'M' || c == 'D') check above,
1399 bOk
= (sStrArray
[nNext
].getLength() <= nMaxLen
);
1402 sal_Int32 nNum
= sStrArray
[nNext
].toInt32();
1403 bOk
= (1 <= nNum
&& nNum
<= nMaxVal
);
1407 ++nDatePatternNumbers
;
1410 bOk
= !IsNum
[nNext
];
1413 const sal_Int32 nSepLen
= lcl_getPatternSeparatorLength( rPat
, nPat
);
1414 // Non-numeric input must match separator exactly to be
1415 // accepted as such.
1416 const sal_Int32 nLen
= sStrArray
[nNext
].getLength();
1417 bOk
= (nLen
== nSepLen
&& rPat
.indexOf( sStrArray
[nNext
], nPat
) == nPat
);
1422 else if ((bOk
= lcl_IsSignedYearSep( sStrArray
[nNext
], rPat
, nPat
)))
1426 else if (nPat
+ nLen
> rPat
.getLength() && sStrArray
[nNext
][ nLen
- 1 ] == ' ')
1428 using namespace comphelper::string
;
1429 // Trailing blanks in input.
1430 OUStringBuffer
aBuf(sStrArray
[nNext
]);
1432 // Expand again in case of pattern "M. D. " and
1433 // input "M. D. ", maybe fetched far, but...
1434 padToLength(aBuf
, rPat
.getLength() - nPat
, ' ');
1435 OUString aStr
= aBuf
.makeStringAndClear();
1436 bOk
= (rPat
.indexOf( aStr
, nPat
) == nPat
);
1439 nPat
+= aStr
.getLength() - 1;
1448 // Check for trailing characters mismatch.
1449 if (nNext
< nStringsCnt
)
1451 // Pattern end but not input end.
1452 // A trailing blank may be part of the current pattern input,
1453 // if pattern is "D.M." and input is "D.M. hh:mm" last was
1454 // ". ", or may be following the current pattern input, if
1455 // pattern is "D.M" and input is "D.M hh:mm" last was "M".
1458 if (nPat
> 0 && nNext
> 0)
1460 // nPat is one behind after the for loop.
1461 sal_Int32 nPatCheck
= nPat
- 1;
1462 switch (rPat
[nPatCheck
])
1472 // Advance position in input to match length of
1473 // non-YMD (separator) characters in pattern.
1478 c
= rPat
[--nPatCheck
];
1479 } while (c
!= 'Y' && c
!= 'M' && c
!= 'D');
1489 // Trailing (or separating if time follows) blanks are ok.
1490 // No blank and a following number is not.
1491 const bool bBlanks
= SkipBlanks( sStrArray
[nCheck
], nPos
);
1492 if (nPos
== sStrArray
[nCheck
].getLength() && (bBlanks
|| !IsNum
[nNext
]))
1494 nAcceptedDatePattern
= nPattern
;
1499 else if (nPat
== rPat
.getLength())
1501 // Input end and pattern end => match.
1502 nAcceptedDatePattern
= nPattern
;
1505 // else Input end but not pattern end, no match.
1508 nAcceptedDatePattern
= -1;
1513 bool ImpSvNumberInputScan::SkipDatePatternSeparator( sal_uInt16 nParticle
, sal_Int32
& rPos
, bool & rSignedYear
)
1515 // If not initialized yet start with first number, if any.
1516 if (!IsAcceptedDatePattern( nNumericsCnt
? nNums
[0] : 0 ))
1520 if (nParticle
< nDatePatternStart
|| nParticle
>= nStringsCnt
|| IsNum
[nParticle
])
1524 sal_uInt16 nNext
= nDatePatternStart
;
1525 const OUString
& rPat
= sDateAcceptancePatterns
[nAcceptedDatePattern
];
1526 for (sal_Int32 nPat
= 0; nPat
< rPat
.getLength() && nNext
< nStringsCnt
; ++nPat
, ++nNext
)
1535 if (nNext
== nParticle
)
1537 const sal_Int32 nSepLen
= lcl_getPatternSeparatorLength( rPat
, nPat
);
1538 const sal_Int32 nLen
= sStrArray
[nNext
].getLength();
1539 bool bOk
= (nLen
== nSepLen
&& rPat
.indexOf( sStrArray
[nNext
], nPat
) == nPat
);
1542 bOk
= lcl_IsSignedYearSep( sStrArray
[nNext
], rPat
, nPat
);
1546 if (!bOk
&& (nPat
+ nLen
> rPat
.getLength() && sStrArray
[nNext
][ nLen
- 1 ] == ' '))
1548 // The same ugly trailing blanks check as in
1549 // IsAcceptedDatePattern().
1550 using namespace comphelper::string
;
1551 OUStringBuffer
aBuf(sStrArray
[nNext
]);
1553 padToLength(aBuf
, rPat
.getLength() - nPat
, ' ');
1554 bOk
= (rPat
.indexOf( aBuf
.makeStringAndClear(), nPat
) == nPat
);
1558 rPos
= nLen
; // yes, set, not add!
1564 nPat
+= sStrArray
[nNext
].getLength() - 1;
1572 sal_uInt16
ImpSvNumberInputScan::GetDatePatternNumbers()
1574 // If not initialized yet start with first number, if any.
1575 if (!IsAcceptedDatePattern( nNumericsCnt
? nNums
[0] : 0 ))
1579 return nDatePatternNumbers
;
1583 bool ImpSvNumberInputScan::IsDatePatternNumberOfType( sal_uInt16 nNumber
, sal_Unicode cType
)
1585 if (GetDatePatternNumbers() <= nNumber
)
1588 sal_uInt16 nNum
= 0;
1589 const OUString
& rPat
= sDateAcceptancePatterns
[nAcceptedDatePattern
];
1590 for (sal_Int32 nPat
= 0; nPat
< rPat
.getLength(); ++nPat
)
1597 if (nNum
== nNumber
)
1598 return rPat
[nPat
] == cType
;
1607 sal_uInt32
ImpSvNumberInputScan::GetDatePatternOrder()
1609 // If not initialized yet start with first number, if any.
1610 if (!IsAcceptedDatePattern( nNumericsCnt
? nNums
[0] : 0 ))
1614 sal_uInt32 nOrder
= 0;
1615 const OUString
& rPat
= sDateAcceptancePatterns
[nAcceptedDatePattern
];
1616 for (sal_Int32 nPat
= 0; nPat
< rPat
.getLength() && !(nOrder
& 0xff0000); ++nPat
)
1623 nOrder
= (nOrder
<< 8) | rPat
[nPat
];
1631 DateOrder
ImpSvNumberInputScan::GetDateOrder( bool bFromFormatIfNoPattern
)
1633 sal_uInt32 nOrder
= GetDatePatternOrder();
1636 if (bFromFormatIfNoPattern
&& mpFormat
)
1637 return mpFormat
->GetDateOrder();
1639 return pFormatter
->GetLocaleData()->getDateOrder();
1641 switch ((nOrder
& 0xff0000) >> 16)
1644 if ((((nOrder
& 0xff00) >> 8) == 'M') && ((nOrder
& 0xff) == 'D'))
1646 return DateOrder::YMD
;
1650 if ((((nOrder
& 0xff00) >> 8) == 'D') && ((nOrder
& 0xff) == 'Y'))
1652 return DateOrder::MDY
;
1656 if ((((nOrder
& 0xff00) >> 8) == 'M') && ((nOrder
& 0xff) == 'Y'))
1658 return DateOrder::DMY
;
1663 switch ((nOrder
& 0xff00) >> 8)
1666 switch (nOrder
& 0xff)
1669 return DateOrder::YMD
;
1673 switch (nOrder
& 0xff)
1676 return DateOrder::DMY
;
1678 return DateOrder::MDY
;
1682 switch (nOrder
& 0xff)
1685 return DateOrder::MDY
;
1687 return DateOrder::DMY
;
1692 switch (nOrder
& 0xff)
1695 return DateOrder::YMD
;
1697 return DateOrder::MDY
;
1699 return DateOrder::DMY
;
1704 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateOrder: undefined, falling back to locale's default");
1705 return pFormatter
->GetLocaleData()->getDateOrder();
1708 bool ImpSvNumberInputScan::GetDateRef( double& fDays
, sal_uInt16
& nCounter
)
1710 using namespace ::com::sun::star::i18n
;
1711 NfEvalDateFormat eEDF
;
1713 if ( mpFormat
&& (mpFormat
->GetType() & SvNumFormatType::DATE
) )
1715 eEDF
= pFormatter
->GetEvalDateFormat();
1718 case NF_EVALDATEFORMAT_INTL
:
1719 case NF_EVALDATEFORMAT_FORMAT
:
1720 nFormatOrder
= 1; // only one loop
1724 if ( nMatchedAllStrings
)
1726 eEDF
= NF_EVALDATEFORMAT_FORMAT_INTL
;
1727 // we have a complete match, use it
1733 eEDF
= NF_EVALDATEFORMAT_INTL
;
1738 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
1739 CalendarWrapper
* pCal
= pFormatter
->GetCalendar();
1740 for ( int nTryOrder
= 1; nTryOrder
<= nFormatOrder
; nTryOrder
++ )
1742 pCal
->setGregorianDateTime( Date( Date::SYSTEM
) ); // today
1743 OUString aOrgCalendar
; // empty => not changed yet
1748 case NF_EVALDATEFORMAT_INTL
:
1749 bFormatTurn
= false;
1750 DateFmt
= GetDateOrder();
1752 case NF_EVALDATEFORMAT_FORMAT
:
1754 DateFmt
= mpFormat
->GetDateOrder();
1756 case NF_EVALDATEFORMAT_INTL_FORMAT
:
1757 if ( nTryOrder
== 1 )
1759 bFormatTurn
= false;
1760 DateFmt
= GetDateOrder();
1765 DateFmt
= mpFormat
->GetDateOrder();
1768 case NF_EVALDATEFORMAT_FORMAT_INTL
:
1769 if ( nTryOrder
== 2 )
1771 bFormatTurn
= false;
1772 DateFmt
= GetDateOrder();
1777 // Even if the format pattern is to be preferred, the input may
1778 // have matched a pattern of the current locale, which then
1779 // again is to be preferred. Both date orders can be different
1780 // so we need to obtain the actual match. For example ISO
1781 // YYYY-MM-DD format vs locale's DD.MM.YY input.
1782 // If no pattern was matched, obtain from format.
1783 // Note that patterns may have been constructed from the
1784 // format's locale and prepended to the current locale's
1785 // patterns, it doesn't necessarily mean a current locale's
1786 // pattern was matched, but may if the format's locale's
1787 // patterns didn't match, which were tried first.
1788 DateFmt
= GetDateOrder(true);
1792 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateRef: unknown NfEvalDateFormat" );
1793 DateFmt
= DateOrder::YMD
;
1794 bFormatTurn
= false;
1799 We are currently not able to fully support a switch to another calendar during
1800 input for the following reasons:
1801 1. We do have a problem if both (locale's default and format's) calendars
1802 define the same YMD order and use the same date separator, there is no way
1803 to distinguish between them if the input results in valid calendar input for
1804 both calendars. How to solve? Would NfEvalDateFormat be sufficient? Should
1805 it always be set to NF_EVALDATEFORMAT_FORMAT_INTL and thus the format's
1806 calendar be preferred? This could be confusing if a Calc cell was formatted
1807 different to the locale's default and has no content yet, then the user has
1808 no clue about the format or calendar being set.
1809 2. In Calc cell edit mode a date is always displayed and edited using the
1810 default edit format of the default calendar (normally being Gregorian). If
1811 input was ambiguous due to issue #1 we'd need a mechanism to tell that a
1812 date was edited and not newly entered. Not feasible. Otherwise we'd need a
1813 mechanism to use a specific edit format with a specific calendar according
1815 3. For some calendars like Japanese Gengou we'd need era input, which isn't
1816 implemented at all. Though this is a rare and special case, forcing a
1817 calendar dependent edit format as suggested in item #2 might require era
1818 input, if it shouldn't result in a fallback to Gregorian calendar.
1819 4. Last and least: the GetMonth() method currently only matches month names of
1820 the default calendar. Alternating month names of the actual format's
1821 calendar would have to be implemented. No problem.
1825 if ( mpFormat
->IsOtherCalendar( nStringScanNumFor
) )
1827 mpFormat
->SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
1831 mpFormat
->SwitchToSpecifiedCalendar( aOrgCalendar
, fOrgDateTime
,
1832 nStringScanNumFor
);
1839 // For incomplete dates, always assume first day of month if not specified.
1840 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
1842 switch (nNumericsCnt
) // count of numbers in string
1845 if (nMonthPos
) // only month (Jan)
1847 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
1855 case 1: // only one number
1857 switch (nMonthPos
) // where is the month
1859 case 0: // not found
1861 // If input matched a date pattern, use the pattern
1862 // to determine if it is a day, month or year. The
1863 // pattern should have only one single value then,
1864 // 'D-', 'M-' or 'Y-'. If input did not match a
1865 // pattern assume the usual day of current month.
1866 sal_uInt32 nDateOrder
= (bFormatTurn
?
1867 mpFormat
->GetExactDateOrder() :
1868 GetDatePatternOrder());
1872 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1875 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1879 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1884 case 1: // month at the beginning (Jan 01)
1885 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
1888 case DateOrder::MDY
:
1889 case DateOrder::YMD
:
1891 sal_uInt16 nDay
= ImplGetDay(0);
1892 sal_uInt16 nYear
= ImplGetYear(0);
1893 if (nDay
== 0 || nDay
> 32)
1895 pCal
->setValue( CalendarFieldIndex::YEAR
, nYear
);
1899 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1903 case DateOrder::DMY
:
1904 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1911 case 3: // month at the end (10 Jan)
1912 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
1915 case DateOrder::DMY
:
1916 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1918 case DateOrder::YMD
:
1919 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1929 } // switch (nMonthPos)
1932 case 2: // 2 numbers
1934 switch (nMonthPos
) // where is the month
1936 case 0: // not found
1938 sal_uInt32 nExactDateOrder
= (bFormatTurn
?
1939 mpFormat
->GetExactDateOrder() :
1940 GetDatePatternOrder());
1941 bool bIsExact
= (0xff < nExactDateOrder
&& nExactDateOrder
<= 0xffff);
1942 if (!bIsExact
&& bFormatTurn
&& IsAcceptedDatePattern( nNums
[0]))
1944 // If input does not match format but pattern, use pattern
1945 // instead, even if eEDF==NF_EVALDATEFORMAT_FORMAT_INTL.
1946 // For example, format has "Y-M-D" and pattern is "D.M.",
1947 // input with 2 numbers can't match format and 31.12. would
1948 // lead to 1931-12-01 (fdo#54344)
1949 nExactDateOrder
= GetDatePatternOrder();
1950 bIsExact
= (0xff < nExactDateOrder
&& nExactDateOrder
<= 0xffff);
1955 // formatted as date and exactly 2 parts
1957 switch ( (nExactDateOrder
>> 8) & 0xff )
1960 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1963 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1966 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1971 switch ( nExactDateOrder
& 0xff )
1974 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
1977 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
1980 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
1985 SAL_WARN_IF( !bHadExact
, "svl.numbers", "ImpSvNumberInputScan::GetDateRef: error in exact date order");
1991 // If input matched against a date acceptance pattern
1992 // do not attempt to mess around with guessing the
1993 // order, either it matches or it doesn't.
1994 if ((bFormatTurn
|| !bIsExact
) && (!bHadExact
|| !pCal
->isValid()))
1996 if ( !bHadExact
&& nExactDateOrder
)
1998 pCal
->setGregorianDateTime( Date( Date::SYSTEM
) ); // reset today
2002 case DateOrder::MDY
:
2004 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2005 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
2006 if ( !pCal
->isValid() ) // 2nd try
2008 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
2009 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
2010 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2013 case DateOrder::DMY
:
2015 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2016 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
2017 if ( !pCal
->isValid() ) // 2nd try
2019 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
2020 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
2021 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2024 case DateOrder::YMD
:
2026 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2027 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
2028 if ( !pCal
->isValid() ) // 2nd try
2030 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
2031 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
2032 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2042 case 1: // month at the beginning (Jan 01 01)
2044 // The input is valid as MDY in almost any
2045 // constellation, there is no date order (M)YD except if
2046 // set in a format applied.
2047 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
2048 sal_uInt32 nExactDateOrder
= (bFormatTurn
? mpFormat
->GetExactDateOrder() : 0);
2049 if ((((nExactDateOrder
>> 8) & 0xff) == 'Y') && ((nExactDateOrder
& 0xff) == 'D'))
2051 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2052 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2056 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2057 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2061 case 2: // month in the middle (10 Jan 94)
2063 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
2064 DateOrder eDF
= (MayBeMonthDate() ? (nMayBeMonthDate
== 2 ? DateOrder::DMY
: DateOrder::YMD
) : DateFmt
);
2067 case DateOrder::DMY
:
2068 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2069 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2071 case DateOrder::YMD
:
2072 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2073 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2081 default: // else, e.g. month at the end (94 10 Jan)
2084 } // switch (nMonthPos)
2087 default: // more than two numbers (31.12.94 8:23) (31.12. 8:23)
2088 switch (nMonthPos
) // where is the month
2090 case 0: // not found
2094 { // find first time number index (should only be 3 or 2 anyway)
2095 for ( sal_uInt16 j
= 0; j
< nNumericsCnt
; j
++ )
2097 if ( nNums
[j
] == nTimePos
- 2 )
2104 // ISO 8601 yyyy-mm-dd forced recognition
2105 DateOrder eDF
= (CanForceToIso8601( DateFmt
) ? DateOrder::YMD
: DateFmt
);
2108 case DateOrder::MDY
:
2109 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2110 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
2112 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(2) );
2114 case DateOrder::DMY
:
2115 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2116 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
2118 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(2) );
2120 case DateOrder::YMD
:
2122 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(2) );
2123 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
2124 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2132 case 1: // month at the beginning (Jan 01 01 8:23)
2136 case DateOrder::MDY
:
2137 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2138 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
2139 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2146 case 2: // month in the middle (10 Jan 94 8:23)
2148 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
2151 case DateOrder::DMY
:
2152 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2153 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2155 case DateOrder::YMD
:
2156 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2157 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2164 default: // else, e.g. month at the end (94 10 Jan 8:23)
2168 } // switch (nMonthPos)
2170 } // switch (nNumericsCnt)
2172 if (mbEraCE
!= kDefaultEra
)
2173 pCal
->setValue( CalendarFieldIndex::ERA
, mbEraCE
? 1 : 0);
2175 if ( res
&& pCal
->isValid() )
2177 double fDiff
= DateTime(*pNullDate
) - pCal
->getEpochStart();
2178 fDays
= ::rtl::math::approxFloor( pCal
->getLocalDateTime() );
2180 nTryOrder
= nFormatOrder
; // break for
2186 if ( aOrgCalendar
.getLength() )
2188 pCal
->loadCalendar( aOrgCalendar
, pLoc
->getLanguageTag().getLocale() ); // restore calendar
2190 #if NF_TEST_CALENDAR
2192 using namespace ::com::sun::star
;
2193 struct entry
{ const char* lan
; const char* cou
; const char* cal
; };
2194 const entry cals
[] = {
2195 { "en", "US", "gregorian" },
2196 { "ar", "TN", "hijri" },
2197 { "he", "IL", "jewish" },
2198 { "ja", "JP", "gengou" },
2199 { "ko", "KR", "hanja_yoil" },
2200 { "th", "TH", "buddhist" },
2201 { "zh", "TW", "ROC" },
2204 lang::Locale aLocale
;
2206 sal_Int16 nDay
, nMyMonth
, nYear
, nHour
, nMinute
, nSecond
;
2207 sal_Int16 nDaySet
, nMonthSet
, nYearSet
, nHourSet
, nMinuteSet
, nSecondSet
;
2208 sal_Int16 nZO
, nDST1
, nDST2
, nDST
, nZOmillis
, nDST1millis
, nDST2millis
, nDSTmillis
;
2209 sal_Int32 nZoneInMillis
, nDST1InMillis
, nDST2InMillis
;
2210 uno::Reference
< uno::XComponentContext
> xContext
=
2211 ::comphelper::getProcessComponentContext();
2212 uno::Reference
< i18n::XCalendar4
> xCal
= i18n::LocaleCalendar2::create(xContext
);
2213 for ( const entry
* p
= cals
; p
->lan
; ++p
)
2215 aLocale
.Language
= OUString::createFromAscii( p
->lan
);
2216 aLocale
.Country
= OUString::createFromAscii( p
->cou
);
2217 xCal
->loadCalendar( OUString::createFromAscii( p
->cal
),
2219 double nDateTime
= 0.0; // 1-Jan-1970 00:00:00
2220 nZO
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET
);
2221 nZOmillis
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS
);
2222 nZoneInMillis
= static_cast<sal_Int32
>(nZO
) * 60000 +
2223 (nZO
< 0 ? -1 : 1) * static_cast<sal_uInt16
>(nZOmillis
);
2224 nDST1
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET
);
2225 nDST1millis
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS
);
2226 nDST1InMillis
= static_cast<sal_Int32
>(nDST1
) * 60000 +
2227 (nDST1
< 0 ? -1 : 1) * static_cast<sal_uInt16
>(nDST1millis
);
2228 nDateTime
-= (double)(nZoneInMillis
+ nDST1InMillis
) / 1000.0 / 60.0 / 60.0 / 24.0;
2229 xCal
->setDateTime( nDateTime
);
2230 nDST2
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET
);
2231 nDST2millis
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS
);
2232 nDST2InMillis
= static_cast<sal_Int32
>(nDST2
) * 60000 +
2233 (nDST2
< 0 ? -1 : 1) * static_cast<sal_uInt16
>(nDST2millis
);
2234 if ( nDST1InMillis
!= nDST2InMillis
)
2236 nDateTime
= 0.0 - (double)(nZoneInMillis
+ nDST2InMillis
) / 1000.0 / 60.0 / 60.0 / 24.0;
2237 xCal
->setDateTime( nDateTime
);
2239 nDaySet
= xCal
->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH
);
2240 nMonthSet
= xCal
->getValue( i18n::CalendarFieldIndex::MONTH
);
2241 nYearSet
= xCal
->getValue( i18n::CalendarFieldIndex::YEAR
);
2242 nHourSet
= xCal
->getValue( i18n::CalendarFieldIndex::HOUR
);
2243 nMinuteSet
= xCal
->getValue( i18n::CalendarFieldIndex::MINUTE
);
2244 nSecondSet
= xCal
->getValue( i18n::CalendarFieldIndex::SECOND
);
2245 nZO
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET
);
2246 nZOmillis
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS
);
2247 nDST
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET
);
2248 nDSTmillis
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS
);
2249 xCal
->setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH
, nDaySet
);
2250 xCal
->setValue( i18n::CalendarFieldIndex::MONTH
, nMonthSet
);
2251 xCal
->setValue( i18n::CalendarFieldIndex::YEAR
, nYearSet
);
2252 xCal
->setValue( i18n::CalendarFieldIndex::HOUR
, nHourSet
);
2253 xCal
->setValue( i18n::CalendarFieldIndex::MINUTE
, nMinuteSet
);
2254 xCal
->setValue( i18n::CalendarFieldIndex::SECOND
, nSecondSet
);
2255 bValid
= xCal
->isValid();
2256 nDay
= xCal
->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH
);
2257 nMyMonth
= xCal
->getValue( i18n::CalendarFieldIndex::MONTH
);
2258 nYear
= xCal
->getValue( i18n::CalendarFieldIndex::YEAR
);
2259 nHour
= xCal
->getValue( i18n::CalendarFieldIndex::HOUR
);
2260 nMinute
= xCal
->getValue( i18n::CalendarFieldIndex::MINUTE
);
2261 nSecond
= xCal
->getValue( i18n::CalendarFieldIndex::SECOND
);
2262 bValid
= bValid
&& nDay
== nDaySet
&& nMyMonth
== nMonthSet
&& nYear
==
2263 nYearSet
&& nHour
== nHourSet
&& nMinute
== nMinuteSet
&& nSecond
2267 #endif // NF_TEST_CALENDAR
2276 * Analyze first string
2280 bool ImpSvNumberInputScan::ScanStartString( const OUString
& rString
)
2284 // First of all, eat leading blanks
2285 SkipBlanks(rString
, nPos
);
2287 // Yes, nMatchedAllStrings should know about the sign position
2288 nSign
= GetSign(rString
, nPos
);
2289 if ( nSign
) // sign?
2291 SkipBlanks(rString
, nPos
);
2293 // #102371# match against format string only if start string is not a sign character
2294 if ( nMatchedAllStrings
&& !(nSign
&& rString
.getLength() == 1) )
2296 // Match against format in any case, so later on for a "x1-2-3" input
2297 // we may distinguish between a xy-m-d (or similar) date and a x0-0-0
2298 // format. No sign detection here!
2299 if ( ScanStringNumFor( rString
, nPos
, 0, true ) )
2301 nMatchedAllStrings
|= nMatchedStartString
;
2305 nMatchedAllStrings
= 0;
2309 // Bail out early for just a sign.
2310 if (nSign
&& nPos
== rString
.getLength())
2313 const sal_Int32 nStartBlanks
= nPos
;
2314 if ( GetDecSep(rString
, nPos
) ) // decimal separator in start string
2316 if (SkipBlanks(rString
, nPos
))
2317 nPos
= nStartBlanks
; // `. 2` not a decimal separator
2319 nDecPos
= 1; // leading decimal separator
2321 else if ( GetCurrency(rString
, nPos
) ) // currency (DM 1)?
2323 eScannedType
= SvNumFormatType::CURRENCY
; // !!! it IS currency !!!
2324 SkipBlanks(rString
, nPos
);
2325 if (nSign
== 0) // no sign yet
2327 nSign
= GetSign(rString
, nPos
);
2328 if ( nSign
) // DM -1
2330 SkipBlanks(rString
, nPos
);
2333 if ( GetDecSep(rString
, nPos
) ) // decimal separator follows currency
2335 if (SkipBlanks(rString
, nPos
))
2337 nPos
= nStartBlanks
; // `DM . 2` not a decimal separator
2338 eScannedType
= SvNumFormatType::UNDEFINED
; // !!! it is NOT currency !!!
2341 nDecPos
= 1; // leading decimal separator
2346 const sal_Int32 nMonthStart
= nPos
;
2347 short nTempMonth
= GetMonth(rString
, nPos
);
2350 // Short month and day names may be identical in some locales, e.g.
2351 // "mar" for "martes" or "marzo" in Spanish.
2352 // Do not let a month name immediately take precedence if a day
2353 // name was meant instead. Assume that both could be valid, until
2354 // encountered differently or the final evaluation in
2355 // IsNumberFormat() checks, but continue with weighing the month
2356 // name higher unless we have both day of week and month name here.
2357 sal_Int32 nTempPos
= nMonthStart
;
2358 nDayOfWeek
= GetDayOfWeek( rString
, nTempPos
);
2361 SkipChar( '.', rString
, nTempPos
); // abbreviated
2362 SkipString( pFormatter
->GetLocaleData()->getLongDateDayOfWeekSep(), rString
, nTempPos
);
2363 SkipBlanks( rString
, nTempPos
);
2364 short nTempTempMonth
= GetMonth( rString
, nTempPos
);
2367 // Fall into the else branch below that handles both.
2371 // Do not set nDayOfWeek hereafter, anywhere.
2375 if ( nTempMonth
) // month (Jan 1)?
2377 // Jan1 without separator is not a date, unless it is followed by a
2378 // separator and a (year) number.
2379 if (nPos
< rString
.getLength() || (nStringsCnt
>= 4 && nNumericsCnt
>= 2))
2381 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date !!!
2382 nMonth
= nTempMonth
;
2383 nMonthPos
= 1; // month at the beginning
2386 SkipChar( '.', rString
, nPos
); // abbreviated
2388 SkipBlanks(rString
, nPos
);
2392 nPos
= nMonthStart
; // rewind month
2397 int nTempDayOfWeek
= GetDayOfWeek( rString
, nPos
);
2398 if ( nTempDayOfWeek
)
2400 // day of week is just parsed away
2401 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date !!!
2402 if ( nPos
< rString
.getLength() )
2404 if ( nTempDayOfWeek
< 0 )
2407 if ( rString
[ nPos
] == '.' )
2415 SkipBlanks(rString
, nPos
);
2416 SkipString( pFormatter
->GetLocaleData()->getLongDateDayOfWeekSep(), rString
, nPos
);
2418 SkipBlanks(rString
, nPos
);
2419 nTempMonth
= GetMonth(rString
, nPos
);
2420 if ( nTempMonth
) // month (Jan 1)?
2422 // Jan1 without separator is not a date, unless it is followed by a
2423 // separator and a (year) number.
2424 if (nPos
< rString
.getLength() || (nStringsCnt
>= 4 && nNumericsCnt
>= 2))
2426 nMonth
= nTempMonth
;
2427 nMonthPos
= 1; // month at the beginning
2430 SkipChar( '.', rString
, nPos
); // abbreviated
2432 SkipBlanks(rString
, nPos
);
2436 nPos
= nMonthStart
; // rewind month
2442 // Determine and remember following date pattern, if any.
2443 IsAcceptedDatePattern( 1);
2447 // Skip one trailing '-' or '/' character to recognize June-2007
2448 if (nMonth
&& nPos
+ 1 == rString
.getLength())
2450 SkipChar('-', rString
, nPos
) || SkipChar('/', rString
, nPos
);
2454 if (nPos
< rString
.getLength()) // not everything consumed
2456 // Does input StartString equal StartString of format?
2457 // This time with sign detection!
2458 if ( !ScanStringNumFor( rString
, nPos
, 0 ) )
2460 return MatchedReturn();
2469 * Analyze string in the middle
2473 bool ImpSvNumberInputScan::ScanMidString( const OUString
& rString
, sal_uInt16 nStringPos
)
2476 SvNumFormatType eOldScannedType
= eScannedType
;
2478 if ( nMatchedAllStrings
)
2479 { // Match against format in any case, so later on for a "1-2-3-4" input
2480 // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2482 if ( ScanStringNumFor( rString
, 0, nStringPos
) )
2484 nMatchedAllStrings
|= nMatchedMidString
;
2488 nMatchedAllStrings
= 0;
2492 const sal_Int32 nStartBlanks
= nPos
;
2493 const bool bBlanks
= SkipBlanks(rString
, nPos
);
2494 if (GetDecSep(rString
, nPos
)) // decimal separator?
2496 if (nDecPos
== 1 || nDecPos
== 3) // .12.4 or 1.E2.1
2498 return MatchedReturn();
2500 else if (nDecPos
== 2) // . dup: 12.4.
2502 bool bSignedYear
= false;
2503 if (bDecSepInDateSeps
|| // . also date separator
2504 SkipDatePatternSeparator( nStringPos
, nPos
, bSignedYear
))
2506 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&&
2507 eScannedType
!= SvNumFormatType::DATE
&&
2508 eScannedType
!= SvNumFormatType::DATETIME
) // already another type
2510 return MatchedReturn();
2512 if (eScannedType
== SvNumFormatType::UNDEFINED
)
2514 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date
2516 SkipBlanks(rString
, nPos
);
2520 return MatchedReturn();
2525 // `1 .2` or `1 . 2` not a decimal separator, reset
2526 nPos
= nStartBlanks
;
2528 else if (SkipBlanks(rString
, nPos
))
2530 // `1. 2` not a decimal separator, reset
2531 nPos
= nStartBlanks
;
2535 nDecPos
= 2; // . in mid string
2538 else if ( (eScannedType
& SvNumFormatType::TIME
) &&
2539 GetTime100SecSep( rString
, nPos
) )
2540 { // hundredth seconds separator
2543 return MatchedReturn();
2545 nDecPos
= 2; // . in mid string
2547 // If this is exactly an ISO 8601 fractional seconds separator, bail
2548 // out early to not get confused by later checks for group separator or
2550 if (bIso8601Tsep
&& nPos
== rString
.getLength() &&
2551 eScannedType
== SvNumFormatType::DATETIME
&& (rString
== "." || rString
== ","))
2554 SkipBlanks(rString
, nPos
);
2557 if (SkipChar('/', rString
, nPos
)) // fraction?
2559 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2560 eScannedType
!= SvNumFormatType::DATE
) // except date
2562 return MatchedReturn(); // => jan/31/1994
2564 else if (eScannedType
!= SvNumFormatType::DATE
&& // analyzed no date until now
2565 ( eSetType
== SvNumFormatType::FRACTION
|| // and preset was fraction
2566 (nNumericsCnt
== 3 && // or 3 numbers
2567 (nStringPos
== 3 || // and 3rd string particle
2568 (nStringPos
== 4 && nSign
))))) // or 4th if signed
2570 SkipBlanks(rString
, nPos
);
2571 if (nPos
== rString
.getLength())
2573 eScannedType
= SvNumFormatType::FRACTION
; // !!! it IS a fraction (so far)
2574 if (eSetType
== SvNumFormatType::FRACTION
&&
2575 nNumericsCnt
== 2 &&
2576 (nStringPos
== 1 || // for 4/5
2577 (nStringPos
== 2 && nSign
))) // or signed -4/5
2579 return true; // don't fall into date trap
2585 nPos
--; // put '/' back
2589 if (GetThousandSep(rString
, nPos
, nStringPos
)) // 1,000
2591 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2592 eScannedType
!= SvNumFormatType::CURRENCY
) // except currency
2594 return MatchedReturn();
2599 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
2600 bool bSignedYear
= false;
2601 bool bDate
= SkipDatePatternSeparator( nStringPos
, nPos
, bSignedYear
); // 12/31 31.12. 12/31/1999 31.12.1999
2604 const OUString
& rDate
= pFormatter
->GetDateSep();
2605 SkipBlanks(rString
, nPos
);
2606 bDate
= SkipString( rDate
, rString
, nPos
); // 10. 10- 10/
2608 if (bDate
|| ((MayBeIso8601() || MayBeMonthDate()) && // 1999-12-31 31-Dec-1999
2609 SkipChar( '-', rString
, nPos
)))
2611 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2612 eScannedType
!= SvNumFormatType::DATE
) // except date
2614 return MatchedReturn();
2616 SkipBlanks(rString
, nPos
);
2617 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date
2618 short nTmpMonth
= GetMonth(rString
, nPos
); // 10. Jan 94
2619 if (nMonth
&& nTmpMonth
) // month dup
2621 return MatchedReturn();
2626 nMonthPos
= 2; // month in the middle
2627 if ( nMonth
< 0 && SkipChar( '.', rString
, nPos
) )
2628 ; // short month may be abbreviated Jan.
2629 else if ( SkipChar( '-', rString
, nPos
) )
2630 ; // #79632# recognize 17-Jan-2001 to be a date
2631 // #99065# short and long month name
2634 SkipString( pLoc
->getLongDateMonthSep(), rString
, nPos
);
2636 SkipBlanks(rString
, nPos
);
2640 if (mbEraCE
!= kDefaultEra
) // signed year twice?
2641 return MatchedReturn();
2643 mbEraCE
= false; // BCE
2647 const sal_Int32 nMonthStart
= nPos
;
2648 short nTempMonth
= GetMonth(rString
, nPos
); // month in the middle (10 Jan 94)
2651 if (nMonth
!= 0) // month dup
2653 return MatchedReturn();
2655 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2656 eScannedType
!= SvNumFormatType::DATE
) // except date
2658 return MatchedReturn();
2660 if (nMonthStart
> 0 && nPos
< rString
.getLength()) // 10Jan or Jan94 without separator are not dates
2662 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date
2663 nMonth
= nTempMonth
;
2664 nMonthPos
= 2; // month in the middle
2667 SkipChar( '.', rString
, nPos
); // abbreviated
2669 SkipString( pLoc
->getLongDateMonthSep(), rString
, nPos
);
2670 SkipBlanks(rString
, nPos
);
2674 nPos
= nMonthStart
; // rewind month
2678 if ( SkipChar('E', rString
, nPos
) || // 10E, 10e, 10,Ee
2679 SkipChar('e', rString
, nPos
) )
2681 if (eScannedType
!= SvNumFormatType::UNDEFINED
) // already another type
2683 return MatchedReturn();
2687 SkipBlanks(rString
, nPos
);
2688 eScannedType
= SvNumFormatType::SCIENTIFIC
; // !!! it IS scientific
2689 if ( nThousand
+2 == nNumericsCnt
&& nDecPos
== 2 ) // special case 1.E2
2691 nDecPos
= 3; // 1,100.E2 1,100,100.E3
2694 nESign
= GetESign(rString
, nPos
); // signed exponent?
2695 SkipBlanks(rString
, nPos
);
2698 const OUString
& rTime
= pLoc
->getTimeSep();
2699 if ( SkipString(rTime
, rString
, nPos
) ) // time separator?
2701 if (nDecPos
) // already . => maybe error
2703 if (bDecSepInDateSeps
) // . also date sep
2705 if ( eScannedType
!= SvNumFormatType::DATE
&& // already another type than date
2706 eScannedType
!= SvNumFormatType::DATETIME
) // or date time
2708 return MatchedReturn();
2710 if (eScannedType
== SvNumFormatType::DATE
)
2712 nDecPos
= 0; // reset for time transition
2717 return MatchedReturn();
2720 if ((eScannedType
== SvNumFormatType::DATE
|| // already date type
2721 eScannedType
== SvNumFormatType::DATETIME
) && // or date time
2722 nNumericsCnt
> 3) // and more than 3 numbers? (31.Dez.94 8:23)
2724 SkipBlanks(rString
, nPos
);
2725 eScannedType
= SvNumFormatType::DATETIME
; // !!! it IS date with time
2727 else if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2728 eScannedType
!= SvNumFormatType::TIME
) // except time
2730 return MatchedReturn();
2734 SkipBlanks(rString
, nPos
);
2735 eScannedType
= SvNumFormatType::TIME
; // !!! it IS a time
2739 nTimePos
= nStringPos
+ 1;
2743 if (nPos
< rString
.getLength())
2745 switch (eScannedType
)
2747 case SvNumFormatType::DATE
:
2748 if (nMonthPos
== 1 && pLoc
->getLongDateOrder() == DateOrder::MDY
)
2750 // #68232# recognize long date separators like ", " in "September 5, 1999"
2751 if (SkipString( pLoc
->getLongDateDaySep(), rString
, nPos
))
2753 SkipBlanks( rString
, nPos
);
2756 else if (nPos
== 0 && rString
.getLength() == 1 && MayBeIso8601())
2758 if ( (nStringPos
== 5 && rString
[0] == 'T') ||
2759 (nStringPos
== 6 && rString
[0] == 'T' && sStrArray
[0] == "-"))
2761 // ISO 8601 combined date and time, yyyy-mm-ddThh:mm or -yyyy-mm-ddThh:mm
2763 bIso8601Tsep
= true;
2765 else if (nStringPos
== 7 && rString
[0] == ':')
2767 // ISO 8601 combined date and time, the time part; we reach
2768 // here if the locale's separator is not ':' so it couldn't
2769 // be detected above in the time block.
2770 if (nNumericsCnt
>= 5)
2771 eScannedType
= SvNumFormatType::DATETIME
;
2776 case SvNumFormatType::DATETIME
:
2777 if (nPos
== 0 && rString
.getLength() == 1 && MayBeIso8601())
2779 if (nStringPos
== 9 && rString
[0] == ':')
2781 // ISO 8601 combined date and time, the time part continued.
2785 #if NF_RECOGNIZE_ISO8601_TIMEZONES
2786 else if (nPos
== 0 && rString
.getLength() == 1 && nStringPos
>= 9 && MayBeIso8601())
2788 // ISO 8601 timezone offset
2789 switch (rString
[ 0 ])
2793 if (nStringPos
== nStringsCnt
- 2 ||
2794 nStringPos
== nStringsCnt
- 4)
2796 ++nPos
; // yyyy-mm-ddThh:mm[:ss]+xx[[:]yy]
2797 // nTimezonePos needed for GetTimeRef()
2800 nTimezonePos
= nStringPos
+ 1;
2805 if (nTimezonePos
&& nStringPos
>= 11 &&
2806 nStringPos
== nStringsCnt
- 2)
2808 ++nPos
; // yyyy-mm-ddThh:mm[:ss]+xx:yy
2819 if (nPos
< rString
.getLength()) // not everything consumed?
2821 if ( nMatchedAllStrings
& ~nMatchedVirgin
)
2823 eScannedType
= eOldScannedType
;
2840 bool ImpSvNumberInputScan::ScanEndString( const OUString
& rString
)
2844 if ( nMatchedAllStrings
)
2845 { // Match against format in any case, so later on for a "1-2-3-4" input
2846 // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2848 if ( ScanStringNumFor( rString
, 0, 0xFFFF ) )
2850 nMatchedAllStrings
|= nMatchedEndString
;
2854 nMatchedAllStrings
= 0;
2858 const sal_Int32 nStartBlanks
= nPos
;
2859 const bool bBlanks
= SkipBlanks(rString
, nPos
);
2860 if (GetDecSep(rString
, nPos
)) // decimal separator?
2862 if (nDecPos
== 1 || nDecPos
== 3) // .12.4 or 12.E4.
2864 return MatchedReturn();
2866 else if (nDecPos
== 2) // . dup: 12.4.
2868 bool bSignedYear
= false;
2869 if (bDecSepInDateSeps
|| // . also date separator
2870 SkipDatePatternSeparator( nStringsCnt
-1, nPos
, bSignedYear
))
2872 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&&
2873 eScannedType
!= SvNumFormatType::DATE
&&
2874 eScannedType
!= SvNumFormatType::DATETIME
) // already another type
2876 return MatchedReturn();
2878 if (eScannedType
== SvNumFormatType::UNDEFINED
)
2880 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date
2882 SkipBlanks(rString
, nPos
);
2886 return MatchedReturn();
2891 // not a decimal separator, reset
2892 nPos
= nStartBlanks
;
2896 nDecPos
= 3; // . in end string
2897 SkipBlanks(rString
, nPos
);
2901 bool bSignDetectedHere
= false;
2902 if ( nSign
== 0 && // conflict - not signed
2903 eScannedType
!= SvNumFormatType::DATE
) // and not date
2904 //!? catch time too?
2906 nSign
= GetSign(rString
, nPos
); // 1- DM
2907 if (bNegCheck
) // '(' as sign
2909 return MatchedReturn();
2913 bSignDetectedHere
= true;
2917 SkipBlanks(rString
, nPos
);
2918 if (bNegCheck
&& SkipChar(')', rString
, nPos
)) // skip ')' if appropriate
2921 SkipBlanks(rString
, nPos
);
2924 if ( GetCurrency(rString
, nPos
) ) // currency symbol?
2926 if (eScannedType
!= SvNumFormatType::UNDEFINED
) // currency dup
2928 return MatchedReturn();
2932 SkipBlanks(rString
, nPos
);
2933 eScannedType
= SvNumFormatType::CURRENCY
;
2934 } // behind currency a '-' is allowed
2935 if (nSign
== 0) // not signed yet
2937 nSign
= GetSign(rString
, nPos
); // DM -
2938 SkipBlanks(rString
, nPos
);
2939 if (bNegCheck
) // 3 DM (
2941 return MatchedReturn();
2944 if ( bNegCheck
&& eScannedType
== SvNumFormatType::CURRENCY
&&
2945 SkipChar(')', rString
, nPos
) )
2947 bNegCheck
= false; // ')' skipped
2948 SkipBlanks(rString
, nPos
); // only if currency
2952 if ( SkipChar('%', rString
, nPos
) ) // 1%
2954 if (eScannedType
!= SvNumFormatType::UNDEFINED
) // already another type
2956 return MatchedReturn();
2958 SkipBlanks(rString
, nPos
);
2959 eScannedType
= SvNumFormatType::PERCENT
;
2962 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
2963 const OUString
& rTime
= pLoc
->getTimeSep();
2964 if ( SkipString(rTime
, rString
, nPos
) ) // 10:
2966 if (nDecPos
) // already , => error
2968 return MatchedReturn();
2970 if (eScannedType
== SvNumFormatType::DATE
&& nNumericsCnt
> 2) // 31.Dez.94 8:
2972 SkipBlanks(rString
, nPos
);
2973 eScannedType
= SvNumFormatType::DATETIME
;
2975 else if (eScannedType
!= SvNumFormatType::UNDEFINED
&&
2976 eScannedType
!= SvNumFormatType::TIME
) // already another type
2978 return MatchedReturn();
2982 SkipBlanks(rString
, nPos
);
2983 eScannedType
= SvNumFormatType::TIME
;
2987 nTimePos
= nStringsCnt
;
2991 bool bSignedYear
= false;
2992 bool bDate
= SkipDatePatternSeparator( nStringsCnt
-1, nPos
, bSignedYear
); // 12/31 31.12. 12/31/1999 31.12.1999
2995 const OUString
& rDate
= pFormatter
->GetDateSep();
2996 bDate
= SkipString( rDate
, rString
, nPos
); // 10. 10- 10/
2998 if (bDate
&& bSignDetectedHere
)
3000 nSign
= 0; // 'D-' takes precedence over signed date
3002 if (bDate
|| ((MayBeIso8601() || MayBeMonthDate())
3003 && SkipChar( '-', rString
, nPos
)))
3005 if (eScannedType
!= SvNumFormatType::UNDEFINED
&&
3006 eScannedType
!= SvNumFormatType::DATE
) // already another type
3008 return MatchedReturn();
3012 SkipBlanks(rString
, nPos
);
3013 eScannedType
= SvNumFormatType::DATE
;
3015 short nTmpMonth
= GetMonth(rString
, nPos
); // 10. Jan
3016 if (nMonth
&& nTmpMonth
) // month dup
3018 return MatchedReturn();
3023 nMonthPos
= 3; // month at end
3026 SkipChar( '.', rString
, nPos
); // abbreviated
3028 SkipBlanks(rString
, nPos
);
3032 const sal_Int32 nMonthStart
= nPos
;
3033 short nTempMonth
= GetMonth(rString
, nPos
); // 10 Jan
3036 if (nMonth
) // month dup
3038 return MatchedReturn();
3040 if (eScannedType
!= SvNumFormatType::UNDEFINED
&&
3041 eScannedType
!= SvNumFormatType::DATE
) // already another type
3043 return MatchedReturn();
3045 if (nMonthStart
> 0) // 10Jan without separator is not a date
3047 eScannedType
= SvNumFormatType::DATE
;
3048 nMonth
= nTempMonth
;
3049 nMonthPos
= 3; // month at end
3052 SkipChar( '.', rString
, nPos
); // abbreviated
3054 SkipBlanks(rString
, nPos
);
3058 nPos
= nMonthStart
; // rewind month
3062 sal_Int32 nOrigPos
= nPos
;
3063 if (GetTimeAmPm(rString
, nPos
))
3065 if (eScannedType
!= SvNumFormatType::UNDEFINED
&&
3066 eScannedType
!= SvNumFormatType::TIME
&&
3067 eScannedType
!= SvNumFormatType::DATETIME
) // already another type
3069 return MatchedReturn();
3073 // If not already scanned as time, 6.78am does not result in 6
3074 // seconds and 78 hundredths in the morning. Keep as suffix.
3075 if (eScannedType
!= SvNumFormatType::TIME
&& nDecPos
== 2 && nNumericsCnt
== 2)
3077 nPos
= nOrigPos
; // rewind am/pm
3081 SkipBlanks(rString
, nPos
);
3082 if ( eScannedType
!= SvNumFormatType::DATETIME
)
3084 eScannedType
= SvNumFormatType::TIME
;
3090 if ( bNegCheck
&& SkipChar(')', rString
, nPos
) )
3092 if (eScannedType
== SvNumFormatType::CURRENCY
) // only if currency
3094 bNegCheck
= false; // skip ')'
3095 SkipBlanks(rString
, nPos
);
3099 return MatchedReturn();
3103 if ( nPos
< rString
.getLength() &&
3104 (eScannedType
== SvNumFormatType::DATE
||
3105 eScannedType
== SvNumFormatType::DATETIME
) )
3107 // day of week is just parsed away
3108 sal_Int32 nOldPos
= nPos
;
3109 const OUString
& rSep
= pFormatter
->GetLocaleData()->getLongDateDayOfWeekSep();
3110 if ( StringContains( rSep
, rString
, nPos
) )
3112 nPos
= nPos
+ rSep
.getLength();
3113 SkipBlanks(rString
, nPos
);
3115 int nTempDayOfWeek
= GetDayOfWeek( rString
, nPos
);
3116 if ( nTempDayOfWeek
)
3118 if ( nPos
< rString
.getLength() )
3120 if ( nTempDayOfWeek
< 0 )
3122 if ( rString
[ nPos
] == '.' )
3127 SkipBlanks(rString
, nPos
);
3136 #if NF_RECOGNIZE_ISO8601_TIMEZONES
3137 if (nPos
== 0 && eScannedType
== SvNumFormatType::DATETIME
&&
3138 rString
.getLength() == 1 && rString
[ 0 ] == 'Z' && MayBeIso8601())
3140 // ISO 8601 timezone UTC yyyy-mm-ddThh:mmZ
3145 if (nPos
< rString
.getLength()) // everything consumed?
3147 // does input EndString equal EndString in Format?
3148 if ( !ScanStringNumFor( rString
, nPos
, 0xFFFF ) )
3158 bool ImpSvNumberInputScan::ScanStringNumFor( const OUString
& rString
, // String to scan
3159 sal_Int32 nPos
, // Position until which was consumed
3160 sal_uInt16 nString
, // Substring of format, 0xFFFF => last
3161 bool bDontDetectNegation
) // Suppress sign detection
3167 const ::utl::TransliterationWrapper
* pTransliteration
= pFormatter
->GetTransliteration();
3168 const OUString
* pStr
;
3169 OUString
aString( rString
);
3170 bool bFound
= false;
3172 bool bContinue
= true;
3176 // Don't try "lower" subformats ff the very first match was the second
3177 // or third subformat.
3178 nSub
= nStringScanNumFor
;
3180 { // Step through subformats, first positive, then negative, then
3181 // other, but not the last (text) subformat.
3182 pStr
= mpFormat
->GetNumForString( nSub
, nString
, true );
3183 if ( pStr
&& pTransliteration
->isEqual( aString
, *pStr
) )
3188 else if ( nSub
< 2 )
3197 while ( bContinue
);
3198 if ( !bFound
&& bFirst
&& nPos
)
3200 // try remaining substring
3202 aString
= aString
.copy(nPos
);
3206 while ( bContinue
);
3210 if ( !bDontDetectNegation
&& (nString
== 0) &&
3211 !bFirst
&& (nSign
< 0) && mpFormat
->IsSecondSubformatRealNegative() )
3213 // simply negated twice? --1
3214 aString
= aString
.replaceAll(" ", "");
3215 if ( (aString
.getLength() == 1) && (aString
[0] == '-') )
3218 nStringScanSign
= -1;
3227 else if ( !bDontDetectNegation
&& (nSub
== 1) &&
3228 mpFormat
->IsSecondSubformatRealNegative() )
3231 if ( nStringScanSign
< 0 )
3233 if ( (nSign
< 0) && (nStringScanNumFor
!= 1) )
3235 nStringScanSign
= 1; // triple negated --1 yyy
3238 else if ( nStringScanSign
== 0 )
3241 { // nSign and nStringScanSign will be combined later,
3242 // flip sign if doubly negated
3243 if ( (nString
== 0) && !bFirst
&&
3244 SvNumberformat::HasStringNegativeSign( aString
) )
3246 nStringScanSign
= -1; // direct double negation
3248 else if ( mpFormat
->IsNegativeWithoutSign() )
3250 nStringScanSign
= -1; // indirect double negation
3255 nStringScanSign
= -1;
3260 nStringScanSign
= -1;
3263 nStringScanNumFor
= nSub
;
3269 * Recognizes types of number, exponential, fraction, percent, currency, date, time.
3270 * Else text => return false
3272 bool ImpSvNumberInputScan::IsNumberFormatMain( const OUString
& rString
, // string to be analyzed
3273 const SvNumberformat
* pFormat
) // maybe number format set to match against
3277 NumberStringDivision( rString
); // breakdown into strings and numbers
3278 if (nStringsCnt
>= SV_MAX_COUNT_INPUT_STRINGS
) // too many elements
3280 return false; // Njet, Nope, ...
3282 if (nNumericsCnt
== 0) // no number in input
3284 if ( nStringsCnt
> 0 )
3286 // Here we may change the original, we don't need it anymore.
3287 // This saves copies and ToUpper() in GetLogical() and is faster.
3288 sStrArray
[0] = comphelper::string::strip(sStrArray
[0], ' ');
3289 OUString
& rStrArray
= sStrArray
[0];
3290 nLogical
= GetLogical( rStrArray
);
3293 eScannedType
= SvNumFormatType::LOGICAL
; // !!! it's a BOOLEAN
3294 nMatchedAllStrings
&= ~nMatchedVirgin
;
3299 return false; // simple text
3304 return false; // simple text
3308 sal_uInt16 i
= 0; // mark any symbol
3309 sal_uInt16 j
= 0; // mark only numbers
3311 switch ( nNumericsCnt
)
3313 case 1 : // Exactly 1 number in input
3315 if (GetNextNumber(i
,j
)) // i=1,0
3316 { // Number at start
3317 if (eSetType
== SvNumFormatType::FRACTION
) // Fraction 1 = 1/1
3319 if (i
>= nStringsCnt
|| // no end string nor decimal separator
3320 pFormatter
->IsDecimalSep( sStrArray
[i
]))
3322 eScannedType
= SvNumFormatType::FRACTION
;
3323 nMatchedAllStrings
&= ~nMatchedVirgin
;
3329 { // Analyze start string
3330 if (!ScanStartString( sStrArray
[i
] )) // i=0
3332 return false; // already an error
3334 i
++; // next symbol, i=1
3336 GetNextNumber(i
,j
); // i=1,2
3337 if (eSetType
== SvNumFormatType::FRACTION
) // Fraction -1 = -1/1
3339 if (nSign
&& !bNegCheck
&& // Sign +, -
3340 eScannedType
== SvNumFormatType::UNDEFINED
&& // not date or currency
3341 nDecPos
== 0 && // no previous decimal separator
3342 (i
>= nStringsCnt
|| // no end string nor decimal separator
3343 pFormatter
->IsDecimalSep( sStrArray
[i
]))
3346 eScannedType
= SvNumFormatType::FRACTION
;
3347 nMatchedAllStrings
&= ~nMatchedVirgin
;
3351 if (i
< nStringsCnt
&& !ScanEndString( sStrArray
[i
] ))
3356 case 2 : // Exactly 2 numbers in input
3358 if (!GetNextNumber(i
,j
)) // i=1,0
3359 { // Analyze start string
3360 if (!ScanStartString( sStrArray
[i
] ))
3361 return false; // already an error
3364 GetNextNumber(i
,j
); // i=1,2
3365 if ( !ScanMidString( sStrArray
[i
], i
) )
3369 i
++; // next symbol, i=2,3
3370 GetNextNumber(i
,j
); // i=3,4
3371 if (i
< nStringsCnt
&& !ScanEndString( sStrArray
[i
] ))
3375 if (eSetType
== SvNumFormatType::FRACTION
) // -1,200. as fraction
3377 if (!bNegCheck
&& // no sign '('
3378 eScannedType
== SvNumFormatType::UNDEFINED
&&
3379 (nDecPos
== 0 || nDecPos
== 3) // no decimal separator or at end
3382 eScannedType
= SvNumFormatType::FRACTION
;
3383 nMatchedAllStrings
&= ~nMatchedVirgin
;
3388 case 3 : // Exactly 3 numbers in input
3390 if (!GetNextNumber(i
,j
)) // i=1,0
3391 { // Analyze start string
3392 if (!ScanStartString( sStrArray
[i
] ))
3394 return false; // already an error
3397 if (nDecPos
== 1) // decimal separator at start => error
3402 GetNextNumber(i
,j
); // i=1,2
3403 if ( !ScanMidString( sStrArray
[i
], i
) )
3408 if (eScannedType
== SvNumFormatType::SCIENTIFIC
) // E only at end
3412 GetNextNumber(i
,j
); // i=3,4
3413 if ( !ScanMidString( sStrArray
[i
], i
) )
3418 GetNextNumber(i
,j
); // i=5,6
3419 if (i
< nStringsCnt
&& !ScanEndString( sStrArray
[i
] ))
3423 if (eSetType
== SvNumFormatType::FRACTION
) // -1,200,100. as fraction
3425 if (!bNegCheck
&& // no sign '('
3426 eScannedType
== SvNumFormatType::UNDEFINED
&&
3427 (nDecPos
== 0 || nDecPos
== 3) // no decimal separator or at end
3430 eScannedType
= SvNumFormatType::FRACTION
;
3431 nMatchedAllStrings
&= ~nMatchedVirgin
;
3435 if ( eScannedType
== SvNumFormatType::FRACTION
&& nDecPos
)
3437 return false; // #36857# not a real fraction
3440 default: // More than 3 numbers in input
3442 if (!GetNextNumber(i
,j
)) // i=1,0
3443 { // Analyze startstring
3444 if (!ScanStartString( sStrArray
[i
] ))
3445 return false; // already an error
3447 if (nDecPos
== 1) // decimal separator at start => error
3450 GetNextNumber(i
,j
); // i=1,2
3451 if ( !ScanMidString( sStrArray
[i
], i
) )
3457 sal_uInt16 nThOld
= 10; // just not 0 or 1
3458 while (nThOld
!= nThousand
&& j
< nNumericsCnt
-1) // Execute at least one time
3459 // but leave one number.
3460 { // Loop over group separators
3462 if (eScannedType
== SvNumFormatType::SCIENTIFIC
) // E only at end
3467 if ( i
< nStringsCnt
&& !ScanMidString( sStrArray
[i
], i
) )
3474 if (eScannedType
== SvNumFormatType::DATE
|| // long date or
3475 eScannedType
== SvNumFormatType::TIME
|| // long time or
3476 eScannedType
== SvNumFormatType::UNDEFINED
) // long number
3478 for (sal_uInt16 k
= j
; k
< nNumericsCnt
-1; k
++)
3480 if (eScannedType
== SvNumFormatType::SCIENTIFIC
) // E only at endd
3485 if ( i
< nStringsCnt
&& !ScanMidString( sStrArray
[i
], i
) )
3493 if (i
< nStringsCnt
&& !ScanEndString( sStrArray
[i
] ))
3497 if (eSetType
== SvNumFormatType::FRACTION
) // -1,200,100. as fraction
3499 if (!bNegCheck
&& // no sign '('
3500 eScannedType
== SvNumFormatType::UNDEFINED
&&
3501 (nDecPos
== 0 || nDecPos
== 3) // no decimal separator or at end
3504 eScannedType
= SvNumFormatType::FRACTION
;
3505 nMatchedAllStrings
&= ~nMatchedVirgin
;
3509 if ( eScannedType
== SvNumFormatType::FRACTION
&& nDecPos
)
3511 return false; // #36857# not a real fraction
3516 if (eScannedType
== SvNumFormatType::UNDEFINED
)
3518 nMatchedAllStrings
&= ~nMatchedVirgin
;
3519 // did match including nMatchedUsedAsReturn
3520 bool bDidMatch
= (nMatchedAllStrings
!= 0);
3521 if ( nMatchedAllStrings
)
3523 bool bMatch
= mpFormat
&& mpFormat
->IsNumForStringElementCountEqual(
3524 nStringScanNumFor
, nStringsCnt
, nNumericsCnt
);
3527 nMatchedAllStrings
= 0;
3530 if ( nMatchedAllStrings
)
3532 // A type DEFINED means that no category could be assigned to the
3533 // overall format because of mixed type subformats. Use the scan
3534 // matched subformat's type if any.
3535 SvNumFormatType eForType
= eSetType
;
3536 if ((eForType
== SvNumFormatType::UNDEFINED
|| eForType
== SvNumFormatType::DEFINED
) && mpFormat
)
3537 eForType
= mpFormat
->GetNumForInfoScannedType( nStringScanNumFor
);
3538 if (eForType
!= SvNumFormatType::UNDEFINED
&& eForType
!= SvNumFormatType::DEFINED
)
3539 eScannedType
= eForType
;
3541 eScannedType
= SvNumFormatType::NUMBER
;
3543 else if ( bDidMatch
)
3545 // Accept a plain fractional number like 123.45 as there may be a
3546 // decimal separator also present as literal like in a 0"."0 weirdo
3548 if (nDecPos
!= 2 || nNumericsCnt
!= 2)
3550 eScannedType
= SvNumFormatType::NUMBER
;
3554 eScannedType
= SvNumFormatType::NUMBER
;
3555 // everything else should have been recognized by now
3558 else if ( eScannedType
== SvNumFormatType::DATE
)
3560 // the very relaxed date input checks may interfere with a preset format
3561 nMatchedAllStrings
&= ~nMatchedVirgin
;
3562 bool bWasReturn
= ((nMatchedAllStrings
& nMatchedUsedAsReturn
) != 0);
3563 if ( nMatchedAllStrings
)
3565 bool bMatch
= mpFormat
&& mpFormat
->IsNumForStringElementCountEqual(
3566 nStringScanNumFor
, nStringsCnt
, nNumericsCnt
);
3569 nMatchedAllStrings
= 0;
3572 if ( nMatchedAllStrings
)
3574 // A type DEFINED means that no category could be assigned to the
3575 // overall format because of mixed type subformats. Do not override
3576 // the scanned type in this case. Otherwise in IsNumberFormat() the
3577 // first numeric particle would be accepted as number.
3578 SvNumFormatType eForType
= eSetType
;
3579 if ((eForType
== SvNumFormatType::UNDEFINED
|| eForType
== SvNumFormatType::DEFINED
) && mpFormat
)
3580 eForType
= mpFormat
->GetNumForInfoScannedType( nStringScanNumFor
);
3581 if (eForType
!= SvNumFormatType::UNDEFINED
&& eForType
!= SvNumFormatType::DEFINED
)
3582 eScannedType
= eForType
;
3584 else if ( bWasReturn
)
3591 nMatchedAllStrings
= 0; // reset flag to no substrings matched
3598 * Return true or false depending on the nMatched... state and remember usage
3600 bool ImpSvNumberInputScan::MatchedReturn()
3602 if ( nMatchedAllStrings
& ~nMatchedVirgin
)
3604 nMatchedAllStrings
|= nMatchedUsedAsReturn
;
3612 * Initialize uppercase months and weekdays
3614 void ImpSvNumberInputScan::InitText()
3616 sal_Int32 j
, nElems
;
3617 const CharClass
* pChrCls
= pFormatter
->GetCharClass();
3618 const CalendarWrapper
* pCal
= pFormatter
->GetCalendar();
3620 pUpperMonthText
.reset();
3621 pUpperAbbrevMonthText
.reset();
3622 css::uno::Sequence
< css::i18n::CalendarItem2
> xElems
= pCal
->getMonths();
3623 nElems
= xElems
.getLength();
3624 pUpperMonthText
.reset( new OUString
[nElems
] );
3625 pUpperAbbrevMonthText
.reset( new OUString
[nElems
] );
3626 for ( j
= 0; j
< nElems
; j
++ )
3628 pUpperMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].FullName
);
3629 pUpperAbbrevMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].AbbrevName
);
3632 pUpperGenitiveMonthText
.reset();
3633 pUpperGenitiveAbbrevMonthText
.reset();
3634 xElems
= pCal
->getGenitiveMonths();
3635 bScanGenitiveMonths
= (nElems
!= xElems
.getLength());
3636 nElems
= xElems
.getLength();
3637 pUpperGenitiveMonthText
.reset( new OUString
[nElems
] );
3638 pUpperGenitiveAbbrevMonthText
.reset( new OUString
[nElems
] );
3639 for ( j
= 0; j
< nElems
; j
++ )
3641 pUpperGenitiveMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].FullName
);
3642 pUpperGenitiveAbbrevMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].AbbrevName
);
3643 if (!bScanGenitiveMonths
&&
3644 (pUpperGenitiveMonthText
[j
] != pUpperMonthText
[j
] ||
3645 pUpperGenitiveAbbrevMonthText
[j
] != pUpperAbbrevMonthText
[j
]))
3647 bScanGenitiveMonths
= true;
3651 pUpperPartitiveMonthText
.reset();
3652 pUpperPartitiveAbbrevMonthText
.reset();
3653 xElems
= pCal
->getPartitiveMonths();
3654 bScanPartitiveMonths
= (nElems
!= xElems
.getLength());
3655 nElems
= xElems
.getLength();
3656 pUpperPartitiveMonthText
.reset( new OUString
[nElems
] );
3657 pUpperPartitiveAbbrevMonthText
.reset( new OUString
[nElems
] );
3658 for ( j
= 0; j
< nElems
; j
++ )
3660 pUpperPartitiveMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].FullName
);
3661 pUpperPartitiveAbbrevMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].AbbrevName
);
3662 if (!bScanPartitiveMonths
&&
3663 (pUpperPartitiveMonthText
[j
] != pUpperGenitiveMonthText
[j
] ||
3664 pUpperPartitiveAbbrevMonthText
[j
] != pUpperGenitiveAbbrevMonthText
[j
]))
3666 bScanPartitiveMonths
= true;
3670 pUpperDayText
.reset();
3671 pUpperAbbrevDayText
.reset();
3672 xElems
= pCal
->getDays();
3673 nElems
= xElems
.getLength();
3674 pUpperDayText
.reset( new OUString
[nElems
] );
3675 pUpperAbbrevDayText
.reset( new OUString
[nElems
] );
3676 for ( j
= 0; j
< nElems
; j
++ )
3678 pUpperDayText
[j
] = pChrCls
->uppercase( xElems
[j
].FullName
);
3679 pUpperAbbrevDayText
[j
] = pChrCls
->uppercase( xElems
[j
].AbbrevName
);
3682 bTextInitialized
= true;
3687 * MUST be called if International/Locale is changed
3689 void ImpSvNumberInputScan::ChangeIntl()
3691 sal_Unicode cDecSep
= pFormatter
->GetNumDecimalSep()[0];
3692 bDecSepInDateSeps
= ( cDecSep
== '-' ||
3693 cDecSep
== pFormatter
->GetDateSep()[0] );
3694 if (!bDecSepInDateSeps
)
3696 sal_Unicode cDecSepAlt
= pFormatter
->GetNumDecimalSepAlt().toChar();
3697 bDecSepInDateSeps
= cDecSepAlt
&& (cDecSepAlt
== '-' || cDecSepAlt
== pFormatter
->GetDateSep()[0]);
3699 bTextInitialized
= false;
3700 aUpperCurrSymbol
.clear();
3701 InvalidateDateAcceptancePatterns();
3705 void ImpSvNumberInputScan::InvalidateDateAcceptancePatterns()
3707 if (sDateAcceptancePatterns
.hasElements())
3709 sDateAcceptancePatterns
= css::uno::Sequence
< OUString
>();
3714 void ImpSvNumberInputScan::ChangeNullDate( const sal_uInt16 Day
,
3715 const sal_uInt16 Month
,
3716 const sal_Int16 Year
)
3720 *pNullDate
= Date(Day
, Month
, Year
);
3724 pNullDate
.reset(new Date(Day
, Month
, Year
));
3730 * Does rString represent a number (also date, time et al)
3732 bool ImpSvNumberInputScan::IsNumberFormat( const OUString
& rString
, // string to be analyzed
3733 SvNumFormatType
& F_Type
, // IN: old type, OUT: new type
3734 double& fOutNumber
, // OUT: number if convertible
3735 const SvNumberformat
* pFormat
, // maybe a number format to match against
3736 SvNumInputOptions eInputOptions
)
3738 bool res
; // return value
3740 eSetType
= F_Type
; // old type set
3742 if ( !rString
.getLength() )
3746 else if (rString
.getLength() > 308) // arbitrary
3752 // NoMoreUpperNeeded, all comparisons on UpperCase
3753 OUString aString
= pFormatter
->GetCharClass()->uppercase( rString
);
3754 // convert native number to ASCII if necessary
3755 TransformInput(pFormatter
, aString
);
3756 res
= IsNumberFormatMain( aString
, pFormat
);
3761 // Accept signed date only for ISO date with at least four digits in
3762 // year to not have an input of -M-D-Y arbitrarily recognized. The
3763 // final order is only determined in GetDateRef().
3764 // Also accept for Y/M/D date pattern match, i.e. if the first number
3766 // Accept only if the year immediately follows the sign character with
3767 // no space in between.
3768 if (nSign
&& (eScannedType
== SvNumFormatType::DATE
||
3769 eScannedType
== SvNumFormatType::DATETIME
) && mbEraCE
== kDefaultEra
&&
3770 (IsDatePatternNumberOfType(0,'Y') || (MayBeIso8601() && sStrArray
[nNums
[0]].getLength() >= 4)))
3772 const sal_Unicode c
= sStrArray
[0][sStrArray
[0].getLength()-1];
3773 if (c
== '-' || c
== '+')
3775 // A '+' sign doesn't change the era.
3777 mbEraCE
= false; // BCE
3781 if ( bNegCheck
|| // ')' not found for '('
3782 (nSign
&& (eScannedType
== SvNumFormatType::DATE
||
3783 eScannedType
== SvNumFormatType::DATETIME
))) // signed date/datetime
3788 { // check count of partial number strings
3789 switch (eScannedType
)
3791 case SvNumFormatType::PERCENT
:
3792 case SvNumFormatType::CURRENCY
:
3793 case SvNumFormatType::NUMBER
:
3794 if (nDecPos
== 1) // .05
3796 // Matched MidStrings function like group separators, but
3797 // there can't be an integer part numeric input, so
3798 // effectively 0 thousands groups.
3799 if ( nMatchedAllStrings
)
3803 else if ( nNumericsCnt
!= 1 )
3808 else if (nDecPos
== 2) // 1.05
3810 // Matched MidStrings function like group separators, but
3811 // let a decimal separator override a literal separator
3812 // string; like 0"." with input 123.45
3813 if ( nMatchedAllStrings
)
3815 if (nNumericsCnt
== 2)
3819 // Assume that if there was a decimal separator
3820 // matching also a literal string then it was the
3821 // last. We could find the last possible match to
3822 // support literals in fractions, but really..
3823 nThousand
= nNumericsCnt
- 1;
3826 else if ( nNumericsCnt
!= nThousand
+2 )
3831 else // 1,100 or 1,100.
3833 // matched MidStrings function like group separators
3834 if ( nMatchedAllStrings
)
3836 nThousand
= nNumericsCnt
- 1;
3838 else if ( nNumericsCnt
!= nThousand
+1 )
3845 case SvNumFormatType::SCIENTIFIC
: // 1.0e-2
3846 if (nDecPos
== 1) // .05
3848 if (nNumericsCnt
!= 2)
3853 else if (nDecPos
== 2) // 1.05
3855 if (nNumericsCnt
!= nThousand
+3)
3860 else // 1,100 or 1,100.
3862 if (nNumericsCnt
!= nThousand
+2)
3869 case SvNumFormatType::DATE
:
3870 if (nMonth
< 0 && nDayOfWeek
< 0 && nNumericsCnt
== 3)
3872 // If both, short month name and day of week name were
3873 // detected, and also numbers for full date, assume that we
3874 // have a day of week instead of month name.
3879 { // month name and numbers
3880 if (nNumericsCnt
> 2)
3887 if (nNumericsCnt
> 3)
3893 // Even if a date pattern was matched, for abbreviated
3894 // pattern like "D.M." an input of "D.M. #" was
3895 // accepted because # could had been a time. Here we do
3896 // not have a combined date/time input though and #
3897 // would be taken as Year in this example, which it is
3898 // not. The count of numbers in pattern must match the
3899 // count of numbers in input.
3900 res
= (GetDatePatternNumbers() == nNumericsCnt
)
3901 || IsAcceptableIso8601() || nMatchedAllStrings
;
3906 case SvNumFormatType::TIME
:
3908 { // hundredth seconds included
3909 if (nNumericsCnt
> 4)
3916 if (nNumericsCnt
> 3)
3923 case SvNumFormatType::DATETIME
:
3924 if (nMonth
< 0 && nDayOfWeek
< 0 && nNumericsCnt
>= 5)
3926 // If both, abbreviated month name and day of week name
3927 // were detected, and also at least numbers for full date
3928 // plus time including minutes, assume that we have a day
3929 // of week instead of month name.
3934 { // month name and numbers
3936 { // hundredth seconds included
3937 if (nNumericsCnt
> 6)
3944 if (nNumericsCnt
> 5)
3953 { // hundredth seconds included
3954 if (nNumericsCnt
> 7)
3961 if (nNumericsCnt
> 6)
3968 res
= IsAcceptedDatePattern( nNums
[0]) || MayBeIso8601() || nMatchedAllStrings
;
3979 OUStringBuffer sResString
;
3982 { // we finally have a number
3983 switch (eScannedType
)
3985 case SvNumFormatType::LOGICAL
:
3988 fOutNumber
= 1.0; // True
3990 else if (nLogical
== -1)
3992 fOutNumber
= 0.0; // False
3996 res
= false; // Oops
4000 case SvNumFormatType::PERCENT
:
4001 case SvNumFormatType::CURRENCY
:
4002 case SvNumFormatType::NUMBER
:
4003 case SvNumFormatType::SCIENTIFIC
:
4004 case SvNumFormatType::DEFINED
: // if no category detected handle as number
4005 if ( nDecPos
== 1 ) // . at start
4007 sResString
.append("0.");
4010 for ( k
= 0; k
<= nThousand
; k
++)
4012 sResString
.append(sStrArray
[nNums
[k
]]); // integer part
4014 if ( nDecPos
== 2 && k
< nNumericsCnt
) // . somewhere
4016 sResString
.append('.');
4017 sal_uInt16 nStop
= (eScannedType
== SvNumFormatType::SCIENTIFIC
?
4018 nNumericsCnt
-1 : nNumericsCnt
);
4019 for ( ; k
< nStop
; k
++)
4021 sResString
.append(sStrArray
[nNums
[k
]]); // fractional part
4025 if (eScannedType
!= SvNumFormatType::SCIENTIFIC
)
4027 fOutNumber
= StringToDouble(sResString
.makeStringAndClear());
4030 { // append exponent
4031 sResString
.append('E');
4034 sResString
.append('-');
4036 sResString
.append(sStrArray
[nNums
[nNumericsCnt
-1]]);
4037 rtl_math_ConversionStatus eStatus
;
4038 fOutNumber
= ::rtl::math::stringToDouble( sResString
.makeStringAndClear(), '.', ',', &eStatus
);
4039 if ( eStatus
== rtl_math_ConversionStatus_OutOfRange
)
4041 F_Type
= SvNumFormatType::TEXT
; // overflow/underflow -> Text
4048 fOutNumber
= DBL_MAX
;
4054 if ( nStringScanSign
)
4058 nSign
*= nStringScanSign
;
4062 nSign
= nStringScanSign
;
4067 fOutNumber
= -fOutNumber
;
4070 if (eScannedType
== SvNumFormatType::PERCENT
)
4076 case SvNumFormatType::FRACTION
:
4077 if (nNumericsCnt
== 1)
4079 fOutNumber
= StringToDouble(sStrArray
[nNums
[0]]);
4081 else if (nNumericsCnt
== 2)
4085 sResString
= sStrArray
[nNums
[0]];
4086 sResString
.append(sStrArray
[nNums
[1]]); // integer part
4087 fOutNumber
= StringToDouble(sResString
.makeStringAndClear());
4091 double fNumerator
= StringToDouble(sStrArray
[nNums
[0]]);
4092 double fDenominator
= StringToDouble(sStrArray
[nNums
[1]]);
4093 if (fDenominator
!= 0.0)
4095 fOutNumber
= fNumerator
/fDenominator
;
4103 else // nNumericsCnt > 2
4106 sResString
= sStrArray
[nNums
[0]];
4109 for (; k
<= nThousand
; k
++)
4111 sResString
.append(sStrArray
[nNums
[k
]]);
4114 fOutNumber
= StringToDouble(sResString
.makeStringAndClear());
4116 if (k
== nNumericsCnt
-2)
4118 double fNumerator
= StringToDouble(sStrArray
[nNums
[k
]]);
4119 double fDenominator
= StringToDouble(sStrArray
[nNums
[k
+ 1]]);
4120 if (fDenominator
!= 0.0)
4122 fOutNumber
+= fNumerator
/fDenominator
;
4131 if ( nStringScanSign
)
4135 nSign
*= nStringScanSign
;
4139 nSign
= nStringScanSign
;
4144 fOutNumber
= -fOutNumber
;
4148 case SvNumFormatType::TIME
:
4149 res
= GetTimeRef(fOutNumber
, 0, nNumericsCnt
, eInputOptions
);
4152 fOutNumber
= -fOutNumber
;
4156 case SvNumFormatType::DATE
:
4157 res
= GetDateRef( fOutNumber
, k
);
4160 case SvNumFormatType::DATETIME
:
4161 res
= GetDateRef( fOutNumber
, k
);
4165 res
= GetTimeRef( fTime
, k
, nNumericsCnt
- k
, eInputOptions
);
4166 fOutNumber
+= fTime
;
4171 SAL_WARN( "svl.numbers", "Some number recognized but what's it?" );
4177 if (res
) // overflow/underflow -> Text
4179 if (fOutNumber
< -DBL_MAX
) // -1.7E308
4181 F_Type
= SvNumFormatType::TEXT
;
4182 fOutNumber
= -DBL_MAX
;
4185 else if (fOutNumber
> DBL_MAX
) // 1.7E308
4187 F_Type
= SvNumFormatType::TEXT
;
4188 fOutNumber
= DBL_MAX
;
4195 eScannedType
= SvNumFormatType::TEXT
;
4199 F_Type
= eScannedType
;
4203 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */