strict date recognition using locale's separator and ISO 8601

- Removed separators '-', '.' and '/' working in all combinations in all
  locales forcing a date even for 23/12.99 (if locale has DMY order)
* Only the locale's date separator is accepted, plus '-' if the input may
  represent an ISO 8601 date yyyy-mm-dd, check is lax though on minimum
  digits, y-m-d is also accepted.
* Additionally, accept yy-month-dd or dd-month-yy with month name. Year must
  be <1 or >31, 2-digit year magic for values 0,32..99 is applied, or has to
  be prefixed with leading zero.
This commit is contained in:
Eike Rathke 2012-01-10 16:42:01 +01:00
parent 958cdbdcf5
commit 8298a4741d
2 changed files with 71 additions and 17 deletions

View file

@ -145,6 +145,7 @@ void ImpSvNumberInputScan::Reset()
nMatchedAllStrings = nMatchedVirgin;
nMayBeIso8601 = 0;
nTimezonePos = 0;
nMayBeMonthDate = 0;
}
@ -994,6 +995,44 @@ bool ImpSvNumberInputScan::MayBeIso8601()
return nMayBeIso8601 > 1;
}
//---------------------------------------------------------------------------
bool ImpSvNumberInputScan::MayBeMonthDate()
{
if (nMayBeMonthDate == 0)
{
nMayBeMonthDate = 1;
if (nAnzNums >= 2 && nNums[1] < nAnzStrings)
{
// "-Jan-"
const String& rM = sStrArray[nNums[0]+1];
if (rM.Len() >= 3 && rM.GetChar(0) == '-' && rM.GetChar( rM.Len()-1) == '-')
{
// Check year length assuming at least 3 digits (including
// leading zero). Two digit years 1..31 are out of luck here
// and may be taken as day of month.
bool bYear1 = (sStrArray[nNums[0]].Len() >= 3);
bool bYear2 = (sStrArray[nNums[1]].Len() >= 3);
sal_Int32 n;
bool bDay1 = (!bYear1 && (n = sStrArray[nNums[0]].ToInt32()) >= 1 && n <= 31);
bool bDay2 = (!bYear2 && (n = sStrArray[nNums[1]].ToInt32()) >= 1 && n <= 31);
if (bDay1 && !bDay2)
nMayBeMonthDate = 2; // dd-month-yy
else if (!bDay1 && bDay2)
nMayBeMonthDate = 3; // yy-month-dd
else if (bDay1 && bDay2)
{
if (bYear1 && !bYear2)
nMayBeMonthDate = 3; // yy-month-dd
else if (!bYear1 && bYear2)
nMayBeMonthDate = 2; // dd-month-yy
}
}
}
}
return nMayBeMonthDate > 1;
}
//---------------------------------------------------------------------------
// GetDateRef
@ -1283,10 +1322,11 @@ input for the following reasons:
}
break;
case 2: // month in the middle (10 Jan 94)
{
pCal->setValue( CalendarFieldIndex::MONTH, Abs(nMonth)-1 );
switch (DateFmt)
DateFormat eDF = (MayBeMonthDate() ? (nMayBeMonthDate == 2 ? DMY : YMD) : DateFmt);
switch (eDF)
{
case MDY: // yes, "10-Jan-94" is valid
case DMY:
pCal->setValue( CalendarFieldIndex::DAY_OF_MONTH, ImplGetDay(0) );
pCal->setValue( CalendarFieldIndex::YEAR, ImplGetYear(1) );
@ -1299,7 +1339,8 @@ input for the following reasons:
res = false;
break;
}
break;
}
break;
default: // else, e.g. month at the end (94 10 Jan)
res = false;
break;
@ -1687,13 +1728,10 @@ bool ImpSvNumberInputScan::ScanMidString( const String& rString,
const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
const String& rDate = pFormatter->GetDateSep();
const String& rTime = pLoc->getTimeSep();
sal_Unicode cTime = rTime.GetChar(0);
SkipBlanks(rString, nPos);
if ( SkipString(rDate, rString, nPos) // 10., 10-, 10/
|| ((cTime != '.') && SkipChar('.', rString, nPos)) // TRICKY:
|| ((cTime != '/') && SkipChar('/', rString, nPos)) // short boolean
|| ((cTime != '-') && SkipChar('-', rString, nPos)) ) // evaluation!
if (SkipString( rDate, rString, nPos) // 10. 10- 10/
|| ((MayBeIso8601() || MayBeMonthDate())
&& SkipChar( '-', rString, nPos)))
{
if ( eScannedType != NUMBERFORMAT_UNDEFINED // already another type
&& eScannedType != NUMBERFORMAT_DATE) // except date
@ -1752,6 +1790,7 @@ bool ImpSvNumberInputScan::ScanMidString( const String& rString,
SkipBlanks(rString, nPos);
}
const String& rTime = pLoc->getTimeSep();
if ( SkipString(rTime, rString, nPos) ) // time separator?
{
if (nDecPos) // already . => maybe error
@ -1945,7 +1984,6 @@ bool ImpSvNumberInputScan::ScanEndString( const String& rString,
}
const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
const String& rDate = pFormatter->GetDateSep();
const String& rTime = pLoc->getTimeSep();
if ( SkipString(rTime, rString, nPos) ) // 10:
{
@ -1968,11 +2006,10 @@ bool ImpSvNumberInputScan::ScanEndString( const String& rString,
nTimePos = nAnzStrings;
}
sal_Unicode cTime = rTime.GetChar(0);
if ( SkipString(rDate, rString, nPos) // 10., 10-, 10/
|| ((cTime != '.') && SkipChar('.', rString, nPos)) // TRICKY:
|| ((cTime != '/') && SkipChar('/', rString, nPos)) // short boolean
|| ((cTime != '-') && SkipChar('-', rString, nPos)) ) // evaluation!
const String& rDate = pFormatter->GetDateSep();
if (SkipString( rDate, rString, nPos) // 10. 10- 10/
|| ((MayBeIso8601() || MayBeMonthDate())
&& SkipChar( '-', rString, nPos)))
{
if (eScannedType != NUMBERFORMAT_UNDEFINED &&
eScannedType != NUMBERFORMAT_DATE) // already another type
@ -2535,8 +2572,6 @@ void ImpSvNumberInputScan::ChangeIntl()
{
sal_Unicode cDecSep = pFormatter->GetNumDecimalSep().GetChar(0);
bDecSepInDateSeps = ( cDecSep == '-' ||
cDecSep == '/' ||
cDecSep == '.' ||
cDecSep == pFormatter->GetDateSep().GetChar(0) );
bTextInitialized = false;
aUpperCurrSymbol.Erase();

View file

@ -78,6 +78,11 @@ public:
*/
bool MayBeIso8601();
/** Whether input may be a dd-month-yy format, with month name, not
number.
*/
bool MayBeMonthDate();
private:
SvNumberFormatter* pFormatter;
String* pUpperMonthText; // Array of month names, uppercase
@ -136,14 +141,28 @@ private:
sal_uInt16 nTimezonePos; // Index of timezone separator (+1)
/** State of ISO 8601 detection.
0:= don't know yet
1:= no
2:= yes, <=2 digits in year
3:= yes, 3 digits in year
4:= yes, >=4 digits in year
@see MayBeIso8601()
*/
sal_uInt8 nMayBeIso8601;
/** State of dd-month-yy or yy-month-dd detection, with month name.
0:= don't know yet
1:= no
2:= yes, dd-month-yy
3:= yes, yy-month-dd
@see MayBeMonthDate()
*/
sal_uInt8 nMayBeMonthDate;
#ifdef _ZFORFIND_CXX // methods private to implementation
void Reset(); // Reset all variables before start of analysis