[Libreoffice-commits] core.git: 2 commits - svl/source

Eike Rathke erack at redhat.com
Thu May 4 15:02:08 UTC 2017


 svl/source/numbers/zforfind.cxx |  111 +++++++++++++++++++++++++++++++++++++---
 svl/source/numbers/zforfind.hxx |   10 +++
 svl/source/numbers/zformat.cxx  |   44 +++++++++++++++
 3 files changed, 157 insertions(+), 8 deletions(-)

New commits:
commit ae7401002a0b6dabe4136dd8c5654ee2a54e7db6
Author: Eike Rathke <erack at redhat.com>
Date:   Thu May 4 17:01:08 2017 +0200

    Number formatter: handle negative signed year date as BCE
    
    Accept input -YYYY-MM-DD or -Y/M/D or M/D/-Y or D.M.-Y ... and display likewise
    if no era word is to be displayed.
    
    Change-Id: I199d34354d5b91dbe2a6a6ac3ae4b50d5dbde670

diff --git a/svl/source/numbers/zforfind.cxx b/svl/source/numbers/zforfind.cxx
index c45050e31093..08fc26e20435 100644
--- a/svl/source/numbers/zforfind.cxx
+++ b/svl/source/numbers/zforfind.cxx
@@ -65,6 +65,7 @@ const sal_uInt8 ImpSvNumberInputScan::nMatchedUsedAsReturn = 0x10;
 
 static const sal_Unicode cNoBreakSpace = 0xA0;
 static const sal_Unicode cNarrowNoBreakSpace = 0x202F;
+static const sal_Int16 kDefaultEra = 1;     // Gregorian CE, positive year
 
 ImpSvNumberInputScan::ImpSvNumberInputScan( SvNumberFormatter* pFormatterP )
         :
@@ -122,6 +123,7 @@ void ImpSvNumberInputScan::Reset()
     nAmPm        = 0;
     nPosThousandString = 0;
     nLogical     = 0;
+    mnEra        = kDefaultEra;
     nStringScanNumFor = 0;
     nStringScanSign = 0;
     nMatchedAllStrings = nMatchedVirgin;
@@ -999,12 +1001,15 @@ sal_uInt16 ImpSvNumberInputScan::ImplGetYear( sal_uInt16 nIndex )
     sal_uInt16 nYear = 0;
 
     sal_Int32 nLen = sStrArray[nNums[nIndex]].getLength();
-    if (nLen <= 4)
+    // 16-bit integer year width can have 5 digits, allow for one additional
+    // leading zero as convention.
+    if (nLen <= 6)
     {
         nYear = (sal_uInt16) sStrArray[nNums[nIndex]].toInt32();
+        // A year in another, not Gregorian CE era is never expanded.
         // A year < 100 entered with at least 3 digits with leading 0 is taken
         // as is without expansion.
-        if (nYear < 100 && nLen < 3)
+        if (mnEra == kDefaultEra && nYear < 100 && nLen < 3)
         {
             nYear = SvNumberFormatter::ExpandTwoDigitYear( nYear, nYear2000 );
         }
@@ -1123,6 +1128,26 @@ bool ImpSvNumberInputScan::MayBeMonthDate()
 }
 
 
+/** If a string is a separator plus '-' minus sign preceding a 'Y' year in
+    a date pattern at position nPat.
+ */
+static bool lcl_IsSignedYearSep( const OUString& rStr, const OUString& rPat, sal_Int32 nPat )
+{
+    bool bOk = false;
+    sal_Int32 nLen = rStr.getLength();
+    if (nLen > 1 && rStr[nLen-1] == '-')
+    {
+        --nLen;
+        if (nPat + nLen < rPat.getLength() && rPat[nPat+nLen] == 'Y')
+        {
+            // Signed year is possible.
+            bOk = (rPat.indexOf( rStr.copy( 0, nLen), nPat) == nPat);
+        }
+    }
+    return bOk;
+}
+
+
 bool ImpSvNumberInputScan::IsAcceptedDatePattern( sal_uInt16 nStartPatternAt )
 {
     if (nAcceptedDatePattern >= -1)
@@ -1210,6 +1235,10 @@ bool ImpSvNumberInputScan::IsAcceptedDatePattern( sal_uInt16 nStartPatternAt )
                     {
                         nPat += nLen - 1;
                     }
+                    else if ((bOk = lcl_IsSignedYearSep( sStrArray[nNext], rPat, nPat)))
+                    {
+                        nPat += nLen - 2;
+                    }
                     else if (nPat + nLen > rPat.getLength() && sStrArray[nNext][ nLen - 1 ] == ' ')
                     {
                         using namespace comphelper::string;
@@ -1295,7 +1324,7 @@ bool ImpSvNumberInputScan::IsAcceptedDatePattern( sal_uInt16 nStartPatternAt )
 }
 
 
-bool ImpSvNumberInputScan::SkipDatePatternSeparator( sal_uInt16 nParticle, sal_Int32 & rPos )
+bool ImpSvNumberInputScan::SkipDatePatternSeparator( sal_uInt16 nParticle, sal_Int32 & rPos, bool & rSignedYear )
 {
     // If not initialized yet start with first number, if any.
     if (!IsAcceptedDatePattern( (nAnzNums ? nNums[0] : 0)))
@@ -1321,6 +1350,12 @@ bool ImpSvNumberInputScan::SkipDatePatternSeparator( sal_uInt16 nParticle, sal_I
             {
                 const sal_Int32 nLen = sStrArray[nNext].getLength();
                 bool bOk = (rPat.indexOf( sStrArray[nNext], nPat) == nPat);
+                if (!bOk)
+                {
+                    bOk = lcl_IsSignedYearSep( sStrArray[nNext], rPat, nPat);
+                    if (bOk)
+                        rSignedYear = true;
+                }
                 if (!bOk && (nPat + nLen > rPat.getLength() && sStrArray[nNext][ nLen - 1 ] == ' '))
                 {
                     // The same ugly trailing blanks check as in
@@ -1358,6 +1393,30 @@ sal_uInt16 ImpSvNumberInputScan::GetDatePatternNumbers()
 }
 
 
+bool ImpSvNumberInputScan::IsDatePatternNumberOfType( sal_uInt16 nNumber, sal_Unicode cType )
+{
+    if (GetDatePatternNumbers() <= nNumber)
+        return false;
+
+    sal_uInt16 nNum = 0;
+    const OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
+    for (sal_Int32 nPat = 0; nPat < rPat.getLength(); ++nPat)
+    {
+        switch (rPat[nPat])
+        {
+            case 'Y':
+            case 'M':
+            case 'D':
+                if (nNum == nNumber)
+                    return rPat[nPat] == cType;
+                ++nNum;
+            break;
+        }
+    }
+    return false;
+}
+
+
 sal_uInt32 ImpSvNumberInputScan::GetDatePatternOrder()
 {
     // If not initialized yet start with first number, if any.
@@ -1910,6 +1969,9 @@ input for the following reasons:
             break;
         }   // switch (nAnzNums)
 
+        if (mnEra != kDefaultEra)
+            pCal->setValue( CalendarFieldIndex::ERA, mnEra );
+
         if ( res && pCal->isValid() )
         {
             double fDiff = DateTime(*pNullDate) - pCal->getEpochStart();
@@ -2231,8 +2293,9 @@ bool ImpSvNumberInputScan::ScanMidString( const OUString& rString,
         }
         else if (nDecPos == 2)                      // . dup: 12.4.
         {
+            bool bSignedYear = false;
             if (bDecSepInDateSeps ||                // . also date separator
-                SkipDatePatternSeparator( nStringPos, nPos))
+                SkipDatePatternSeparator( nStringPos, nPos, bSignedYear))
             {
                 if ( eScannedType != css::util::NumberFormat::UNDEFINED &&
                      eScannedType != css::util::NumberFormat::DATE &&
@@ -2311,7 +2374,8 @@ bool ImpSvNumberInputScan::ScanMidString( const OUString& rString,
     }
 
     const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
-    bool bDate = SkipDatePatternSeparator( nStringPos, nPos);   // 12/31  31.12.  12/31/1999  31.12.1999
+    bool bSignedYear = false;
+    bool bDate = SkipDatePatternSeparator( nStringPos, nPos, bSignedYear);   // 12/31  31.12.  12/31/1999  31.12.1999
     if (!bDate)
     {
         const OUString& rDate = pFormatter->GetDateSep();
@@ -2348,6 +2412,13 @@ bool ImpSvNumberInputScan::ScanMidString( const OUString& rString,
             }
             SkipBlanks(rString, nPos);
         }
+        if (bSignedYear)
+        {
+            if (mnEra != kDefaultEra)               // signed year twice?
+                return MatchedReturn();
+
+            mnEra = 0;  // BCE
+        }
     }
 
     const sal_Int32 nMonthStart = nPos;
@@ -2568,8 +2639,9 @@ bool ImpSvNumberInputScan::ScanEndString( const OUString& rString,
         }
         else if (nDecPos == 2)                      // . dup: 12.4.
         {
+            bool bSignedYear = false;
             if (bDecSepInDateSeps ||                // . also date separator
-                SkipDatePatternSeparator( nAnzStrings-1, nPos))
+                SkipDatePatternSeparator( nAnzStrings-1, nPos, bSignedYear))
             {
                 if ( eScannedType != css::util::NumberFormat::UNDEFINED &&
                      eScannedType != css::util::NumberFormat::DATE &&
@@ -2685,7 +2757,8 @@ bool ImpSvNumberInputScan::ScanEndString( const OUString& rString,
         }
     }
 
-    bool bDate = SkipDatePatternSeparator( nAnzStrings-1, nPos);   // 12/31  31.12.  12/31/1999  31.12.1999
+    bool bSignedYear = false;
+    bool bDate = SkipDatePatternSeparator( nAnzStrings-1, nPos, bSignedYear);   // 12/31  31.12.  12/31/1999  31.12.1999
     if (!bDate)
     {
         const OUString& rDate = pFormatter->GetDateSep();
@@ -3444,6 +3517,26 @@ bool ImpSvNumberInputScan::IsNumberFormat( const OUString& rString,         // s
 
     if (res)
     {
+        // Accept signed date only for ISO date with at least four digits in
+        // year to not have an input of -M-D-Y arbitrarily recognized. The
+        // final order is only determined in GetDateRef().
+        // Also accept for Y/M/D date pattern match, i.e. if the first number
+        // is year.
+        // Accept only if the year immediately follows the sign character with
+        // no space in between.
+        if (nSign && (eScannedType == css::util::NumberFormat::DATE ||
+                      eScannedType == css::util::NumberFormat::DATETIME) && mnEra == kDefaultEra &&
+                (IsDatePatternNumberOfType(0,'Y') || (MayBeIso8601() && sStrArray[nNums[0]].getLength() >= 4)))
+        {
+            const sal_Unicode c = sStrArray[0][sStrArray[0].getLength()-1];
+            if (c == '-' || c == '+')
+            {
+                // A '+' sign doesn't change the era.
+                if (nSign < 0)
+                    mnEra = 0;  // BCE
+                nSign = 0;
+            }
+        }
         if ( nNegCheck ||                             // ')' not found for '('
              (nSign && (eScannedType == css::util::NumberFormat::DATE ||
                         eScannedType == css::util::NumberFormat::DATETIME))) // signed date/datetime
diff --git a/svl/source/numbers/zforfind.hxx b/svl/source/numbers/zforfind.hxx
index e7d0be0d9acf..6f95d4c6e6b3 100644
--- a/svl/source/numbers/zforfind.hxx
+++ b/svl/source/numbers/zforfind.hxx
@@ -115,6 +115,7 @@ private:
     short  nESign;                              // Sign of exponent
     short  nAmPm;                               // +1 AM, -1 PM, 0 if none
     short  nLogical;                            // -1 => False, 1 => True
+    sal_Int16 mnEra;                            // Era if date, 0 => BCE, 1 => CE (currently only Gregorian)
     sal_uInt16 nThousand;                       // Count of group (AKA thousand) separators
     sal_uInt16 nPosThousandString;              // Position of concatenated 000,000,000 string
     short  eScannedType;                        // Scanned type
@@ -386,14 +387,21 @@ private:
     /** Sets (not advances!) rPos to sStrArray[nParticle].getLength() if string
         matches separator in pattern at nParticle.
 
+        Also detects a signed year case like M/D/-Y
+
         @returns TRUE if separator matched.
      */
-    bool SkipDatePatternSeparator( sal_uInt16 nParticle, sal_Int32 & rPos );
+    bool SkipDatePatternSeparator( sal_uInt16 nParticle, sal_Int32 & rPos, bool & rSignedYear );
 
     /** Returns count of numbers in accepted date pattern.
      */
     sal_uInt16 GetDatePatternNumbers();
 
+    /** Whether numeric string nNumber is of type cType in accepted date
+        pattern, 'Y', 'M' or 'D'.
+     */
+    bool IsDatePatternNumberOfType( sal_uInt16 nNumber, sal_Unicode cType );
+
     /** Obtain order of accepted date pattern coded as, for example,
         ('D'<<16)|('M'<<8)|'Y'
     */
diff --git a/svl/source/numbers/zformat.cxx b/svl/source/numbers/zformat.cxx
index fa0b536b5b69..24da6cb89549 100644
--- a/svl/source/numbers/zformat.cxx
+++ b/svl/source/numbers/zformat.cxx
@@ -3463,6 +3463,30 @@ bool SvNumberformat::ImpIsIso8601( const ImpSvNumFor& rNumFor ) const
     return bIsIso;
 }
 
+static bool lcl_hasEra( const ImpSvNumFor& rNumFor )
+{
+    const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
+    const sal_uInt16 nAnz = rNumFor.GetCount();
+    for ( sal_uInt16 i = 0; i < nAnz; i++ )
+    {
+        switch ( rInfo.nTypeArray[i] )
+        {
+            case NF_KEY_RR :
+            case NF_KEY_G :
+            case NF_KEY_GG :
+            case NF_KEY_GGG :
+                return true;
+        }
+    }
+    return false;
+}
+
+static bool lcl_isSignedYear( const CalendarWrapper& rCal, const ImpSvNumFor& rNumFor )
+{
+    return rCal.getValue( css::i18n::CalendarFieldIndex::ERA ) == 0 &&
+        rCal.getUniqueID() == GREGORIAN && !lcl_hasEra( rNumFor );
+}
+
 bool SvNumberformat::ImpGetDateOutput(double fNumber,
                                       sal_uInt16 nIx,
                                       OUStringBuffer& sBuff)
@@ -3583,6 +3607,11 @@ bool SvNumberformat::ImpGetDateOutput(double fNumber,
             {
                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
             }
+            // Prepend a minus sign if Gregorian BCE and era is not displayed.
+            if (lcl_isSignedYear( rCal, NumFor[nIx] ))
+            {
+                sBuff.append('-');
+            }
             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
             if ( bOtherCalendar )
             {
@@ -3594,6 +3623,11 @@ bool SvNumberformat::ImpGetDateOutput(double fNumber,
             {
                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
             }
+            // Prepend a minus sign if Gregorian BCE and era is not displayed.
+            if (lcl_isSignedYear( rCal, NumFor[nIx] ))
+            {
+                sBuff.append('-');
+            }
             aYear = rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum );
             if (aYear.getLength() < 4)
             {
@@ -3930,6 +3964,11 @@ bool SvNumberformat::ImpGetDateTimeOutput(double fNumber,
             {
                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
             }
+            // Prepend a minus sign if Gregorian BCE and era is not displayed.
+            if (lcl_isSignedYear( rCal, NumFor[nIx] ))
+            {
+                sBuff.append('-');
+            }
             sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
             if ( bOtherCalendar )
             {
@@ -3941,6 +3980,11 @@ bool SvNumberformat::ImpGetDateTimeOutput(double fNumber,
             {
                 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
             }
+            // Prepend a minus sign if Gregorian BCE and era is not displayed.
+            if (lcl_isSignedYear( rCal, NumFor[nIx] ))
+            {
+                sBuff.append('-');
+            }
             aYear = rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum );
             if (aYear.getLength() < 4)
             {
commit 83f1381039898329a3e67330c1e59c039311363f
Author: Eike Rathke <erack at redhat.com>
Date:   Thu May 4 16:28:09 2017 +0200

    Bail out early for just a sign as start string
    
    Change-Id: I9852aa70f6cf61c00d882cd7ff1171275c5b9c24

diff --git a/svl/source/numbers/zforfind.cxx b/svl/source/numbers/zforfind.cxx
index 59eb48e595b7..c45050e31093 100644
--- a/svl/source/numbers/zforfind.cxx
+++ b/svl/source/numbers/zforfind.cxx
@@ -2045,6 +2045,10 @@ bool ImpSvNumberInputScan::ScanStartString( const OUString& rString,
         }
     }
 
+    // Bail out early for just a sign.
+    if (nSign && nPos == rString.getLength())
+        return true;
+
     if ( GetDecSep(rString, nPos) )                 // decimal separator in start string
     {
         nDecPos = 1;


More information about the Libreoffice-commits mailing list