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>
40 #include "zforfind.hxx"
43 #define NF_TEST_CALENDAR 0
45 #define NF_TEST_CALENDAR 0
48 #include <comphelper/processfactory.hxx>
49 #include <com/sun/star/i18n/XCalendar4.hpp>
53 const sal_uInt8
ImpSvNumberInputScan::nMatchedEndString
= 0x01;
54 const sal_uInt8
ImpSvNumberInputScan::nMatchedMidString
= 0x02;
55 const sal_uInt8
ImpSvNumberInputScan::nMatchedStartString
= 0x04;
56 const sal_uInt8
ImpSvNumberInputScan::nMatchedVirgin
= 0x08;
57 const sal_uInt8
ImpSvNumberInputScan::nMatchedUsedAsReturn
= 0x10;
59 /* It is not clear how we want timezones to be handled. Convert them to local
60 * time isn't wanted, as it isn't done in any other place and timezone
61 * information isn't stored anywhere. Ignoring them and pretending local time
62 * may be wrong too and might not be what the user expects. Keep the input as
63 * string so that no information is lost.
64 * Anyway, defining NF_RECOGNIZE_ISO8601_TIMEZONES to 1 would be the way how it
65 * would work, together with the nTimezonePos handling in GetTimeRef(). */
66 #define NF_RECOGNIZE_ISO8601_TIMEZONES 0
68 static const sal_Unicode cNoBreakSpace
= 0xA0;
69 static const sal_Unicode cNarrowNoBreakSpace
= 0x202F;
70 static const bool kDefaultEra
= true; // Gregorian CE, positive year
72 ImpSvNumberInputScan::ImpSvNumberInputScan( SvNumberFormatter
* pFormatterP
)
74 bTextInitialized( false ),
75 bScanGenitiveMonths( false ),
76 bScanPartitiveMonths( false ),
77 eScannedType( SvNumFormatType::UNDEFINED
),
78 eSetType( SvNumFormatType::UNDEFINED
)
80 pFormatter
= pFormatterP
;
81 pNullDate
.reset( new Date(30,12,1899) );
82 nYear2000
= SvNumberFormatter::GetYear2000Default();
88 ImpSvNumberInputScan::~ImpSvNumberInputScan()
93 void ImpSvNumberInputScan::Reset()
107 eScannedType
= SvNumFormatType::UNDEFINED
;
109 nPosThousandString
= 0;
111 mbEraCE
= kDefaultEra
;
112 nStringScanNumFor
= 0;
114 nMatchedAllStrings
= nMatchedVirgin
;
116 bIso8601Tsep
= false;
118 nAcceptedDatePattern
= -2;
119 nDatePatternStart
= 0;
120 nDatePatternNumbers
= 0;
122 for (sal_uInt32 i
= 0; i
< SV_MAX_COUNT_INPUT_STRINGS
; i
++)
129 // native number transliteration if necessary
130 static void TransformInput( SvNumberFormatter
const * pFormatter
, OUString
& rStr
)
132 sal_Int32 nPos
, nLen
;
133 for ( nPos
= 0, nLen
= rStr
.getLength(); nPos
< nLen
; ++nPos
)
135 if ( 256 <= rStr
[ nPos
] &&
136 pFormatter
->GetCharClass()->isDigit( rStr
, nPos
) )
143 rStr
= pFormatter
->GetNatNum()->getNativeNumberString( rStr
,
144 pFormatter
->GetLanguageTag().getLocale(), 0 );
150 * Only simple unsigned floating point values without any error detection,
151 * decimal separator has to be '.'
153 double ImpSvNumberInputScan::StringToDouble( const OUString
& rStr
, bool bForceFraction
)
159 sal_Int32 nLen
= rStr
.getLength();
160 bool bPreSep
= !bForceFraction
;
164 if (rStr
[nPos
] == '.')
170 fNum
= fNum
* 10.0 + static_cast<double>(rStr
[nPos
] - '0');
174 fFrac
= fFrac
* 10.0 + static_cast<double>(rStr
[nPos
] - '0');
181 return fNum
+ ::rtl::math::pow10Exp( fFrac
, nExp
);
188 * Splits up the input into numbers and strings for further processing
189 * (by the Turing machine).
191 * Starting state = GetChar
192 * ---------------+-------------------+-----------------------------+---------------
193 * Old State | Character read | Event | New state
194 * ---------------+-------------------+-----------------------------+---------------
195 * GetChar | Number | Symbol = Character | GetValue
196 * | Else | Symbol = Character | GetString
197 * ---------------|-------------------+-----------------------------+---------------
198 * GetValue | Number | Symbol = Symbol + Character | GetValue
199 * | Else | Dec(CharPos) | Stop
200 * ---------------+-------------------+-----------------------------+---------------
201 * GetString | Number | Dec(CharPos) | Stop
202 * | Else | Symbol = Symbol + Character | GetString
203 * ---------------+-------------------+-----------------------------+---------------
205 enum ScanState
// States of the Turing machine
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;
222 while ( ((cToken
= *pHere
) != 0) && eState
!= SsStop
)
228 if ( rtl::isAsciiDigit( cToken
) )
235 eState
= SsGetString
;
240 if ( rtl::isAsciiDigit( cToken
) )
251 if ( !rtl::isAsciiDigit( cToken
) )
268 rSymbol
= OUString( pStr
, nChars
);
281 // FIXME: should be grouping; it is only used though in case nStringsCnt is
282 // near SV_MAX_COUNT_INPUT_STRINGS, in NumberStringDivision().
284 bool ImpSvNumberInputScan::SkipThousands( const sal_Unicode
*& pStr
,
285 OUString
& rSymbol
) const
288 OUStringBuffer
sBuff(rSymbol
);
290 const OUString
& rThSep
= pFormatter
->GetNumThousandSep();
291 const sal_Unicode
* pHere
= pStr
;
292 ScanState eState
= SsStart
;
293 sal_Int32 nCounter
= 0; // counts 3 digits
295 while ( ((cToken
= *pHere
) != 0) && eState
!= SsStop
)
301 if ( StringPtrContains( rThSep
, pHere
-1, 0 ) )
305 pHere
+= rThSep
.getLength() - 1;
314 if ( rtl::isAsciiDigit( cToken
) )
316 sBuff
.append(cToken
);
321 res
= true; // .000 combination found
335 if (eState
== SsGetValue
) // break with less than 3 digits
339 sBuff
.remove( sBuff
.getLength() - nCounter
, nCounter
);
341 pHere
-= nCounter
+ rThSep
.getLength(); // put back ThSep also
343 rSymbol
= sBuff
.makeStringAndClear();
350 void ImpSvNumberInputScan::NumberStringDivision( const OUString
& rString
)
352 const sal_Unicode
* pStr
= rString
.getStr();
353 const sal_Unicode
* const pEnd
= pStr
+ rString
.getLength();
354 while ( pStr
< pEnd
&& nStringsCnt
< SV_MAX_COUNT_INPUT_STRINGS
)
356 if ( NextNumberStringSymbol( pStr
, sStrArray
[nStringsCnt
] ) )
358 IsNum
[nStringsCnt
] = true;
359 nNums
[nNumericsCnt
] = nStringsCnt
;
361 if (nStringsCnt
>= SV_MAX_COUNT_INPUT_STRINGS
- 7 &&
362 nPosThousandString
== 0) // Only once
364 if ( SkipThousands( pStr
, sStrArray
[nStringsCnt
] ) )
366 nPosThousandString
= nStringsCnt
;
372 IsNum
[nStringsCnt
] = false;
380 * Whether rString contains rWhat at nPos
382 bool ImpSvNumberInputScan::StringContainsImpl( const OUString
& rWhat
,
383 const OUString
& rString
, sal_Int32 nPos
)
385 if ( nPos
+ rWhat
.getLength() <= rString
.getLength() )
387 return StringPtrContainsImpl( rWhat
, rString
.getStr(), nPos
);
394 * Whether pString contains rWhat at nPos
396 bool ImpSvNumberInputScan::StringPtrContainsImpl( const OUString
& rWhat
,
397 const sal_Unicode
* pString
, sal_Int32 nPos
)
399 if ( rWhat
.isEmpty() )
403 const sal_Unicode
* pWhat
= rWhat
.getStr();
404 const sal_Unicode
* const pEnd
= pWhat
+ rWhat
.getLength();
405 const sal_Unicode
* pStr
= pString
+ nPos
;
406 while ( pWhat
< pEnd
)
408 if ( *pWhat
!= *pStr
)
420 * Whether rString contains word rWhat at nPos
422 bool ImpSvNumberInputScan::StringContainsWord( const OUString
& rWhat
,
423 const OUString
& rString
, sal_Int32 nPos
) const
425 if (rWhat
.isEmpty() || rString
.getLength() < nPos
+ rWhat
.getLength())
428 if (StringPtrContainsImpl( rWhat
, rString
.getStr(), nPos
))
430 nPos
+= rWhat
.getLength();
431 if (nPos
== rString
.getLength())
432 return true; // word at end of string
434 /* TODO: we COULD invoke bells and whistles word break iterator to find
435 * the next boundary, but really ... this is called for date input, so
436 * how many languages do not separate the day and month names in some
439 // Check simple ASCII first before invoking i18n or anything else.
440 const sal_Unicode c
= rString
[nPos
];
442 // Common separating ASCII characters in date context.
454 if (rtl::isAsciiAlphanumeric( c
))
455 return false; // Alpha or numeric is not word gap.
457 sal_Int32 nIndex
= nPos
;
458 rString
.iterateCodePoints( &nIndex
);
460 return true; // Surrogate, assume these to be new words.
462 const sal_Int32 nType
= pFormatter
->GetCharClass()->getCharacterType( rString
, nPos
);
463 using namespace ::com::sun::star::i18n
;
465 if ((nType
& (KCharacterType::UPPER
| KCharacterType::LOWER
| KCharacterType::DIGIT
)) != 0)
466 return false; // Alpha or numeric is not word gap.
468 if (nType
& KCharacterType::LETTER
)
469 return true; // Letter other than alpha is new word. (Is it?)
471 return true; // Catch all remaining as gap until we know better.
479 * Skips the supplied char
481 inline bool ImpSvNumberInputScan::SkipChar( sal_Unicode c
, const OUString
& rString
,
484 if ((nPos
< rString
.getLength()) && (rString
[nPos
] == c
))
496 inline void ImpSvNumberInputScan::SkipBlanks( const OUString
& rString
,
499 if ( nPos
< rString
.getLength() )
501 const sal_Unicode
* p
= rString
.getStr() + nPos
;
502 while ( *p
== ' ' || *p
== cNoBreakSpace
|| *p
== cNarrowNoBreakSpace
)
512 * jump over rWhat in rString at nPos
514 inline bool ImpSvNumberInputScan::SkipString( const OUString
& rWhat
,
515 const OUString
& rString
, sal_Int32
& nPos
)
517 if ( StringContains( rWhat
, rString
, nPos
) )
519 nPos
= nPos
+ rWhat
.getLength();
527 * Recognizes exactly ,111 in {3} and {3,2} or ,11 in {3,2} grouping
529 inline bool ImpSvNumberInputScan::GetThousandSep( const OUString
& rString
,
531 sal_uInt16 nStringPos
) const
533 const OUString
& rSep
= pFormatter
->GetNumThousandSep();
534 // Is it an ordinary space instead of a no-break space?
535 bool bSpaceBreak
= (rSep
[0] == cNoBreakSpace
|| rSep
[0] == cNarrowNoBreakSpace
) &&
536 rString
[0] == u
' ' &&
537 rSep
.getLength() == 1 && rString
.getLength() == 1;
538 if (!((rString
== rSep
|| bSpaceBreak
) && // nothing else
539 nStringPos
< nStringsCnt
- 1 && // safety first!
540 IsNum
[ nStringPos
+ 1 ] )) // number follows
542 return false; // no? => out
545 utl::DigitGroupingIterator
aGrouping( pFormatter
->GetLocaleData()->getDigitGrouping());
546 // Match ,### in {3} or ,## in {3,2}
547 /* FIXME: this could be refined to match ,## in {3,2} only if ,##,## or
548 * ,##,### and to match ,### in {3,2} only if it's the last. However,
549 * currently there is no track kept where group separators occur. In {3,2}
550 * #,###,### and #,##,## would be valid input, which maybe isn't even bad
551 * for #,###,###. Other combinations such as #,###,## maybe not. */
552 sal_Int32 nLen
= sStrArray
[ nStringPos
+ 1 ].getLength();
553 if (nLen
== aGrouping
.get() || // with 3 (or so) digits
554 nLen
== aGrouping
.advance().get() || // or with 2 (or 3 or so) digits
555 nPosThousandString
== nStringPos
+ 1 ) // or concatenated
557 nPos
= nPos
+ rSep
.getLength();
565 * Conversion of text to logical value
570 short ImpSvNumberInputScan::GetLogical( const OUString
& rString
) const
574 const ImpSvNumberformatScan
* pFS
= pFormatter
->GetFormatScanner();
575 if ( rString
== pFS
->GetTrueString() )
579 else if ( rString
== pFS
->GetFalseString() )
592 * Converts a string containing a month name (JAN, January) at nPos into the
593 * month number (negative if abbreviated), returns 0 if nothing found
595 short ImpSvNumberInputScan::GetMonth( const OUString
& rString
, sal_Int32
& nPos
)
597 short res
= 0; // no month found
599 if (rString
.getLength() > nPos
) // only if needed
601 if ( !bTextInitialized
)
605 sal_Int16 nMonths
= pFormatter
->GetCalendar()->getNumberOfMonthsInYear();
606 for ( sal_Int16 i
= 0; i
< nMonths
; i
++ )
608 if ( bScanGenitiveMonths
&& StringContainsWord( pUpperGenitiveMonthText
[i
], rString
, nPos
) )
609 { // genitive full names first
610 nPos
= nPos
+ pUpperGenitiveMonthText
[i
].getLength();
614 else if ( bScanGenitiveMonths
&& StringContainsWord( pUpperGenitiveAbbrevMonthText
[i
], rString
, nPos
) )
615 { // genitive abbreviated
616 nPos
= nPos
+ pUpperGenitiveAbbrevMonthText
[i
].getLength();
617 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
620 else if ( bScanPartitiveMonths
&& StringContainsWord( pUpperPartitiveMonthText
[i
], rString
, nPos
) )
621 { // partitive full names
622 nPos
= nPos
+ pUpperPartitiveMonthText
[i
].getLength();
626 else if ( bScanPartitiveMonths
&& StringContainsWord( pUpperPartitiveAbbrevMonthText
[i
], rString
, nPos
) )
627 { // partitive abbreviated
628 nPos
= nPos
+ pUpperPartitiveAbbrevMonthText
[i
].getLength();
629 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
632 else if ( StringContainsWord( pUpperMonthText
[i
], rString
, nPos
) )
634 nPos
= nPos
+ pUpperMonthText
[i
].getLength();
638 else if ( StringContainsWord( pUpperAbbrevMonthText
[i
], rString
, nPos
) )
639 { // noun abbreviated
640 nPos
= nPos
+ pUpperAbbrevMonthText
[i
].getLength();
641 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
644 else if ( i
== 8 && pUpperAbbrevMonthText
[i
] == "SEPT" &&
645 StringContainsWord( "SEP", rString
, nPos
) )
646 { // #102136# The correct English form of month September abbreviated is
647 // SEPT, but almost every data contains SEP instead.
649 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
655 // Brutal hack for German locales that know "Januar" or "Jänner".
656 /* TODO: add alternative month names to locale data? if there are
657 * more languages.. */
658 const LanguageTag
& rLanguageTag
= pFormatter
->GetLanguageTag();
659 if (rLanguageTag
.getLanguage() == "de")
661 if (rLanguageTag
.getCountry() == "AT")
663 // Locale data has Jänner/Jän
664 assert(pUpperMonthText
[0] == u
"J\u00C4NNER");
665 if (StringContainsWord( "JANUAR", rString
, nPos
))
670 else if (StringContainsWord( "JAN", rString
, nPos
))
678 // Locale data has Januar/Jan
679 assert(pUpperMonthText
[0] == "JANUAR");
680 if (StringContainsWord( u
"J\u00C4NNER", rString
, nPos
))
685 else if (StringContainsWord( u
"J\u00C4N", rString
, nPos
))
700 * Converts a string containing a DayOfWeek name (Mon, Monday) at nPos into the
701 * DayOfWeek number + 1 (negative if abbreviated), returns 0 if nothing found
703 int ImpSvNumberInputScan::GetDayOfWeek( const OUString
& rString
, sal_Int32
& nPos
)
705 int res
= 0; // no day found
707 if (rString
.getLength() > nPos
) // only if needed
709 if ( !bTextInitialized
)
713 sal_Int16 nDays
= pFormatter
->GetCalendar()->getNumberOfDaysInWeek();
714 for ( sal_Int16 i
= 0; i
< nDays
; i
++ )
716 if ( StringContainsWord( pUpperDayText
[i
], rString
, nPos
) )
717 { // full names first
718 nPos
= nPos
+ pUpperDayText
[i
].getLength();
722 if ( StringContainsWord( pUpperAbbrevDayText
[i
], rString
, nPos
) )
724 nPos
= nPos
+ pUpperAbbrevDayText
[i
].getLength();
725 res
= -(i
+ 1); // negative
736 * Reading a currency symbol
740 bool ImpSvNumberInputScan::GetCurrency( const OUString
& rString
, sal_Int32
& nPos
)
742 if ( rString
.getLength() > nPos
)
744 if ( !aUpperCurrSymbol
.getLength() )
745 { // If no format specified the currency of the currently active locale.
746 LanguageType eLang
= (mpFormat
? mpFormat
->GetLanguage() :
747 pFormatter
->GetLocaleData()->getLanguageTag().getLanguageType());
748 aUpperCurrSymbol
= pFormatter
->GetCharClass()->uppercase(
749 SvNumberFormatter::GetCurrencyEntry( eLang
).GetSymbol() );
751 if ( StringContains( aUpperCurrSymbol
, rString
, nPos
) )
753 nPos
= nPos
+ aUpperCurrSymbol
.getLength();
758 OUString aSymbol
, aExtension
;
759 if ( mpFormat
->GetNewCurrencySymbol( aSymbol
, aExtension
) )
761 if ( aSymbol
.getLength() <= rString
.getLength() - nPos
)
763 aSymbol
= pFormatter
->GetCharClass()->uppercase(aSymbol
);
764 if ( StringContains( aSymbol
, rString
, nPos
) )
766 nPos
= nPos
+ aSymbol
.getLength();
779 * Reading the time period specifier (AM/PM) for the 12 hour clock
782 * "AM" or "PM" => true
790 bool ImpSvNumberInputScan::GetTimeAmPm( const OUString
& rString
, sal_Int32
& nPos
)
793 if ( rString
.getLength() > nPos
)
795 const CharClass
* pChr
= pFormatter
->GetCharClass();
796 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
797 if ( StringContains( pChr
->uppercase( pLoc
->getTimeAM() ), rString
, nPos
) )
800 nPos
= nPos
+ pLoc
->getTimeAM().getLength();
803 else if ( StringContains( pChr
->uppercase( pLoc
->getTimePM() ), rString
, nPos
) )
806 nPos
= nPos
+ pLoc
->getTimePM().getLength();
816 * Read a decimal separator (',')
820 inline bool ImpSvNumberInputScan::GetDecSep( const OUString
& rString
, sal_Int32
& nPos
) const
822 if ( rString
.getLength() > nPos
)
824 const OUString
& rSep
= pFormatter
->GetNumDecimalSep();
825 if ( rString
.match( rSep
, nPos
) )
827 nPos
= nPos
+ rSep
.getLength();
830 const OUString
& rSepAlt
= pFormatter
->GetNumDecimalSepAlt();
831 if ( !rSepAlt
.isEmpty() && rString
.match( rSepAlt
, nPos
) )
833 nPos
= nPos
+ rSepAlt
.getLength();
842 * Reading a hundredth seconds separator
844 inline bool ImpSvNumberInputScan::GetTime100SecSep( const OUString
& rString
, sal_Int32
& nPos
) const
846 if ( rString
.getLength() > nPos
)
850 // ISO 8601 specifies both '.' dot and ',' comma as fractional
852 if (rString
[nPos
] == '.' || rString
[nPos
] == ',')
858 // Even in an otherwise ISO 8601 string be lenient and accept the
859 // locale defined separator.
860 const OUString
& rSep
= pFormatter
->GetLocaleData()->getTime100SecSep();
861 if ( rString
.match( rSep
, nPos
))
863 nPos
= nPos
+ rSep
.getLength();
872 * Read a sign including brackets
875 * '(' => -1, bNegCheck = 1
878 int ImpSvNumberInputScan::GetSign( const OUString
& rString
, sal_Int32
& nPos
)
880 if (rString
.getLength() > nPos
)
881 switch (rString
[ nPos
])
886 case '(': // '(' similar to '-' ?!?
901 * Read a sign with an exponent
906 short ImpSvNumberInputScan::GetESign( const OUString
& rString
, sal_Int32
& nPos
)
908 if (rString
.getLength() > nPos
)
910 switch (rString
[nPos
])
927 * i counts string portions, j counts numbers thereof.
928 * It should had been called SkipNumber instead.
930 inline bool ImpSvNumberInputScan::GetNextNumber( sal_uInt16
& i
, sal_uInt16
& j
) const
932 if ( i
< nStringsCnt
&& IsNum
[i
] )
942 bool ImpSvNumberInputScan::GetTimeRef( double& fOutNumber
,
943 sal_uInt16 nIndex
, // j-value of the first numeric time part of input, default 0
944 sal_uInt16 nCnt
) const // count of numeric time parts
948 sal_uInt16 nMinute
= 0;
949 sal_uInt16 nSecond
= 0;
950 double fSecond100
= 0.0;
951 sal_uInt16 nStartIndex
= nIndex
;
953 if (nDecPos
== 2 && (nCnt
== 3 || nCnt
== 2)) // 20:45.5 or 45.5
957 else if (nIndex
- nStartIndex
< nCnt
)
959 nHour
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
++]].toInt32());
965 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetTimeRef: bad number index");
967 if (nDecPos
== 2 && nCnt
== 2) // 45.5
971 else if (nIndex
- nStartIndex
< nCnt
)
973 nMinute
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
++]].toInt32());
975 if (nIndex
- nStartIndex
< nCnt
)
977 nSecond
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
++]].toInt32());
979 if (nIndex
- nStartIndex
< nCnt
)
981 fSecond100
= StringToDouble( sStrArray
[nNums
[nIndex
]], true );
983 if (nAmPm
&& nHour
> 12) // not a valid AM/PM clock time
987 else if (nAmPm
== -1 && nHour
!= 12) // PM
991 else if (nAmPm
== 1 && nHour
== 12) // 12 AM
995 fOutNumber
= (static_cast<double>(nHour
)*3600 +
996 static_cast<double>(nMinute
)*60 +
997 static_cast<double>(nSecond
) +
1003 sal_uInt16
ImpSvNumberInputScan::ImplGetDay( sal_uInt16 nIndex
) const
1005 sal_uInt16 nRes
= 0;
1007 if (sStrArray
[nNums
[nIndex
]].getLength() <= 2)
1009 sal_uInt16 nNum
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
]].toInt32());
1020 sal_uInt16
ImpSvNumberInputScan::ImplGetMonth( sal_uInt16 nIndex
) const
1022 // Preset invalid month number
1023 sal_uInt16 nRes
= pFormatter
->GetCalendar()->getNumberOfMonthsInYear();
1025 if (sStrArray
[nNums
[nIndex
]].getLength() <= 2)
1027 sal_uInt16 nNum
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
]].toInt32());
1028 if ( 0 < nNum
&& nNum
<= nRes
)
1030 nRes
= nNum
- 1; // zero based for CalendarFieldIndex::MONTH
1039 * 30 -> 1930, 29 -> 2029, or 56 -> 1756, 55 -> 1855, ...
1041 sal_uInt16
ImpSvNumberInputScan::ImplGetYear( sal_uInt16 nIndex
)
1043 sal_uInt16 nYear
= 0;
1045 sal_Int32 nLen
= sStrArray
[nNums
[nIndex
]].getLength();
1046 // 16-bit integer year width can have 5 digits, allow for one additional
1047 // leading zero as convention.
1050 nYear
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
]].toInt32());
1051 // A year in another, not Gregorian CE era is never expanded.
1052 // A year < 100 entered with at least 3 digits with leading 0 is taken
1053 // as is without expansion.
1054 if (mbEraCE
== kDefaultEra
&& nYear
< 100 && nLen
< 3)
1056 nYear
= SvNumberFormatter::ExpandTwoDigitYear( nYear
, nYear2000
);
1064 bool ImpSvNumberInputScan::MayBeIso8601()
1066 if (nMayBeIso8601
== 0)
1069 sal_Int32 nLen
= ((nNumericsCnt
>= 1 && nNums
[0] < nStringsCnt
) ? sStrArray
[nNums
[0]].getLength() : 0);
1073 if (nNumericsCnt
>= 3 && nNums
[2] < nStringsCnt
&&
1074 sStrArray
[nNums
[0]+1] == "-" && // separator year-month
1075 (n
= sStrArray
[nNums
[1]].toInt32()) >= 1 && n
<= 12 && // month
1076 sStrArray
[nNums
[1]+1] == "-" && // separator month-day
1077 (n
= sStrArray
[nNums
[2]].toInt32()) >= 1 && n
<= 31) // day
1079 // Year (nNums[0]) value not checked, may be anything, but
1080 // length (number of digits) is checked.
1081 nMayBeIso8601
= (nLen
>= 4 ? 4 : (nLen
== 3 ? 3 : (nLen
> 0 ? 2 : 1)));
1085 return nMayBeIso8601
> 1;
1089 bool ImpSvNumberInputScan::CanForceToIso8601( DateOrder eDateOrder
)
1091 int nCanForceToIso8601
= 0;
1092 if (!MayBeIso8601())
1096 else if (nMayBeIso8601
>= 3)
1098 return true; // at least 3 digits in year
1102 if (eDateOrder
== DateOrder::Invalid
)
1104 // As if any of the cases below can be applied, but only if a
1105 // locale dependent date pattern was not matched.
1106 if ((GetDatePatternNumbers() == nNumericsCnt
) && IsDatePatternNumberOfType(0,'Y'))
1108 eDateOrder
= GetDateOrder();
1111 nCanForceToIso8601
= 1;
1117 case DateOrder::DMY
: // "day" value out of range => ISO 8601 year
1118 if ((n
= sStrArray
[nNums
[0]].toInt32()) < 1 || n
> 31)
1120 nCanForceToIso8601
= 2;
1123 case DateOrder::MDY
: // "month" value out of range => ISO 8601 year
1124 if ((n
= sStrArray
[nNums
[0]].toInt32()) < 1 || n
> 12)
1126 nCanForceToIso8601
= 2;
1129 case DateOrder::YMD
: // always possible
1130 nCanForceToIso8601
= 2;
1134 return nCanForceToIso8601
> 1;
1138 bool ImpSvNumberInputScan::IsAcceptableIso8601()
1140 if (mpFormat
&& (mpFormat
->GetType() & SvNumFormatType::DATE
))
1142 switch (pFormatter
->GetEvalDateFormat())
1144 case NF_EVALDATEFORMAT_INTL
:
1145 return CanForceToIso8601( GetDateOrder());
1146 case NF_EVALDATEFORMAT_FORMAT
:
1147 return CanForceToIso8601( mpFormat
->GetDateOrder());
1149 return CanForceToIso8601( GetDateOrder()) || CanForceToIso8601( mpFormat
->GetDateOrder());
1152 return CanForceToIso8601( GetDateOrder());
1156 bool ImpSvNumberInputScan::MayBeMonthDate()
1158 if (nMayBeMonthDate
== 0)
1160 nMayBeMonthDate
= 1;
1161 if (nNumericsCnt
>= 2 && nNums
[1] < nStringsCnt
)
1164 const OUString
& rM
= sStrArray
[ nNums
[ 0 ] + 1 ];
1165 if (rM
.getLength() >= 3 && rM
[0] == '-' && rM
[ rM
.getLength() - 1] == '-')
1167 // Check year length assuming at least 3 digits (including
1168 // leading zero). Two digit years 1..31 are out of luck here
1169 // and may be taken as day of month.
1170 bool bYear1
= (sStrArray
[nNums
[0]].getLength() >= 3);
1171 bool bYear2
= (sStrArray
[nNums
[1]].getLength() >= 3);
1173 bool bDay1
= !bYear1
;
1176 n
= sStrArray
[nNums
[0]].toInt32();
1177 bDay1
= n
>= 1 && n
<= 31;
1179 bool bDay2
= !bYear2
;
1182 n
= sStrArray
[nNums
[1]].toInt32();
1183 bDay2
= n
>= 1 && n
<= 31;
1186 if (bDay1
&& !bDay2
)
1188 nMayBeMonthDate
= 2; // dd-month-yy
1190 else if (!bDay1
&& bDay2
)
1192 nMayBeMonthDate
= 3; // yy-month-dd
1194 else if (bDay1
&& bDay2
)
1196 // Ambiguous ##-MMM-## date, but some big vendor's database
1197 // reports write this crap, assume this always to be
1198 nMayBeMonthDate
= 2; // dd-month-yy
1203 return nMayBeMonthDate
> 1;
1207 /** If a string is a separator plus '-' minus sign preceding a 'Y' year in
1208 a date pattern at position nPat.
1210 static bool lcl_IsSignedYearSep( const OUString
& rStr
, const OUString
& rPat
, sal_Int32 nPat
)
1213 sal_Int32 nLen
= rStr
.getLength();
1214 if (nLen
> 1 && rStr
[nLen
-1] == '-')
1217 if (nPat
+ nLen
< rPat
.getLength() && rPat
[nPat
+nLen
] == 'Y')
1219 // Signed year is possible.
1220 bOk
= (rPat
.indexOf( rStr
.copy( 0, nLen
), nPat
) == nPat
);
1227 bool ImpSvNumberInputScan::IsAcceptedDatePattern( sal_uInt16 nStartPatternAt
)
1229 if (nAcceptedDatePattern
>= -1)
1231 return (nAcceptedDatePattern
>= 0);
1235 nAcceptedDatePattern
= -1;
1237 else if (!sDateAcceptancePatterns
.getLength())
1239 // The current locale is the format's locale, if a format is present.
1240 const NfEvalDateFormat eEDF
= pFormatter
->GetEvalDateFormat();
1241 if (!mpFormat
|| eEDF
== NF_EVALDATEFORMAT_FORMAT
|| mpFormat
->GetLanguage() == pFormatter
->GetLanguage())
1243 sDateAcceptancePatterns
= pFormatter
->GetLocaleData()->getDateAcceptancePatterns();
1247 OnDemandLocaleDataWrapper
& xLocaleData
= pFormatter
->GetOnDemandLocaleDataWrapper(
1248 SvNumberFormatter::InputScannerPrivateAccess());
1249 const LanguageTag
aSaveLocale( xLocaleData
->getLanguageTag() );
1250 assert(mpFormat
->GetLanguage() == aSaveLocale
.getLanguageType()); // prerequisite
1251 // Obtain formatter's locale's (e.g. system) patterns.
1252 xLocaleData
.changeLocale( LanguageTag( pFormatter
->GetLanguage()));
1253 const css::uno::Sequence
<OUString
> aLocalePatterns( xLocaleData
->getDateAcceptancePatterns());
1254 // Reset to format's locale.
1255 xLocaleData
.changeLocale( aSaveLocale
);
1256 // When concatenating don't care about duplicates, combining
1257 // weeding those out reallocs yet another time and probably doesn't
1258 // take less time than looping over two additional patterns below..
1261 case NF_EVALDATEFORMAT_FORMAT
:
1262 assert(!"shouldn't reach here");
1264 case NF_EVALDATEFORMAT_INTL
:
1265 sDateAcceptancePatterns
= aLocalePatterns
;
1267 case NF_EVALDATEFORMAT_INTL_FORMAT
:
1268 sDateAcceptancePatterns
= comphelper::concatSequences(
1270 xLocaleData
->getDateAcceptancePatterns());
1272 case NF_EVALDATEFORMAT_FORMAT_INTL
:
1273 sDateAcceptancePatterns
= comphelper::concatSequences(
1274 xLocaleData
->getDateAcceptancePatterns(),
1279 SAL_WARN_IF( !sDateAcceptancePatterns
.getLength(), "svl.numbers", "ImpSvNumberInputScan::IsAcceptedDatePattern: no date acceptance patterns");
1280 nAcceptedDatePattern
= (sDateAcceptancePatterns
.getLength() ? -2 : -1);
1283 if (nAcceptedDatePattern
== -1)
1287 nDatePatternStart
= nStartPatternAt
; // remember start particle
1289 const sal_Int32 nMonthsInYear
= pFormatter
->GetCalendar()->getNumberOfMonthsInYear();
1291 for (sal_Int32 nPattern
=0; nPattern
< sDateAcceptancePatterns
.getLength(); ++nPattern
)
1293 sal_uInt16 nNext
= nDatePatternStart
;
1294 nDatePatternNumbers
= 0;
1296 const OUString
& rPat
= sDateAcceptancePatterns
[nPattern
];
1298 for ( ; nPat
< rPat
.getLength() && bOk
&& nNext
< nStringsCnt
; ++nPat
, ++nNext
)
1300 const sal_Unicode c
= rPat
[nPat
];
1307 if (bOk
&& (c
== 'M' || c
== 'D'))
1309 // Check the D and M cases for plausibility. This also
1310 // prevents recognition of date instead of number with a
1311 // numeric group input if date separator is identical to
1312 // group separator, for example with D.M as a pattern and
1313 // #.### as a group.
1314 sal_Int32 nMaxLen
, nMaxVal
;
1319 nMaxVal
= nMonthsInYear
;
1326 // This merely exists against
1327 // -Werror=maybe-uninitialized, which is nonsense
1328 // after the (c == 'M' || c == 'D') check above,
1333 bOk
= (sStrArray
[nNext
].getLength() <= nMaxLen
);
1336 sal_Int32 nNum
= sStrArray
[nNext
].toInt32();
1337 bOk
= (1 <= nNum
&& nNum
<= nMaxVal
);
1341 ++nDatePatternNumbers
;
1344 bOk
= !IsNum
[nNext
];
1347 const sal_Int32 nLen
= sStrArray
[nNext
].getLength();
1348 bOk
= (rPat
.indexOf( sStrArray
[nNext
], nPat
) == nPat
);
1353 else if ((bOk
= lcl_IsSignedYearSep( sStrArray
[nNext
], rPat
, nPat
)))
1357 else if (nPat
+ nLen
> rPat
.getLength() && sStrArray
[nNext
][ nLen
- 1 ] == ' ')
1359 using namespace comphelper::string
;
1360 // Trailing blanks in input.
1361 OUStringBuffer
aBuf(sStrArray
[nNext
]);
1363 // Expand again in case of pattern "M. D. " and
1364 // input "M. D. ", maybe fetched far, but..
1365 padToLength(aBuf
, rPat
.getLength() - nPat
, ' ');
1366 OUString aStr
= aBuf
.makeStringAndClear();
1367 bOk
= (rPat
.indexOf( aStr
, nPat
) == nPat
);
1370 nPat
+= aStr
.getLength() - 1;
1379 // Check for trailing characters mismatch.
1380 if (nNext
< nStringsCnt
)
1382 // Pattern end but not input end.
1383 // A trailing blank may be part of the current pattern input,
1384 // if pattern is "D.M." and input is "D.M. hh:mm" last was
1385 // ". ", or may be following the current pattern input, if
1386 // pattern is "D.M" and input is "D.M hh:mm" last was "M".
1389 if (nPat
> 0 && nNext
> 0)
1391 // nPat is one behind after the for loop.
1392 sal_Int32 nPatCheck
= nPat
- 1;
1393 switch (rPat
[nPatCheck
])
1403 // Advance position in input to match length of
1404 // non-YMD (separator) characters in pattern.
1409 } while ((c
= rPat
[--nPatCheck
]) != 'Y' && c
!= 'M' && c
!= 'D');
1419 // Trailing (or separating if time follows) blanks are ok.
1420 SkipBlanks( sStrArray
[nCheck
], nPos
);
1421 if (nPos
== sStrArray
[nCheck
].getLength())
1423 nAcceptedDatePattern
= nPattern
;
1428 else if (nPat
== rPat
.getLength())
1430 // Input end and pattern end => match.
1431 nAcceptedDatePattern
= nPattern
;
1434 // else Input end but not pattern end, no match.
1437 nAcceptedDatePattern
= -1;
1442 bool ImpSvNumberInputScan::SkipDatePatternSeparator( sal_uInt16 nParticle
, sal_Int32
& rPos
, bool & rSignedYear
)
1444 // If not initialized yet start with first number, if any.
1445 if (!IsAcceptedDatePattern( nNumericsCnt
? nNums
[0] : 0 ))
1449 if (nParticle
< nDatePatternStart
|| nParticle
>= nStringsCnt
|| IsNum
[nParticle
])
1453 sal_uInt16 nNext
= nDatePatternStart
;
1454 const OUString
& rPat
= sDateAcceptancePatterns
[nAcceptedDatePattern
];
1455 for (sal_Int32 nPat
= 0; nPat
< rPat
.getLength() && nNext
< nStringsCnt
; ++nPat
, ++nNext
)
1464 if (nNext
== nParticle
)
1466 const sal_Int32 nLen
= sStrArray
[nNext
].getLength();
1467 bool bOk
= (rPat
.indexOf( sStrArray
[nNext
], nPat
) == nPat
);
1470 bOk
= lcl_IsSignedYearSep( sStrArray
[nNext
], rPat
, nPat
);
1474 if (!bOk
&& (nPat
+ nLen
> rPat
.getLength() && sStrArray
[nNext
][ nLen
- 1 ] == ' '))
1476 // The same ugly trailing blanks check as in
1477 // IsAcceptedDatePattern().
1478 using namespace comphelper::string
;
1479 OUStringBuffer
aBuf(sStrArray
[nNext
]);
1481 padToLength(aBuf
, rPat
.getLength() - nPat
, ' ');
1482 bOk
= (rPat
.indexOf( aBuf
.makeStringAndClear(), nPat
) == nPat
);
1486 rPos
= nLen
; // yes, set, not add!
1492 nPat
+= sStrArray
[nNext
].getLength() - 1;
1500 sal_uInt16
ImpSvNumberInputScan::GetDatePatternNumbers()
1502 // If not initialized yet start with first number, if any.
1503 if (!IsAcceptedDatePattern( nNumericsCnt
? nNums
[0] : 0 ))
1507 return nDatePatternNumbers
;
1511 bool ImpSvNumberInputScan::IsDatePatternNumberOfType( sal_uInt16 nNumber
, sal_Unicode cType
)
1513 if (GetDatePatternNumbers() <= nNumber
)
1516 sal_uInt16 nNum
= 0;
1517 const OUString
& rPat
= sDateAcceptancePatterns
[nAcceptedDatePattern
];
1518 for (sal_Int32 nPat
= 0; nPat
< rPat
.getLength(); ++nPat
)
1525 if (nNum
== nNumber
)
1526 return rPat
[nPat
] == cType
;
1535 sal_uInt32
ImpSvNumberInputScan::GetDatePatternOrder()
1537 // If not initialized yet start with first number, if any.
1538 if (!IsAcceptedDatePattern( nNumericsCnt
? nNums
[0] : 0 ))
1542 sal_uInt32 nOrder
= 0;
1543 const OUString
& rPat
= sDateAcceptancePatterns
[nAcceptedDatePattern
];
1544 for (sal_Int32 nPat
= 0; nPat
< rPat
.getLength() && !(nOrder
& 0xff0000); ++nPat
)
1551 nOrder
= (nOrder
<< 8) | rPat
[nPat
];
1559 DateOrder
ImpSvNumberInputScan::GetDateOrder()
1561 sal_uInt32 nOrder
= GetDatePatternOrder();
1564 return pFormatter
->GetLocaleData()->getDateOrder();
1566 switch ((nOrder
& 0xff0000) >> 16)
1569 if ((((nOrder
& 0xff00) >> 8) == 'M') && ((nOrder
& 0xff) == 'D'))
1571 return DateOrder::YMD
;
1575 if ((((nOrder
& 0xff00) >> 8) == 'D') && ((nOrder
& 0xff) == 'Y'))
1577 return DateOrder::MDY
;
1581 if ((((nOrder
& 0xff00) >> 8) == 'M') && ((nOrder
& 0xff) == 'Y'))
1583 return DateOrder::DMY
;
1588 switch ((nOrder
& 0xff00) >> 8)
1591 switch (nOrder
& 0xff)
1594 return DateOrder::YMD
;
1598 switch (nOrder
& 0xff)
1601 return DateOrder::DMY
;
1603 return DateOrder::MDY
;
1607 switch (nOrder
& 0xff)
1610 return DateOrder::MDY
;
1612 return DateOrder::DMY
;
1617 switch (nOrder
& 0xff)
1620 return DateOrder::YMD
;
1622 return DateOrder::MDY
;
1624 return DateOrder::DMY
;
1629 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateOrder: undefined, falling back to locale's default");
1630 return pFormatter
->GetLocaleData()->getDateOrder();
1633 bool ImpSvNumberInputScan::GetDateRef( double& fDays
, sal_uInt16
& nCounter
)
1635 using namespace ::com::sun::star::i18n
;
1636 NfEvalDateFormat eEDF
;
1638 if ( mpFormat
&& (mpFormat
->GetType() & SvNumFormatType::DATE
) )
1640 eEDF
= pFormatter
->GetEvalDateFormat();
1643 case NF_EVALDATEFORMAT_INTL
:
1644 case NF_EVALDATEFORMAT_FORMAT
:
1645 nFormatOrder
= 1; // only one loop
1649 if ( nMatchedAllStrings
)
1651 eEDF
= NF_EVALDATEFORMAT_FORMAT_INTL
;
1652 // we have a complete match, use it
1658 eEDF
= NF_EVALDATEFORMAT_INTL
;
1663 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
1664 CalendarWrapper
* pCal
= pFormatter
->GetCalendar();
1665 for ( int nTryOrder
= 1; nTryOrder
<= nFormatOrder
; nTryOrder
++ )
1667 pCal
->setGregorianDateTime( Date( Date::SYSTEM
) ); // today
1668 OUString aOrgCalendar
; // empty => not changed yet
1673 case NF_EVALDATEFORMAT_INTL
:
1674 bFormatTurn
= false;
1675 DateFmt
= GetDateOrder();
1677 case NF_EVALDATEFORMAT_FORMAT
:
1679 DateFmt
= mpFormat
->GetDateOrder();
1681 case NF_EVALDATEFORMAT_INTL_FORMAT
:
1682 if ( nTryOrder
== 1 )
1684 bFormatTurn
= false;
1685 DateFmt
= GetDateOrder();
1690 DateFmt
= mpFormat
->GetDateOrder();
1693 case NF_EVALDATEFORMAT_FORMAT_INTL
:
1694 if ( nTryOrder
== 2 )
1696 bFormatTurn
= false;
1697 DateFmt
= GetDateOrder();
1702 // Even if the format pattern is to be preferred, the input may
1703 // have matched a pattern of the current locale, which then
1704 // again is to be preferred. Both date orders can be different
1705 // so we need to obtain the actual match. For example ISO
1706 // YYYY-MM-DD format vs locale's DD.MM.YY input.
1707 if (!GetDatePatternOrder())
1709 // No pattern match => format match.
1710 DateFmt
= mpFormat
->GetDateOrder();
1714 // Pattern match. Note that patterns may have been
1715 // constructed from the format's locale and prepended to
1716 // the current locale's patterns, it doesn't necessarily
1717 // mean a current locale's pattern was matched, but may if
1718 // the format's locale's patterns didn't match, which were
1720 DateFmt
= GetDateOrder();
1725 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateRef: unknown NfEvalDateFormat" );
1726 DateFmt
= DateOrder::YMD
;
1727 bFormatTurn
= false;
1732 We are currently not able to fully support a switch to another calendar during
1733 input for the following reasons:
1734 1. We do have a problem if both (locale's default and format's) calendars
1735 define the same YMD order and use the same date separator, there is no way
1736 to distinguish between them if the input results in valid calendar input for
1737 both calendars. How to solve? Would NfEvalDateFormat be sufficient? Should
1738 it always be set to NF_EVALDATEFORMAT_FORMAT_INTL and thus the format's
1739 calendar be preferred? This could be confusing if a Calc cell was formatted
1740 different to the locale's default and has no content yet, then the user has
1741 no clue about the format or calendar being set.
1742 2. In Calc cell edit mode a date is always displayed and edited using the
1743 default edit format of the default calendar (normally being Gregorian). If
1744 input was ambiguous due to issue #1 we'd need a mechanism to tell that a
1745 date was edited and not newly entered. Not feasible. Otherwise we'd need a
1746 mechanism to use a specific edit format with a specific calendar according
1748 3. For some calendars like Japanese Gengou we'd need era input, which isn't
1749 implemented at all. Though this is a rare and special case, forcing a
1750 calendar dependent edit format as suggested in item #2 might require era
1751 input, if it shouldn't result in a fallback to Gregorian calendar.
1752 4. Last and least: the GetMonth() method currently only matches month names of
1753 the default calendar. Alternating month names of the actual format's
1754 calendar would have to be implemented. No problem.
1758 if ( mpFormat
->IsOtherCalendar( nStringScanNumFor
) )
1760 mpFormat
->SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
1764 mpFormat
->SwitchToSpecifiedCalendar( aOrgCalendar
, fOrgDateTime
,
1765 nStringScanNumFor
);
1772 // For incomplete dates, always assume first day of month if not specified.
1773 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
1775 switch (nNumericsCnt
) // count of numbers in string
1778 if (nMonthPos
) // only month (Jan)
1780 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
1788 case 1: // only one number
1790 switch (nMonthPos
) // where is the month
1792 case 0: // not found
1794 // If input matched a date pattern, use the pattern
1795 // to determine if it is a day, month or year. The
1796 // pattern should have only one single value then,
1797 // 'D-', 'M-' or 'Y-'. If input did not match a
1798 // pattern assume the usual day of current month.
1799 sal_uInt32 nDateOrder
= (bFormatTurn
?
1800 mpFormat
->GetExactDateOrder() :
1801 GetDatePatternOrder());
1805 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1808 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1812 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1817 case 1: // month at the beginning (Jan 01)
1818 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
1821 case DateOrder::MDY
:
1822 case DateOrder::YMD
:
1824 sal_uInt16 nDay
= ImplGetDay(0);
1825 sal_uInt16 nYear
= ImplGetYear(0);
1826 if (nDay
== 0 || nDay
> 32)
1828 pCal
->setValue( CalendarFieldIndex::YEAR
, nYear
);
1832 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1836 case DateOrder::DMY
:
1837 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1844 case 3: // month at the end (10 Jan)
1845 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
1848 case DateOrder::DMY
:
1849 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1851 case DateOrder::YMD
:
1852 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1862 } // switch (nMonthPos)
1865 case 2: // 2 numbers
1867 switch (nMonthPos
) // where is the month
1869 case 0: // not found
1871 sal_uInt32 nExactDateOrder
= (bFormatTurn
?
1872 mpFormat
->GetExactDateOrder() :
1873 GetDatePatternOrder());
1874 bool bIsExact
= (0xff < nExactDateOrder
&& nExactDateOrder
<= 0xffff);
1875 if (!bIsExact
&& bFormatTurn
&& IsAcceptedDatePattern( nNums
[0]))
1877 // If input does not match format but pattern, use pattern
1878 // instead, even if eEDF==NF_EVALDATEFORMAT_FORMAT_INTL.
1879 // For example, format has "Y-M-D" and pattern is "D.M.",
1880 // input with 2 numbers can't match format and 31.12. would
1881 // lead to 1931-12-01 (fdo#54344)
1882 nExactDateOrder
= GetDatePatternOrder();
1883 bIsExact
= (0xff < nExactDateOrder
&& nExactDateOrder
<= 0xffff);
1888 // formatted as date and exactly 2 parts
1890 switch ( (nExactDateOrder
>> 8) & 0xff )
1893 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1896 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1899 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1904 switch ( nExactDateOrder
& 0xff )
1907 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
1910 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
1913 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
1918 SAL_WARN_IF( !bHadExact
, "svl.numbers", "ImpSvNumberInputScan::GetDateRef: error in exact date order");
1924 // If input matched against a date acceptance pattern
1925 // do not attempt to mess around with guessing the
1926 // order, either it matches or it doesn't.
1927 if ((bFormatTurn
|| !bIsExact
) && (!bHadExact
|| !pCal
->isValid()))
1929 if ( !bHadExact
&& nExactDateOrder
)
1931 pCal
->setGregorianDateTime( Date( Date::SYSTEM
) ); // reset today
1935 case DateOrder::MDY
:
1937 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
1938 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1939 if ( !pCal
->isValid() ) // 2nd try
1941 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
1942 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1943 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
1946 case DateOrder::DMY
:
1948 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1949 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
1950 if ( !pCal
->isValid() ) // 2nd try
1952 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
1953 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1954 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
1957 case DateOrder::YMD
:
1959 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
1960 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1961 if ( !pCal
->isValid() ) // 2nd try
1963 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
1964 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
1965 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1975 case 1: // month at the beginning (Jan 01 01)
1977 // The input is valid as MDY in almost any
1978 // constellation, there is no date order (M)YD except if
1979 // set in a format applied.
1980 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
1981 sal_uInt32 nExactDateOrder
= (bFormatTurn
? mpFormat
->GetExactDateOrder() : 0);
1982 if ((((nExactDateOrder
>> 8) & 0xff) == 'Y') && ((nExactDateOrder
& 0xff) == 'D'))
1984 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
1985 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1989 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1990 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
1994 case 2: // month in the middle (10 Jan 94)
1996 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
1997 DateOrder eDF
= (MayBeMonthDate() ? (nMayBeMonthDate
== 2 ? DateOrder::DMY
: DateOrder::YMD
) : DateFmt
);
2000 case DateOrder::DMY
:
2001 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2002 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2004 case DateOrder::YMD
:
2005 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2006 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2014 default: // else, e.g. month at the end (94 10 Jan)
2017 } // switch (nMonthPos)
2020 default: // more than two numbers (31.12.94 8:23) (31.12. 8:23)
2021 switch (nMonthPos
) // where is the month
2023 case 0: // not found
2027 { // find first time number index (should only be 3 or 2 anyway)
2028 for ( sal_uInt16 j
= 0; j
< nNumericsCnt
; j
++ )
2030 if ( nNums
[j
] == nTimePos
- 2 )
2037 // ISO 8601 yyyy-mm-dd forced recognition
2038 DateOrder eDF
= (CanForceToIso8601( DateFmt
) ? DateOrder::YMD
: DateFmt
);
2041 case DateOrder::MDY
:
2042 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2043 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
2045 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(2) );
2047 case DateOrder::DMY
:
2048 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2049 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
2051 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(2) );
2053 case DateOrder::YMD
:
2055 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(2) );
2056 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
2057 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2065 case 1: // month at the beginning (Jan 01 01 8:23)
2069 case DateOrder::MDY
:
2070 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2071 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
2072 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2079 case 2: // month in the middle (10 Jan 94 8:23)
2081 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
2084 case DateOrder::DMY
:
2085 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2086 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2088 case DateOrder::YMD
:
2089 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2090 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2097 default: // else, e.g. month at the end (94 10 Jan 8:23)
2101 } // switch (nMonthPos)
2103 } // switch (nNumericsCnt)
2105 if (mbEraCE
!= kDefaultEra
)
2106 pCal
->setValue( CalendarFieldIndex::ERA
, mbEraCE
? 1 : 0);
2108 if ( res
&& pCal
->isValid() )
2110 double fDiff
= DateTime(*pNullDate
) - pCal
->getEpochStart();
2111 fDays
= ::rtl::math::approxFloor( pCal
->getLocalDateTime() );
2113 nTryOrder
= nFormatOrder
; // break for
2119 if ( aOrgCalendar
.getLength() )
2121 pCal
->loadCalendar( aOrgCalendar
, pLoc
->getLanguageTag().getLocale() ); // restore calendar
2123 #if NF_TEST_CALENDAR
2125 using namespace ::com::sun::star
;
2126 struct entry
{ const char* lan
; const char* cou
; const char* cal
; };
2127 const entry cals
[] = {
2128 { "en", "US", "gregorian" },
2129 { "ar", "TN", "hijri" },
2130 { "he", "IL", "jewish" },
2131 { "ja", "JP", "gengou" },
2132 { "ko", "KR", "hanja_yoil" },
2133 { "th", "TH", "buddhist" },
2134 { "zh", "TW", "ROC" },
2137 lang::Locale aLocale
;
2139 sal_Int16 nDay
, nMyMonth
, nYear
, nHour
, nMinute
, nSecond
;
2140 sal_Int16 nDaySet
, nMonthSet
, nYearSet
, nHourSet
, nMinuteSet
, nSecondSet
;
2141 sal_Int16 nZO
, nDST1
, nDST2
, nDST
, nZOmillis
, nDST1millis
, nDST2millis
, nDSTmillis
;
2142 sal_Int32 nZoneInMillis
, nDST1InMillis
, nDST2InMillis
;
2143 uno::Reference
< uno::XComponentContext
> xContext
=
2144 ::comphelper::getProcessComponentContext();
2145 uno::Reference
< i18n::XCalendar4
> xCal
= i18n::LocaleCalendar2::create(xContext
);
2146 for ( const entry
* p
= cals
; p
->lan
; ++p
)
2148 aLocale
.Language
= OUString::createFromAscii( p
->lan
);
2149 aLocale
.Country
= OUString::createFromAscii( p
->cou
);
2150 xCal
->loadCalendar( OUString::createFromAscii( p
->cal
),
2152 double nDateTime
= 0.0; // 1-Jan-1970 00:00:00
2153 nZO
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET
);
2154 nZOmillis
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS
);
2155 nZoneInMillis
= static_cast<sal_Int32
>(nZO
) * 60000 +
2156 (nZO
< 0 ? -1 : 1) * static_cast<sal_uInt16
>(nZOmillis
);
2157 nDST1
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET
);
2158 nDST1millis
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS
);
2159 nDST1InMillis
= static_cast<sal_Int32
>(nDST1
) * 60000 +
2160 (nDST1
< 0 ? -1 : 1) * static_cast<sal_uInt16
>(nDST1millis
);
2161 nDateTime
-= (double)(nZoneInMillis
+ nDST1InMillis
) / 1000.0 / 60.0 / 60.0 / 24.0;
2162 xCal
->setDateTime( nDateTime
);
2163 nDST2
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET
);
2164 nDST2millis
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS
);
2165 nDST2InMillis
= static_cast<sal_Int32
>(nDST2
) * 60000 +
2166 (nDST2
< 0 ? -1 : 1) * static_cast<sal_uInt16
>(nDST2millis
);
2167 if ( nDST1InMillis
!= nDST2InMillis
)
2169 nDateTime
= 0.0 - (double)(nZoneInMillis
+ nDST2InMillis
) / 1000.0 / 60.0 / 60.0 / 24.0;
2170 xCal
->setDateTime( nDateTime
);
2172 nDaySet
= xCal
->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH
);
2173 nMonthSet
= xCal
->getValue( i18n::CalendarFieldIndex::MONTH
);
2174 nYearSet
= xCal
->getValue( i18n::CalendarFieldIndex::YEAR
);
2175 nHourSet
= xCal
->getValue( i18n::CalendarFieldIndex::HOUR
);
2176 nMinuteSet
= xCal
->getValue( i18n::CalendarFieldIndex::MINUTE
);
2177 nSecondSet
= xCal
->getValue( i18n::CalendarFieldIndex::SECOND
);
2178 nZO
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET
);
2179 nZOmillis
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS
);
2180 nDST
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET
);
2181 nDSTmillis
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS
);
2182 xCal
->setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH
, nDaySet
);
2183 xCal
->setValue( i18n::CalendarFieldIndex::MONTH
, nMonthSet
);
2184 xCal
->setValue( i18n::CalendarFieldIndex::YEAR
, nYearSet
);
2185 xCal
->setValue( i18n::CalendarFieldIndex::HOUR
, nHourSet
);
2186 xCal
->setValue( i18n::CalendarFieldIndex::MINUTE
, nMinuteSet
);
2187 xCal
->setValue( i18n::CalendarFieldIndex::SECOND
, nSecondSet
);
2188 bValid
= xCal
->isValid();
2189 nDay
= xCal
->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH
);
2190 nMyMonth
= xCal
->getValue( i18n::CalendarFieldIndex::MONTH
);
2191 nYear
= xCal
->getValue( i18n::CalendarFieldIndex::YEAR
);
2192 nHour
= xCal
->getValue( i18n::CalendarFieldIndex::HOUR
);
2193 nMinute
= xCal
->getValue( i18n::CalendarFieldIndex::MINUTE
);
2194 nSecond
= xCal
->getValue( i18n::CalendarFieldIndex::SECOND
);
2195 bValid
= bValid
&& nDay
== nDaySet
&& nMyMonth
== nMonthSet
&& nYear
==
2196 nYearSet
&& nHour
== nHourSet
&& nMinute
== nMinuteSet
&& nSecond
2200 #endif // NF_TEST_CALENDAR
2209 * Analyze first string
2213 bool ImpSvNumberInputScan::ScanStartString( const OUString
& rString
)
2217 // First of all, eat leading blanks
2218 SkipBlanks(rString
, nPos
);
2220 // Yes, nMatchedAllStrings should know about the sign position
2221 nSign
= GetSign(rString
, nPos
);
2222 if ( nSign
) // sign?
2224 SkipBlanks(rString
, nPos
);
2226 // #102371# match against format string only if start string is not a sign character
2227 if ( nMatchedAllStrings
&& !(nSign
&& rString
.getLength() == 1) )
2229 // Match against format in any case, so later on for a "x1-2-3" input
2230 // we may distinguish between a xy-m-d (or similar) date and a x0-0-0
2231 // format. No sign detection here!
2232 if ( ScanStringNumFor( rString
, nPos
, 0, true ) )
2234 nMatchedAllStrings
|= nMatchedStartString
;
2238 nMatchedAllStrings
= 0;
2242 // Bail out early for just a sign.
2243 if (nSign
&& nPos
== rString
.getLength())
2246 if ( GetDecSep(rString
, nPos
) ) // decimal separator in start string
2249 SkipBlanks(rString
, nPos
);
2251 else if ( GetCurrency(rString
, nPos
) ) // currency (DM 1)?
2253 eScannedType
= SvNumFormatType::CURRENCY
; // !!! it IS currency !!!
2254 SkipBlanks(rString
, nPos
);
2255 if (nSign
== 0) // no sign yet
2257 nSign
= GetSign(rString
, nPos
);
2258 if ( nSign
) // DM -1
2260 SkipBlanks(rString
, nPos
);
2263 if ( GetDecSep(rString
, nPos
) ) // decimal separator follows currency
2266 SkipBlanks(rString
, nPos
);
2271 const sal_Int32 nMonthStart
= nPos
;
2272 short nTempMonth
= GetMonth(rString
, nPos
);
2275 // Short month and day names may be identical in some locales, e.g.
2276 // "mar" for "martes" or "marzo" in Spanish.
2277 // Do not let a month name immediately take precedence if a day
2278 // name was meant instead. Assume that both could be valid, until
2279 // encountered differently or the final evaluation in
2280 // IsNumberFormat() checks, but continue with weighing the month
2281 // name higher unless we have both day of week and month name here.
2282 sal_Int32 nTempPos
= nMonthStart
;
2283 nDayOfWeek
= GetDayOfWeek( rString
, nTempPos
);
2286 SkipChar( '.', rString
, nTempPos
); // abbreviated
2287 SkipString( pFormatter
->GetLocaleData()->getLongDateDayOfWeekSep(), rString
, nTempPos
);
2288 SkipBlanks( rString
, nTempPos
);
2289 short nTempTempMonth
= GetMonth( rString
, nTempPos
);
2292 // Fall into the else branch below that handles both.
2296 // Do not set nDayOfWeek hereafter, anywhere.
2300 if ( nTempMonth
) // month (Jan 1)?
2302 // Jan1 without separator is not a date, unless it is followed by a
2303 // separator and a (year) number.
2304 if (nPos
< rString
.getLength() || (nStringsCnt
>= 4 && nNumericsCnt
>= 2))
2306 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date !!!
2307 nMonth
= nTempMonth
;
2308 nMonthPos
= 1; // month at the beginning
2311 SkipChar( '.', rString
, nPos
); // abbreviated
2313 SkipBlanks(rString
, nPos
);
2317 nPos
= nMonthStart
; // rewind month
2322 int nTempDayOfWeek
= GetDayOfWeek( rString
, nPos
);
2323 if ( nTempDayOfWeek
)
2325 // day of week is just parsed away
2326 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date !!!
2327 if ( nPos
< rString
.getLength() )
2329 if ( nTempDayOfWeek
< 0 )
2332 if ( rString
[ nPos
] == '.' )
2340 SkipBlanks(rString
, nPos
);
2341 SkipString( pFormatter
->GetLocaleData()->getLongDateDayOfWeekSep(), rString
, nPos
);
2343 SkipBlanks(rString
, nPos
);
2344 nTempMonth
= GetMonth(rString
, nPos
);
2345 if ( nTempMonth
) // month (Jan 1)?
2347 // Jan1 without separator is not a date, unless it is followed by a
2348 // separator and a (year) number.
2349 if (nPos
< rString
.getLength() || (nStringsCnt
>= 4 && nNumericsCnt
>= 2))
2351 nMonth
= nTempMonth
;
2352 nMonthPos
= 1; // month a the beginning
2355 SkipChar( '.', rString
, nPos
); // abbreviated
2357 SkipBlanks(rString
, nPos
);
2361 nPos
= nMonthStart
; // rewind month
2367 // Determine and remember following date pattern, if any.
2368 IsAcceptedDatePattern( 1);
2374 // skip any trailing '-' or '/' chars
2375 if (nPos
< rString
.getLength())
2377 while (SkipChar ('-', rString
, nPos
) || SkipChar ('/', rString
, nPos
))
2380 if (nPos
< rString
.getLength()) // not everything consumed
2382 // Does input StartString equal StartString of format?
2383 // This time with sign detection!
2384 if ( !ScanStringNumFor( rString
, nPos
, 0 ) )
2386 return MatchedReturn();
2395 * Analyze string in the middle
2399 bool ImpSvNumberInputScan::ScanMidString( const OUString
& rString
, sal_uInt16 nStringPos
)
2402 SvNumFormatType eOldScannedType
= eScannedType
;
2404 if ( nMatchedAllStrings
)
2405 { // Match against format in any case, so later on for a "1-2-3-4" input
2406 // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2408 if ( ScanStringNumFor( rString
, 0, nStringPos
) )
2410 nMatchedAllStrings
|= nMatchedMidString
;
2414 nMatchedAllStrings
= 0;
2418 SkipBlanks(rString
, nPos
);
2419 if (GetDecSep(rString
, nPos
)) // decimal separator?
2421 if (nDecPos
== 1 || nDecPos
== 3) // .12.4 or 1.E2.1
2423 return MatchedReturn();
2425 else if (nDecPos
== 2) // . dup: 12.4.
2427 bool bSignedYear
= false;
2428 if (bDecSepInDateSeps
|| // . also date separator
2429 SkipDatePatternSeparator( nStringPos
, nPos
, bSignedYear
))
2431 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&&
2432 eScannedType
!= SvNumFormatType::DATE
&&
2433 eScannedType
!= SvNumFormatType::DATETIME
) // already another type
2435 return MatchedReturn();
2437 if (eScannedType
== SvNumFormatType::UNDEFINED
)
2439 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date
2441 SkipBlanks(rString
, nPos
);
2445 return MatchedReturn();
2450 nDecPos
= 2; // . in mid string
2451 SkipBlanks(rString
, nPos
);
2454 else if ( (eScannedType
& SvNumFormatType::TIME
) &&
2455 GetTime100SecSep( rString
, nPos
) )
2456 { // hundredth seconds separator
2459 return MatchedReturn();
2461 nDecPos
= 2; // . in mid string
2463 // If this is exactly an ISO 8601 fractional seconds separator, bail
2464 // out early to not get confused by later checks for group separator or
2466 if (bIso8601Tsep
&& nPos
== rString
.getLength() &&
2467 eScannedType
== SvNumFormatType::DATETIME
&& (rString
== "." || rString
== ","))
2470 SkipBlanks(rString
, nPos
);
2473 if (SkipChar('/', rString
, nPos
)) // fraction?
2475 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2476 eScannedType
!= SvNumFormatType::DATE
) // except date
2478 return MatchedReturn(); // => jan/31/1994
2480 else if (eScannedType
!= SvNumFormatType::DATE
&& // analyzed no date until now
2481 ( eSetType
== SvNumFormatType::FRACTION
|| // and preset was fraction
2482 (nNumericsCnt
== 3 && // or 3 numbers
2483 (nStringPos
== 3 || // and 3rd string particle
2484 (nStringPos
== 4 && nSign
))))) // or 4th if signed
2486 SkipBlanks(rString
, nPos
);
2487 if (nPos
== rString
.getLength())
2489 eScannedType
= SvNumFormatType::FRACTION
; // !!! it IS a fraction (so far)
2490 if (eSetType
== SvNumFormatType::FRACTION
&&
2491 nNumericsCnt
== 2 &&
2492 (nStringPos
== 1 || // for 4/5
2493 (nStringPos
== 2 && nSign
))) // or signed -4/5
2495 return true; // don't fall into date trap
2501 nPos
--; // put '/' back
2505 if (GetThousandSep(rString
, nPos
, nStringPos
)) // 1,000
2507 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2508 eScannedType
!= SvNumFormatType::CURRENCY
) // except currency
2510 return MatchedReturn();
2515 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
2516 bool bSignedYear
= false;
2517 bool bDate
= SkipDatePatternSeparator( nStringPos
, nPos
, bSignedYear
); // 12/31 31.12. 12/31/1999 31.12.1999
2520 const OUString
& rDate
= pFormatter
->GetDateSep();
2521 SkipBlanks(rString
, nPos
);
2522 bDate
= SkipString( rDate
, rString
, nPos
); // 10. 10- 10/
2524 if (bDate
|| ((MayBeIso8601() || MayBeMonthDate()) && // 1999-12-31 31-Dec-1999
2525 SkipChar( '-', rString
, nPos
)))
2527 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2528 eScannedType
!= SvNumFormatType::DATE
) // except date
2530 return MatchedReturn();
2532 SkipBlanks(rString
, nPos
);
2533 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date
2534 short nTmpMonth
= GetMonth(rString
, nPos
); // 10. Jan 94
2535 if (nMonth
&& nTmpMonth
) // month dup
2537 return MatchedReturn();
2542 nMonthPos
= 2; // month in the middle
2543 if ( nMonth
< 0 && SkipChar( '.', rString
, nPos
) )
2544 ; // short month may be abbreviated Jan.
2545 else if ( SkipChar( '-', rString
, nPos
) )
2546 ; // #79632# recognize 17-Jan-2001 to be a date
2547 // #99065# short and long month name
2550 SkipString( pLoc
->getLongDateMonthSep(), rString
, nPos
);
2552 SkipBlanks(rString
, nPos
);
2556 if (mbEraCE
!= kDefaultEra
) // signed year twice?
2557 return MatchedReturn();
2559 mbEraCE
= false; // BCE
2563 const sal_Int32 nMonthStart
= nPos
;
2564 short nTempMonth
= GetMonth(rString
, nPos
); // month in the middle (10 Jan 94)
2567 if (nMonth
!= 0) // month dup
2569 return MatchedReturn();
2571 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2572 eScannedType
!= SvNumFormatType::DATE
) // except date
2574 return MatchedReturn();
2576 if (nMonthStart
> 0 && nPos
< rString
.getLength()) // 10Jan or Jan94 without separator are not dates
2578 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date
2579 nMonth
= nTempMonth
;
2580 nMonthPos
= 2; // month in the middle
2583 SkipChar( '.', rString
, nPos
); // abbreviated
2585 SkipString( pLoc
->getLongDateMonthSep(), rString
, nPos
);
2586 SkipBlanks(rString
, nPos
);
2590 nPos
= nMonthStart
; // rewind month
2594 if ( SkipChar('E', rString
, nPos
) || // 10E, 10e, 10,Ee
2595 SkipChar('e', rString
, nPos
) )
2597 if (eScannedType
!= SvNumFormatType::UNDEFINED
) // already another type
2599 return MatchedReturn();
2603 SkipBlanks(rString
, nPos
);
2604 eScannedType
= SvNumFormatType::SCIENTIFIC
; // !!! it IS scientific
2605 if ( nThousand
+2 == nNumericsCnt
&& nDecPos
== 2 ) // special case 1.E2
2607 nDecPos
= 3; // 1,100.E2 1,100,100.E3
2610 nESign
= GetESign(rString
, nPos
); // signed exponent?
2611 SkipBlanks(rString
, nPos
);
2614 const OUString
& rTime
= pLoc
->getTimeSep();
2615 if ( SkipString(rTime
, rString
, nPos
) ) // time separator?
2617 if (nDecPos
) // already . => maybe error
2619 if (bDecSepInDateSeps
) // . also date sep
2621 if ( eScannedType
!= SvNumFormatType::DATE
&& // already another type than date
2622 eScannedType
!= SvNumFormatType::DATETIME
) // or date time
2624 return MatchedReturn();
2626 if (eScannedType
== SvNumFormatType::DATE
)
2628 nDecPos
= 0; // reset for time transition
2633 return MatchedReturn();
2636 if ((eScannedType
== SvNumFormatType::DATE
|| // already date type
2637 eScannedType
== SvNumFormatType::DATETIME
) && // or date time
2638 nNumericsCnt
> 3) // and more than 3 numbers? (31.Dez.94 8:23)
2640 SkipBlanks(rString
, nPos
);
2641 eScannedType
= SvNumFormatType::DATETIME
; // !!! it IS date with time
2643 else if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2644 eScannedType
!= SvNumFormatType::TIME
) // except time
2646 return MatchedReturn();
2650 SkipBlanks(rString
, nPos
);
2651 eScannedType
= SvNumFormatType::TIME
; // !!! it IS a time
2655 nTimePos
= nStringPos
+ 1;
2659 if (nPos
< rString
.getLength())
2661 switch (eScannedType
)
2663 case SvNumFormatType::DATE
:
2664 if (nMonthPos
== 1 && pLoc
->getLongDateOrder() == DateOrder::MDY
)
2666 // #68232# recognize long date separators like ", " in "September 5, 1999"
2667 if (SkipString( pLoc
->getLongDateDaySep(), rString
, nPos
))
2669 SkipBlanks( rString
, nPos
);
2672 else if (nPos
== 0 && rString
.getLength() == 1 && MayBeIso8601())
2674 if ( (nStringPos
== 5 && rString
[0] == 'T') ||
2675 (nStringPos
== 6 && rString
[0] == 'T' && sStrArray
[0] == "-"))
2677 // ISO 8601 combined date and time, yyyy-mm-ddThh:mm or -yyyy-mm-ddThh:mm
2679 bIso8601Tsep
= true;
2681 else if (nStringPos
== 7 && rString
[0] == ':')
2683 // ISO 8601 combined date and time, the time part; we reach
2684 // here if the locale's separator is not ':' so it couldn't
2685 // be detected above in the time block.
2686 if (nNumericsCnt
>= 5)
2687 eScannedType
= SvNumFormatType::DATETIME
;
2692 case SvNumFormatType::DATETIME
:
2693 if (nPos
== 0 && rString
.getLength() == 1 && MayBeIso8601())
2695 if (nStringPos
== 9 && rString
[0] == ':')
2697 // ISO 8601 combined date and time, the time part continued.
2701 #if NF_RECOGNIZE_ISO8601_TIMEZONES
2702 else if (nPos
== 0 && rString
.getLength() == 1 && nStringPos
>= 9 && MayBeIso8601())
2704 // ISO 8601 timezone offset
2705 switch (rString
[ 0 ])
2709 if (nStringPos
== nStringsCnt
- 2 ||
2710 nStringPos
== nStringsCnt
- 4)
2712 ++nPos
; // yyyy-mm-ddThh:mm[:ss]+xx[[:]yy]
2713 // nTimezonePos needed for GetTimeRef()
2716 nTimezonePos
= nStringPos
+ 1;
2721 if (nTimezonePos
&& nStringPos
>= 11 &&
2722 nStringPos
== nStringsCnt
- 2)
2724 ++nPos
; // yyyy-mm-ddThh:mm[:ss]+xx:yy
2735 if (nPos
< rString
.getLength()) // not everything consumed?
2737 if ( nMatchedAllStrings
& ~nMatchedVirgin
)
2739 eScannedType
= eOldScannedType
;
2756 bool ImpSvNumberInputScan::ScanEndString( const OUString
& rString
)
2760 if ( nMatchedAllStrings
)
2761 { // Match against format in any case, so later on for a "1-2-3-4" input
2762 // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2764 if ( ScanStringNumFor( rString
, 0, 0xFFFF ) )
2766 nMatchedAllStrings
|= nMatchedEndString
;
2770 nMatchedAllStrings
= 0;
2774 SkipBlanks(rString
, nPos
);
2775 if (GetDecSep(rString
, nPos
)) // decimal separator?
2777 if (nDecPos
== 1 || nDecPos
== 3) // .12.4 or 12.E4.
2779 return MatchedReturn();
2781 else if (nDecPos
== 2) // . dup: 12.4.
2783 bool bSignedYear
= false;
2784 if (bDecSepInDateSeps
|| // . also date separator
2785 SkipDatePatternSeparator( nStringsCnt
-1, nPos
, bSignedYear
))
2787 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&&
2788 eScannedType
!= SvNumFormatType::DATE
&&
2789 eScannedType
!= SvNumFormatType::DATETIME
) // already another type
2791 return MatchedReturn();
2793 if (eScannedType
== SvNumFormatType::UNDEFINED
)
2795 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date
2797 SkipBlanks(rString
, nPos
);
2801 return MatchedReturn();
2806 nDecPos
= 3; // . in end string
2807 SkipBlanks(rString
, nPos
);
2811 bool bSignDetectedHere
= false;
2812 if ( nSign
== 0 && // conflict - not signed
2813 eScannedType
!= SvNumFormatType::DATE
) // and not date
2814 //!? catch time too?
2816 nSign
= GetSign(rString
, nPos
); // 1- DM
2817 if (bNegCheck
) // '(' as sign
2819 return MatchedReturn();
2823 bSignDetectedHere
= true;
2827 SkipBlanks(rString
, nPos
);
2828 if (bNegCheck
&& SkipChar(')', rString
, nPos
)) // skip ')' if appropriate
2831 SkipBlanks(rString
, nPos
);
2834 if ( GetCurrency(rString
, nPos
) ) // currency symbol?
2836 if (eScannedType
!= SvNumFormatType::UNDEFINED
) // currency dup
2838 return MatchedReturn();
2842 SkipBlanks(rString
, nPos
);
2843 eScannedType
= SvNumFormatType::CURRENCY
;
2844 } // behind currency a '-' is allowed
2845 if (nSign
== 0) // not signed yet
2847 nSign
= GetSign(rString
, nPos
); // DM -
2848 SkipBlanks(rString
, nPos
);
2849 if (bNegCheck
) // 3 DM (
2851 return MatchedReturn();
2854 if ( bNegCheck
&& eScannedType
== SvNumFormatType::CURRENCY
&&
2855 SkipChar(')', rString
, nPos
) )
2857 bNegCheck
= false; // ')' skipped
2858 SkipBlanks(rString
, nPos
); // only if currency
2862 if ( SkipChar('%', rString
, nPos
) ) // 1%
2864 if (eScannedType
!= SvNumFormatType::UNDEFINED
) // already another type
2866 return MatchedReturn();
2868 SkipBlanks(rString
, nPos
);
2869 eScannedType
= SvNumFormatType::PERCENT
;
2872 const LocaleDataWrapper
* pLoc
= pFormatter
->GetLocaleData();
2873 const OUString
& rTime
= pLoc
->getTimeSep();
2874 if ( SkipString(rTime
, rString
, nPos
) ) // 10:
2876 if (nDecPos
) // already , => error
2878 return MatchedReturn();
2880 if (eScannedType
== SvNumFormatType::DATE
&& nNumericsCnt
> 2) // 31.Dez.94 8:
2882 SkipBlanks(rString
, nPos
);
2883 eScannedType
= SvNumFormatType::DATETIME
;
2885 else if (eScannedType
!= SvNumFormatType::UNDEFINED
&&
2886 eScannedType
!= SvNumFormatType::TIME
) // already another type
2888 return MatchedReturn();
2892 SkipBlanks(rString
, nPos
);
2893 eScannedType
= SvNumFormatType::TIME
;
2897 nTimePos
= nStringsCnt
;
2901 bool bSignedYear
= false;
2902 bool bDate
= SkipDatePatternSeparator( nStringsCnt
-1, nPos
, bSignedYear
); // 12/31 31.12. 12/31/1999 31.12.1999
2905 const OUString
& rDate
= pFormatter
->GetDateSep();
2906 bDate
= SkipString( rDate
, rString
, nPos
); // 10. 10- 10/
2908 if (bDate
&& bSignDetectedHere
)
2910 nSign
= 0; // 'D-' takes precedence over signed date
2912 if (bDate
|| ((MayBeIso8601() || MayBeMonthDate())
2913 && SkipChar( '-', rString
, nPos
)))
2915 if (eScannedType
!= SvNumFormatType::UNDEFINED
&&
2916 eScannedType
!= SvNumFormatType::DATE
) // already another type
2918 return MatchedReturn();
2922 SkipBlanks(rString
, nPos
);
2923 eScannedType
= SvNumFormatType::DATE
;
2925 short nTmpMonth
= GetMonth(rString
, nPos
); // 10. Jan
2926 if (nMonth
&& nTmpMonth
) // month dup
2928 return MatchedReturn();
2933 nMonthPos
= 3; // month at end
2936 SkipChar( '.', rString
, nPos
); // abbreviated
2938 SkipBlanks(rString
, nPos
);
2942 const sal_Int32 nMonthStart
= nPos
;
2943 short nTempMonth
= GetMonth(rString
, nPos
); // 10 Jan
2946 if (nMonth
) // month dup
2948 return MatchedReturn();
2950 if (eScannedType
!= SvNumFormatType::UNDEFINED
&&
2951 eScannedType
!= SvNumFormatType::DATE
) // already another type
2953 return MatchedReturn();
2955 if (nMonthStart
> 0) // 10Jan without separator is not a date
2957 eScannedType
= SvNumFormatType::DATE
;
2958 nMonth
= nTempMonth
;
2959 nMonthPos
= 3; // month at end
2962 SkipChar( '.', rString
, nPos
); // abbreviated
2964 SkipBlanks(rString
, nPos
);
2968 nPos
= nMonthStart
; // rewind month
2972 sal_Int32 nOrigPos
= nPos
;
2973 if (GetTimeAmPm(rString
, nPos
))
2975 if (eScannedType
!= SvNumFormatType::UNDEFINED
&&
2976 eScannedType
!= SvNumFormatType::TIME
&&
2977 eScannedType
!= SvNumFormatType::DATETIME
) // already another type
2979 return MatchedReturn();
2983 // If not already scanned as time, 6.78am does not result in 6
2984 // seconds and 78 hundredths in the morning. Keep as suffix.
2985 if (eScannedType
!= SvNumFormatType::TIME
&& nDecPos
== 2 && nNumericsCnt
== 2)
2987 nPos
= nOrigPos
; // rewind am/pm
2991 SkipBlanks(rString
, nPos
);
2992 if ( eScannedType
!= SvNumFormatType::DATETIME
)
2994 eScannedType
= SvNumFormatType::TIME
;
3000 if ( bNegCheck
&& SkipChar(')', rString
, nPos
) )
3002 if (eScannedType
== SvNumFormatType::CURRENCY
) // only if currency
3004 bNegCheck
= false; // skip ')'
3005 SkipBlanks(rString
, nPos
);
3009 return MatchedReturn();
3013 if ( nPos
< rString
.getLength() &&
3014 (eScannedType
== SvNumFormatType::DATE
||
3015 eScannedType
== SvNumFormatType::DATETIME
) )
3017 // day of week is just parsed away
3018 sal_Int32 nOldPos
= nPos
;
3019 const OUString
& rSep
= pFormatter
->GetLocaleData()->getLongDateDayOfWeekSep();
3020 if ( StringContains( rSep
, rString
, nPos
) )
3022 nPos
= nPos
+ rSep
.getLength();
3023 SkipBlanks(rString
, nPos
);
3025 int nTempDayOfWeek
= GetDayOfWeek( rString
, nPos
);
3026 if ( nTempDayOfWeek
)
3028 if ( nPos
< rString
.getLength() )
3030 if ( nTempDayOfWeek
< 0 )
3032 if ( rString
[ nPos
] == '.' )
3037 SkipBlanks(rString
, nPos
);
3046 #if NF_RECOGNIZE_ISO8601_TIMEZONES
3047 if (nPos
== 0 && eScannedType
== SvNumFormatType::DATETIME
&&
3048 rString
.getLength() == 1 && rString
[ 0 ] == 'Z' && MayBeIso8601())
3050 // ISO 8601 timezone UTC yyyy-mm-ddThh:mmZ
3055 if (nPos
< rString
.getLength()) // everything consumed?
3057 // does input EndString equal EndString in Format?
3058 if ( !ScanStringNumFor( rString
, nPos
, 0xFFFF ) )
3068 bool ImpSvNumberInputScan::ScanStringNumFor( const OUString
& rString
, // String to scan
3069 sal_Int32 nPos
, // Position until which was consumed
3070 sal_uInt16 nString
, // Substring of format, 0xFFFF => last
3071 bool bDontDetectNegation
) // Suppress sign detection
3077 const ::utl::TransliterationWrapper
* pTransliteration
= pFormatter
->GetTransliteration();
3078 const OUString
* pStr
;
3079 OUString
aString( rString
);
3080 bool bFound
= false;
3082 bool bContinue
= true;
3086 // Don't try "lower" subformats ff the very first match was the second
3087 // or third subformat.
3088 nSub
= nStringScanNumFor
;
3090 { // Step through subformats, first positive, then negative, then
3091 // other, but not the last (text) subformat.
3092 pStr
= mpFormat
->GetNumForString( nSub
, nString
, true );
3093 if ( pStr
&& pTransliteration
->isEqual( aString
, *pStr
) )
3098 else if ( nSub
< 2 )
3107 while ( bContinue
);
3108 if ( !bFound
&& bFirst
&& nPos
)
3110 // try remaining substring
3112 aString
= aString
.copy(nPos
);
3116 while ( bContinue
);
3120 if ( !bDontDetectNegation
&& (nString
== 0) &&
3121 !bFirst
&& (nSign
< 0) && mpFormat
->IsSecondSubformatRealNegative() )
3123 // simply negated twice? --1
3124 aString
= aString
.replaceAll(" ", "");
3125 if ( (aString
.getLength() == 1) && (aString
[0] == '-') )
3128 nStringScanSign
= -1;
3137 else if ( !bDontDetectNegation
&& (nSub
== 1) &&
3138 mpFormat
->IsSecondSubformatRealNegative() )
3141 if ( nStringScanSign
< 0 )
3143 if ( (nSign
< 0) && (nStringScanNumFor
!= 1) )
3145 nStringScanSign
= 1; // triple negated --1 yyy
3148 else if ( nStringScanSign
== 0 )
3151 { // nSign and nStringScanSign will be combined later,
3152 // flip sign if doubly negated
3153 if ( (nString
== 0) && !bFirst
&&
3154 SvNumberformat::HasStringNegativeSign( aString
) )
3156 nStringScanSign
= -1; // direct double negation
3158 else if ( mpFormat
->IsNegativeWithoutSign() )
3160 nStringScanSign
= -1; // indirect double negation
3165 nStringScanSign
= -1;
3170 nStringScanSign
= -1;
3173 nStringScanNumFor
= nSub
;
3179 * Recognizes types of number, exponential, fraction, percent, currency, date, time.
3180 * Else text => return false
3182 bool ImpSvNumberInputScan::IsNumberFormatMain( const OUString
& rString
, // string to be analyzed
3183 const SvNumberformat
* pFormat
) // maybe number format set to match against
3187 NumberStringDivision( rString
); // breakdown into strings and numbers
3188 if (nStringsCnt
>= SV_MAX_COUNT_INPUT_STRINGS
) // too many elements
3190 return false; // Njet, Nope, ...
3192 if (nNumericsCnt
== 0) // no number in input
3194 if ( nStringsCnt
> 0 )
3196 // Here we may change the original, we don't need it anymore.
3197 // This saves copies and ToUpper() in GetLogical() and is faster.
3198 sStrArray
[0] = comphelper::string::strip(sStrArray
[0], ' ');
3199 OUString
& rStrArray
= sStrArray
[0];
3200 nLogical
= GetLogical( rStrArray
);
3203 eScannedType
= SvNumFormatType::LOGICAL
; // !!! it's a BOOLEAN
3204 nMatchedAllStrings
&= ~nMatchedVirgin
;
3209 return false; // simple text
3214 return false; // simple text
3218 sal_uInt16 i
= 0; // mark any symbol
3219 sal_uInt16 j
= 0; // mark only numbers
3221 switch ( nNumericsCnt
)
3223 case 1 : // Exactly 1 number in input
3225 if (GetNextNumber(i
,j
)) // i=1,0
3226 { // Number at start
3227 if (eSetType
== SvNumFormatType::FRACTION
) // Fraction 1 = 1/1
3229 if (i
>= nStringsCnt
|| // no end string nor decimal separator
3230 pFormatter
->IsDecimalSep( sStrArray
[i
]))
3232 eScannedType
= SvNumFormatType::FRACTION
;
3233 nMatchedAllStrings
&= ~nMatchedVirgin
;
3239 { // Analyze start string
3240 if (!ScanStartString( sStrArray
[i
] )) // i=0
3242 return false; // already an error
3244 i
++; // next symbol, i=1
3246 GetNextNumber(i
,j
); // i=1,2
3247 if (eSetType
== SvNumFormatType::FRACTION
) // Fraction -1 = -1/1
3249 if (nSign
&& !bNegCheck
&& // Sign +, -
3250 eScannedType
== SvNumFormatType::UNDEFINED
&& // not date or currency
3251 nDecPos
== 0 && // no previous decimal separator
3252 (i
>= nStringsCnt
|| // no end string nor decimal separator
3253 pFormatter
->IsDecimalSep( sStrArray
[i
]))
3256 eScannedType
= SvNumFormatType::FRACTION
;
3257 nMatchedAllStrings
&= ~nMatchedVirgin
;
3261 if (i
< nStringsCnt
&& !ScanEndString( sStrArray
[i
] ))
3266 case 2 : // Exactly 2 numbers in input
3268 if (!GetNextNumber(i
,j
)) // i=1,0
3269 { // Analyze start string
3270 if (!ScanStartString( sStrArray
[i
] ))
3271 return false; // already an error
3274 GetNextNumber(i
,j
); // i=1,2
3275 if ( !ScanMidString( sStrArray
[i
], i
) )
3279 i
++; // next symbol, i=2,3
3280 GetNextNumber(i
,j
); // i=3,4
3281 if (i
< nStringsCnt
&& !ScanEndString( sStrArray
[i
] ))
3285 if (eSetType
== SvNumFormatType::FRACTION
) // -1,200. as fraction
3287 if (!bNegCheck
&& // no sign '('
3288 eScannedType
== SvNumFormatType::UNDEFINED
&&
3289 (nDecPos
== 0 || nDecPos
== 3) // no decimal separator or at end
3292 eScannedType
= SvNumFormatType::FRACTION
;
3293 nMatchedAllStrings
&= ~nMatchedVirgin
;
3298 case 3 : // Exactly 3 numbers in input
3300 if (!GetNextNumber(i
,j
)) // i=1,0
3301 { // Analyze start string
3302 if (!ScanStartString( sStrArray
[i
] ))
3304 return false; // already an error
3307 if (nDecPos
== 1) // decimal separator at start => error
3312 GetNextNumber(i
,j
); // i=1,2
3313 if ( !ScanMidString( sStrArray
[i
], i
) )
3318 if (eScannedType
== SvNumFormatType::SCIENTIFIC
) // E only at end
3322 GetNextNumber(i
,j
); // i=3,4
3323 if ( !ScanMidString( sStrArray
[i
], i
) )
3328 GetNextNumber(i
,j
); // i=5,6
3329 if (i
< nStringsCnt
&& !ScanEndString( sStrArray
[i
] ))
3333 if (eSetType
== SvNumFormatType::FRACTION
) // -1,200,100. as fraction
3335 if (!bNegCheck
&& // no sign '('
3336 eScannedType
== SvNumFormatType::UNDEFINED
&&
3337 (nDecPos
== 0 || nDecPos
== 3) // no decimal separator or at end
3340 eScannedType
= SvNumFormatType::FRACTION
;
3341 nMatchedAllStrings
&= ~nMatchedVirgin
;
3345 if ( eScannedType
== SvNumFormatType::FRACTION
&& nDecPos
)
3347 return false; // #36857# not a real fraction
3350 default: // More than 3 numbers in input
3352 if (!GetNextNumber(i
,j
)) // i=1,0
3353 { // Analyze startstring
3354 if (!ScanStartString( sStrArray
[i
] ))
3355 return false; // already an error
3357 if (nDecPos
== 1) // decimal separator at start => error
3360 GetNextNumber(i
,j
); // i=1,2
3361 if ( !ScanMidString( sStrArray
[i
], i
) )
3367 sal_uInt16 nThOld
= 10; // just not 0 or 1
3368 while (nThOld
!= nThousand
&& j
< nNumericsCnt
-1) // Execute at least one time
3369 // but leave one number.
3370 { // Loop over group separators
3372 if (eScannedType
== SvNumFormatType::SCIENTIFIC
) // E only at end
3377 if ( i
< nStringsCnt
&& !ScanMidString( sStrArray
[i
], i
) )
3384 if (eScannedType
== SvNumFormatType::DATE
|| // long date or
3385 eScannedType
== SvNumFormatType::TIME
|| // long time or
3386 eScannedType
== SvNumFormatType::UNDEFINED
) // long number
3388 for (sal_uInt16 k
= j
; k
< nNumericsCnt
-1; k
++)
3390 if (eScannedType
== SvNumFormatType::SCIENTIFIC
) // E only at endd
3395 if ( i
< nStringsCnt
&& !ScanMidString( sStrArray
[i
], i
) )
3403 if (i
< nStringsCnt
&& !ScanEndString( sStrArray
[i
] ))
3407 if (eSetType
== SvNumFormatType::FRACTION
) // -1,200,100. as fraction
3409 if (!bNegCheck
&& // no sign '('
3410 eScannedType
== SvNumFormatType::UNDEFINED
&&
3411 (nDecPos
== 0 || nDecPos
== 3) // no decimal separator or at end
3414 eScannedType
= SvNumFormatType::FRACTION
;
3415 nMatchedAllStrings
&= ~nMatchedVirgin
;
3419 if ( eScannedType
== SvNumFormatType::FRACTION
&& nDecPos
)
3421 return false; // #36857# not a real fraction
3426 if (eScannedType
== SvNumFormatType::UNDEFINED
)
3428 nMatchedAllStrings
&= ~nMatchedVirgin
;
3429 // did match including nMatchedUsedAsReturn
3430 bool bDidMatch
= (nMatchedAllStrings
!= 0);
3431 if ( nMatchedAllStrings
)
3433 bool bMatch
= mpFormat
&& mpFormat
->IsNumForStringElementCountEqual(
3434 nStringScanNumFor
, nStringsCnt
, nNumericsCnt
);
3437 nMatchedAllStrings
= 0;
3440 if ( nMatchedAllStrings
)
3442 // A type DEFINED means that no category could be assigned to the
3443 // overall format because of mixed type subformats. Use the scan
3444 // matched subformat's type if any.
3445 SvNumFormatType eForType
= eSetType
;
3446 if ((eForType
== SvNumFormatType::UNDEFINED
|| eForType
== SvNumFormatType::DEFINED
) && mpFormat
)
3447 eForType
= mpFormat
->GetNumForInfoScannedType( nStringScanNumFor
);
3448 if (eForType
!= SvNumFormatType::UNDEFINED
&& eForType
!= SvNumFormatType::DEFINED
)
3449 eScannedType
= eForType
;
3451 eScannedType
= SvNumFormatType::NUMBER
;
3453 else if ( bDidMatch
)
3459 eScannedType
= SvNumFormatType::NUMBER
;
3460 // everything else should have been recognized by now
3463 else if ( eScannedType
== SvNumFormatType::DATE
)
3465 // the very relaxed date input checks may interfere with a preset format
3466 nMatchedAllStrings
&= ~nMatchedVirgin
;
3467 bool bWasReturn
= ((nMatchedAllStrings
& nMatchedUsedAsReturn
) != 0);
3468 if ( nMatchedAllStrings
)
3470 bool bMatch
= mpFormat
&& mpFormat
->IsNumForStringElementCountEqual(
3471 nStringScanNumFor
, nStringsCnt
, nNumericsCnt
);
3474 nMatchedAllStrings
= 0;
3477 if ( nMatchedAllStrings
)
3479 // A type DEFINED means that no category could be assigned to the
3480 // overall format because of mixed type subformats. Do not override
3481 // the scanned type in this case. Otherwise in IsNumberFormat() the
3482 // first numeric particle would be accepted as number.
3483 SvNumFormatType eForType
= eSetType
;
3484 if ((eForType
== SvNumFormatType::UNDEFINED
|| eForType
== SvNumFormatType::DEFINED
) && mpFormat
)
3485 eForType
= mpFormat
->GetNumForInfoScannedType( nStringScanNumFor
);
3486 if (eForType
!= SvNumFormatType::UNDEFINED
&& eForType
!= SvNumFormatType::DEFINED
)
3487 eScannedType
= eForType
;
3489 else if ( bWasReturn
)
3496 nMatchedAllStrings
= 0; // reset flag to no substrings matched
3503 * Return true or false depending on the nMatched... state and remember usage
3505 bool ImpSvNumberInputScan::MatchedReturn()
3507 if ( nMatchedAllStrings
& ~nMatchedVirgin
)
3509 nMatchedAllStrings
|= nMatchedUsedAsReturn
;
3517 * Initialize uppercase months and weekdays
3519 void ImpSvNumberInputScan::InitText()
3521 sal_Int32 j
, nElems
;
3522 const CharClass
* pChrCls
= pFormatter
->GetCharClass();
3523 const CalendarWrapper
* pCal
= pFormatter
->GetCalendar();
3525 pUpperMonthText
.reset();
3526 pUpperAbbrevMonthText
.reset();
3527 css::uno::Sequence
< css::i18n::CalendarItem2
> xElems
= pCal
->getMonths();
3528 nElems
= xElems
.getLength();
3529 pUpperMonthText
.reset( new OUString
[nElems
] );
3530 pUpperAbbrevMonthText
.reset( new OUString
[nElems
] );
3531 for ( j
= 0; j
< nElems
; j
++ )
3533 pUpperMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].FullName
);
3534 pUpperAbbrevMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].AbbrevName
);
3537 pUpperGenitiveMonthText
.reset();
3538 pUpperGenitiveAbbrevMonthText
.reset();
3539 xElems
= pCal
->getGenitiveMonths();
3540 bScanGenitiveMonths
= (nElems
!= xElems
.getLength());
3541 nElems
= xElems
.getLength();
3542 pUpperGenitiveMonthText
.reset( new OUString
[nElems
] );
3543 pUpperGenitiveAbbrevMonthText
.reset( new OUString
[nElems
] );
3544 for ( j
= 0; j
< nElems
; j
++ )
3546 pUpperGenitiveMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].FullName
);
3547 pUpperGenitiveAbbrevMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].AbbrevName
);
3548 if (!bScanGenitiveMonths
&&
3549 (pUpperGenitiveMonthText
[j
] != pUpperMonthText
[j
] ||
3550 pUpperGenitiveAbbrevMonthText
[j
] != pUpperAbbrevMonthText
[j
]))
3552 bScanGenitiveMonths
= true;
3556 pUpperPartitiveMonthText
.reset();
3557 pUpperPartitiveAbbrevMonthText
.reset();
3558 xElems
= pCal
->getPartitiveMonths();
3559 bScanPartitiveMonths
= (nElems
!= xElems
.getLength());
3560 nElems
= xElems
.getLength();
3561 pUpperPartitiveMonthText
.reset( new OUString
[nElems
] );
3562 pUpperPartitiveAbbrevMonthText
.reset( new OUString
[nElems
] );
3563 for ( j
= 0; j
< nElems
; j
++ )
3565 pUpperPartitiveMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].FullName
);
3566 pUpperPartitiveAbbrevMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].AbbrevName
);
3567 if (!bScanPartitiveMonths
&&
3568 (pUpperPartitiveMonthText
[j
] != pUpperGenitiveMonthText
[j
] ||
3569 pUpperPartitiveAbbrevMonthText
[j
] != pUpperGenitiveAbbrevMonthText
[j
]))
3571 bScanPartitiveMonths
= true;
3575 pUpperDayText
.reset();
3576 pUpperAbbrevDayText
.reset();
3577 xElems
= pCal
->getDays();
3578 nElems
= xElems
.getLength();
3579 pUpperDayText
.reset( new OUString
[nElems
] );
3580 pUpperAbbrevDayText
.reset( new OUString
[nElems
] );
3581 for ( j
= 0; j
< nElems
; j
++ )
3583 pUpperDayText
[j
] = pChrCls
->uppercase( xElems
[j
].FullName
);
3584 pUpperAbbrevDayText
[j
] = pChrCls
->uppercase( xElems
[j
].AbbrevName
);
3587 bTextInitialized
= true;
3592 * MUST be called if International/Locale is changed
3594 void ImpSvNumberInputScan::ChangeIntl()
3596 sal_Unicode cDecSep
= pFormatter
->GetNumDecimalSep()[0];
3597 bDecSepInDateSeps
= ( cDecSep
== '-' ||
3598 cDecSep
== pFormatter
->GetDateSep()[0] );
3599 if (!bDecSepInDateSeps
)
3601 sal_Unicode cDecSepAlt
= pFormatter
->GetNumDecimalSepAlt().toChar();
3602 bDecSepInDateSeps
= cDecSepAlt
&& (cDecSepAlt
== '-' || cDecSepAlt
== pFormatter
->GetDateSep()[0]);
3604 bTextInitialized
= false;
3605 aUpperCurrSymbol
.clear();
3606 InvalidateDateAcceptancePatterns();
3610 void ImpSvNumberInputScan::InvalidateDateAcceptancePatterns()
3612 if (sDateAcceptancePatterns
.getLength())
3614 sDateAcceptancePatterns
= css::uno::Sequence
< OUString
>();
3619 void ImpSvNumberInputScan::ChangeNullDate( const sal_uInt16 Day
,
3620 const sal_uInt16 Month
,
3621 const sal_Int16 Year
)
3625 *pNullDate
= Date(Day
, Month
, Year
);
3629 pNullDate
.reset(new Date(Day
, Month
, Year
));
3635 * Does rString represent a number (also date, time et al)
3637 bool ImpSvNumberInputScan::IsNumberFormat( const OUString
& rString
, // string to be analyzed
3638 SvNumFormatType
& F_Type
, // IN: old type, OUT: new type
3639 double& fOutNumber
, // OUT: number if convertible
3640 const SvNumberformat
* pFormat
) // maybe a number format to match against
3643 bool res
; // return value
3645 eSetType
= F_Type
; // old type set
3647 if ( !rString
.getLength() )
3651 else if (rString
.getLength() > 308) // arbitrary
3657 // NoMoreUpperNeeded, all comparisons on UpperCase
3658 aString
= pFormatter
->GetCharClass()->uppercase( rString
);
3659 // convert native number to ASCII if necessary
3660 TransformInput(pFormatter
, aString
);
3661 res
= IsNumberFormatMain( aString
, pFormat
);
3666 // Accept signed date only for ISO date with at least four digits in
3667 // year to not have an input of -M-D-Y arbitrarily recognized. The
3668 // final order is only determined in GetDateRef().
3669 // Also accept for Y/M/D date pattern match, i.e. if the first number
3671 // Accept only if the year immediately follows the sign character with
3672 // no space in between.
3673 if (nSign
&& (eScannedType
== SvNumFormatType::DATE
||
3674 eScannedType
== SvNumFormatType::DATETIME
) && mbEraCE
== kDefaultEra
&&
3675 (IsDatePatternNumberOfType(0,'Y') || (MayBeIso8601() && sStrArray
[nNums
[0]].getLength() >= 4)))
3677 const sal_Unicode c
= sStrArray
[0][sStrArray
[0].getLength()-1];
3678 if (c
== '-' || c
== '+')
3680 // A '+' sign doesn't change the era.
3682 mbEraCE
= false; // BCE
3686 if ( bNegCheck
|| // ')' not found for '('
3687 (nSign
&& (eScannedType
== SvNumFormatType::DATE
||
3688 eScannedType
== SvNumFormatType::DATETIME
))) // signed date/datetime
3693 { // check count of partial number strings
3694 switch (eScannedType
)
3696 case SvNumFormatType::PERCENT
:
3697 case SvNumFormatType::CURRENCY
:
3698 case SvNumFormatType::NUMBER
:
3699 if (nDecPos
== 1) // .05
3701 // matched MidStrings function like group separators
3702 if ( nMatchedAllStrings
)
3704 nThousand
= nNumericsCnt
- 1;
3706 else if ( nNumericsCnt
!= 1 )
3711 else if (nDecPos
== 2) // 1.05
3713 // matched MidStrings function like group separators
3714 if ( nMatchedAllStrings
)
3716 nThousand
= nNumericsCnt
- 1;
3718 else if ( nNumericsCnt
!= nThousand
+2 )
3723 else // 1,100 or 1,100.
3725 // matched MidStrings function like group separators
3726 if ( nMatchedAllStrings
)
3728 nThousand
= nNumericsCnt
- 1;
3730 else if ( nNumericsCnt
!= nThousand
+1 )
3737 case SvNumFormatType::SCIENTIFIC
: // 1.0e-2
3738 if (nDecPos
== 1) // .05
3740 if (nNumericsCnt
!= 2)
3745 else if (nDecPos
== 2) // 1.05
3747 if (nNumericsCnt
!= nThousand
+3)
3752 else // 1,100 or 1,100.
3754 if (nNumericsCnt
!= nThousand
+2)
3761 case SvNumFormatType::DATE
:
3762 if (nMonth
< 0 && nDayOfWeek
< 0 && nNumericsCnt
== 3)
3764 // If both, short month name and day of week name were
3765 // detected, and also numbers for full date, assume that we
3766 // have a day of week instead of month name.
3771 { // month name and numbers
3772 if (nNumericsCnt
> 2)
3779 if (nNumericsCnt
> 3)
3785 // Even if a date pattern was matched, for abbreviated
3786 // pattern like "D.M." an input of "D.M. #" was
3787 // accepted because # could had been a time. Here we do
3788 // not have a combined date/time input though and #
3789 // would be taken as Year in this example, which it is
3790 // not. The count of numbers in pattern must match the
3791 // count of numbers in input.
3792 res
= (GetDatePatternNumbers() == nNumericsCnt
)
3793 || IsAcceptableIso8601() || nMatchedAllStrings
;
3798 case SvNumFormatType::TIME
:
3800 { // hundredth seconds included
3801 if (nNumericsCnt
> 4)
3808 if (nNumericsCnt
> 3)
3815 case SvNumFormatType::DATETIME
:
3816 if (nMonth
< 0 && nDayOfWeek
< 0 && nNumericsCnt
>= 5)
3818 // If both, abbreviated month name and day of week name
3819 // were detected, and also at least numbers for full date
3820 // plus time including minutes, assume that we have a day
3821 // of week instead of month name.
3826 { // month name and numbers
3828 { // hundredth seconds included
3829 if (nNumericsCnt
> 6)
3836 if (nNumericsCnt
> 5)
3845 { // hundredth seconds included
3846 if (nNumericsCnt
> 7)
3853 if (nNumericsCnt
> 6)
3860 res
= IsAcceptedDatePattern( nNums
[0]) || MayBeIso8601() || nMatchedAllStrings
;
3871 OUStringBuffer sResString
;
3874 { // we finally have a number
3875 switch (eScannedType
)
3877 case SvNumFormatType::LOGICAL
:
3880 fOutNumber
= 1.0; // True
3882 else if (nLogical
== -1)
3884 fOutNumber
= 0.0; // False
3888 res
= false; // Oops
3892 case SvNumFormatType::PERCENT
:
3893 case SvNumFormatType::CURRENCY
:
3894 case SvNumFormatType::NUMBER
:
3895 case SvNumFormatType::SCIENTIFIC
:
3896 case SvNumFormatType::DEFINED
: // if no category detected handle as number
3897 if ( nDecPos
== 1 ) // . at start
3899 sResString
.append("0.");
3902 for ( k
= 0; k
<= nThousand
; k
++)
3904 sResString
.append(sStrArray
[nNums
[k
]]); // integer part
3906 if ( nDecPos
== 2 && k
< nNumericsCnt
) // . somewhere
3908 sResString
.append('.');
3909 sal_uInt16 nStop
= (eScannedType
== SvNumFormatType::SCIENTIFIC
?
3910 nNumericsCnt
-1 : nNumericsCnt
);
3911 for ( ; k
< nStop
; k
++)
3913 sResString
.append(sStrArray
[nNums
[k
]]); // fractional part
3917 if (eScannedType
!= SvNumFormatType::SCIENTIFIC
)
3919 fOutNumber
= StringToDouble(sResString
.makeStringAndClear());
3922 { // append exponent
3923 sResString
.append('E');
3926 sResString
.append('-');
3928 sResString
.append(sStrArray
[nNums
[nNumericsCnt
-1]]);
3929 rtl_math_ConversionStatus eStatus
;
3930 fOutNumber
= ::rtl::math::stringToDouble( sResString
.makeStringAndClear(), '.', ',', &eStatus
);
3931 if ( eStatus
== rtl_math_ConversionStatus_OutOfRange
)
3933 F_Type
= SvNumFormatType::TEXT
; // overflow/underflow -> Text
3940 fOutNumber
= DBL_MAX
;
3946 if ( nStringScanSign
)
3950 nSign
*= nStringScanSign
;
3954 nSign
= nStringScanSign
;
3959 fOutNumber
= -fOutNumber
;
3962 if (eScannedType
== SvNumFormatType::PERCENT
)
3968 case SvNumFormatType::FRACTION
:
3969 if (nNumericsCnt
== 1)
3971 fOutNumber
= StringToDouble(sStrArray
[nNums
[0]]);
3973 else if (nNumericsCnt
== 2)
3977 sResString
= sStrArray
[nNums
[0]];
3978 sResString
.append(sStrArray
[nNums
[1]]); // integer part
3979 fOutNumber
= StringToDouble(sResString
.makeStringAndClear());
3983 double fNumerator
= StringToDouble(sStrArray
[nNums
[0]]);
3984 double fDenominator
= StringToDouble(sStrArray
[nNums
[1]]);
3985 if (fDenominator
!= 0.0)
3987 fOutNumber
= fNumerator
/fDenominator
;
3995 else // nNumericsCnt > 2
3998 sResString
= sStrArray
[nNums
[0]];
4001 for (; k
<= nThousand
; k
++)
4003 sResString
.append(sStrArray
[nNums
[k
]]);
4006 fOutNumber
= StringToDouble(sResString
.makeStringAndClear());
4008 if (k
== nNumericsCnt
-2)
4010 double fNumerator
= StringToDouble(sStrArray
[nNums
[k
]]);
4011 double fDenominator
= StringToDouble(sStrArray
[nNums
[k
+ 1]]);
4012 if (fDenominator
!= 0.0)
4014 fOutNumber
+= fNumerator
/fDenominator
;
4023 if ( nStringScanSign
)
4027 nSign
*= nStringScanSign
;
4031 nSign
= nStringScanSign
;
4036 fOutNumber
= -fOutNumber
;
4040 case SvNumFormatType::TIME
:
4041 res
= GetTimeRef(fOutNumber
, 0, nNumericsCnt
);
4044 fOutNumber
= -fOutNumber
;
4048 case SvNumFormatType::DATE
:
4049 res
= GetDateRef( fOutNumber
, k
);
4052 case SvNumFormatType::DATETIME
:
4053 res
= GetDateRef( fOutNumber
, k
);
4057 res
= GetTimeRef( fTime
, k
, nNumericsCnt
- k
);
4058 fOutNumber
+= fTime
;
4063 SAL_WARN( "svl.numbers", "Some number recognized but what's it?" );
4069 if (res
) // overflow/underflow -> Text
4071 if (fOutNumber
< -DBL_MAX
) // -1.7E308
4073 F_Type
= SvNumFormatType::TEXT
;
4074 fOutNumber
= -DBL_MAX
;
4077 else if (fOutNumber
> DBL_MAX
) // 1.7E308
4079 F_Type
= SvNumFormatType::TEXT
;
4080 fOutNumber
= DBL_MAX
;
4087 eScannedType
= SvNumFormatType::TEXT
;
4091 F_Type
= eScannedType
;
4095 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */