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 <o3tl/string_view.hxx>
25 #include <sal/log.hxx>
26 #include <tools/date.hxx>
27 #include <rtl/math.hxx>
28 #include <rtl/character.hxx>
29 #include <unotools/charclass.hxx>
30 #include <unotools/calendarwrapper.hxx>
31 #include <unotools/localedatawrapper.hxx>
32 #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
33 #include <com/sun/star/i18n/LocaleCalendar2.hpp>
34 #include <unotools/digitgroupingiterator.hxx>
35 #include <comphelper/sequence.hxx>
37 #include <svl/zforlist.hxx>
38 #include "zforscan.hxx"
39 #include <svl/zformat.hxx>
43 #include "zforfind.hxx"
46 #define NF_TEST_CALENDAR 0
48 #define NF_TEST_CALENDAR 0
51 #include <comphelper/processfactory.hxx>
52 #include <com/sun/star/i18n/XCalendar4.hpp>
56 const sal_uInt8
ImpSvNumberInputScan::nMatchedEndString
= 0x01;
57 const sal_uInt8
ImpSvNumberInputScan::nMatchedMidString
= 0x02;
58 const sal_uInt8
ImpSvNumberInputScan::nMatchedStartString
= 0x04;
59 const sal_uInt8
ImpSvNumberInputScan::nMatchedVirgin
= 0x08;
60 const sal_uInt8
ImpSvNumberInputScan::nMatchedUsedAsReturn
= 0x10;
62 /* It is not clear how we want timezones to be handled. Convert them to local
63 * time isn't wanted, as it isn't done in any other place and timezone
64 * information isn't stored anywhere. Ignoring them and pretending local time
65 * may be wrong too and might not be what the user expects. Keep the input as
66 * string so that no information is lost.
67 * Anyway, defining NF_RECOGNIZE_ISO8601_TIMEZONES to 1 would be the way how it
68 * would work, together with the nTimezonePos handling in GetTimeRef(). */
69 #define NF_RECOGNIZE_ISO8601_TIMEZONES 0
71 const sal_Unicode cNoBreakSpace
= 0xA0;
72 const sal_Unicode cNarrowNoBreakSpace
= 0x202F;
73 const bool kDefaultEra
= true; // Gregorian CE, positive year
75 ImpSvNumberInputScan::ImpSvNumberInputScan(SvNFLanguageData
& rCurrentLanguage
)
77 mrCurrentLanguageData(rCurrentLanguage
),
78 bTextInitialized( false ),
79 bScanGenitiveMonths( false ),
80 bScanPartitiveMonths( false ),
81 eScannedType( SvNumFormatType::UNDEFINED
),
82 eSetType( SvNumFormatType::UNDEFINED
)
84 moNullDate
.emplace( 30,12,1899 );
85 nYear2000
= SvNumberFormatter::GetYear2000Default();
90 ImpSvNumberInputScan::~ImpSvNumberInputScan()
95 void ImpSvNumberInputScan::Reset()
109 eScannedType
= SvNumFormatType::UNDEFINED
;
111 nPosThousandString
= 0;
113 mbEraCE
= kDefaultEra
;
114 nStringScanNumFor
= 0;
116 nMatchedAllStrings
= nMatchedVirgin
;
118 bIso8601Tsep
= false;
120 nAcceptedDatePattern
= -2;
121 nDatePatternStart
= 0;
122 nDatePatternNumbers
= 0;
124 for (sal_uInt32 i
= 0; i
< SV_MAX_COUNT_INPUT_STRINGS
; i
++)
131 // native number transliteration if necessary
132 static void TransformInput(const NativeNumberWrapper
& rNatNum
, const SvNFLanguageData
& rCurrentLanguage
, OUString
& rStr
)
134 sal_Int32 nPos
, nLen
;
135 for ( nPos
= 0, nLen
= rStr
.getLength(); nPos
< nLen
; ++nPos
)
137 if ( 256 <= rStr
[ nPos
] &&
138 rCurrentLanguage
.GetCharClass()->isDigit( rStr
, nPos
) )
145 rStr
= rNatNum
.getNativeNumberString(rStr
, rCurrentLanguage
.GetLanguageTag().getLocale(), 0);
151 * Only simple unsigned floating point values without any error detection,
152 * decimal separator has to be '.'
154 double ImpSvNumberInputScan::StringToDouble( std::u16string_view aStr
, bool bForceFraction
)
156 std::unique_ptr
<char[]> bufInHeap
;
157 constexpr int bufOnStackSize
= 256;
158 char bufOnStack
[bufOnStackSize
];
159 char* buf
= bufOnStack
;
160 const sal_Int32 bufsize
= aStr
.size() + (bForceFraction
? 2 : 1);
161 if (bufsize
> bufOnStackSize
)
163 bufInHeap
= std::make_unique
<char[]>(bufsize
);
164 buf
= bufInHeap
.get();
169 for (size_t nPos
= 0; nPos
< aStr
.size(); ++nPos
)
171 sal_Unicode c
= aStr
[nPos
];
172 if (c
== '.' || (c
>= '0' && c
<= '9'))
173 *p
++ = static_cast<char>(c
);
179 return strtod_nolocale(buf
, nullptr);
185 * Splits up the input into numbers and strings for further processing
186 * (by the Turing machine).
188 * Starting state = GetChar
189 * ---------------+-------------------+-----------------------------+---------------
190 * Old State | Character read | Event | New state
191 * ---------------+-------------------+-----------------------------+---------------
192 * GetChar | Number | Symbol = Character | GetValue
193 * | Else | Symbol = Character | GetString
194 * ---------------|-------------------+-----------------------------+---------------
195 * GetValue | Number | Symbol = Symbol + Character | GetValue
196 * | Else | Dec(CharPos) | Stop
197 * ---------------+-------------------+-----------------------------+---------------
198 * GetString | Number | Dec(CharPos) | Stop
199 * | Else | Symbol = Symbol + Character | GetString
200 * ---------------+-------------------+-----------------------------+---------------
202 enum ScanState
// States of the Turing machine
212 bool ImpSvNumberInputScan::NextNumberStringSymbol( const sal_Unicode
*& pStr
,
215 bool isNumber
= false;
217 ScanState eState
= SsStart
;
218 const sal_Unicode
* pHere
= pStr
;
219 sal_Int32 nChars
= 0;
224 if (cToken
== 0 || eState
== SsStop
)
230 if ( rtl::isAsciiDigit( cToken
) )
237 eState
= SsGetString
;
242 if ( rtl::isAsciiDigit( cToken
) )
253 if ( !rtl::isAsciiDigit( cToken
) )
270 rSymbol
= OUString( pStr
, nChars
);
283 // FIXME: should be grouping; it is only used though in case nStringsCnt is
284 // near SV_MAX_COUNT_INPUT_STRINGS, in NumberStringDivision().
286 bool ImpSvNumberInputScan::SkipThousands( const sal_Unicode
*& pStr
,
287 OUString
& rSymbol
) const
290 OUStringBuffer
sBuff(rSymbol
);
292 const OUString
& rThSep
= mrCurrentLanguageData
.GetNumThousandSep();
293 const sal_Unicode
* pHere
= pStr
;
294 ScanState eState
= SsStart
;
295 sal_Int32 nCounter
= 0; // counts 3 digits
300 if (cToken
== 0 || eState
== SsStop
)
306 if ( StringPtrContains( rThSep
, pHere
-1, 0 ) )
310 pHere
+= rThSep
.getLength() - 1;
319 if ( rtl::isAsciiDigit( cToken
) )
321 sBuff
.append(cToken
);
326 res
= true; // .000 combination found
340 if (eState
== SsGetValue
) // break with less than 3 digits
344 sBuff
.remove( sBuff
.getLength() - nCounter
, nCounter
);
346 pHere
-= nCounter
+ rThSep
.getLength(); // put back ThSep also
348 rSymbol
= sBuff
.makeStringAndClear();
355 void ImpSvNumberInputScan::NumberStringDivision( const OUString
& rString
)
357 const sal_Unicode
* pStr
= rString
.getStr();
358 const sal_Unicode
* const pEnd
= pStr
+ rString
.getLength();
359 while ( pStr
< pEnd
&& nStringsCnt
< SV_MAX_COUNT_INPUT_STRINGS
)
361 if ( NextNumberStringSymbol( pStr
, sStrArray
[nStringsCnt
] ) )
363 IsNum
[nStringsCnt
] = true;
364 nNums
[nNumericsCnt
] = nStringsCnt
;
366 if (nStringsCnt
>= SV_MAX_COUNT_INPUT_STRINGS
- 7 &&
367 nPosThousandString
== 0) // Only once
369 if ( SkipThousands( pStr
, sStrArray
[nStringsCnt
] ) )
371 nPosThousandString
= nStringsCnt
;
377 IsNum
[nStringsCnt
] = false;
385 * Whether rString contains rWhat at nPos
387 bool ImpSvNumberInputScan::StringContainsImpl( const OUString
& rWhat
,
388 const OUString
& rString
, sal_Int32 nPos
)
390 if ( nPos
+ rWhat
.getLength() <= rString
.getLength() )
392 return StringPtrContainsImpl( rWhat
, rString
.getStr(), nPos
);
399 * Whether pString contains rWhat at nPos
401 bool ImpSvNumberInputScan::StringPtrContainsImpl( const OUString
& rWhat
,
402 const sal_Unicode
* pString
, sal_Int32 nPos
)
404 if ( rWhat
.isEmpty() )
408 const sal_Unicode
* pWhat
= rWhat
.getStr();
409 const sal_Unicode
* const pEnd
= pWhat
+ rWhat
.getLength();
410 const sal_Unicode
* pStr
= pString
+ nPos
;
411 while ( pWhat
< pEnd
)
413 if ( *pWhat
!= *pStr
)
425 * Whether rString contains word rWhat at nPos
427 bool ImpSvNumberInputScan::StringContainsWord( const OUString
& rWhat
,
428 const OUString
& rString
, sal_Int32 nPos
) const
430 if (rWhat
.isEmpty() || rString
.getLength() < nPos
+ rWhat
.getLength())
433 if (StringPtrContainsImpl( rWhat
, rString
.getStr(), nPos
))
435 nPos
+= rWhat
.getLength();
436 if (nPos
== rString
.getLength())
437 return true; // word at end of string
439 /* TODO: we COULD invoke bells and whistles word break iterator to find
440 * the next boundary, but really ... this is called for date input, so
441 * how many languages do not separate the day and month names in some
444 // Check simple ASCII first before invoking i18n or anything else.
445 const sal_Unicode c
= rString
[nPos
];
447 // Common separating ASCII characters in date context.
459 if (rtl::isAsciiAlphanumeric( c
))
460 return false; // Alpha or numeric is not word gap.
462 sal_Int32 nIndex
= nPos
;
463 rString
.iterateCodePoints( &nIndex
);
465 return true; // Surrogate, assume these to be new words.
467 const sal_Int32 nType
= mrCurrentLanguageData
.GetCharClass()->getCharacterType( rString
, nPos
);
468 using namespace ::com::sun::star::i18n
;
470 if ((nType
& (KCharacterType::UPPER
| KCharacterType::LOWER
| KCharacterType::DIGIT
)) != 0)
471 return false; // Alpha or numeric is not word gap.
473 if (nType
& KCharacterType::LETTER
)
474 return true; // Letter other than alpha is new word. (Is it?)
476 return true; // Catch all remaining as gap until we know better.
484 * Skips the supplied char
486 inline bool ImpSvNumberInputScan::SkipChar( sal_Unicode c
, std::u16string_view rString
,
489 if ((nPos
< static_cast<sal_Int32
>(rString
.size())) && (rString
[nPos
] == c
))
501 inline bool ImpSvNumberInputScan::SkipBlanks( const OUString
& rString
,
504 sal_Int32 nHere
= nPos
;
505 if ( nPos
< rString
.getLength() )
507 const sal_Unicode
* p
= rString
.getStr() + nPos
;
508 while ( *p
== ' ' || *p
== cNoBreakSpace
|| *p
== cNarrowNoBreakSpace
)
519 * jump over rWhat in rString at nPos
521 inline bool ImpSvNumberInputScan::SkipString( const OUString
& rWhat
,
522 const OUString
& rString
, sal_Int32
& nPos
)
524 if ( StringContains( rWhat
, rString
, nPos
) )
526 nPos
= nPos
+ rWhat
.getLength();
534 * Recognizes exactly ,111 in {3} and {3,2} or ,11 in {3,2} grouping
536 inline bool ImpSvNumberInputScan::GetThousandSep( std::u16string_view rString
,
538 sal_uInt16 nStringPos
) const
540 const OUString
& rSep
= mrCurrentLanguageData
.GetNumThousandSep();
541 // Is it an ordinary space instead of a no-break space?
542 bool bSpaceBreak
= (rSep
[0] == cNoBreakSpace
|| rSep
[0] == cNarrowNoBreakSpace
) &&
543 rString
[0] == u
' ' &&
544 rSep
.getLength() == 1 && rString
.size() == 1;
545 if (!((rString
== rSep
|| bSpaceBreak
) && // nothing else
546 nStringPos
< nStringsCnt
- 1 && // safety first!
547 IsNum
[ nStringPos
+ 1 ] )) // number follows
549 return false; // no? => out
552 utl::DigitGroupingIterator
aGrouping( mrCurrentLanguageData
.GetLocaleData()->getDigitGrouping());
553 // Match ,### in {3} or ,## in {3,2}
554 /* FIXME: this could be refined to match ,## in {3,2} only if ,##,## or
555 * ,##,### and to match ,### in {3,2} only if it's the last. However,
556 * currently there is no track kept where group separators occur. In {3,2}
557 * #,###,### and #,##,## would be valid input, which maybe isn't even bad
558 * for #,###,###. Other combinations such as #,###,## maybe not. */
559 sal_Int32 nLen
= sStrArray
[ nStringPos
+ 1 ].getLength();
560 if (nLen
== aGrouping
.get() || // with 3 (or so) digits
561 nLen
== aGrouping
.advance().get() || // or with 2 (or 3 or so) digits
562 nPosThousandString
== nStringPos
+ 1 ) // or concatenated
564 nPos
= nPos
+ rSep
.getLength();
572 * Conversion of text to logical value
577 short ImpSvNumberInputScan::GetLogical( std::u16string_view rString
) const
581 const ImpSvNumberformatScan
* pFS
= mrCurrentLanguageData
.GetFormatScanner();
582 if ( rString
== pFS
->GetTrueString() )
586 else if ( rString
== pFS
->GetFalseString() )
599 * Converts a string containing a month name (JAN, January) at nPos into the
600 * month number (negative if abbreviated), returns 0 if nothing found
602 short ImpSvNumberInputScan::GetMonth( const OUString
& rString
, sal_Int32
& nPos
)
604 short res
= 0; // no month found
606 if (rString
.getLength() > nPos
) // only if needed
608 if ( !bTextInitialized
)
612 sal_Int16 nMonths
= mrCurrentLanguageData
.GetCalendar()->getNumberOfMonthsInYear();
613 for ( sal_Int16 i
= 0; i
< nMonths
; i
++ )
615 if ( bScanGenitiveMonths
&& StringContainsWord( pUpperGenitiveMonthText
[i
], rString
, nPos
) )
616 { // genitive full names first
617 nPos
= nPos
+ pUpperGenitiveMonthText
[i
].getLength();
621 else if ( bScanGenitiveMonths
&& StringContainsWord( pUpperGenitiveAbbrevMonthText
[i
], rString
, nPos
) )
622 { // genitive abbreviated
623 nPos
= nPos
+ pUpperGenitiveAbbrevMonthText
[i
].getLength();
624 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
627 else if ( bScanPartitiveMonths
&& StringContainsWord( pUpperPartitiveMonthText
[i
], rString
, nPos
) )
628 { // partitive full names
629 nPos
= nPos
+ pUpperPartitiveMonthText
[i
].getLength();
633 else if ( bScanPartitiveMonths
&& StringContainsWord( pUpperPartitiveAbbrevMonthText
[i
], rString
, nPos
) )
634 { // partitive abbreviated
635 nPos
= nPos
+ pUpperPartitiveAbbrevMonthText
[i
].getLength();
636 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
639 else if ( StringContainsWord( pUpperMonthText
[i
], rString
, nPos
) )
641 nPos
= nPos
+ pUpperMonthText
[i
].getLength();
645 else if ( StringContainsWord( pUpperAbbrevMonthText
[i
], rString
, nPos
) )
646 { // noun abbreviated
647 nPos
= nPos
+ pUpperAbbrevMonthText
[i
].getLength();
648 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
651 else if (i
== 2 && mrCurrentLanguageData
.GetLanguageTag().getLanguage() == "de")
653 if (pUpperAbbrevMonthText
[i
] == u
"M\u00C4R" && StringContainsWord( "MRZ", rString
, nPos
))
654 { // Accept MRZ for MÄR
656 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
659 else if (pUpperAbbrevMonthText
[i
] == "MRZ" && StringContainsWord( u
"M\u00C4R"_ustr
, rString
, nPos
))
660 { // And vice versa, accept MÄR for MRZ
662 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
668 // This assumes the weirdness is applicable to all locales.
669 // It is the case for at least en-* and de-* locales.
670 if (pUpperAbbrevMonthText
[i
] == "SEPT" && StringContainsWord( "SEP", rString
, nPos
))
671 { // #102136# The correct English form of month September abbreviated is
672 // SEPT, but almost every data contains SEP instead.
674 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
677 else if (pUpperAbbrevMonthText
[i
] == "SEP" && StringContainsWord( "SEPT", rString
, nPos
))
678 { // And vice versa, accept SEPT for SEP
680 res
= sal::static_int_cast
< short >(-(i
+1)); // negative
687 // Brutal hack for German locales that know "Januar" or "Jänner".
688 /* TODO: add alternative month names to locale data? if there are
689 * more languages... */
690 const LanguageTag
& rLanguageTag
= mrCurrentLanguageData
.GetLanguageTag();
691 if (rLanguageTag
.getLanguage() == "de")
693 if (rLanguageTag
.getCountry() == "AT")
695 // Locale data has Jänner/Jän
696 assert(pUpperMonthText
[0] == u
"J\u00C4NNER");
697 if (StringContainsWord( "JANUAR", rString
, nPos
))
702 else if (StringContainsWord( "JAN", rString
, nPos
))
710 // Locale data has Januar/Jan
711 assert(pUpperMonthText
[0] == "JANUAR");
712 if (StringContainsWord( u
"J\u00C4NNER"_ustr
, rString
, nPos
))
717 else if (StringContainsWord( u
"J\u00C4N"_ustr
, rString
, nPos
))
732 * Converts a string containing a DayOfWeek name (Mon, Monday) at nPos into the
733 * DayOfWeek number + 1 (negative if abbreviated), returns 0 if nothing found
735 int ImpSvNumberInputScan::GetDayOfWeek( const OUString
& rString
, sal_Int32
& nPos
)
737 int res
= 0; // no day found
739 if (rString
.getLength() > nPos
) // only if needed
741 if ( !bTextInitialized
)
745 sal_Int16 nDays
= mrCurrentLanguageData
.GetCalendar()->getNumberOfDaysInWeek();
746 for ( sal_Int16 i
= 0; i
< nDays
; i
++ )
748 if ( StringContainsWord( pUpperDayText
[i
], rString
, nPos
) )
749 { // full names first
750 nPos
= nPos
+ pUpperDayText
[i
].getLength();
754 if ( StringContainsWord( pUpperAbbrevDayText
[i
], rString
, nPos
) )
756 nPos
= nPos
+ pUpperAbbrevDayText
[i
].getLength();
757 res
= -(i
+ 1); // negative
768 * Reading a currency symbol
772 bool ImpSvNumberInputScan::GetCurrency( const OUString
& rString
, sal_Int32
& nPos
)
774 if ( rString
.getLength() > nPos
)
776 if ( !aUpperCurrSymbol
.getLength() )
777 { // If no format specified the currency of the currently active locale.
778 LanguageType eLang
= (mpFormat
? mpFormat
->GetLanguage() :
779 mrCurrentLanguageData
.GetLocaleData()->getLanguageTag().getLanguageType());
780 aUpperCurrSymbol
= mrCurrentLanguageData
.GetCharClass()->uppercase(
781 SvNumberFormatter::GetCurrencyEntry( eLang
).GetSymbol() );
783 if ( StringContains( aUpperCurrSymbol
, rString
, nPos
) )
785 nPos
= nPos
+ aUpperCurrSymbol
.getLength();
790 OUString aSymbol
, aExtension
;
791 if ( mpFormat
->GetNewCurrencySymbol( aSymbol
, aExtension
) )
793 if ( aSymbol
.getLength() <= rString
.getLength() - nPos
)
795 aSymbol
= mrCurrentLanguageData
.GetCharClass()->uppercase(aSymbol
);
796 if ( StringContains( aSymbol
, rString
, nPos
) )
798 nPos
= nPos
+ aSymbol
.getLength();
811 * Reading the time period specifier (AM/PM) for the 12 hour clock
814 * "AM" or "PM" => true
822 bool ImpSvNumberInputScan::GetTimeAmPm( const OUString
& rString
, sal_Int32
& nPos
)
825 if ( rString
.getLength() > nPos
)
827 const CharClass
* pChr
= mrCurrentLanguageData
.GetCharClass();
828 const LocaleDataWrapper
* pLoc
= mrCurrentLanguageData
.GetLocaleData();
829 if ( StringContains( pChr
->uppercase( pLoc
->getTimeAM() ), rString
, nPos
) )
832 nPos
= nPos
+ pLoc
->getTimeAM().getLength();
835 else if ( StringContains( pChr
->uppercase( pLoc
->getTimePM() ), rString
, nPos
) )
838 nPos
= nPos
+ pLoc
->getTimePM().getLength();
848 * Read a decimal separator (',')
852 inline bool ImpSvNumberInputScan::GetDecSep( std::u16string_view rString
, sal_Int32
& nPos
) const
854 if ( static_cast<sal_Int32
>(rString
.size()) > nPos
)
856 const OUString
& rSep
= mrCurrentLanguageData
.GetNumDecimalSep();
857 if ( o3tl::starts_with(rString
.substr(nPos
), rSep
) )
859 nPos
= nPos
+ rSep
.getLength();
862 const OUString
& rSepAlt
= mrCurrentLanguageData
.GetNumDecimalSepAlt();
863 if ( !rSepAlt
.isEmpty() && o3tl::starts_with(rString
.substr(nPos
), rSepAlt
) )
865 nPos
= nPos
+ rSepAlt
.getLength();
874 * Reading a hundredth seconds separator
876 inline bool ImpSvNumberInputScan::GetTime100SecSep( std::u16string_view rString
, sal_Int32
& nPos
) const
878 if ( static_cast<sal_Int32
>(rString
.size()) > nPos
)
882 // ISO 8601 specifies both '.' dot and ',' comma as fractional
884 if (rString
[nPos
] == '.' || rString
[nPos
] == ',')
890 // Even in an otherwise ISO 8601 string be lenient and accept the
891 // locale defined separator.
892 const OUString
& rSep
= mrCurrentLanguageData
.GetLocaleData()->getTime100SecSep();
893 if ( o3tl::starts_with(rString
.substr(nPos
), rSep
))
895 nPos
= nPos
+ rSep
.getLength();
904 * Read a sign including brackets
908 * '(' => -1, bNegCheck = 1
911 int ImpSvNumberInputScan::GetSign( std::u16string_view rString
, sal_Int32
& nPos
)
913 if (static_cast<sal_Int32
>(rString
.size()) > nPos
)
914 switch (rString
[ nPos
])
919 case '(': // '(' similar to '-' ?!?
923 // tdf#117037 - unicode minus (0x2212)
936 * Read a sign with an exponent
941 short ImpSvNumberInputScan::GetESign( std::u16string_view rString
, sal_Int32
& nPos
)
943 if (static_cast<sal_Int32
>(rString
.size()) > nPos
)
945 switch (rString
[nPos
])
962 * i counts string portions, j counts numbers thereof.
963 * It should had been called SkipNumber instead.
965 inline bool ImpSvNumberInputScan::GetNextNumber( sal_uInt16
& i
, sal_uInt16
& j
) const
967 if ( i
< nStringsCnt
&& IsNum
[i
] )
977 bool ImpSvNumberInputScan::GetTimeRef( double& fOutNumber
,
978 sal_uInt16 nIndex
, // j-value of the first numeric time part of input, default 0
979 sal_uInt16 nCnt
, // count of numeric time parts
980 SvNumInputOptions eInputOptions
985 sal_Int32 nMinute
= 0;
986 sal_Int32 nSecond
= 0;
987 double fSecond100
= 0.0;
988 sal_uInt16 nStartIndex
= nIndex
;
990 if (nDecPos
== 2 && (nCnt
== 3 || nCnt
== 2)) // 20:45.5 or 45.5
994 else if (mpFormat
&& nDecPos
== 0 && nCnt
== 2 && mpFormat
->IsMinuteSecondFormat())
996 // Input on MM:SS format, instead of doing HH:MM:00
999 else if (nIndex
- nStartIndex
< nCnt
)
1001 const OUString
& rValStr
= sStrArray
[nNums
[nIndex
++]];
1002 nHour
= rValStr
.toInt32();
1003 if (nHour
== 0 && rValStr
!= "0" && rValStr
!= "00")
1004 bRet
= false; // overflow -> Text
1010 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetTimeRef: bad number index");
1013 // 0:123 or 0:0:123 or 0:123:59 is valid
1014 bool bAllowDuration
= (nHour
== 0 && !nAmPm
);
1016 if (nAmPm
&& nHour
> 12) // not a valid AM/PM clock time
1020 else if (nAmPm
== -1 && nHour
!= 12) // PM
1024 else if (nAmPm
== 1 && nHour
== 12) // 12 AM
1029 if (nDecPos
== 2 && nCnt
== 2) // 45.5
1033 else if (nIndex
- nStartIndex
< nCnt
)
1035 const OUString
& rValStr
= sStrArray
[nNums
[nIndex
++]];
1036 nMinute
= rValStr
.toInt32();
1037 if (nMinute
== 0 && rValStr
!= "0" && rValStr
!= "00")
1038 bRet
= false; // overflow -> Text
1039 if (!(eInputOptions
& SvNumInputOptions::LAX_TIME
) && !bAllowDuration
1040 && nIndex
> 1 && nMinute
> 59)
1041 bRet
= false; // 1:60 or 1:123 is invalid, 123:1 or 0:123 is valid
1043 bAllowDuration
= (nMinute
== 0);
1045 if (nIndex
- nStartIndex
< nCnt
)
1047 const OUString
& rValStr
= sStrArray
[nNums
[nIndex
++]];
1048 nSecond
= rValStr
.toInt32();
1049 if (nSecond
== 0 && rValStr
!= "0" && rValStr
!= "00")
1050 bRet
= false; // overflow -> Text
1051 if (!(eInputOptions
& SvNumInputOptions::LAX_TIME
) && !bAllowDuration
1052 && nIndex
> 1 && nSecond
> 59 && !(nHour
== 23 && nMinute
== 59 && nSecond
== 60))
1053 bRet
= false; // 1:60 or 1:123 or 1:1:123 is invalid, 123:1 or 123:1:1 or 0:0:123 is valid, or leap second
1055 if (nIndex
- nStartIndex
< nCnt
)
1057 fSecond100
= StringToDouble( sStrArray
[nNums
[nIndex
]], true );
1059 fOutNumber
= (static_cast<double>(nHour
)*3600 +
1060 static_cast<double>(nMinute
)*60 +
1061 static_cast<double>(nSecond
) +
1062 fSecond100
)/86400.0;
1067 sal_uInt16
ImpSvNumberInputScan::ImplGetDay( sal_uInt16 nIndex
) const
1069 sal_uInt16 nRes
= 0;
1071 if (sStrArray
[nNums
[nIndex
]].getLength() <= 2)
1073 sal_uInt16 nNum
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
]].toInt32());
1084 sal_uInt16
ImpSvNumberInputScan::ImplGetMonth( sal_uInt16 nIndex
) const
1086 // Preset invalid month number
1087 sal_uInt16 nRes
= mrCurrentLanguageData
.GetCalendar()->getNumberOfMonthsInYear();
1089 if (sStrArray
[nNums
[nIndex
]].getLength() <= 2)
1091 sal_uInt16 nNum
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
]].toInt32());
1092 if ( 0 < nNum
&& nNum
<= nRes
)
1094 nRes
= nNum
- 1; // zero based for CalendarFieldIndex::MONTH
1103 * 30 -> 1930, 29 -> 2029, or 56 -> 1756, 55 -> 1855, ...
1105 sal_uInt16
ImpSvNumberInputScan::ImplGetYear( sal_uInt16 nIndex
)
1107 sal_uInt16 nYear
= 0;
1109 sal_Int32 nLen
= sStrArray
[nNums
[nIndex
]].getLength();
1110 // 16-bit integer year width can have 5 digits, allow for one additional
1111 // leading zero as convention.
1114 nYear
= static_cast<sal_uInt16
>(sStrArray
[nNums
[nIndex
]].toInt32());
1115 // A year in another, not Gregorian CE era is never expanded.
1116 // A year < 100 entered with at least 3 digits with leading 0 is taken
1117 // as is without expansion.
1118 if (mbEraCE
== kDefaultEra
&& nYear
< 100 && nLen
< 3)
1120 nYear
= SvNumberFormatter::ExpandTwoDigitYear( nYear
, nYear2000
);
1128 bool ImpSvNumberInputScan::MayBeIso8601()
1130 if (nMayBeIso8601
== 0)
1133 sal_Int32 nLen
= ((nNumericsCnt
>= 1 && nNums
[0] < nStringsCnt
) ? sStrArray
[nNums
[0]].getLength() : 0);
1137 if (nNumericsCnt
>= 3 && nNums
[2] < nStringsCnt
&&
1138 sStrArray
[nNums
[0]+1] == "-" && // separator year-month
1139 (n
= sStrArray
[nNums
[1]].toInt32()) >= 1 && n
<= 12 && // month
1140 sStrArray
[nNums
[1]+1] == "-" && // separator month-day
1141 (n
= sStrArray
[nNums
[2]].toInt32()) >= 1 && n
<= 31) // day
1143 // Year (nNums[0]) value not checked, may be anything, but
1144 // length (number of digits) is checked.
1145 nMayBeIso8601
= (nLen
>= 4 ? 4 : (nLen
== 3 ? 3 : (nLen
> 0 ? 2 : 1)));
1149 return nMayBeIso8601
> 1;
1153 bool ImpSvNumberInputScan::CanForceToIso8601( DateOrder eDateOrder
)
1155 int nCanForceToIso8601
= 0;
1156 if (!MayBeIso8601())
1160 else if (nMayBeIso8601
>= 3)
1162 return true; // at least 3 digits in year
1166 if (eDateOrder
== DateOrder::Invalid
)
1168 // As if any of the cases below can be applied, but only if a
1169 // locale dependent date pattern was not matched.
1170 if ((GetDatePatternNumbers() == nNumericsCnt
) && IsDatePatternNumberOfType(0,'Y'))
1172 eDateOrder
= GetDateOrder();
1175 nCanForceToIso8601
= 1;
1181 case DateOrder::DMY
: // "day" value out of range => ISO 8601 year
1182 n
= sStrArray
[nNums
[0]].toInt32();
1183 if (n
< 1 || n
> 31)
1185 nCanForceToIso8601
= 2;
1188 case DateOrder::MDY
: // "month" value out of range => ISO 8601 year
1189 n
= sStrArray
[nNums
[0]].toInt32();
1190 if (n
< 1 || n
> 12)
1192 nCanForceToIso8601
= 2;
1195 case DateOrder::YMD
: // always possible
1196 nCanForceToIso8601
= 2;
1200 return nCanForceToIso8601
> 1;
1204 bool ImpSvNumberInputScan::IsAcceptableIso8601()
1206 if (mpFormat
&& (mpFormat
->GetType() & SvNumFormatType::DATE
))
1208 switch (mrCurrentLanguageData
.GetEvalDateFormat())
1210 case NF_EVALDATEFORMAT_INTL
:
1211 return CanForceToIso8601( GetDateOrder());
1212 case NF_EVALDATEFORMAT_FORMAT
:
1213 return CanForceToIso8601( mpFormat
->GetDateOrder());
1215 return CanForceToIso8601( GetDateOrder()) || CanForceToIso8601( mpFormat
->GetDateOrder());
1218 return CanForceToIso8601( GetDateOrder());
1222 bool ImpSvNumberInputScan::MayBeMonthDate()
1224 if (nMayBeMonthDate
== 0)
1226 nMayBeMonthDate
= 1;
1227 if (nNumericsCnt
>= 2 && nNums
[1] < nStringsCnt
)
1230 const OUString
& rM
= sStrArray
[ nNums
[ 0 ] + 1 ];
1231 if (rM
.getLength() >= 3 && rM
[0] == '-' && rM
[ rM
.getLength() - 1] == '-')
1233 // Check year length assuming at least 3 digits (including
1234 // leading zero). Two digit years 1..31 are out of luck here
1235 // and may be taken as day of month.
1236 bool bYear1
= (sStrArray
[nNums
[0]].getLength() >= 3);
1237 bool bYear2
= (sStrArray
[nNums
[1]].getLength() >= 3);
1239 bool bDay1
= !bYear1
;
1242 n
= sStrArray
[nNums
[0]].toInt32();
1243 bDay1
= n
>= 1 && n
<= 31;
1245 bool bDay2
= !bYear2
;
1248 n
= sStrArray
[nNums
[1]].toInt32();
1249 bDay2
= n
>= 1 && n
<= 31;
1252 if (bDay1
&& !bDay2
)
1254 nMayBeMonthDate
= 2; // dd-month-yy
1256 else if (!bDay1
&& bDay2
)
1258 nMayBeMonthDate
= 3; // yy-month-dd
1260 else if (bDay1
&& bDay2
)
1262 // Ambiguous ##-MMM-## date, but some big vendor's database
1263 // reports write this crap, assume this always to be
1264 nMayBeMonthDate
= 2; // dd-month-yy
1269 return nMayBeMonthDate
> 1;
1273 /** If a string is a separator plus '-' minus sign preceding a 'Y' year in
1274 a date pattern at position nPat.
1276 static bool lcl_IsSignedYearSep( std::u16string_view rStr
, std::u16string_view rPat
, sal_Int32 nPat
)
1279 sal_Int32 nLen
= rStr
.size();
1280 if (nLen
> 1 && rStr
[nLen
-1] == '-')
1283 if (nPat
+ nLen
< static_cast<sal_Int32
>(rPat
.size()) && rPat
[nPat
+nLen
] == 'Y')
1285 // Signed year is possible.
1286 bOk
= (rPat
.find( rStr
.substr( 0, nLen
), nPat
) == static_cast<size_t>(nPat
));
1293 /** Length of separator usually is 1 but theoretically could be anything. */
1294 static sal_Int32
lcl_getPatternSeparatorLength( std::u16string_view rPat
, sal_Int32 nPat
)
1296 sal_Int32 nSep
= nPat
;
1298 while (nSep
< static_cast<sal_Int32
>(rPat
.size()) && (c
= rPat
[nSep
]) != 'D' && c
!= 'M' && c
!= 'Y')
1304 bool ImpSvNumberInputScan::IsAcceptedDatePattern( sal_uInt16 nStartPatternAt
)
1306 if (nAcceptedDatePattern
>= -1)
1308 return (nAcceptedDatePattern
>= 0);
1312 nAcceptedDatePattern
= -1;
1314 else if (!sDateAcceptancePatterns
.hasElements())
1316 // The current locale is the format's locale, if a format is present.
1317 const NfEvalDateFormat eEDF
= mrCurrentLanguageData
.GetEvalDateFormat();
1318 if (!mpFormat
|| eEDF
== NF_EVALDATEFORMAT_FORMAT
|| mpFormat
->GetLanguage() == mrCurrentLanguageData
.GetIniLanguage())
1320 sDateAcceptancePatterns
= mrCurrentLanguageData
.GetLocaleData()->getDateAcceptancePatterns();
1324 OnDemandLocaleDataWrapper
& xLocaleData
= mrCurrentLanguageData
.GetOnDemandLocaleDataWrapper(
1325 SvNFLanguageData::InputScannerPrivateAccess());
1326 const LanguageTag
aSaveLocale( xLocaleData
->getLanguageTag() );
1327 assert(mpFormat
->GetLanguage() == aSaveLocale
.getLanguageType()); // prerequisite
1328 // Obtain formatter's locale's (e.g. system) patterns.
1329 xLocaleData
.changeLocale( LanguageTag( mrCurrentLanguageData
.GetIniLanguage()));
1330 const css::uno::Sequence
<OUString
> aLocalePatterns( xLocaleData
->getDateAcceptancePatterns());
1331 // Reset to format's locale.
1332 xLocaleData
.changeLocale( aSaveLocale
);
1333 // When concatenating don't care about duplicates, combining
1334 // weeding those out reallocs yet another time and probably doesn't
1335 // take less time than looping over two additional patterns below...
1338 case NF_EVALDATEFORMAT_FORMAT
:
1339 assert(!"shouldn't reach here");
1341 case NF_EVALDATEFORMAT_INTL
:
1342 sDateAcceptancePatterns
= aLocalePatterns
;
1344 case NF_EVALDATEFORMAT_INTL_FORMAT
:
1345 sDateAcceptancePatterns
= comphelper::concatSequences(
1347 xLocaleData
->getDateAcceptancePatterns());
1349 case NF_EVALDATEFORMAT_FORMAT_INTL
:
1350 sDateAcceptancePatterns
= comphelper::concatSequences(
1351 xLocaleData
->getDateAcceptancePatterns(),
1356 SAL_WARN_IF( !sDateAcceptancePatterns
.hasElements(), "svl.numbers", "ImpSvNumberInputScan::IsAcceptedDatePattern: no date acceptance patterns");
1357 nAcceptedDatePattern
= (sDateAcceptancePatterns
.hasElements() ? -2 : -1);
1360 if (nAcceptedDatePattern
== -1)
1364 nDatePatternStart
= nStartPatternAt
; // remember start particle
1366 const sal_Int32 nMonthsInYear
= mrCurrentLanguageData
.GetCalendar()->getNumberOfMonthsInYear();
1368 for (sal_Int32 nPattern
=0; nPattern
< sDateAcceptancePatterns
.getLength(); ++nPattern
)
1370 const OUString
& rPat
= sDateAcceptancePatterns
[nPattern
];
1371 if (rPat
.getLength() == 3)
1373 // Ignore a pattern that would match numeric input with decimal
1374 // separator. It may had been read from configuration or resulted
1375 // from the locales' patterns concatenation above.
1376 if ( rPat
[1] == mrCurrentLanguageData
.GetLocaleData()->getNumDecimalSep().toChar()
1377 || rPat
[1] == mrCurrentLanguageData
.GetLocaleData()->getNumDecimalSepAlt().toChar())
1379 SAL_WARN("svl.numbers", "ignoring date acceptance pattern with decimal separator ambiguity: " << rPat
);
1380 continue; // for, next pattern
1383 sal_uInt16 nNext
= nDatePatternStart
;
1384 nDatePatternNumbers
= 0;
1387 for ( ; nPat
< rPat
.getLength() && bOk
&& nNext
< nStringsCnt
; ++nPat
, ++nNext
)
1389 const sal_Unicode c
= rPat
[nPat
];
1396 if (bOk
&& (c
== 'M' || c
== 'D'))
1398 // Check the D and M cases for plausibility. This also
1399 // prevents recognition of date instead of number with a
1400 // numeric group input if date separator is identical to
1401 // group separator, for example with D.M as a pattern and
1402 // #.### as a group.
1403 sal_Int32 nMaxLen
, nMaxVal
;
1408 nMaxVal
= nMonthsInYear
;
1415 // This merely exists against
1416 // -Werror=maybe-uninitialized, which is nonsense
1417 // after the (c == 'M' || c == 'D') check above,
1422 bOk
= (sStrArray
[nNext
].getLength() <= nMaxLen
);
1425 sal_Int32 nNum
= sStrArray
[nNext
].toInt32();
1426 bOk
= (1 <= nNum
&& nNum
<= nMaxVal
);
1430 ++nDatePatternNumbers
;
1433 bOk
= !IsNum
[nNext
];
1436 const sal_Int32 nSepLen
= lcl_getPatternSeparatorLength( rPat
, nPat
);
1437 // Non-numeric input must match separator exactly to be
1438 // accepted as such.
1439 const sal_Int32 nLen
= sStrArray
[nNext
].getLength();
1440 bOk
= (nLen
== nSepLen
&& rPat
.indexOf( sStrArray
[nNext
], nPat
) == nPat
);
1445 else if ((bOk
= lcl_IsSignedYearSep( sStrArray
[nNext
], rPat
, nPat
)))
1449 else if (nPat
+ nLen
> rPat
.getLength() && sStrArray
[nNext
][ nLen
- 1 ] == ' ')
1451 using namespace comphelper::string
;
1452 // Trailing blanks in input.
1453 OUStringBuffer
aBuf(sStrArray
[nNext
]);
1455 // Expand again in case of pattern "M. D. " and
1456 // input "M. D. ", maybe fetched far, but...
1457 padToLength(aBuf
, rPat
.getLength() - nPat
, ' ');
1458 bOk
= (rPat
.indexOf( aBuf
, nPat
) == nPat
);
1461 nPat
+= aBuf
.getLength() - 1;
1470 // Check for trailing characters mismatch.
1471 if (nNext
< nStringsCnt
)
1473 // Pattern end but not input end.
1474 // A trailing blank may be part of the current pattern input,
1475 // if pattern is "D.M." and input is "D.M. hh:mm" last was
1476 // ". ", or may be following the current pattern input, if
1477 // pattern is "D.M" and input is "D.M hh:mm" last was "M".
1480 if (nPat
> 0 && nNext
> 0)
1482 // nPat is one behind after the for loop.
1483 sal_Int32 nPatCheck
= nPat
- 1;
1484 switch (rPat
[nPatCheck
])
1494 // Advance position in input to match length of
1495 // non-YMD (separator) characters in pattern.
1500 c
= rPat
[--nPatCheck
];
1501 } while (c
!= 'Y' && c
!= 'M' && c
!= 'D' && nPatCheck
> 0);
1511 // Trailing (or separating if time follows) blanks are ok.
1512 // No blank and a following number is not.
1513 const bool bBlanks
= SkipBlanks( sStrArray
[nCheck
], nPos
);
1514 if (nPos
== sStrArray
[nCheck
].getLength() && (bBlanks
|| !IsNum
[nNext
]))
1516 nAcceptedDatePattern
= nPattern
;
1521 else if (nPat
== rPat
.getLength())
1523 // Input end and pattern end => match.
1524 nAcceptedDatePattern
= nPattern
;
1527 // else Input end but not pattern end, no match.
1530 nAcceptedDatePattern
= -1;
1535 bool ImpSvNumberInputScan::SkipDatePatternSeparator( sal_uInt16 nParticle
, sal_Int32
& rPos
, bool & rSignedYear
)
1537 // If not initialized yet start with first number, if any.
1538 if (!IsAcceptedDatePattern( nNumericsCnt
? nNums
[0] : 0 ))
1542 if (nParticle
< nDatePatternStart
|| nParticle
>= nStringsCnt
|| IsNum
[nParticle
])
1546 sal_uInt16 nNext
= nDatePatternStart
;
1547 const OUString
& rPat
= sDateAcceptancePatterns
[nAcceptedDatePattern
];
1548 for (sal_Int32 nPat
= 0; nPat
< rPat
.getLength() && nNext
< nStringsCnt
; ++nPat
, ++nNext
)
1557 if (nNext
== nParticle
)
1559 const sal_Int32 nSepLen
= lcl_getPatternSeparatorLength( rPat
, nPat
);
1560 const sal_Int32 nLen
= sStrArray
[nNext
].getLength();
1561 bool bOk
= (nLen
== nSepLen
&& rPat
.indexOf( sStrArray
[nNext
], nPat
) == nPat
);
1564 bOk
= lcl_IsSignedYearSep( sStrArray
[nNext
], rPat
, nPat
);
1568 if (!bOk
&& (nPat
+ nLen
> rPat
.getLength() && sStrArray
[nNext
][ nLen
- 1 ] == ' '))
1570 // The same ugly trailing blanks check as in
1571 // IsAcceptedDatePattern().
1572 using namespace comphelper::string
;
1573 OUStringBuffer
aBuf(sStrArray
[nNext
]);
1575 padToLength(aBuf
, rPat
.getLength() - nPat
, ' ');
1576 bOk
= (rPat
.indexOf(aBuf
, nPat
) == nPat
);
1580 rPos
= nLen
; // yes, set, not add!
1586 nPat
+= sStrArray
[nNext
].getLength() - 1;
1594 sal_uInt16
ImpSvNumberInputScan::GetDatePatternNumbers()
1596 // If not initialized yet start with first number, if any.
1597 if (!IsAcceptedDatePattern( nNumericsCnt
? nNums
[0] : 0 ))
1601 return nDatePatternNumbers
;
1605 bool ImpSvNumberInputScan::IsDatePatternNumberOfType( sal_uInt16 nNumber
, sal_Unicode cType
)
1607 if (GetDatePatternNumbers() <= nNumber
)
1610 sal_uInt16 nNum
= 0;
1611 const OUString
& rPat
= sDateAcceptancePatterns
[nAcceptedDatePattern
];
1612 for (sal_Int32 nPat
= 0; nPat
< rPat
.getLength(); ++nPat
)
1619 if (nNum
== nNumber
)
1620 return rPat
[nPat
] == cType
;
1629 sal_uInt32
ImpSvNumberInputScan::GetDatePatternOrder()
1631 // If not initialized yet start with first number, if any.
1632 if (!IsAcceptedDatePattern( nNumericsCnt
? nNums
[0] : 0 ))
1636 sal_uInt32 nOrder
= 0;
1637 const OUString
& rPat
= sDateAcceptancePatterns
[nAcceptedDatePattern
];
1638 for (sal_Int32 nPat
= 0; nPat
< rPat
.getLength() && !(nOrder
& 0xff0000); ++nPat
)
1645 nOrder
= (nOrder
<< 8) | rPat
[nPat
];
1653 DateOrder
ImpSvNumberInputScan::GetDateOrder( bool bFromFormatIfNoPattern
)
1655 sal_uInt32 nOrder
= GetDatePatternOrder();
1658 if (bFromFormatIfNoPattern
&& mpFormat
)
1659 return mpFormat
->GetDateOrder();
1661 return mrCurrentLanguageData
.GetLocaleData()->getDateOrder();
1663 switch ((nOrder
& 0xff0000) >> 16)
1666 if ((((nOrder
& 0xff00) >> 8) == 'M') && ((nOrder
& 0xff) == 'D'))
1668 return DateOrder::YMD
;
1672 if ((((nOrder
& 0xff00) >> 8) == 'D') && ((nOrder
& 0xff) == 'Y'))
1674 return DateOrder::MDY
;
1678 if ((((nOrder
& 0xff00) >> 8) == 'M') && ((nOrder
& 0xff) == 'Y'))
1680 return DateOrder::DMY
;
1685 switch ((nOrder
& 0xff00) >> 8)
1688 switch (nOrder
& 0xff)
1691 return DateOrder::YMD
;
1695 switch (nOrder
& 0xff)
1698 return DateOrder::DMY
;
1700 return DateOrder::MDY
;
1704 switch (nOrder
& 0xff)
1707 return DateOrder::MDY
;
1709 return DateOrder::DMY
;
1714 switch (nOrder
& 0xff)
1717 return DateOrder::YMD
;
1719 return DateOrder::MDY
;
1721 return DateOrder::DMY
;
1726 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateOrder: undefined, falling back to locale's default");
1727 return mrCurrentLanguageData
.GetLocaleData()->getDateOrder();
1730 LongDateOrder
ImpSvNumberInputScan::GetMiddleMonthLongDateOrder( bool bFormatTurn
,
1731 const LocaleDataWrapper
* pLoc
,
1732 DateOrder eDateOrder
)
1734 if (MayBeMonthDate())
1735 return (nMayBeMonthDate
== 2) ? LongDateOrder::DMY
: LongDateOrder::YMD
;
1738 const sal_uInt32 nExactDateOrder
= (bFormatTurn
? mpFormat
->GetExactDateOrder() : 0);
1739 if (!nExactDateOrder
)
1740 eLDO
= pLoc
->getLongDateOrder();
1741 else if ((((nExactDateOrder
>> 16) & 0xff) == 'Y') && ((nExactDateOrder
& 0xff) == 'D'))
1742 eLDO
= LongDateOrder::YMD
;
1743 else if ((((nExactDateOrder
>> 16) & 0xff) == 'D') && ((nExactDateOrder
& 0xff) == 'Y'))
1744 eLDO
= LongDateOrder::DMY
;
1746 eLDO
= pLoc
->getLongDateOrder();
1747 if (eLDO
!= LongDateOrder::YMD
&& eLDO
!= LongDateOrder::DMY
)
1751 case DateOrder::YMD
:
1752 eLDO
= LongDateOrder::YMD
;
1754 case DateOrder::DMY
:
1755 eLDO
= LongDateOrder::DMY
;
1758 ; // nothing, not a date
1761 else if (eLDO
== LongDateOrder::DMY
&& eDateOrder
== DateOrder::YMD
)
1763 // Check possible order and maybe switch.
1764 if (!ImplGetDay(0) && ImplGetDay(1))
1765 eLDO
= LongDateOrder::YMD
;
1767 else if (eLDO
== LongDateOrder::YMD
&& eDateOrder
== DateOrder::DMY
)
1769 // Check possible order and maybe switch.
1770 if (!ImplGetDay(1) && ImplGetDay(0))
1771 eLDO
= LongDateOrder::DMY
;
1776 bool ImpSvNumberInputScan::GetDateRef( double& fDays
, sal_uInt16
& nCounter
)
1778 using namespace ::com::sun::star::i18n
;
1779 NfEvalDateFormat eEDF
;
1781 if ( mpFormat
&& (mpFormat
->GetType() & SvNumFormatType::DATE
) )
1783 eEDF
= mrCurrentLanguageData
.GetEvalDateFormat();
1786 case NF_EVALDATEFORMAT_INTL
:
1787 case NF_EVALDATEFORMAT_FORMAT
:
1788 nFormatOrder
= 1; // only one loop
1792 if ( nMatchedAllStrings
)
1794 eEDF
= NF_EVALDATEFORMAT_FORMAT_INTL
;
1795 // we have a complete match, use it
1801 eEDF
= NF_EVALDATEFORMAT_INTL
;
1806 const LocaleDataWrapper
* pLoc
= mrCurrentLanguageData
.GetLocaleData();
1807 CalendarWrapper
* pCal
= mrCurrentLanguageData
.GetCalendar();
1808 for ( int nTryOrder
= 1; nTryOrder
<= nFormatOrder
; nTryOrder
++ )
1810 pCal
->setGregorianDateTime( Date( Date::SYSTEM
) ); // today
1811 OUString aOrgCalendar
; // empty => not changed yet
1816 case NF_EVALDATEFORMAT_INTL
:
1817 bFormatTurn
= false;
1818 DateFmt
= GetDateOrder();
1820 case NF_EVALDATEFORMAT_FORMAT
:
1822 DateFmt
= mpFormat
->GetDateOrder();
1824 case NF_EVALDATEFORMAT_INTL_FORMAT
:
1825 if ( nTryOrder
== 1 )
1827 bFormatTurn
= false;
1828 DateFmt
= GetDateOrder();
1833 DateFmt
= mpFormat
->GetDateOrder();
1836 case NF_EVALDATEFORMAT_FORMAT_INTL
:
1837 if ( nTryOrder
== 2 )
1839 bFormatTurn
= false;
1840 DateFmt
= GetDateOrder();
1845 // Even if the format pattern is to be preferred, the input may
1846 // have matched a pattern of the current locale, which then
1847 // again is to be preferred. Both date orders can be different
1848 // so we need to obtain the actual match. For example ISO
1849 // YYYY-MM-DD format vs locale's DD.MM.YY input.
1850 // If no pattern was matched, obtain from format.
1851 // Note that patterns may have been constructed from the
1852 // format's locale and prepended to the current locale's
1853 // patterns, it doesn't necessarily mean a current locale's
1854 // pattern was matched, but may if the format's locale's
1855 // patterns didn't match, which were tried first.
1856 DateFmt
= GetDateOrder(true);
1860 SAL_WARN( "svl.numbers", "ImpSvNumberInputScan::GetDateRef: unknown NfEvalDateFormat" );
1861 DateFmt
= DateOrder::YMD
;
1862 bFormatTurn
= false;
1867 We are currently not able to fully support a switch to another calendar during
1868 input for the following reasons:
1869 1. We do have a problem if both (locale's default and format's) calendars
1870 define the same YMD order and use the same date separator, there is no way
1871 to distinguish between them if the input results in valid calendar input for
1872 both calendars. How to solve? Would NfEvalDateFormat be sufficient? Should
1873 it always be set to NF_EVALDATEFORMAT_FORMAT_INTL and thus the format's
1874 calendar be preferred? This could be confusing if a Calc cell was formatted
1875 different to the locale's default and has no content yet, then the user has
1876 no clue about the format or calendar being set.
1877 2. In Calc cell edit mode a date is always displayed and edited using the
1878 default edit format of the default calendar (normally being Gregorian). If
1879 input was ambiguous due to issue #1 we'd need a mechanism to tell that a
1880 date was edited and not newly entered. Not feasible. Otherwise we'd need a
1881 mechanism to use a specific edit format with a specific calendar according
1883 3. For some calendars like Japanese Gengou we'd need era input, which isn't
1884 implemented at all. Though this is a rare and special case, forcing a
1885 calendar dependent edit format as suggested in item #2 might require era
1886 input, if it shouldn't result in a fallback to Gregorian calendar.
1887 4. Last and least: the GetMonth() method currently only matches month names of
1888 the default calendar. Alternating month names of the actual format's
1889 calendar would have to be implemented. No problem.
1893 if ( mpFormat
->IsOtherCalendar( nStringScanNumFor
) )
1895 mpFormat
->SwitchToOtherCalendar( aOrgCalendar
, fOrgDateTime
);
1899 mpFormat
->SwitchToSpecifiedCalendar( aOrgCalendar
, fOrgDateTime
,
1900 nStringScanNumFor
);
1907 // For incomplete dates, always assume first day of month if not specified.
1908 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
1910 switch (nNumericsCnt
) // count of numbers in string
1913 if (nMonthPos
) // only month (Jan)
1915 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
1923 case 1: // only one number
1925 switch (nMonthPos
) // where is the month
1927 case 0: // not found
1929 // If input matched a date pattern, use the pattern
1930 // to determine if it is a day, month or year. The
1931 // pattern should have only one single value then,
1932 // 'D-', 'M-' or 'Y-'. If input did not match a
1933 // pattern assume the usual day of current month.
1934 sal_uInt32 nDateOrder
= (bFormatTurn
?
1935 mpFormat
->GetExactDateOrder() :
1936 GetDatePatternOrder());
1940 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1943 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
1947 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1952 case 1: // month at the beginning (Jan 01)
1953 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
1956 case DateOrder::MDY
:
1957 case DateOrder::YMD
:
1959 sal_uInt16 nDay
= ImplGetDay(0);
1960 sal_uInt16 nYear
= ImplGetYear(0);
1961 if (nDay
== 0 || nDay
> 32)
1963 pCal
->setValue( CalendarFieldIndex::YEAR
, nYear
);
1967 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1971 case DateOrder::DMY
:
1972 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1979 case 3: // month at the end (10 Jan)
1980 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
1983 case DateOrder::DMY
:
1984 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
1986 case DateOrder::YMD
:
1987 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
1997 } // switch (nMonthPos)
2000 case 2: // 2 numbers
2002 switch (nMonthPos
) // where is the month
2004 case 0: // not found
2006 sal_uInt32 nExactDateOrder
= (bFormatTurn
?
2007 mpFormat
->GetExactDateOrder() :
2008 GetDatePatternOrder());
2009 bool bIsExact
= (0xff < nExactDateOrder
&& nExactDateOrder
<= 0xffff);
2010 if (!bIsExact
&& bFormatTurn
&& IsAcceptedDatePattern( nNums
[0]))
2012 // If input does not match format but pattern, use pattern
2013 // instead, even if eEDF==NF_EVALDATEFORMAT_FORMAT_INTL.
2014 // For example, format has "Y-M-D" and pattern is "D.M.",
2015 // input with 2 numbers can't match format and 31.12. would
2016 // lead to 1931-12-01 (fdo#54344)
2017 nExactDateOrder
= GetDatePatternOrder();
2018 bIsExact
= (0xff < nExactDateOrder
&& nExactDateOrder
<= 0xffff);
2023 // formatted as date and exactly 2 parts
2025 switch ( (nExactDateOrder
>> 8) & 0xff )
2028 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2031 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
2034 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2039 switch ( nExactDateOrder
& 0xff )
2042 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2045 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
2048 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2053 SAL_WARN_IF( !bHadExact
, "svl.numbers", "ImpSvNumberInputScan::GetDateRef: error in exact date order");
2059 // If input matched against a date acceptance pattern
2060 // do not attempt to mess around with guessing the
2061 // order, either it matches or it doesn't.
2062 if ((bFormatTurn
|| !bIsExact
) && (!bHadExact
|| !pCal
->isValid()))
2064 if ( !bHadExact
&& nExactDateOrder
)
2066 pCal
->setGregorianDateTime( Date( Date::SYSTEM
) ); // reset today
2070 case DateOrder::MDY
:
2072 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2073 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
2074 if ( !pCal
->isValid() ) // 2nd try
2076 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
2077 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
2078 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2081 case DateOrder::DMY
:
2083 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2084 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
2085 if ( !pCal
->isValid() ) // 2nd try
2087 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
2088 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
2089 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2092 case DateOrder::YMD
:
2094 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2095 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
2096 if ( !pCal
->isValid() ) // 2nd try
2098 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, 1 );
2099 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
2100 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2110 case 1: // month at the beginning (Jan 01 01)
2112 // The input is valid as MDY in almost any
2113 // constellation, there is no date order (M)YD except if
2114 // set in a format applied.
2115 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
2116 sal_uInt32 nExactDateOrder
= (bFormatTurn
? mpFormat
->GetExactDateOrder() : 0);
2117 if ((((nExactDateOrder
>> 8) & 0xff) == 'Y') && ((nExactDateOrder
& 0xff) == 'D'))
2119 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2120 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2124 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2125 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2129 case 2: // month in the middle (10 Jan 94)
2131 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
2132 const LongDateOrder eLDO
= GetMiddleMonthLongDateOrder( bFormatTurn
, pLoc
, DateFmt
);
2135 case LongDateOrder::DMY
:
2136 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2137 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2139 case LongDateOrder::YMD
:
2140 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2141 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2149 case 3: // month at the end (94 10 Jan)
2150 if (pLoc
->getLongDateOrder() != LongDateOrder::YDM
)
2154 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2155 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
2156 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2162 } // switch (nMonthPos)
2165 default: // more than two numbers (31.12.94 8:23) (31.12. 8:23)
2166 switch (nMonthPos
) // where is the month
2168 case 0: // not found
2172 { // find first time number index (should only be 3 or 2 anyway)
2173 for ( sal_uInt16 j
= 0; j
< nNumericsCnt
; j
++ )
2175 if ( nNums
[j
] == nTimePos
- 2 )
2182 // ISO 8601 yyyy-mm-dd forced recognition
2183 DateOrder eDF
= (CanForceToIso8601( DateFmt
) ? DateOrder::YMD
: DateFmt
);
2186 case DateOrder::MDY
:
2187 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2188 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(0) );
2190 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(2) );
2192 case DateOrder::DMY
:
2193 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2194 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
2196 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(2) );
2198 case DateOrder::YMD
:
2200 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(2) );
2201 pCal
->setValue( CalendarFieldIndex::MONTH
, ImplGetMonth(1) );
2202 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2210 case 1: // month at the beginning (Jan 01 01 8:23)
2213 // The input is valid as MDY in almost any
2214 // constellation, there is no date order (M)YD except if
2215 // set in a format applied.
2216 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
2217 sal_uInt32 nExactDateOrder
= (bFormatTurn
? mpFormat
->GetExactDateOrder() : 0);
2218 if ((((nExactDateOrder
>> 8) & 0xff) == 'Y') && ((nExactDateOrder
& 0xff) == 'D'))
2220 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2221 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2225 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2226 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2230 case 2: // month in the middle (10 Jan 94 8:23)
2233 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
2234 const LongDateOrder eLDO
= GetMiddleMonthLongDateOrder( bFormatTurn
, pLoc
, DateFmt
);
2237 case LongDateOrder::DMY
:
2238 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(0) );
2239 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(1) );
2241 case LongDateOrder::YMD
:
2242 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2243 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2251 case 3: // month at the end (94 10 Jan 8:23)
2253 if (pLoc
->getLongDateOrder() != LongDateOrder::YDM
)
2257 pCal
->setValue( CalendarFieldIndex::DAY_OF_MONTH
, ImplGetDay(1) );
2258 pCal
->setValue( CalendarFieldIndex::MONTH
, std::abs(nMonth
)-1 );
2259 pCal
->setValue( CalendarFieldIndex::YEAR
, ImplGetYear(0) );
2266 } // switch (nMonthPos)
2268 } // switch (nNumericsCnt)
2270 if (mbEraCE
!= kDefaultEra
)
2271 pCal
->setValue( CalendarFieldIndex::ERA
, mbEraCE
? 1 : 0);
2273 if ( res
&& pCal
->isValid() )
2275 double fDiff
= DateTime::Sub( DateTime(*moNullDate
), pCal
->getEpochStart());
2276 fDays
= ::rtl::math::approxFloor( pCal
->getLocalDateTime() );
2278 nTryOrder
= nFormatOrder
; // break for
2284 if ( aOrgCalendar
.getLength() )
2286 pCal
->loadCalendar( aOrgCalendar
, pLoc
->getLanguageTag().getLocale() ); // restore calendar
2288 #if NF_TEST_CALENDAR
2290 using namespace ::com::sun::star
;
2291 struct entry
{ const char* lan
; const char* cou
; const char* cal
; };
2292 const entry cals
[] = {
2293 { "en", "US", "gregorian" },
2294 { "ar", "TN", "hijri" },
2295 { "he", "IL", "jewish" },
2296 { "ja", "JP", "gengou" },
2297 { "ko", "KR", "hanja_yoil" },
2298 { "th", "TH", "buddhist" },
2299 { "zh", "TW", "ROC" },
2302 lang::Locale aLocale
;
2304 sal_Int16 nDay
, nMyMonth
, nYear
, nHour
, nMinute
, nSecond
;
2305 sal_Int16 nDaySet
, nMonthSet
, nYearSet
, nHourSet
, nMinuteSet
, nSecondSet
;
2306 sal_Int16 nZO
, nDST1
, nDST2
, nDST
, nZOmillis
, nDST1millis
, nDST2millis
, nDSTmillis
;
2307 sal_Int32 nZoneInMillis
, nDST1InMillis
, nDST2InMillis
;
2308 uno::Reference
< uno::XComponentContext
> xContext
=
2309 ::comphelper::getProcessComponentContext();
2310 uno::Reference
< i18n::XCalendar4
> xCal
= i18n::LocaleCalendar2::create(xContext
);
2311 for ( const entry
* p
= cals
; p
->lan
; ++p
)
2313 aLocale
.Language
= OUString::createFromAscii( p
->lan
);
2314 aLocale
.Country
= OUString::createFromAscii( p
->cou
);
2315 xCal
->loadCalendar( OUString::createFromAscii( p
->cal
),
2317 double nDateTime
= 0.0; // 1-Jan-1970 00:00:00
2318 nZO
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET
);
2319 nZOmillis
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS
);
2320 nZoneInMillis
= static_cast<sal_Int32
>(nZO
) * 60000 +
2321 (nZO
< 0 ? -1 : 1) * static_cast<sal_uInt16
>(nZOmillis
);
2322 nDST1
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET
);
2323 nDST1millis
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS
);
2324 nDST1InMillis
= static_cast<sal_Int32
>(nDST1
) * 60000 +
2325 (nDST1
< 0 ? -1 : 1) * static_cast<sal_uInt16
>(nDST1millis
);
2326 nDateTime
-= (double)(nZoneInMillis
+ nDST1InMillis
) / 1000.0 / 60.0 / 60.0 / 24.0;
2327 xCal
->setDateTime( nDateTime
);
2328 nDST2
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET
);
2329 nDST2millis
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS
);
2330 nDST2InMillis
= static_cast<sal_Int32
>(nDST2
) * 60000 +
2331 (nDST2
< 0 ? -1 : 1) * static_cast<sal_uInt16
>(nDST2millis
);
2332 if ( nDST1InMillis
!= nDST2InMillis
)
2334 nDateTime
= 0.0 - (double)(nZoneInMillis
+ nDST2InMillis
) / 1000.0 / 60.0 / 60.0 / 24.0;
2335 xCal
->setDateTime( nDateTime
);
2337 nDaySet
= xCal
->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH
);
2338 nMonthSet
= xCal
->getValue( i18n::CalendarFieldIndex::MONTH
);
2339 nYearSet
= xCal
->getValue( i18n::CalendarFieldIndex::YEAR
);
2340 nHourSet
= xCal
->getValue( i18n::CalendarFieldIndex::HOUR
);
2341 nMinuteSet
= xCal
->getValue( i18n::CalendarFieldIndex::MINUTE
);
2342 nSecondSet
= xCal
->getValue( i18n::CalendarFieldIndex::SECOND
);
2343 nZO
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET
);
2344 nZOmillis
= xCal
->getValue( i18n::CalendarFieldIndex::ZONE_OFFSET_SECOND_MILLIS
);
2345 nDST
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET
);
2346 nDSTmillis
= xCal
->getValue( i18n::CalendarFieldIndex::DST_OFFSET_SECOND_MILLIS
);
2347 xCal
->setValue( i18n::CalendarFieldIndex::DAY_OF_MONTH
, nDaySet
);
2348 xCal
->setValue( i18n::CalendarFieldIndex::MONTH
, nMonthSet
);
2349 xCal
->setValue( i18n::CalendarFieldIndex::YEAR
, nYearSet
);
2350 xCal
->setValue( i18n::CalendarFieldIndex::HOUR
, nHourSet
);
2351 xCal
->setValue( i18n::CalendarFieldIndex::MINUTE
, nMinuteSet
);
2352 xCal
->setValue( i18n::CalendarFieldIndex::SECOND
, nSecondSet
);
2353 bValid
= xCal
->isValid();
2354 nDay
= xCal
->getValue( i18n::CalendarFieldIndex::DAY_OF_MONTH
);
2355 nMyMonth
= xCal
->getValue( i18n::CalendarFieldIndex::MONTH
);
2356 nYear
= xCal
->getValue( i18n::CalendarFieldIndex::YEAR
);
2357 nHour
= xCal
->getValue( i18n::CalendarFieldIndex::HOUR
);
2358 nMinute
= xCal
->getValue( i18n::CalendarFieldIndex::MINUTE
);
2359 nSecond
= xCal
->getValue( i18n::CalendarFieldIndex::SECOND
);
2360 bValid
= bValid
&& nDay
== nDaySet
&& nMyMonth
== nMonthSet
&& nYear
==
2361 nYearSet
&& nHour
== nHourSet
&& nMinute
== nMinuteSet
&& nSecond
2365 #endif // NF_TEST_CALENDAR
2374 * Analyze first string
2378 bool ImpSvNumberInputScan::ScanStartString( const OUString
& rString
)
2382 // First of all, eat leading blanks
2383 SkipBlanks(rString
, nPos
);
2385 // Yes, nMatchedAllStrings should know about the sign position
2386 nSign
= GetSign(rString
, nPos
);
2387 if ( nSign
) // sign?
2389 SkipBlanks(rString
, nPos
);
2391 // #102371# match against format string only if start string is not a sign character
2392 if ( nMatchedAllStrings
&& !(nSign
&& rString
.getLength() == 1) )
2394 // Match against format in any case, so later on for a "x1-2-3" input
2395 // we may distinguish between a xy-m-d (or similar) date and a x0-0-0
2396 // format. No sign detection here!
2397 if ( ScanStringNumFor( rString
, nPos
, 0, true ) )
2399 nMatchedAllStrings
|= nMatchedStartString
;
2403 nMatchedAllStrings
= 0;
2407 // Bail out early for just a sign.
2408 if (nSign
&& nPos
== rString
.getLength())
2411 const sal_Int32 nStartBlanks
= nPos
;
2412 if ( GetDecSep(rString
, nPos
) ) // decimal separator in start string
2414 if (SkipBlanks(rString
, nPos
))
2415 nPos
= nStartBlanks
; // `. 2` not a decimal separator
2417 nDecPos
= 1; // leading decimal separator
2419 else if ( GetCurrency(rString
, nPos
) ) // currency (DM 1)?
2421 eScannedType
= SvNumFormatType::CURRENCY
; // !!! it IS currency !!!
2422 SkipBlanks(rString
, nPos
);
2423 if (nSign
== 0) // no sign yet
2425 nSign
= GetSign(rString
, nPos
);
2426 if ( nSign
) // DM -1
2428 SkipBlanks(rString
, nPos
);
2431 if ( GetDecSep(rString
, nPos
) ) // decimal separator follows currency
2433 if (SkipBlanks(rString
, nPos
))
2435 nPos
= nStartBlanks
; // `DM . 2` not a decimal separator
2436 eScannedType
= SvNumFormatType::UNDEFINED
; // !!! it is NOT currency !!!
2439 nDecPos
= 1; // leading decimal separator
2444 const sal_Int32 nMonthStart
= nPos
;
2445 short nTempMonth
= GetMonth(rString
, nPos
);
2448 // Short month and day names may be identical in some locales, e.g.
2449 // "mar" for "martes" or "marzo" in Spanish.
2450 // Do not let a month name immediately take precedence if a day
2451 // name was meant instead. Assume that both could be valid, until
2452 // encountered differently or the final evaluation in
2453 // IsNumberFormat() checks, but continue with weighing the month
2454 // name higher unless we have both day of week and month name here.
2455 sal_Int32 nTempPos
= nMonthStart
;
2456 nDayOfWeek
= GetDayOfWeek( rString
, nTempPos
);
2459 SkipChar( '.', rString
, nTempPos
); // abbreviated
2460 SkipString( mrCurrentLanguageData
.GetLocaleData()->getLongDateDayOfWeekSep(), rString
, nTempPos
);
2461 SkipBlanks( rString
, nTempPos
);
2462 short nTempTempMonth
= GetMonth( rString
, nTempPos
);
2465 // Fall into the else branch below that handles both.
2469 // Do not set nDayOfWeek hereafter, anywhere.
2473 if ( nTempMonth
) // month (Jan 1)?
2475 // Jan1 without separator is not a date, unless it is followed by a
2476 // separator and a (year) number.
2477 if (nPos
< rString
.getLength() || (nStringsCnt
>= 4 && nNumericsCnt
>= 2))
2479 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date !!!
2480 nMonth
= nTempMonth
;
2481 nMonthPos
= 1; // month at the beginning
2484 SkipChar( '.', rString
, nPos
); // abbreviated
2486 SkipBlanks(rString
, nPos
);
2490 nPos
= nMonthStart
; // rewind month
2495 int nTempDayOfWeek
= GetDayOfWeek( rString
, nPos
);
2496 if ( nTempDayOfWeek
)
2498 // day of week is just parsed away
2499 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date !!!
2500 if ( nPos
< rString
.getLength() )
2502 if ( nTempDayOfWeek
< 0 )
2505 if ( rString
[ nPos
] == '.' )
2513 SkipBlanks(rString
, nPos
);
2514 SkipString( mrCurrentLanguageData
.GetLocaleData()->getLongDateDayOfWeekSep(), rString
, nPos
);
2516 SkipBlanks(rString
, nPos
);
2517 nTempMonth
= GetMonth(rString
, nPos
);
2518 if ( nTempMonth
) // month (Jan 1)?
2520 // Jan1 without separator is not a date, unless it is followed by a
2521 // separator and a (year) number.
2522 if (nPos
< rString
.getLength() || (nStringsCnt
>= 4 && nNumericsCnt
>= 2))
2524 nMonth
= nTempMonth
;
2525 nMonthPos
= 1; // month at the beginning
2528 SkipChar( '.', rString
, nPos
); // abbreviated
2530 SkipBlanks(rString
, nPos
);
2534 nPos
= nMonthStart
; // rewind month
2540 // Determine and remember following date pattern, if any.
2541 IsAcceptedDatePattern( 1);
2545 // Skip one trailing '-' or '/' character to recognize June-2007
2546 if (nMonth
&& nPos
+ 1 == rString
.getLength())
2548 SkipChar('-', rString
, nPos
) || SkipChar('/', rString
, nPos
);
2552 if (nPos
< rString
.getLength()) // not everything consumed
2554 // Does input StartString equal StartString of format?
2555 // This time with sign detection!
2556 if ( !ScanStringNumFor( rString
, nPos
, 0 ) )
2558 return MatchedReturn();
2567 * Analyze string in the middle
2571 bool ImpSvNumberInputScan::ScanMidString( const OUString
& rString
, sal_uInt16 nStringPos
, sal_uInt16 nCurNumCount
)
2574 SvNumFormatType eOldScannedType
= eScannedType
;
2576 if ( nMatchedAllStrings
)
2577 { // Match against format in any case, so later on for a "1-2-3-4" input
2578 // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2580 if ( ScanStringNumFor( rString
, 0, nStringPos
) )
2582 nMatchedAllStrings
|= nMatchedMidString
;
2586 nMatchedAllStrings
= 0;
2590 const sal_Int32 nStartBlanks
= nPos
;
2591 const bool bBlanks
= SkipBlanks(rString
, nPos
);
2592 if (GetDecSep(rString
, nPos
)) // decimal separator?
2594 if (nDecPos
== 1 || nDecPos
== 3) // .12.4 or 1.E2.1
2596 return MatchedReturn();
2598 else if (nDecPos
== 2) // . dup: 12.4.
2600 bool bSignedYear
= false;
2601 if (bDecSepInDateSeps
|| // . also date separator
2602 SkipDatePatternSeparator( nStringPos
, nPos
, bSignedYear
))
2604 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&&
2605 eScannedType
!= SvNumFormatType::DATE
&&
2606 eScannedType
!= SvNumFormatType::DATETIME
) // already another type
2608 return MatchedReturn();
2610 if (eScannedType
== SvNumFormatType::UNDEFINED
)
2612 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date
2614 SkipBlanks(rString
, nPos
);
2618 return MatchedReturn();
2623 // `1 .2` or `1 . 2` not a decimal separator, reset
2624 nPos
= nStartBlanks
;
2626 else if (SkipBlanks(rString
, nPos
))
2628 // `1. 2` not a decimal separator, reset
2629 nPos
= nStartBlanks
;
2633 nDecPos
= 2; // . in mid string
2636 else if ( (eScannedType
& SvNumFormatType::TIME
) &&
2637 GetTime100SecSep( rString
, nPos
) )
2638 { // hundredth seconds separator
2641 return MatchedReturn();
2643 nDecPos
= 2; // . in mid string
2645 // If this is exactly an ISO 8601 fractional seconds separator, bail
2646 // out early to not get confused by later checks for group separator or
2648 if (bIso8601Tsep
&& nPos
== rString
.getLength() &&
2649 eScannedType
== SvNumFormatType::DATETIME
&& (rString
== "." || rString
== ","))
2652 SkipBlanks(rString
, nPos
);
2655 if (SkipChar('/', rString
, nPos
)) // fraction?
2657 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2658 eScannedType
!= SvNumFormatType::DATE
) // except date
2660 return MatchedReturn(); // => jan/31/1994
2662 else if (eScannedType
!= SvNumFormatType::DATE
&& // analyzed no date until now
2663 (eSetType
== SvNumFormatType::FRACTION
|| // and preset was fraction
2664 (nNumericsCnt
== 3 && // or 3 numbers
2665 (nStringPos
== 3 || // and 4th string particle
2666 (nStringPos
== 4 && nSign
)) && // or 5th if signed
2667 sStrArray
[nStringPos
-2].indexOf('/') == -1))) // and not 23/11/1999
2668 // that was not accepted as date yet
2670 SkipBlanks(rString
, nPos
);
2671 if (nPos
== rString
.getLength())
2673 eScannedType
= SvNumFormatType::FRACTION
; // !!! it IS a fraction (so far)
2674 if (eSetType
== SvNumFormatType::FRACTION
&&
2675 nNumericsCnt
== 2 &&
2676 (nStringPos
== 1 || // for 4/5
2677 (nStringPos
== 2 && nSign
))) // or signed -4/5
2679 return true; // don't fall into date trap
2685 nPos
--; // put '/' back
2689 if (GetThousandSep(rString
, nPos
, nStringPos
)) // 1,000
2691 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2692 eScannedType
!= SvNumFormatType::CURRENCY
) // except currency
2694 return MatchedReturn();
2699 const LocaleDataWrapper
* pLoc
= mrCurrentLanguageData
.GetLocaleData();
2700 bool bSignedYear
= false;
2701 bool bDate
= SkipDatePatternSeparator( nStringPos
, nPos
, bSignedYear
); // 12/31 31.12. 12/31/1999 31.12.1999
2704 const OUString
& rDate
= mrCurrentLanguageData
.GetDateSep();
2705 SkipBlanks(rString
, nPos
);
2706 bDate
= SkipString( rDate
, rString
, nPos
); // 10. 10- 10/
2708 if (!bDate
&& nStringPos
== 1 && mpFormat
&& (mpFormat
->GetType() & SvNumFormatType::DATE
))
2710 // If a DMY format was given and a mid string starts with a literal
2711 // ". " dot+space and could contain a following month name and ends
2712 // with a space or LongDateMonthSeparator, like it's scanned in
2713 // `14". AUG "18`, then it may be a date as well. Regardless whether
2714 // defined such by the locale or not.
2715 // This *could* check for presence of ". "MMM or ". "MMMM in the actual
2716 // format code for further restriction to match only if present, but..
2718 const sal_uInt32 nExactDateOrder
= mpFormat
->GetExactDateOrder();
2720 if (((nExactDateOrder
& 0xff) == 'Y') && (((nExactDateOrder
>> 8) & 0xff) == 'M')
2721 && (((nExactDateOrder
>> 16) & 0xff) == 'D'))
2723 const sal_Int32 nTmpPos
= nPos
;
2724 if (SkipChar('.', rString
, nPos
) && SkipBlanks(rString
, nPos
) && nPos
+ 2 < rString
.getLength()
2725 && (rString
.endsWith(" ") || rString
.endsWith( pLoc
->getLongDateMonthSep())))
2731 if (bDate
|| ((MayBeIso8601() || MayBeMonthDate()) && // 1999-12-31 31-Dec-1999
2732 SkipChar( '-', rString
, nPos
)))
2734 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2735 eScannedType
!= SvNumFormatType::DATE
) // except date
2737 return MatchedReturn();
2739 SkipBlanks(rString
, nPos
);
2740 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date
2741 short nTmpMonth
= GetMonth(rString
, nPos
); // 10. Jan 94
2742 if (nMonth
&& nTmpMonth
) // month dup
2744 return MatchedReturn();
2749 nMonthPos
= 2; // month in the middle
2750 if ( nMonth
< 0 && SkipChar( '.', rString
, nPos
) )
2751 ; // short month may be abbreviated Jan.
2752 else if ( SkipChar( '-', rString
, nPos
) )
2753 ; // #79632# recognize 17-Jan-2001 to be a date
2754 // #99065# short and long month name
2757 SkipString( pLoc
->getLongDateMonthSep(), rString
, nPos
);
2759 SkipBlanks(rString
, nPos
);
2763 if (mbEraCE
!= kDefaultEra
) // signed year twice?
2764 return MatchedReturn();
2766 mbEraCE
= false; // BCE
2770 const sal_Int32 nMonthStart
= nPos
;
2771 short nTempMonth
= GetMonth(rString
, nPos
); // month in the middle (10 Jan 94) or at the end (94 10 Jan)
2774 if (nMonth
!= 0) // month dup
2776 return MatchedReturn();
2778 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2779 eScannedType
!= SvNumFormatType::DATE
) // except date
2781 return MatchedReturn();
2783 if (nMonthStart
> 0 && nPos
< rString
.getLength()) // 10Jan or Jan94 without separator are not dates
2785 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date
2786 nMonth
= nTempMonth
;
2787 if (nCurNumCount
<= 1)
2788 nMonthPos
= 2; // month in the middle
2790 nMonthPos
= 3; // month at the end
2793 SkipChar( '.', rString
, nPos
); // abbreviated
2795 SkipString( pLoc
->getLongDateMonthSep(), rString
, nPos
);
2796 SkipBlanks(rString
, nPos
);
2800 nPos
= nMonthStart
; // rewind month
2804 if ( SkipChar('E', rString
, nPos
) || // 10E, 10e, 10,Ee
2805 SkipChar('e', rString
, nPos
) )
2807 if (eScannedType
!= SvNumFormatType::UNDEFINED
) // already another type
2809 return MatchedReturn();
2813 SkipBlanks(rString
, nPos
);
2814 eScannedType
= SvNumFormatType::SCIENTIFIC
; // !!! it IS scientific
2815 if ( nThousand
+2 == nNumericsCnt
&& nDecPos
== 2 ) // special case 1.E2
2817 nDecPos
= 3; // 1,100.E2 1,100,100.E3
2820 nESign
= GetESign(rString
, nPos
); // signed exponent?
2821 SkipBlanks(rString
, nPos
);
2824 const OUString
& rTime
= pLoc
->getTimeSep();
2825 if ( SkipString(rTime
, rString
, nPos
) ) // time separator?
2827 if (nDecPos
) // already . => maybe error
2829 if (bDecSepInDateSeps
) // . also date sep
2831 if ( eScannedType
!= SvNumFormatType::DATE
&& // already another type than date
2832 eScannedType
!= SvNumFormatType::DATETIME
) // or date time
2834 return MatchedReturn();
2836 if (eScannedType
== SvNumFormatType::DATE
)
2838 nDecPos
= 0; // reset for time transition
2843 return MatchedReturn();
2846 if ((eScannedType
== SvNumFormatType::DATE
|| // already date type
2847 eScannedType
== SvNumFormatType::DATETIME
) && // or date time
2848 nNumericsCnt
> 3) // and more than 3 numbers? (31.Dez.94 8:23)
2850 SkipBlanks(rString
, nPos
);
2851 eScannedType
= SvNumFormatType::DATETIME
; // !!! it IS date with time
2853 else if ( eScannedType
!= SvNumFormatType::UNDEFINED
&& // already another type
2854 eScannedType
!= SvNumFormatType::TIME
) // except time
2856 return MatchedReturn();
2860 SkipBlanks(rString
, nPos
);
2861 eScannedType
= SvNumFormatType::TIME
; // !!! it IS a time
2865 nTimePos
= nStringPos
+ 1;
2869 if (nPos
< rString
.getLength())
2871 switch (eScannedType
)
2873 case SvNumFormatType::DATE
:
2874 if (nMonthPos
== 1 && pLoc
->getLongDateOrder() == LongDateOrder::MDY
)
2876 // #68232# recognize long date separators like ", " in "September 5, 1999"
2877 if (SkipString( pLoc
->getLongDateDaySep(), rString
, nPos
))
2879 SkipBlanks( rString
, nPos
);
2882 else if (nPos
== 0 && rString
.getLength() == 1 && MayBeIso8601())
2884 if ( (nStringPos
== 5 && rString
[0] == 'T') ||
2885 (nStringPos
== 6 && rString
[0] == 'T' && sStrArray
[0] == "-"))
2887 // ISO 8601 combined date and time, yyyy-mm-ddThh:mm or -yyyy-mm-ddThh:mm
2889 bIso8601Tsep
= true;
2891 else if (nStringPos
== 7 && rString
[0] == ':')
2893 // ISO 8601 combined date and time, the time part; we reach
2894 // here if the locale's separator is not ':' so it couldn't
2895 // be detected above in the time block.
2896 if (nNumericsCnt
>= 5)
2897 eScannedType
= SvNumFormatType::DATETIME
;
2902 case SvNumFormatType::DATETIME
:
2903 if (nPos
== 0 && rString
.getLength() == 1 && MayBeIso8601())
2905 if (nStringPos
== 9 && rString
[0] == ':')
2907 // ISO 8601 combined date and time, the time part continued.
2911 #if NF_RECOGNIZE_ISO8601_TIMEZONES
2912 else if (nPos
== 0 && rString
.getLength() == 1 && nStringPos
>= 9 && MayBeIso8601())
2914 // ISO 8601 timezone offset
2915 switch (rString
[ 0 ])
2919 if (nStringPos
== nStringsCnt
- 2 ||
2920 nStringPos
== nStringsCnt
- 4)
2922 ++nPos
; // yyyy-mm-ddThh:mm[:ss]+xx[[:]yy]
2923 // nTimezonePos needed for GetTimeRef()
2926 nTimezonePos
= nStringPos
+ 1;
2931 if (nTimezonePos
&& nStringPos
>= 11 &&
2932 nStringPos
== nStringsCnt
- 2)
2934 ++nPos
; // yyyy-mm-ddThh:mm[:ss]+xx:yy
2945 if (nPos
< rString
.getLength()) // not everything consumed?
2947 if ( nMatchedAllStrings
& ~nMatchedVirgin
)
2949 eScannedType
= eOldScannedType
;
2966 bool ImpSvNumberInputScan::ScanEndString( const OUString
& rString
)
2970 if ( nMatchedAllStrings
)
2971 { // Match against format in any case, so later on for a "1-2-3-4" input
2972 // we may distinguish between a y-m-d (or similar) date and a 0-0-0-0
2974 if ( ScanStringNumFor( rString
, 0, 0xFFFF ) )
2976 nMatchedAllStrings
|= nMatchedEndString
;
2980 nMatchedAllStrings
= 0;
2984 const sal_Int32 nStartBlanks
= nPos
;
2985 const bool bBlanks
= SkipBlanks(rString
, nPos
);
2986 if (GetDecSep(rString
, nPos
)) // decimal separator?
2988 if (nDecPos
== 1 || nDecPos
== 3) // .12.4 or 12.E4.
2990 return MatchedReturn();
2992 else if (nDecPos
== 2) // . dup: 12.4.
2994 bool bSignedYear
= false;
2995 if (bDecSepInDateSeps
|| // . also date separator
2996 SkipDatePatternSeparator( nStringsCnt
-1, nPos
, bSignedYear
))
2998 if ( eScannedType
!= SvNumFormatType::UNDEFINED
&&
2999 eScannedType
!= SvNumFormatType::DATE
&&
3000 eScannedType
!= SvNumFormatType::DATETIME
) // already another type
3002 return MatchedReturn();
3004 if (eScannedType
== SvNumFormatType::UNDEFINED
)
3006 eScannedType
= SvNumFormatType::DATE
; // !!! it IS a date
3008 SkipBlanks(rString
, nPos
);
3012 return MatchedReturn();
3017 // not a decimal separator, reset
3018 nPos
= nStartBlanks
;
3022 nDecPos
= 3; // . in end string
3023 SkipBlanks(rString
, nPos
);
3027 bool bSignDetectedHere
= false;
3028 if ( nSign
== 0 && // conflict - not signed
3029 eScannedType
!= SvNumFormatType::DATE
) // and not date
3030 //!? catch time too?
3032 nSign
= GetSign(rString
, nPos
); // 1- DM
3033 if (bNegCheck
) // '(' as sign
3035 return MatchedReturn();
3039 bSignDetectedHere
= true;
3043 SkipBlanks(rString
, nPos
);
3044 if (bNegCheck
&& SkipChar(')', rString
, nPos
)) // skip ')' if appropriate
3047 SkipBlanks(rString
, nPos
);
3050 if ( GetCurrency(rString
, nPos
) ) // currency symbol?
3052 if (eScannedType
!= SvNumFormatType::UNDEFINED
) // currency dup
3054 return MatchedReturn();
3058 SkipBlanks(rString
, nPos
);
3059 eScannedType
= SvNumFormatType::CURRENCY
;
3060 } // behind currency a '-' is allowed
3061 if (nSign
== 0) // not signed yet
3063 nSign
= GetSign(rString
, nPos
); // DM -
3064 SkipBlanks(rString
, nPos
);
3065 if (bNegCheck
) // 3 DM (
3067 return MatchedReturn();
3070 if ( bNegCheck
&& eScannedType
== SvNumFormatType::CURRENCY
&&
3071 SkipChar(')', rString
, nPos
) )
3073 bNegCheck
= false; // ')' skipped
3074 SkipBlanks(rString
, nPos
); // only if currency
3078 if ( SkipChar('%', rString
, nPos
) ) // 1%
3080 if (eScannedType
!= SvNumFormatType::UNDEFINED
) // already another type
3082 return MatchedReturn();
3084 SkipBlanks(rString
, nPos
);
3085 eScannedType
= SvNumFormatType::PERCENT
;
3088 const LocaleDataWrapper
* pLoc
= mrCurrentLanguageData
.GetLocaleData();
3089 const OUString
& rTime
= pLoc
->getTimeSep();
3090 if ( SkipString(rTime
, rString
, nPos
) ) // 10:
3092 if (nDecPos
) // already , => error
3094 return MatchedReturn();
3096 if (eScannedType
== SvNumFormatType::DATE
&& nNumericsCnt
> 2) // 31.Dez.94 8:
3098 SkipBlanks(rString
, nPos
);
3099 eScannedType
= SvNumFormatType::DATETIME
;
3101 else if (eScannedType
!= SvNumFormatType::UNDEFINED
&&
3102 eScannedType
!= SvNumFormatType::TIME
) // already another type
3104 return MatchedReturn();
3108 SkipBlanks(rString
, nPos
);
3109 eScannedType
= SvNumFormatType::TIME
;
3113 nTimePos
= nStringsCnt
;
3117 bool bSignedYear
= false;
3118 bool bDate
= SkipDatePatternSeparator( nStringsCnt
-1, nPos
, bSignedYear
); // 12/31 31.12. 12/31/1999 31.12.1999
3121 const OUString
& rDate
= mrCurrentLanguageData
.GetDateSep();
3122 bDate
= SkipString( rDate
, rString
, nPos
); // 10. 10- 10/
3124 if (bDate
&& bSignDetectedHere
)
3126 nSign
= 0; // 'D-' takes precedence over signed date
3128 if (bDate
|| ((MayBeIso8601() || MayBeMonthDate())
3129 && SkipChar( '-', rString
, nPos
)))
3131 if (eScannedType
!= SvNumFormatType::UNDEFINED
&&
3132 eScannedType
!= SvNumFormatType::DATE
) // already another type
3134 return MatchedReturn();
3138 SkipBlanks(rString
, nPos
);
3139 eScannedType
= SvNumFormatType::DATE
;
3141 short nTmpMonth
= GetMonth(rString
, nPos
); // 10. Jan
3142 if (nMonth
&& nTmpMonth
) // month dup
3144 return MatchedReturn();
3149 nMonthPos
= 3; // month at end
3152 SkipChar( '.', rString
, nPos
); // abbreviated
3154 SkipBlanks(rString
, nPos
);
3158 const sal_Int32 nMonthStart
= nPos
;
3159 short nTempMonth
= GetMonth(rString
, nPos
); // 10 Jan
3162 if (nMonth
) // month dup
3164 return MatchedReturn();
3166 if (eScannedType
!= SvNumFormatType::UNDEFINED
&&
3167 eScannedType
!= SvNumFormatType::DATE
) // already another type
3169 return MatchedReturn();
3171 if (nMonthStart
> 0) // 10Jan without separator is not a date
3173 eScannedType
= SvNumFormatType::DATE
;
3174 nMonth
= nTempMonth
;
3175 nMonthPos
= 3; // month at end
3178 SkipChar( '.', rString
, nPos
); // abbreviated
3180 SkipBlanks(rString
, nPos
);
3184 nPos
= nMonthStart
; // rewind month
3188 sal_Int32 nOrigPos
= nPos
;
3189 if (GetTimeAmPm(rString
, nPos
))
3191 if (eScannedType
!= SvNumFormatType::UNDEFINED
&&
3192 eScannedType
!= SvNumFormatType::TIME
&&
3193 eScannedType
!= SvNumFormatType::DATETIME
) // already another type
3195 return MatchedReturn();
3199 // If not already scanned as time, 6.78am does not result in 6
3200 // seconds and 78 hundredths in the morning. Keep as suffix.
3201 if (eScannedType
!= SvNumFormatType::TIME
&& nDecPos
== 2 && nNumericsCnt
== 2)
3203 nPos
= nOrigPos
; // rewind am/pm
3207 SkipBlanks(rString
, nPos
);
3208 if ( eScannedType
!= SvNumFormatType::DATETIME
)
3210 eScannedType
= SvNumFormatType::TIME
;
3216 if ( bNegCheck
&& SkipChar(')', rString
, nPos
) )
3218 if (eScannedType
== SvNumFormatType::CURRENCY
) // only if currency
3220 bNegCheck
= false; // skip ')'
3221 SkipBlanks(rString
, nPos
);
3225 return MatchedReturn();
3229 if ( nPos
< rString
.getLength() &&
3230 (eScannedType
== SvNumFormatType::DATE
||
3231 eScannedType
== SvNumFormatType::DATETIME
) )
3233 // day of week is just parsed away
3234 sal_Int32 nOldPos
= nPos
;
3235 const OUString
& rSep
= mrCurrentLanguageData
.GetLocaleData()->getLongDateDayOfWeekSep();
3236 if ( StringContains( rSep
, rString
, nPos
) )
3238 nPos
= nPos
+ rSep
.getLength();
3239 SkipBlanks(rString
, nPos
);
3241 int nTempDayOfWeek
= GetDayOfWeek( rString
, nPos
);
3242 if ( nTempDayOfWeek
)
3244 if ( nPos
< rString
.getLength() )
3246 if ( nTempDayOfWeek
< 0 )
3248 if ( rString
[ nPos
] == '.' )
3253 SkipBlanks(rString
, nPos
);
3262 #if NF_RECOGNIZE_ISO8601_TIMEZONES
3263 if (nPos
== 0 && eScannedType
== SvNumFormatType::DATETIME
&&
3264 rString
.getLength() == 1 && rString
[ 0 ] == 'Z' && MayBeIso8601())
3266 // ISO 8601 timezone UTC yyyy-mm-ddThh:mmZ
3271 if (nPos
< rString
.getLength()) // everything consumed?
3273 // does input EndString equal EndString in Format?
3274 if ( !ScanStringNumFor( rString
, nPos
, 0xFFFF ) )
3284 bool ImpSvNumberInputScan::ScanStringNumFor( const OUString
& rString
, // String to scan
3285 sal_Int32 nPos
, // Position until which was consumed
3286 sal_uInt16 nString
, // Substring of format, 0xFFFF => last
3287 bool bDontDetectNegation
) // Suppress sign detection
3293 const ::utl::TransliterationWrapper
* pTransliteration
= mrCurrentLanguageData
.GetTransliteration();
3294 const OUString
* pStr
;
3295 OUString
aString( rString
);
3296 bool bFound
= false;
3298 bool bContinue
= true;
3302 // Don't try "lower" subformats ff the very first match was the second
3303 // or third subformat.
3304 nSub
= nStringScanNumFor
;
3306 { // Step through subformats, first positive, then negative, then
3307 // other, but not the last (text) subformat.
3308 pStr
= mpFormat
->GetNumForString( nSub
, nString
, true );
3309 if ( pStr
&& pTransliteration
->isEqual( aString
, *pStr
) )
3314 else if ( nSub
< 2 )
3323 while ( bContinue
);
3324 if ( !bFound
&& bFirst
&& nPos
)
3326 // try remaining substring
3328 aString
= aString
.copy(nPos
);
3332 while ( bContinue
);
3336 if ( !bDontDetectNegation
&& (nString
== 0) &&
3337 !bFirst
&& (nSign
< 0) && mpFormat
->IsSecondSubformatRealNegative() )
3339 // simply negated twice? --1
3340 aString
= aString
.replaceAll(" ", "");
3341 if ( (aString
.getLength() == 1) && (aString
[0] == '-') )
3344 nStringScanSign
= -1;
3353 else if ( !bDontDetectNegation
&& (nSub
== 1) &&
3354 mpFormat
->IsSecondSubformatRealNegative() )
3357 if ( nStringScanSign
< 0 )
3359 if ( (nSign
< 0) && (nStringScanNumFor
!= 1) )
3361 nStringScanSign
= 1; // triple negated --1 yyy
3364 else if ( nStringScanSign
== 0 )
3367 { // nSign and nStringScanSign will be combined later,
3368 // flip sign if doubly negated
3369 if ( (nString
== 0) && !bFirst
&&
3370 SvNumberformat::HasStringNegativeSign( aString
) )
3372 nStringScanSign
= -1; // direct double negation
3374 else if ( mpFormat
->IsNegativeWithoutSign() )
3376 nStringScanSign
= -1; // indirect double negation
3381 nStringScanSign
= -1;
3386 nStringScanSign
= -1;
3389 nStringScanNumFor
= nSub
;
3395 * Recognizes types of number, exponential, fraction, percent, currency, date, time.
3396 * Else text => return false
3398 bool ImpSvNumberInputScan::IsNumberFormatMain( const OUString
& rString
, // string to be analyzed
3399 const SvNumberformat
* pFormat
) // maybe number format set to match against
3403 NumberStringDivision( rString
); // breakdown into strings and numbers
3404 if (nStringsCnt
>= SV_MAX_COUNT_INPUT_STRINGS
) // too many elements
3406 return false; // Njet, Nope, ...
3408 if (nNumericsCnt
== 0) // no number in input
3410 if ( nStringsCnt
> 0 )
3412 // Here we may change the original, we don't need it anymore.
3413 // This saves copies and ToUpper() in GetLogical() and is faster.
3414 sStrArray
[0] = comphelper::string::strip(sStrArray
[0], ' ');
3415 OUString
& rStrArray
= sStrArray
[0];
3416 nLogical
= GetLogical( rStrArray
);
3419 eScannedType
= SvNumFormatType::LOGICAL
; // !!! it's a BOOLEAN
3420 nMatchedAllStrings
&= ~nMatchedVirgin
;
3425 return false; // simple text
3430 return false; // simple text
3434 sal_uInt16 i
= 0; // mark any symbol
3435 sal_uInt16 j
= 0; // mark only numbers
3437 switch ( nNumericsCnt
)
3439 case 1 : // Exactly 1 number in input
3441 if (GetNextNumber(i
,j
)) // i=1,0
3442 { // Number at start
3443 if (eSetType
== SvNumFormatType::FRACTION
) // Fraction 1 = 1/1
3445 if (i
>= nStringsCnt
|| // no end string nor decimal separator
3446 mrCurrentLanguageData
.IsDecimalSep( sStrArray
[i
]))
3448 eScannedType
= SvNumFormatType::FRACTION
;
3449 nMatchedAllStrings
&= ~nMatchedVirgin
;
3455 { // Analyze start string
3456 if (!ScanStartString( sStrArray
[i
] )) // i=0
3458 return false; // already an error
3460 i
++; // next symbol, i=1
3462 GetNextNumber(i
,j
); // i=1,2
3463 if (eSetType
== SvNumFormatType::FRACTION
) // Fraction -1 = -1/1
3465 if (nSign
&& !bNegCheck
&& // Sign +, -
3466 eScannedType
== SvNumFormatType::UNDEFINED
&& // not date or currency
3467 nDecPos
== 0 && // no previous decimal separator
3468 (i
>= nStringsCnt
|| // no end string nor decimal separator
3469 mrCurrentLanguageData
.IsDecimalSep( sStrArray
[i
]))
3472 eScannedType
= SvNumFormatType::FRACTION
;
3473 nMatchedAllStrings
&= ~nMatchedVirgin
;
3477 if (i
< nStringsCnt
&& !ScanEndString( sStrArray
[i
] ))
3482 case 2 : // Exactly 2 numbers in input
3484 if (!GetNextNumber(i
,j
)) // i=1,0
3485 { // Analyze start string
3486 if (!ScanStartString( sStrArray
[i
] ))
3487 return false; // already an error
3490 GetNextNumber(i
,j
); // i=1,2
3491 if ( !ScanMidString( sStrArray
[i
], i
, j
) )
3495 i
++; // next symbol, i=2,3
3496 GetNextNumber(i
,j
); // i=3,4
3497 if (i
< nStringsCnt
&& !ScanEndString( sStrArray
[i
] ))
3501 if (eSetType
== SvNumFormatType::FRACTION
) // -1,200. as fraction
3503 if (!bNegCheck
&& // no sign '('
3504 eScannedType
== SvNumFormatType::UNDEFINED
&&
3505 (nDecPos
== 0 || nDecPos
== 3) // no decimal separator or at end
3508 eScannedType
= SvNumFormatType::FRACTION
;
3509 nMatchedAllStrings
&= ~nMatchedVirgin
;
3514 case 3 : // Exactly 3 numbers in input
3516 if (!GetNextNumber(i
,j
)) // i=1,0
3517 { // Analyze start string
3518 if (!ScanStartString( sStrArray
[i
] ))
3520 return false; // already an error
3523 if (nDecPos
== 1) // decimal separator at start => error
3528 GetNextNumber(i
,j
); // i=1,2
3529 if ( !ScanMidString( sStrArray
[i
], i
, j
) )
3534 if (eScannedType
== SvNumFormatType::SCIENTIFIC
) // E only at end
3538 GetNextNumber(i
,j
); // i=3,4
3539 if ( !ScanMidString( sStrArray
[i
], i
, j
) )
3544 GetNextNumber(i
,j
); // i=5,6
3545 if (i
< nStringsCnt
&& !ScanEndString( sStrArray
[i
] ))
3549 if (eSetType
== SvNumFormatType::FRACTION
) // -1,200,100. as fraction
3551 if (!bNegCheck
&& // no sign '('
3552 eScannedType
== SvNumFormatType::UNDEFINED
&&
3553 (nDecPos
== 0 || nDecPos
== 3) // no decimal separator or at end
3556 eScannedType
= SvNumFormatType::FRACTION
;
3557 nMatchedAllStrings
&= ~nMatchedVirgin
;
3561 if ( eScannedType
== SvNumFormatType::FRACTION
&& nDecPos
)
3563 return false; // #36857# not a real fraction
3566 default: // More than 3 numbers in input
3568 if (!GetNextNumber(i
,j
)) // i=1,0
3569 { // Analyze startstring
3570 if (!ScanStartString( sStrArray
[i
] ))
3571 return false; // already an error
3573 if (nDecPos
== 1) // decimal separator at start => error
3576 GetNextNumber(i
,j
); // i=1,2
3577 if ( !ScanMidString( sStrArray
[i
], i
, j
) )
3583 sal_uInt16 nThOld
= 10; // just not 0 or 1
3584 while (nThOld
!= nThousand
&& j
< nNumericsCnt
-1) // Execute at least one time
3585 // but leave one number.
3586 { // Loop over group separators
3588 if (eScannedType
== SvNumFormatType::SCIENTIFIC
) // E only at end
3593 if ( i
< nStringsCnt
&& !ScanMidString( sStrArray
[i
], i
, j
) )
3600 if (eScannedType
== SvNumFormatType::DATE
|| // long date or
3601 eScannedType
== SvNumFormatType::TIME
|| // long time or
3602 eScannedType
== SvNumFormatType::UNDEFINED
) // long number
3604 for (sal_uInt16 k
= j
; k
< nNumericsCnt
-1; k
++)
3606 if (eScannedType
== SvNumFormatType::SCIENTIFIC
) // E only at endd
3611 if ( i
< nStringsCnt
&& !ScanMidString( sStrArray
[i
], i
, j
) )
3619 if (i
< nStringsCnt
&& !ScanEndString( sStrArray
[i
] ))
3623 if (eSetType
== SvNumFormatType::FRACTION
) // -1,200,100. as fraction
3625 if (!bNegCheck
&& // no sign '('
3626 eScannedType
== SvNumFormatType::UNDEFINED
&&
3627 (nDecPos
== 0 || nDecPos
== 3) // no decimal separator or at end
3630 eScannedType
= SvNumFormatType::FRACTION
;
3631 nMatchedAllStrings
&= ~nMatchedVirgin
;
3635 if ( eScannedType
== SvNumFormatType::FRACTION
&& nDecPos
)
3637 return false; // #36857# not a real fraction
3642 if (eScannedType
== SvNumFormatType::UNDEFINED
)
3644 nMatchedAllStrings
&= ~nMatchedVirgin
;
3645 // did match including nMatchedUsedAsReturn
3646 bool bDidMatch
= (nMatchedAllStrings
!= 0);
3647 if ( nMatchedAllStrings
)
3649 bool bMatch
= mpFormat
&& mpFormat
->IsNumForStringElementCountEqual(
3650 nStringScanNumFor
, nStringsCnt
, nNumericsCnt
);
3653 nMatchedAllStrings
= 0;
3656 if ( nMatchedAllStrings
)
3658 // A type DEFINED means that no category could be assigned to the
3659 // overall format because of mixed type subformats. Use the scan
3660 // matched subformat's type if any.
3661 SvNumFormatType eForType
= eSetType
;
3662 if ((eForType
== SvNumFormatType::UNDEFINED
|| eForType
== SvNumFormatType::DEFINED
) && mpFormat
)
3663 eForType
= mpFormat
->GetNumForInfoScannedType( nStringScanNumFor
);
3664 if (eForType
!= SvNumFormatType::UNDEFINED
&& eForType
!= SvNumFormatType::DEFINED
)
3665 eScannedType
= eForType
;
3667 eScannedType
= SvNumFormatType::NUMBER
;
3669 else if ( bDidMatch
)
3671 // Accept a plain fractional number like 123.45 as there may be a
3672 // decimal separator also present as literal like in a 0"."0 weirdo
3674 if (nDecPos
!= 2 || nNumericsCnt
!= 2)
3676 eScannedType
= SvNumFormatType::NUMBER
;
3680 eScannedType
= SvNumFormatType::NUMBER
;
3681 // everything else should have been recognized by now
3684 else if ( eScannedType
== SvNumFormatType::DATE
)
3686 // the very relaxed date input checks may interfere with a preset format
3687 nMatchedAllStrings
&= ~nMatchedVirgin
;
3688 bool bWasReturn
= ((nMatchedAllStrings
& nMatchedUsedAsReturn
) != 0);
3689 if ( nMatchedAllStrings
)
3691 bool bMatch
= mpFormat
&& mpFormat
->IsNumForStringElementCountEqual(
3692 nStringScanNumFor
, nStringsCnt
, nNumericsCnt
);
3695 nMatchedAllStrings
= 0;
3698 if ( nMatchedAllStrings
)
3700 // A type DEFINED means that no category could be assigned to the
3701 // overall format because of mixed type subformats. Do not override
3702 // the scanned type in this case. Otherwise in IsNumberFormat() the
3703 // first numeric particle would be accepted as number.
3704 SvNumFormatType eForType
= eSetType
;
3705 if ((eForType
== SvNumFormatType::UNDEFINED
|| eForType
== SvNumFormatType::DEFINED
) && mpFormat
)
3706 eForType
= mpFormat
->GetNumForInfoScannedType( nStringScanNumFor
);
3707 if (eForType
!= SvNumFormatType::UNDEFINED
&& eForType
!= SvNumFormatType::DEFINED
)
3708 eScannedType
= eForType
;
3710 else if ( bWasReturn
)
3717 nMatchedAllStrings
= 0; // reset flag to no substrings matched
3724 * Return true or false depending on the nMatched... state and remember usage
3726 bool ImpSvNumberInputScan::MatchedReturn()
3728 if ( nMatchedAllStrings
& ~nMatchedVirgin
)
3730 nMatchedAllStrings
|= nMatchedUsedAsReturn
;
3738 * Initialize uppercase months and weekdays
3740 void ImpSvNumberInputScan::InitText()
3742 sal_Int32 j
, nElems
;
3743 const CharClass
* pChrCls
= mrCurrentLanguageData
.GetCharClass();
3744 const CalendarWrapper
* pCal
= mrCurrentLanguageData
.GetCalendar();
3746 pUpperMonthText
.reset();
3747 pUpperAbbrevMonthText
.reset();
3748 css::uno::Sequence
< css::i18n::CalendarItem2
> xElems
= pCal
->getMonths();
3749 nElems
= xElems
.getLength();
3750 pUpperMonthText
.reset( new OUString
[nElems
] );
3751 pUpperAbbrevMonthText
.reset( new OUString
[nElems
] );
3752 for ( j
= 0; j
< nElems
; j
++ )
3754 pUpperMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].FullName
);
3755 pUpperAbbrevMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].AbbrevName
);
3758 pUpperGenitiveMonthText
.reset();
3759 pUpperGenitiveAbbrevMonthText
.reset();
3760 xElems
= pCal
->getGenitiveMonths();
3761 bScanGenitiveMonths
= (nElems
!= xElems
.getLength());
3762 nElems
= xElems
.getLength();
3763 pUpperGenitiveMonthText
.reset( new OUString
[nElems
] );
3764 pUpperGenitiveAbbrevMonthText
.reset( new OUString
[nElems
] );
3765 for ( j
= 0; j
< nElems
; j
++ )
3767 pUpperGenitiveMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].FullName
);
3768 pUpperGenitiveAbbrevMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].AbbrevName
);
3769 if (!bScanGenitiveMonths
&&
3770 (pUpperGenitiveMonthText
[j
] != pUpperMonthText
[j
] ||
3771 pUpperGenitiveAbbrevMonthText
[j
] != pUpperAbbrevMonthText
[j
]))
3773 bScanGenitiveMonths
= true;
3777 pUpperPartitiveMonthText
.reset();
3778 pUpperPartitiveAbbrevMonthText
.reset();
3779 xElems
= pCal
->getPartitiveMonths();
3780 bScanPartitiveMonths
= (nElems
!= xElems
.getLength());
3781 nElems
= xElems
.getLength();
3782 pUpperPartitiveMonthText
.reset( new OUString
[nElems
] );
3783 pUpperPartitiveAbbrevMonthText
.reset( new OUString
[nElems
] );
3784 for ( j
= 0; j
< nElems
; j
++ )
3786 pUpperPartitiveMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].FullName
);
3787 pUpperPartitiveAbbrevMonthText
[j
] = pChrCls
->uppercase( xElems
[j
].AbbrevName
);
3788 if (!bScanPartitiveMonths
&&
3789 (pUpperPartitiveMonthText
[j
] != pUpperGenitiveMonthText
[j
] ||
3790 pUpperPartitiveAbbrevMonthText
[j
] != pUpperGenitiveAbbrevMonthText
[j
]))
3792 bScanPartitiveMonths
= true;
3796 pUpperDayText
.reset();
3797 pUpperAbbrevDayText
.reset();
3798 xElems
= pCal
->getDays();
3799 nElems
= xElems
.getLength();
3800 pUpperDayText
.reset( new OUString
[nElems
] );
3801 pUpperAbbrevDayText
.reset( new OUString
[nElems
] );
3802 for ( j
= 0; j
< nElems
; j
++ )
3804 pUpperDayText
[j
] = pChrCls
->uppercase( xElems
[j
].FullName
);
3805 pUpperAbbrevDayText
[j
] = pChrCls
->uppercase( xElems
[j
].AbbrevName
);
3808 bTextInitialized
= true;
3813 * MUST be called if International/Locale is changed
3815 void ImpSvNumberInputScan::ChangeIntl()
3817 sal_Unicode cDecSep
= mrCurrentLanguageData
.GetNumDecimalSep()[0];
3818 bDecSepInDateSeps
= ( cDecSep
== '-' ||
3819 cDecSep
== mrCurrentLanguageData
.GetDateSep()[0] );
3820 if (!bDecSepInDateSeps
)
3822 sal_Unicode cDecSepAlt
= mrCurrentLanguageData
.GetNumDecimalSepAlt().toChar();
3823 bDecSepInDateSeps
= cDecSepAlt
&& (cDecSepAlt
== '-' || cDecSepAlt
== mrCurrentLanguageData
.GetDateSep()[0]);
3825 bTextInitialized
= false;
3826 aUpperCurrSymbol
.clear();
3827 InvalidateDateAcceptancePatterns();
3831 void ImpSvNumberInputScan::InvalidateDateAcceptancePatterns()
3833 if (sDateAcceptancePatterns
.hasElements())
3835 sDateAcceptancePatterns
= css::uno::Sequence
< OUString
>();
3840 void ImpSvNumberInputScan::ChangeNullDate( const sal_uInt16 Day
,
3841 const sal_uInt16 Month
,
3842 const sal_Int16 Year
)
3844 moNullDate
= Date(Day
, Month
, Year
);
3849 * Does rString represent a number (also date, time et al)
3851 bool ImpSvNumberInputScan::IsNumberFormat( const OUString
& rString
, // string to be analyzed
3852 SvNumFormatType
& F_Type
, // IN: old type, OUT: new type
3853 double& fOutNumber
, // OUT: number if convertible
3854 const SvNumberformat
* pFormat
, // maybe a number format to match against
3855 const NativeNumberWrapper
& rNatNum
,
3856 SvNumInputOptions eInputOptions
)
3858 bool res
; // return value
3860 eSetType
= F_Type
; // old type set
3862 if ( !rString
.getLength() )
3866 else if (rString
.getLength() > 308) // arbitrary
3872 // NoMoreUpperNeeded, all comparisons on UpperCase
3873 OUString aString
= mrCurrentLanguageData
.GetCharClass()->uppercase( rString
);
3874 // convert native number to ASCII if necessary
3875 TransformInput(rNatNum
, mrCurrentLanguageData
, aString
);
3876 res
= IsNumberFormatMain( aString
, pFormat
);
3881 // Accept signed date only for ISO date with at least four digits in
3882 // year to not have an input of -M-D-Y arbitrarily recognized. The
3883 // final order is only determined in GetDateRef().
3884 // Also accept for Y/M/D date pattern match, i.e. if the first number
3886 // Accept only if the year immediately follows the sign character with
3887 // no space in between.
3888 if (nSign
&& (eScannedType
== SvNumFormatType::DATE
||
3889 eScannedType
== SvNumFormatType::DATETIME
) && mbEraCE
== kDefaultEra
&&
3890 (IsDatePatternNumberOfType(0,'Y') || (MayBeIso8601() && sStrArray
[nNums
[0]].getLength() >= 4)))
3892 const sal_Unicode c
= sStrArray
[0][sStrArray
[0].getLength()-1];
3893 if (c
== '-' || c
== '+')
3895 // A '+' sign doesn't change the era.
3897 mbEraCE
= false; // BCE
3901 if ( bNegCheck
|| // ')' not found for '('
3902 (nSign
&& (eScannedType
== SvNumFormatType::DATE
||
3903 eScannedType
== SvNumFormatType::DATETIME
))) // signed date/datetime
3908 { // check count of partial number strings
3909 switch (eScannedType
)
3911 case SvNumFormatType::PERCENT
:
3912 case SvNumFormatType::CURRENCY
:
3913 case SvNumFormatType::NUMBER
:
3914 if (nDecPos
== 1) // .05
3916 // Matched MidStrings function like group separators, but
3917 // there can't be an integer part numeric input, so
3918 // effectively 0 thousands groups.
3919 if ( nMatchedAllStrings
)
3923 else if ( nNumericsCnt
!= 1 )
3928 else if (nDecPos
== 2) // 1.05
3930 // Matched MidStrings function like group separators, but
3931 // let a decimal separator override a literal separator
3932 // string; like 0"." with input 123.45
3933 if ( nMatchedAllStrings
)
3935 if (nNumericsCnt
== 2)
3939 // Assume that if there was a decimal separator
3940 // matching also a literal string then it was the
3941 // last. We could find the last possible match to
3942 // support literals in fractions, but really..
3943 nThousand
= nNumericsCnt
- 1;
3946 else if ( nNumericsCnt
!= nThousand
+2 )
3951 else // 1,100 or 1,100.
3953 // matched MidStrings function like group separators
3954 if ( nMatchedAllStrings
)
3956 nThousand
= nNumericsCnt
- 1;
3958 else if ( nNumericsCnt
!= nThousand
+1 )
3965 case SvNumFormatType::SCIENTIFIC
: // 1.0e-2
3966 if (nDecPos
== 1) // .05
3968 if (nNumericsCnt
!= 2)
3973 else if (nDecPos
== 2) // 1.05
3975 if (nNumericsCnt
!= nThousand
+3)
3980 else // 1,100 or 1,100.
3982 if (nNumericsCnt
!= nThousand
+2)
3989 case SvNumFormatType::DATE
:
3990 if (nMonth
< 0 && nDayOfWeek
< 0 && nNumericsCnt
== 3)
3992 // If both, short month name and day of week name were
3993 // detected, and also numbers for full date, assume that we
3994 // have a day of week instead of month name.
3999 { // month name and numbers
4000 if (nNumericsCnt
> 2)
4007 if (nNumericsCnt
> 3)
4013 // Even if a date pattern was matched, for abbreviated
4014 // pattern like "D.M." an input of "D.M. #" was
4015 // accepted because # could had been a time. Here we do
4016 // not have a combined date/time input though and #
4017 // would be taken as Year in this example, which it is
4018 // not. The count of numbers in pattern must match the
4019 // count of numbers in input.
4020 res
= (GetDatePatternNumbers() == nNumericsCnt
)
4021 || IsAcceptableIso8601() || nMatchedAllStrings
;
4026 case SvNumFormatType::TIME
:
4028 { // hundredth seconds included
4029 if (nNumericsCnt
> 4)
4036 if (nNumericsCnt
> 3)
4043 case SvNumFormatType::DATETIME
:
4044 if (nMonth
< 0 && nDayOfWeek
< 0 && nNumericsCnt
>= 5)
4046 // If both, abbreviated month name and day of week name
4047 // were detected, and also at least numbers for full date
4048 // plus time including minutes, assume that we have a day
4049 // of week instead of month name.
4054 { // month name and numbers
4056 { // hundredth seconds included
4057 if (nNumericsCnt
> 6)
4064 if (nNumericsCnt
> 5)
4073 { // hundredth seconds included
4074 if (nNumericsCnt
> 7)
4081 if (nNumericsCnt
> 6)
4088 res
= IsAcceptedDatePattern( nNums
[0]) || MayBeIso8601() || nMatchedAllStrings
;
4099 OUStringBuffer sResString
;
4102 { // we finally have a number
4103 switch (eScannedType
)
4105 case SvNumFormatType::LOGICAL
:
4108 fOutNumber
= 1.0; // True
4110 else if (nLogical
== -1)
4112 fOutNumber
= 0.0; // False
4116 res
= false; // Oops
4120 case SvNumFormatType::PERCENT
:
4121 case SvNumFormatType::CURRENCY
:
4122 case SvNumFormatType::NUMBER
:
4123 case SvNumFormatType::SCIENTIFIC
:
4124 case SvNumFormatType::DEFINED
: // if no category detected handle as number
4125 if ( nDecPos
== 1 ) // . at start
4127 sResString
.append("0.");
4130 for ( k
= 0; k
<= nThousand
; k
++)
4132 sResString
.append(sStrArray
[nNums
[k
]]); // integer part
4134 if ( nDecPos
== 2 && k
< nNumericsCnt
) // . somewhere
4136 sResString
.append('.');
4137 sal_uInt16 nStop
= (eScannedType
== SvNumFormatType::SCIENTIFIC
?
4138 nNumericsCnt
-1 : nNumericsCnt
);
4139 for ( ; k
< nStop
; k
++)
4141 sResString
.append(sStrArray
[nNums
[k
]]); // fractional part
4145 if (eScannedType
!= SvNumFormatType::SCIENTIFIC
)
4147 fOutNumber
= StringToDouble(sResString
);
4150 { // append exponent
4151 sResString
.append('E');
4154 sResString
.append('-');
4156 sResString
.append(sStrArray
[nNums
[nNumericsCnt
-1]]);
4157 rtl_math_ConversionStatus eStatus
;
4158 fOutNumber
= ::rtl::math::stringToDouble( sResString
, '.', ',', &eStatus
);
4159 if ( eStatus
== rtl_math_ConversionStatus_OutOfRange
)
4161 F_Type
= SvNumFormatType::TEXT
; // overflow/underflow -> Text
4168 fOutNumber
= DBL_MAX
;
4174 if ( nStringScanSign
)
4178 nSign
*= nStringScanSign
;
4182 nSign
= nStringScanSign
;
4187 fOutNumber
= -fOutNumber
;
4190 if (eScannedType
== SvNumFormatType::PERCENT
)
4196 case SvNumFormatType::FRACTION
:
4197 if (nNumericsCnt
== 1)
4199 fOutNumber
= StringToDouble(sStrArray
[nNums
[0]]);
4201 else if (nNumericsCnt
== 2)
4205 sResString
= sStrArray
[nNums
[0]];
4206 sResString
.append(sStrArray
[nNums
[1]]); // integer part
4207 fOutNumber
= StringToDouble(sResString
);
4211 double fNumerator
= StringToDouble(sStrArray
[nNums
[0]]);
4212 double fDenominator
= StringToDouble(sStrArray
[nNums
[1]]);
4213 if (fDenominator
!= 0.0)
4215 fOutNumber
= fNumerator
/fDenominator
;
4223 else // nNumericsCnt > 2
4226 sResString
= sStrArray
[nNums
[0]];
4229 for (; k
<= nThousand
; k
++)
4231 sResString
.append(sStrArray
[nNums
[k
]]);
4234 fOutNumber
= StringToDouble(sResString
);
4236 if (k
== nNumericsCnt
-2)
4238 double fNumerator
= StringToDouble(sStrArray
[nNums
[k
]]);
4239 double fDenominator
= StringToDouble(sStrArray
[nNums
[k
+ 1]]);
4240 if (fDenominator
!= 0.0)
4242 fOutNumber
+= fNumerator
/fDenominator
;
4251 if ( nStringScanSign
)
4255 nSign
*= nStringScanSign
;
4259 nSign
= nStringScanSign
;
4264 fOutNumber
= -fOutNumber
;
4268 case SvNumFormatType::TIME
:
4269 res
= GetTimeRef(fOutNumber
, 0, nNumericsCnt
, eInputOptions
);
4272 fOutNumber
= -fOutNumber
;
4276 case SvNumFormatType::DATE
:
4277 res
= GetDateRef( fOutNumber
, k
);
4280 case SvNumFormatType::DATETIME
:
4281 res
= GetDateRef( fOutNumber
, k
);
4285 res
= GetTimeRef( fTime
, k
, nNumericsCnt
- k
, eInputOptions
);
4286 fOutNumber
+= fTime
;
4291 SAL_WARN( "svl.numbers", "Some number recognized but what's it?" );
4297 if (res
) // overflow/underflow -> Text
4299 if (fOutNumber
< -DBL_MAX
) // -1.7E308
4301 F_Type
= SvNumFormatType::TEXT
;
4302 fOutNumber
= -DBL_MAX
;
4305 else if (fOutNumber
> DBL_MAX
) // 1.7E308
4307 F_Type
= SvNumFormatType::TEXT
;
4308 fOutNumber
= DBL_MAX
;
4315 eScannedType
= SvNumFormatType::TEXT
;
4319 F_Type
= eScannedType
;
4323 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */