[Libreoffice-commits] .: 15 commits - i18npool/inc i18npool/source offapi/com offapi/UnoApi_offapi.mk sc/qa svl/source unotools/inc unotools/source

Eike Rathke erack at kemper.freedesktop.org
Wed Jan 11 04:19:24 PST 2012


 i18npool/inc/localedata.hxx                 |    7 
 i18npool/source/localedata/LocaleNode.cxx   |  227 ++++++++++++++++
 i18npool/source/localedata/data/bg_BG.xml   |    4 
 i18npool/source/localedata/data/de_DE.xml   |    1 
 i18npool/source/localedata/data/en_US.xml   |    1 
 i18npool/source/localedata/data/es_BO.xml   |    2 
 i18npool/source/localedata/data/ko_KR.xml   |    2 
 i18npool/source/localedata/data/ku_TR.xml   |    2 
 i18npool/source/localedata/data/locale.dtd  |   17 +
 i18npool/source/localedata/data/sl_SI.xml   |    1 
 i18npool/source/localedata/data/tr_TR.xml   |   14 -
 i18npool/source/localedata/localedata.cxx   |   27 ++
 offapi/UnoApi_offapi.mk                     |    1 
 offapi/com/sun/star/i18n/LocaleData.idl     |    4 
 offapi/com/sun/star/i18n/XLocaleData4.idl   |   66 ++++
 sc/qa/unit/ucalc.cxx                        |    8 
 svl/source/numbers/zforfind.cxx             |  376 +++++++++++++++++++++++++---
 svl/source/numbers/zforfind.hxx             |   84 ++++++
 svl/source/numbers/zformat.cxx              |   26 +
 unotools/inc/unotools/localedatawrapper.hxx |    5 
 unotools/source/i18n/localedatawrapper.cxx  |   25 +
 21 files changed, 827 insertions(+), 73 deletions(-)

New commits:
commit 51a5998433641aa62c4ce94af0be11d3c6e81852
Author: Eike Rathke <erack at redhat.com>
Date:   Wed Jan 11 12:32:46 2012 +0100

    adapted test case cell input to new date acceptance reality
    
    Cell input in testDataPilotLabels() like 1.2.3 was prefixed with single
    quote / apostrophe to not form a date input but string instead. With the
    new date acceptance, in en-US locale 1.2.3 isn't a date anymore and the
    quote becomes part of the cell content, so the test checking for 1.2.3
    failed because the result was '1.2.3

diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index 18fb569..d45ddb4 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -1390,14 +1390,14 @@ void Test::testDataPilotLabels()
     DPFieldDef aFields[] = {
         { "Software", sheet::DataPilotFieldOrientation_ROW },
         { "Version",  sheet::DataPilotFieldOrientation_COLUMN },
-        { "'1.2.3",   sheet::DataPilotFieldOrientation_DATA }
+        { "1.2.3",    sheet::DataPilotFieldOrientation_DATA }
     };
 
     // Raw data
     const char* aData[][3] = {
-        { "LibreOffice", "'3.3.0", "30" },
-        { "LibreOffice", "'3.3.1", "20" },
-        { "LibreOffice", "'3.4.0", "45" },
+        { "LibreOffice", "3.3.0", "30" },
+        { "LibreOffice", "3.3.1", "20" },
+        { "LibreOffice", "3.4.0", "45" },
     };
 
     size_t nFieldCount = SAL_N_ELEMENTS(aFields);
commit cfec56f89f295002b2b2b9f68cd8f62dcca7c59c
Author: Eike Rathke <erack at redhat.com>
Date:   Tue Jan 10 16:46:17 2012 +0100

    fdo#44636 use date acceptance patterns in number scanner
    
    Get rid of the lax "any 1-2 possible date separators used with 2-3 numbers
    forms a date" date recognition. Use locale data's DateAcceptancePattern
    elements to match against, or ISO 8601 yyyy-mm-dd, or an already applied date
    format.

diff --git a/svl/source/numbers/zforfind.cxx b/svl/source/numbers/zforfind.cxx
index ba315fc..7d6f99e 100644
--- a/svl/source/numbers/zforfind.cxx
+++ b/svl/source/numbers/zforfind.cxx
@@ -146,6 +146,8 @@ void ImpSvNumberInputScan::Reset()
     nMayBeIso8601 = 0;
     nTimezonePos = 0;
     nMayBeMonthDate = 0;
+    nAcceptedDatePattern = -2;
+    nDatePatternStart = 0;
 }
 
 
@@ -1034,6 +1036,227 @@ bool ImpSvNumberInputScan::MayBeMonthDate()
 }
 
 //---------------------------------------------------------------------------
+
+bool ImpSvNumberInputScan::IsAcceptedDatePattern( sal_uInt16 nStartPatternAt )
+{
+    if (nAcceptedDatePattern >= -1)
+        return (nAcceptedDatePattern >= 0);
+
+    if (!nAnzNums)
+        nAcceptedDatePattern = -1;
+    else if (!sDateAcceptancePatterns.getLength())
+    {
+        sDateAcceptancePatterns = pFormatter->GetLocaleData()->getDateAcceptancePatterns();
+        SAL_WARN_IF( !sDateAcceptancePatterns.getLength(), "nf.date", "ImpSvNumberInputScan::IsAcceptedDatePattern: no date acceptance patterns");
+        nAcceptedDatePattern = (sDateAcceptancePatterns.getLength() ? -2 : -1);
+    }
+
+    if (nAcceptedDatePattern == -1)
+        return false;
+
+    nDatePatternStart = nStartPatternAt;    // remember start particle
+
+    for (sal_Int32 nPattern=0; nPattern < sDateAcceptancePatterns.getLength(); ++nPattern)
+    {
+        sal_uInt16 nNext = nDatePatternStart;
+        bool bOk = true;
+        const rtl::OUString& rPat = sDateAcceptancePatterns[nPattern];
+        sal_Int32 nPat = 0;
+        for ( ; nPat < rPat.getLength() && bOk && nNext < nAnzStrings; ++nPat, ++nNext)
+        {
+            switch (rPat[nPat])
+            {
+                case 'Y':
+                case 'M':
+                case 'D':
+                    bOk = IsNum[nNext];
+                    break;
+                default:
+                    bOk = !IsNum[nNext];
+                    if (bOk)
+                    {
+                        const xub_StrLen nLen = sStrArray[nNext].Len();
+                        bOk = (rPat.indexOf( sStrArray[nNext], nPat) == nPat);
+                        if (bOk)
+                            nPat += nLen - 1;
+                        else if (nPat + nLen > rPat.getLength() && sStrArray[nNext].GetChar(nLen-1) == ' ')
+                        {
+                            // Trailing blanks in input.
+                            String aStr( sStrArray[nNext]);
+                            aStr.EraseTrailingChars(' ');
+                            // Expand again in case of pattern "M. D. " and
+                            // input "M. D.  ", maybe fetched far, but..
+                            aStr.Expand( rPat.getLength() - nPat, ' ');
+                            bOk = (rPat.indexOf( aStr, nPat) == nPat);
+                            if (bOk)
+                                nPat += aStr.Len() - 1;
+                        }
+                    }
+                    break;
+            }
+        }
+        if (bOk)
+        {
+            // Check for trailing characters mismatch.
+            if (nNext < nAnzStrings)
+            {
+                // Pattern end but not input end.
+                if (!IsNum[nNext])
+                {
+                    // Trailing (or separating if time follows) blanks are ok.
+                    xub_StrLen nPos = 0;
+                    SkipBlanks( sStrArray[nNext], nPos);
+                    if (nPos == sStrArray[nNext].Len())
+                    {
+                        nAcceptedDatePattern = nPattern;
+                        return true;
+                    }
+                }
+            }
+            else if (nPat == rPat.getLength())
+            {
+                // Input end and pattern end => match.
+                nAcceptedDatePattern = nPattern;
+                return true;
+            }
+            // else Input end but not pattern end, no match.
+        }
+    }
+    nAcceptedDatePattern = -1;
+    return false;
+}
+
+//---------------------------------------------------------------------------
+
+bool ImpSvNumberInputScan::SkipDatePatternSeparator( sal_uInt16 nParticle, xub_StrLen & rPos )
+{
+    // If not initialized yet start with first number, if any.
+    if (!IsAcceptedDatePattern( (nAnzNums ? nNums[0] : 0)))
+        return false;
+
+    if (nParticle < nDatePatternStart || nParticle >= nAnzStrings || IsNum[nParticle])
+        return false;
+
+    sal_uInt16 nNext = nDatePatternStart;
+    const rtl::OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
+    for (sal_Int32 nPat = 0; nPat < rPat.getLength() && nNext < nAnzStrings; ++nPat, ++nNext)
+    {
+        switch (rPat[nPat])
+        {
+            case 'Y':
+            case 'M':
+            case 'D':
+                break;
+            default:
+                if (nNext == nParticle)
+                {
+                    const xub_StrLen nLen = sStrArray[nNext].Len();
+                    bool bOk = (rPat.indexOf( sStrArray[nNext], nPat) == nPat);
+                    if (!bOk && (nPat + nLen > rPat.getLength() && sStrArray[nNext].GetChar(nLen-1) == ' '))
+                    {
+                        // The same ugly trailing blanks check as in
+                        // IsAcceptedDatePattern().
+                        String aStr( sStrArray[nNext]);
+                        aStr.EraseTrailingChars(' ');
+                        aStr.Expand( rPat.getLength() - nPat, ' ');
+                        bOk = (rPat.indexOf( aStr, nPat) == nPat);
+                    }
+                    if (bOk)
+                    {
+                        rPos = nLen;    // yes, set, not add!
+                        return true;
+                    }
+                    else
+                        return false;
+                }
+                nPat += sStrArray[nNext].Len() - 1;
+                break;
+        }
+    }
+    return false;
+}
+
+//---------------------------------------------------------------------------
+
+sal_uInt32 ImpSvNumberInputScan::GetDatePatternOrder()
+{
+    // If not initialized yet start with first number, if any.
+    if (!IsAcceptedDatePattern( (nAnzNums ? nNums[0] : 0)))
+        return 0;
+
+    sal_uInt32 nOrder = 0;
+    const rtl::OUString& rPat = sDateAcceptancePatterns[nAcceptedDatePattern];
+    for (sal_Int32 nPat = 0; nPat < rPat.getLength() && !(nOrder & 0xff0000); ++nPat)
+    {
+        switch (rPat[nPat])
+        {
+            case 'Y':
+            case 'M':
+            case 'D':
+                nOrder = (nOrder << 8) | rPat[nPat];
+                break;
+        }
+    }
+    return nOrder;
+}
+
+//---------------------------------------------------------------------------
+
+DateFormat ImpSvNumberInputScan::GetDateOrder()
+{
+    sal_uInt32 nOrder = GetDatePatternOrder();
+    if (!nOrder)
+        return pFormatter->GetLocaleData()->getDateFormat();
+    switch ((nOrder & 0xff0000) >> 16)
+    {
+        case 'Y':
+            if ((((nOrder & 0xff00) >> 8) == 'M') && ((nOrder & 0xff) == 'D'))
+                return YMD;
+            break;
+        case 'M':
+            if ((((nOrder & 0xff00) >> 8) == 'D') && ((nOrder & 0xff) == 'Y'))
+                return MDY;
+            break;
+        case 'D':
+            if ((((nOrder & 0xff00) >> 8) == 'M') && ((nOrder & 0xff) == 'Y'))
+                return DMY;
+            break;
+        default:
+        case 0:
+            switch ((nOrder & 0xff00) >> 8)
+            {
+                case 'Y':
+                    switch ((nOrder & 0xff))
+                    {
+                        case 'M':
+                            return YMD;
+                    }
+                    break;
+                case 'M':
+                    switch ((nOrder & 0xff))
+                    {
+                        case 'Y':
+                            return DMY;
+                        case 'D':
+                            return MDY;
+                    }
+                    break;
+                case 'D':
+                    switch ((nOrder & 0xff))
+                    {
+                        case 'Y':
+                            return MDY;
+                        case 'M':
+                            return DMY;
+                    }
+                    break;
+            }
+    }
+    SAL_WARN( "nf.date", "ImpSvNumberInputScan::GetDateOrder: undefined, falling back to locale's default");
+    return pFormatter->GetLocaleData()->getDateFormat();
+}
+
+//---------------------------------------------------------------------------
 //      GetDateRef
 
 bool ImpSvNumberInputScan::GetDateRef( double& fDays, sal_uInt16& nCounter,
@@ -1077,7 +1300,7 @@ bool ImpSvNumberInputScan::GetDateRef( double& fDays, sal_uInt16& nCounter,
         {
             case NF_EVALDATEFORMAT_INTL :
                 bFormatTurn = false;
-                DateFmt = pLoc->getDateFormat();
+                DateFmt = GetDateOrder();
             break;
             case NF_EVALDATEFORMAT_FORMAT :
                 bFormatTurn = true;
@@ -1087,7 +1310,7 @@ bool ImpSvNumberInputScan::GetDateRef( double& fDays, sal_uInt16& nCounter,
                 if ( nTryOrder == 1 )
                 {
                     bFormatTurn = false;
-                    DateFmt = pLoc->getDateFormat();
+                    DateFmt = GetDateOrder();
                 }
                 else
                 {
@@ -1099,7 +1322,7 @@ bool ImpSvNumberInputScan::GetDateRef( double& fDays, sal_uInt16& nCounter,
                 if ( nTryOrder == 2 )
                 {
                     bFormatTurn = false;
-                    DateFmt = pLoc->getDateFormat();
+                    DateFmt = GetDateOrder();
                 }
                 else
                 {
@@ -1175,7 +1398,8 @@ input for the following reasons:
                         switch (DateFmt)
                         {
                             case MDY:
-                            case YMD: {
+                            case YMD:
+                            {
                                 sal_uInt16 nDay = ImplGetDay(0);
                                 sal_uInt16 nYear = ImplGetYear(0);
                                 if (nDay == 0 || nDay > 32) {
@@ -1221,8 +1445,11 @@ input for the following reasons:
                     case 0:             // not found
                     {
                         bool bHadExact;
-                        sal_uInt32 nExactDateOrder = (bFormatTurn ? pFormat->GetExactDateOrder() : 0);
-                        if ( 0xff < nExactDateOrder && nExactDateOrder <= 0xffff )
+                        sal_uInt32 nExactDateOrder = (bFormatTurn ?
+                                pFormat->GetExactDateOrder() :
+                                GetDatePatternOrder());
+                        bool bIsExact = (0xff < nExactDateOrder && nExactDateOrder <= 0xffff);
+                        if (bIsExact)
                         {   // formatted as date and exactly 2 parts
                             bHadExact = true;
                             switch ( (nExactDateOrder >> 8) & 0xff )
@@ -1253,10 +1480,14 @@ input for the following reasons:
                                 default:
                                     bHadExact = false;
                             }
+                            SAL_WARN_IF( !bHadExact, "nf.date", "ImpSvNumberInputScan::GetDateRef: error in exact date order");
                         }
                         else
                             bHadExact = false;
-                        if ( !bHadExact || !pCal->isValid() )
+                        // If input matched against a date acceptance pattern
+                        // do not attempt to mess around with guessing the
+                        // order, either it matches or it doesn't.
+                        if ((bFormatTurn || !bIsExact) && (!bHadExact || !pCal->isValid()))
                         {
                             if ( !bHadExact && nExactDateOrder )
                                 pCal->setGregorianDateTime( Date( Date::SYSTEM ) );   // reset today
@@ -1620,6 +1851,11 @@ bool ImpSvNumberInputScan::ScanStartString( const String& rString,
                         SkipBlanks(rString, nPos);
                     }
                 }
+                if (!nMonth)
+                {
+                    // Determine and remember following date pattern, if any.
+                    IsAcceptedDatePattern( 1);
+                }
             }
         }
     }
@@ -1673,7 +1909,8 @@ bool ImpSvNumberInputScan::ScanMidString( const String& rString,
             return MatchedReturn();
         else if (nDecPos == 2)                      // . dup: 12.4.
         {
-            if (bDecSepInDateSeps)                  // . also date separator
+            if (bDecSepInDateSeps                   // . also date separator
+                    || SkipDatePatternSeparator( nStringPos, nPos))
             {
                 if (    eScannedType != NUMBERFORMAT_UNDEFINED &&
                         eScannedType != NUMBERFORMAT_DATE &&
@@ -1727,10 +1964,15 @@ bool ImpSvNumberInputScan::ScanMidString( const String& rString,
     }
 
     const LocaleDataWrapper* pLoc = pFormatter->GetLocaleData();
-    const String& rDate = pFormatter->GetDateSep();
-    SkipBlanks(rString, nPos);
-    if (SkipString( rDate, rString, nPos)               // 10.  10-  10/
-            || ((MayBeIso8601() || MayBeMonthDate())
+    bool bDate = SkipDatePatternSeparator( nStringPos, nPos);   // 12/31  31.12.  12/31/1999  31.12.1999
+    if (!bDate)
+    {
+        const String& rDate = pFormatter->GetDateSep();
+        SkipBlanks(rString, nPos);
+        bDate = SkipString( rDate, rString, nPos);      // 10.  10-  10/
+    }
+    if (bDate
+            || ((MayBeIso8601() || MayBeMonthDate())    // 1999-12-31  31-Dec-1999
                 && SkipChar( '-', rString, nPos)))
     {
         if (   eScannedType != NUMBERFORMAT_UNDEFINED   // already another type
@@ -1915,7 +2157,8 @@ bool ImpSvNumberInputScan::ScanEndString( const String& rString,
             return MatchedReturn();
         else if (nDecPos == 2)                      // . dup: 12.4.
         {
-            if (bDecSepInDateSeps)                  // . also date sep
+            if (bDecSepInDateSeps                   // . also date separator
+                    || SkipDatePatternSeparator( nAnzStrings-1, nPos))
             {
                 if (    eScannedType != NUMBERFORMAT_UNDEFINED &&
                         eScannedType != NUMBERFORMAT_DATE &&
@@ -2006,8 +2249,13 @@ bool ImpSvNumberInputScan::ScanEndString( const String& rString,
             nTimePos = nAnzStrings;
     }
 
-    const String& rDate = pFormatter->GetDateSep();
-    if (SkipString( rDate, rString, nPos)               // 10.  10-  10/
+    bool bDate = SkipDatePatternSeparator( nAnzStrings-1, nPos);   // 12/31  31.12.  12/31/1999  31.12.1999
+    if (!bDate)
+    {
+        const String& rDate = pFormatter->GetDateSep();
+        bDate = SkipString( rDate, rString, nPos);      // 10.  10-  10/
+    }
+    if (bDate
             || ((MayBeIso8601() || MayBeMonthDate())
                 && SkipChar( '-', rString, nPos)))
     {
@@ -2575,6 +2823,8 @@ void ImpSvNumberInputScan::ChangeIntl()
                           cDecSep == pFormatter->GetDateSep().GetChar(0) );
     bTextInitialized = false;
     aUpperCurrSymbol.Erase();
+    if (sDateAcceptancePatterns.getLength())
+        sDateAcceptancePatterns = ::com::sun::star::uno::Sequence< ::rtl::OUString >();
 }
 
 
@@ -2690,23 +2940,9 @@ bool ImpSvNumberInputScan::IsNumberFormat(
                     {
                         if (nAnzNums > 3)
                             res = false;
-                        else if (nAnzNums == 2)
-                        {                           // check locale dependent abbreviation
-                            /* FIXME: here go locale data acceptance patterns
-                             * instead */
-                            // Only one separator, 11/23 yes, 23/11 yes, 11/23/
-                            // no, 23/11/ no, 11-23 yes, 23-11 no?, 11-23- no,
-                            // 23-11- no, 23.11 no, 23.11. yes.
-                            sal_Unicode cDateSep = pFormatter->GetDateSep().GetChar(0);
-                            if (cDateSep == '.' && (nAnzStrings == 3 ||
-                                        (nNums[1]+1 < nAnzStrings &&
-                                         sStrArray[nNums[1]+1].GetChar(0) != cDateSep)))
-                                res = false;
-                            else if ((cDateSep == '/' || cDateSep == '-') &&
-                                    (nNums[1]+1 < nAnzStrings &&
-                                     sStrArray[nNums[1]+1].GetChar(0) == cDateSep))
-                                res = false;
-                        }
+                        else
+                            res = IsAcceptedDatePattern( nNums[0]) ||
+                                MayBeIso8601() || nMatchedAllStrings;
                     }
                     break;
 
@@ -2749,6 +2985,9 @@ bool ImpSvNumberInputScan::IsNumberFormat(
                             if (nAnzNums > 6)
                                 res = false;
                         }
+                        if (res)
+                            res = IsAcceptedDatePattern( nNums[0]) ||
+                                MayBeIso8601() || nMatchedAllStrings;
                     }
                     break;
 
diff --git a/svl/source/numbers/zforfind.hxx b/svl/source/numbers/zforfind.hxx
index 5d6cb85..ce6d897 100644
--- a/svl/source/numbers/zforfind.hxx
+++ b/svl/source/numbers/zforfind.hxx
@@ -30,6 +30,8 @@
 #define _ZFORFIND_HXX
 
 #include <tools/string.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <rtl/ustring.hxx>
 
 class Date;
 class SvNumberformat;
@@ -163,6 +165,21 @@ private:
      */
     sal_uInt8   nMayBeMonthDate;
 
+    /** Input matched this locale dependent date acceptance pattern.
+        -2 if not checked yet, -1 if no match, >=0 matched pattern.
+
+        @see IsAcceptedDatePattern()
+     */
+    sal_Int32   nAcceptedDatePattern;
+    com::sun::star::uno::Sequence< rtl::OUString >  sDateAcceptancePatterns;
+
+    /** If input matched a date acceptance pattern that starts at input
+        particle sStrArray[nDatePatternStart].
+
+        @see IsAcceptedDatePattern()
+     */
+    sal_uInt16  nDatePatternStart;
+
 #ifdef _ZFORFIND_CXX        // methods private to implementation
     void Reset();                               // Reset all variables before start of analysis
 
@@ -319,6 +336,35 @@ private:
     // native number transliteration if necessary
     void TransformInput( String& rString );
 
+    /** Whether input matches locale dependent date acceptance pattern.
+
+        @param nStartPatternAt
+               The pattern matching starts at input particle
+               sStrArray[nStartPatternAt].
+
+        NOTE: once called the result is remembered, subsequent calls with
+        different parameters do not check for a match and do not lead to a
+        different result.
+     */
+    bool IsAcceptedDatePattern( sal_uInt16 nStartPatternAt );
+
+    /** Sets (not advances!) rPos to sStrArray[nParticle].Len() if string
+        matches separator in pattern at nParticle.
+
+        @returns TRUE if separator matched.
+     */
+    bool SkipDatePatternSeparator( sal_uInt16 nParticle, xub_StrLen & rPos );
+
+    /** Obtain order of accepted date pattern coded as, for example,
+        ('D'<<16)|('M'<<8)|'Y'
+    */
+    sal_uInt32 GetDatePatternOrder();
+
+    /** Obtain date format order, from accepted date pattern if available or
+        otherwise the locale's default order.
+     */
+    DateFormat GetDateOrder();
+
 #endif  // _ZFORFIND_CXX
 };
 
commit ac32bcac33e3326149fe700b2de4d22542d6c82a
Author: Eike Rathke <erack at redhat.com>
Date:   Tue Jan 10 16:45:26 2012 +0100

    [bg-BG] special DateAcceptancePattern "D.M.Y г."
    
    Special special.. upper and lower case letter GHE and space and non-breaking
    space.

diff --git a/i18npool/source/localedata/data/bg_BG.xml b/i18npool/source/localedata/data/bg_BG.xml
index 2abd5cc..80e8827 100644
--- a/i18npool/source/localedata/data/bg_BG.xml
+++ b/i18npool/source/localedata/data/bg_BG.xml
@@ -35,6 +35,10 @@
     <MeasurementSystem>metric</MeasurementSystem>
   </LC_CTYPE>
   <LC_FORMAT replaceFrom="[CURRENCY]" replaceTo="[$лв.-402]">
+    <DateAcceptancePattern>D.M.Y г.</DateAcceptancePattern>
+    <DateAcceptancePattern>D.M.Y г.</DateAcceptancePattern>
+    <DateAcceptancePattern>D.M.Y Г.</DateAcceptancePattern>
+    <DateAcceptancePattern>D.M.Y Г.</DateAcceptancePattern>
     <FormatElement msgid="FixedFormatskey1" default="true" type="medium" usage="FIXED_NUMBER" formatindex="0">
       <FormatCode>General</FormatCode>
     </FormatElement>
commit ee3315bb22282d8725bef07c5fe2789e215309af
Author: Eike Rathke <erack at redhat.com>
Date:   Tue Jan 10 16:45:25 2012 +0100

    [sl-SI] special DateAcceptancePattern "D. M. Y"

diff --git a/i18npool/source/localedata/data/sl_SI.xml b/i18npool/source/localedata/data/sl_SI.xml
index e61ce24..21514d3 100644
--- a/i18npool/source/localedata/data/sl_SI.xml
+++ b/i18npool/source/localedata/data/sl_SI.xml
@@ -35,6 +35,7 @@
     <MeasurementSystem>metric</MeasurementSystem>
   </LC_CTYPE>
   <LC_FORMAT replaceFrom="[CURRENCY]" replaceTo="[$SIT-424]">
+    <DateAcceptancePattern>D. M. Y</DateAcceptancePattern>
     <FormatElement msgid="FixedFormatskey1" default="true" type="medium" usage="FIXED_NUMBER" formatindex="0">
       <FormatCode>Standard</FormatCode>
     </FormatElement>
commit a2685c5fd47396589e0767c5926a5bdb9f7b3390
Author: Eike Rathke <erack at redhat.com>
Date:   Tue Jan 10 16:45:24 2012 +0100

    bumped locale data wrapper to use XLocaleData4 and provide date acceptance patterns

diff --git a/unotools/inc/unotools/localedatawrapper.hxx b/unotools/inc/unotools/localedatawrapper.hxx
index 0bfaa97..2f4186f 100644
--- a/unotools/inc/unotools/localedatawrapper.hxx
+++ b/unotools/inc/unotools/localedatawrapper.hxx
@@ -30,7 +30,7 @@
 #define _UNOTOOLS_LOCALEDATAWRAPPER_HXX
 
 #include <tools/string.hxx>
-#include <com/sun/star/i18n/XLocaleData3.hpp>
+#include <com/sun/star/i18n/XLocaleData4.hpp>
 #include <com/sun/star/i18n/LocaleItem.hpp>
 #include <com/sun/star/i18n/reservedWords.hpp>
 #include <unotools/readwritemutexguard.hxx>
@@ -67,7 +67,7 @@ class UNOTOOLS_DLLPUBLIC LocaleDataWrapper
     static  sal_uInt8                nLocaleDataChecking;    // 0:=dontknow, 1:=yes, 2:=no
 
     ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >    xSMgr;
-    ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XLocaleData3 >            xLD;
+    ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XLocaleData4 >            xLD;
     ::com::sun::star::lang::Locale                                                      aLocale;
     ::boost::shared_ptr< ::com::sun::star::i18n::Calendar2 >                            xDefaultCalendar;
     ::com::sun::star::i18n::LocaleDataItem                                              aLocaleDataItem;
@@ -163,6 +163,7 @@ public:
     ::com::sun::star::i18n::ForbiddenCharacters getForbiddenCharacters() const;
     ::com::sun::star::uno::Sequence< ::rtl::OUString > getReservedWord() const;
     ::com::sun::star::uno::Sequence< ::com::sun::star::lang::Locale > getAllInstalledLocaleNames() const;
+    ::com::sun::star::uno::Sequence< ::rtl::OUString > getDateAcceptancePatterns() const;
 
     /// same as the wrapper implementation but static
     static ::com::sun::star::uno::Sequence< ::com::sun::star::lang::Locale > getInstalledLocaleNames();
diff --git a/unotools/source/i18n/localedatawrapper.cxx b/unotools/source/i18n/localedatawrapper.cxx
index f6248e9..704415d 100644
--- a/unotools/source/i18n/localedatawrapper.cxx
+++ b/unotools/source/i18n/localedatawrapper.cxx
@@ -82,7 +82,7 @@ LocaleDataWrapper::LocaleDataWrapper(
         bReservedWordValid( sal_False )
 {
     setLocale( rLocale );
-    xLD = Reference< XLocaleData3 > (
+    xLD = Reference< XLocaleData4 > (
         intl_createInstance( xSMgr, "com.sun.star.i18n.LocaleData",
                              "LocaleDataWrapper" ), uno::UNO_QUERY );
 }
@@ -1950,4 +1950,27 @@ void LocaleDataWrapper::evaluateLocaleDataChecking()
     return ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::Calendar2 >(0);
 }
 
+
+// --- XLocaleData4 ----------------------------------------------------------
+
+::com::sun::star::uno::Sequence< ::rtl::OUString > LocaleDataWrapper::getDateAcceptancePatterns() const
+{
+    try
+    {
+        if ( xLD.is() )
+            return xLD->getDateAcceptancePatterns( getLocale() );
+    }
+    catch ( Exception& e )
+    {
+#ifdef DBG_UTIL
+        rtl::OStringBuffer aMsg("getDateAcceptancePatterns: Exception caught\n");
+        aMsg.append(rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8));
+        DBG_ERRORFILE(aMsg.getStr());
+#else
+        (void)e;
+#endif
+    }
+    return ::com::sun::star::uno::Sequence< ::rtl::OUString >(0);
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit ee660ba9b7bf83e21a9fb9bdcb10318e152ab988
Author: Eike Rathke <erack at redhat.com>
Date:   Tue Jan 10 16:45:23 2012 +0100

    added DateAcceptancePattern element

diff --git a/i18npool/source/localedata/data/de_DE.xml b/i18npool/source/localedata/data/de_DE.xml
index a83b3db..2b72bfa 100644
--- a/i18npool/source/localedata/data/de_DE.xml
+++ b/i18npool/source/localedata/data/de_DE.xml
@@ -35,6 +35,7 @@
     <MeasurementSystem>metric</MeasurementSystem>
   </LC_CTYPE>
   <LC_FORMAT>
+    <DateAcceptancePattern>D.M.</DateAcceptancePattern>
     <FormatElement msgid="FixedFormatskey1" default="true" type="medium" usage="FIXED_NUMBER" formatindex="0">
       <FormatCode>Standard</FormatCode>
     </FormatElement>
diff --git a/i18npool/source/localedata/data/en_US.xml b/i18npool/source/localedata/data/en_US.xml
index f6b771b..90ceee4 100644
--- a/i18npool/source/localedata/data/en_US.xml
+++ b/i18npool/source/localedata/data/en_US.xml
@@ -35,6 +35,7 @@
     <MeasurementSystem>US</MeasurementSystem>
   </LC_CTYPE>
   <LC_FORMAT replaceFrom="[CURRENCY]" replaceTo="[$$-409]">
+    <DateAcceptancePattern>M/D</DateAcceptancePattern>
     <FormatElement msgid="FixedFormatskey1" default="true" type="medium" usage="FIXED_NUMBER" formatindex="0">
       <FormatCode>General</FormatCode>
     </FormatElement>
diff --git a/i18npool/source/localedata/data/locale.dtd b/i18npool/source/localedata/data/locale.dtd
index d22c16f..ae4cec5 100644
--- a/i18npool/source/localedata/data/locale.dtd
+++ b/i18npool/source/localedata/data/locale.dtd
@@ -132,7 +132,7 @@
      that otherwise inherit the format codes.
 
 -->
-<!ELEMENT LC_FORMAT (FormatElement*) >
+<!ELEMENT LC_FORMAT (DateAcceptancePattern*, FormatElement*) >
 <!-- All FormatElement elements must be given if the RefLocale mechanism is not used! -->
 <!ATTLIST LC_FORMAT %RefLocale;>
 <!ATTLIST LC_FORMAT %replaceFrom;>
@@ -145,6 +145,21 @@
      to be omitted. LCIDs are defined in i18npool/inc/i18npool/lang.h
 -->
 
+<!ELEMENT DateAcceptancePattern (#PCDATA)>
+<!-- Pattern that defines an input sequence match to be accepted as
+     (abbreviated) date. For example, in en_US locale "M/D" accepts an input of
+     11/23 as CurrentYear-November-23 whereas 11/23/ would not be a date. In
+     de_DE locale "D.M." accepts an input of 23.11. as  CurrentYear-November-23
+     whereas 23.11 would not be a date.
+
+     For each locale one pattern that matches a full date is automatically
+     generated from FormatElement formatIndex="21" and does not need to be
+     defined, for example "M/D/Y" or "D.M.Y", so only if you want abbreviations
+     to be in effect add some patterns.
+
+     NOTE: use only single letter D,M,Y. Multiple patterns can be defined.
+-->
+
 <!ELEMENT LC_FORMAT_1 (FormatElement*) >
 <!ATTLIST LC_FORMAT_1 %RefLocale;>
 <!ATTLIST LC_FORMAT_1 %replaceFrom;>
commit b75958473503f7eca096b8843e57a031bbabe0ab
Author: Eike Rathke <erack at redhat.com>
Date:   Tue Jan 10 16:44:42 2012 +0100

    implemented date acceptance patterns API

diff --git a/i18npool/inc/localedata.hxx b/i18npool/inc/localedata.hxx
index a28f104..2453aa4 100644
--- a/i18npool/inc/localedata.hxx
+++ b/i18npool/inc/localedata.hxx
@@ -32,7 +32,7 @@
 
 #include <comphelper/processfactory.hxx>
 
-#include <com/sun/star/i18n/XLocaleData3.hpp>
+#include <com/sun/star/i18n/XLocaleData4.hpp>
 
 
 #include <cppuhelper/implbase2.hxx> // helper for implementations
@@ -74,7 +74,7 @@ inline sal_Bool operator ==(const com::sun::star::lang::Locale& l1, const com::s
 
 class LocaleData : public cppu::WeakImplHelper2
 <
-    com::sun::star::i18n::XLocaleData3,
+    com::sun::star::i18n::XLocaleData4,
     com::sun::star::lang::XServiceInfo
 >
 {
@@ -105,6 +105,9 @@ public:
     virtual com::sun::star::uno::Sequence< com::sun::star::uno::Sequence< beans::PropertyValue > > SAL_CALL getContinuousNumberingLevels( const com::sun::star::lang::Locale& rLocale ) throw(com::sun::star::uno::RuntimeException);
     virtual com::sun::star::uno::Sequence< com::sun::star::uno::Reference< container::XIndexAccess > > SAL_CALL getOutlineNumberingLevels( const com::sun::star::lang::Locale& rLocale ) throw(com::sun::star::uno::RuntimeException);
 
+    // XLocaleData4
+    virtual com::sun::star::uno::Sequence< rtl::OUString > SAL_CALL getDateAcceptancePatterns( const com::sun::star::lang::Locale& rLocale ) throw(com::sun::star::uno::RuntimeException);
+
     // following methods are used by indexentry service
     virtual com::sun::star::uno::Sequence< rtl::OUString > SAL_CALL getIndexAlgorithm( const com::sun::star::lang::Locale& rLocale ) throw(com::sun::star::uno::RuntimeException);
     virtual rtl::OUString SAL_CALL getDefaultIndexAlgorithm( const com::sun::star::lang::Locale& rLocale ) throw(com::sun::star::uno::RuntimeException);
diff --git a/i18npool/source/localedata/LocaleNode.cxx b/i18npool/source/localedata/LocaleNode.cxx
index a7b653c..f2e8c45 100644
--- a/i18npool/source/localedata/LocaleNode.cxx
+++ b/i18npool/source/localedata/LocaleNode.cxx
@@ -31,6 +31,7 @@
 #include <string.h>
 #include <iostream>
 #include <set>
+#include <vector>
 
 #include <rtl/ustrbuf.hxx>
 #include <sal/macros.h>
@@ -380,6 +381,9 @@ void LCInfoNode::generateCode (const OFileWriter &of) const
     of.writeFunction("getLCInfo_", "0", "LCInfoArray");
 }
 
+
+OUString aDateSep;
+
 void LCCTYPENode::generateCode (const OFileWriter &of) const
 {
     const LocaleNode * sepNode = 0;
@@ -392,7 +396,7 @@ void LCCTYPENode::generateCode (const OFileWriter &of) const
     of.writeAsciiString("\n\n");
     of.writeParameter("LC_CTYPE_Unoid", str);;
 
-    OUString aDateSep =
+    aDateSep =
         writeParameterCheckLen( of, "DateSeparator", "dateSeparator", 1, 1);
     OUString aThoSep =
         writeParameterCheckLen( of, "ThousandSeparator", "thousandSeparator", 1, 1);
@@ -596,20 +600,20 @@ void LCCTYPENode::generateCode (const OFileWriter &of) const
 
 static OUString sTheCurrencyReplaceTo;
 static OUString sTheCompatibleCurrency;
+static OUString sTheDateEditFormat;
+static ::std::vector< OUString > theDateAcceptancePatterns;
 
 sal_Int16 LCFormatNode::mnSection = 0;
 sal_Int16 LCFormatNode::mnFormats = 0;
 
 void LCFormatNode::generateCode (const OFileWriter &of) const
 {
-    OUString str;
-    if (mnSection == 0)
-    {
-        sTheCurrencyReplaceTo = OUString();
-        sTheCompatibleCurrency = OUString();
-    }
-    else if (mnSection >= 2)
+    if (mnSection >= 2)
         incError("more than 2 LC_FORMAT sections");
+
+    theDateAcceptancePatterns.clear();
+
+    OUString str;
     OUString strFrom( getAttr().getValueByName("replaceFrom"));
     of.writeParameter("replaceFrom", strFrom, mnSection);
     str = getAttr().getValueByName("replaceTo");
@@ -631,8 +635,10 @@ void LCFormatNode::generateCode (const OFileWriter &of) const
             sTheCompatibleCurrency = str.copy( 2, nHyphen - 2);
         }
     }
+
     ::rtl::OUString useLocale =   getAttr().getValueByName("ref");
-    if (!useLocale.isEmpty()) {
+    if (!useLocale.isEmpty())
+    {
         switch (mnSection)
         {
             case 0:
@@ -642,16 +648,28 @@ void LCFormatNode::generateCode (const OFileWriter &of) const
                 of.writeRefFunction("getAllFormats1_", useLocale, "replaceTo1");
                 break;
         }
+        of.writeRefFunction("getDateAcceptancePatterns_", useLocale);
         return;
     }
+
     sal_Int16 formatCount = mnFormats;
     NameSet  aMsgIdSet;
     ValueSet aFormatIndexSet;
     NameSet  aDefaultsSet;
     bool bCtypeIsRef = false;
 
-    for (sal_Int16 i = 0; i< getNumberOfChildren() ; i++,formatCount++) {
+    for (sal_Int16 i = 0; i< getNumberOfChildren() ; i++, formatCount++)
+    {
         LocaleNode * currNode = getChildAt (i);
+        if (currNode->getName().equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DateAcceptancePattern")))
+        {
+            if (mnSection > 0)
+                incError( "DateAcceptancePattern only handled in LC_FORMAT, not LC_FORMAT_1");
+            else
+                theDateAcceptancePatterns.push_back( currNode->getValue());
+            --formatCount;
+            continue;   // for
+        }
         OUString aUsage;
         OUString aType;
         OUString aFormatIndex;
@@ -705,6 +723,9 @@ void LCFormatNode::generateCode (const OFileWriter &of) const
             const LocaleNode* pCtype = 0;
             switch (formatindex)
             {
+                case cssi::NumberFormatIndex::DATE_SYS_DDMMYYYY :
+                    sTheDateEditFormat = n->getValue();
+                    break;
                 case cssi::NumberFormatIndex::NUMBER_1000DEC2 : // #,##0.00
                 case cssi::NumberFormatIndex::TIME_MMSS00 :     // MM:SS.00
                 case cssi::NumberFormatIndex::TIME_HH_MMSS00 :  // [HH]:MM:SS.00
@@ -962,6 +983,192 @@ void LCFormatNode::generateCode (const OFileWriter &of) const
     }
 
     mnFormats = mnFormats + formatCount;
+
+    if (mnSection == 0)
+    {
+        // Extract and add date acceptance pattern for full date, so we provide
+        // at least one valid pattern, even if the number parser doesn't need
+        // that one.
+        /* XXX NOTE: only simple [...] modifier and "..." quotes detected and
+         * ignored, not nested, no fancy stuff. */
+        sal_Int32 nIndex = 0;
+        // aDateSep can be empty if LC_CTYPE was a ref=..., determine from
+        // FormatCode then.
+        sal_uInt32 cDateSep = (aDateSep.isEmpty() ? 0 : aDateSep.iterateCodePoints( &nIndex));
+        nIndex = 0;
+        OUStringBuffer aPatternBuf(5);
+        sal_uInt8 nDetected = 0;    // bits Y,M,D
+        bool bInModifier = false;
+        bool bQuoted = false;
+        while (nIndex < sTheDateEditFormat.getLength() && nDetected < 7)
+        {
+            sal_uInt32 cChar = sTheDateEditFormat.iterateCodePoints( &nIndex);
+            if (bInModifier)
+            {
+                if (cChar == ']')
+                    bInModifier = false;
+                continue;   // while
+            }
+            if (bQuoted)
+            {
+                if (cChar == '"')
+                    bQuoted = false;
+                continue;   // while
+            }
+            switch (cChar)
+            {
+                case 'Y':
+                case 'y':
+                    if (!(nDetected & 4))
+                    {
+                        aPatternBuf.append( 'Y');
+                        nDetected |= 4;
+                    }
+                    break;
+                case 'M':
+                case 'm':
+                    if (!(nDetected & 2))
+                    {
+                        aPatternBuf.append( 'M');
+                        nDetected |= 2;
+                    }
+                    break;
+                case 'D':
+                case 'd':
+                    if (!(nDetected & 1))
+                    {
+                        aPatternBuf.append( 'D');
+                        nDetected |= 1;
+                    }
+                    break;
+                case '[':
+                    bInModifier = true;
+                    break;
+                case '"':
+                    bQuoted = true;
+                    break;
+                case '\\':
+                    cChar = sTheDateEditFormat.iterateCodePoints( &nIndex);
+                    break;
+                case '-':
+                    // Assume a YYYY-MM-DD format or some such. There are
+                    // locales that use an ISO 8601 edit format regardless of
+                    // what the locale data and other formats say, for example
+                    // hu_HU.
+                    cDateSep = cChar;
+                    // fallthru
+                default:
+                    if (!cDateSep)
+                        cDateSep = cChar;
+                    if (cChar == cDateSep)
+                        aPatternBuf.append( OUString( &cDateSep, 1));
+                    break;
+                // The localized legacy:
+                case 'A':
+                    if (((nDetected & 7) == 3) || ((nDetected & 7) == 0))
+                    {
+                        // es DD/MM/AAAA
+                        // fr JJ.MM.AAAA
+                        // it GG/MM/AAAA
+                        // fr_CA AAAA-MM-JJ
+                        aPatternBuf.append( 'Y');
+                        nDetected |= 4;
+                    }
+                    break;
+                case 'J':
+                    if (((nDetected & 7) == 0) || ((nDetected & 7) == 6))
+                    {
+                        // fr JJ.MM.AAAA
+                        // fr_CA AAAA-MM-JJ
+                        aPatternBuf.append( 'D');
+                        nDetected |= 1;
+                    }
+                    else if ((nDetected & 7) == 3)
+                    {
+                        // nl DD-MM-JJJJ
+                        // de TT.MM.JJJJ
+                        aPatternBuf.append( 'Y');
+                        nDetected |= 4;
+                    }
+                    break;
+                case 'T':
+                    if ((nDetected & 7) == 0)
+                    {
+                        // de TT.MM.JJJJ
+                        aPatternBuf.append( 'D');
+                        nDetected |= 1;
+                    }
+                    break;
+                case 'G':
+                    if ((nDetected & 7) == 0)
+                    {
+                        // it GG/MM/AAAA
+                        aPatternBuf.append( 'D');
+                        nDetected |= 1;
+                    }
+                    break;
+                case 'P':
+                    if ((nDetected & 7) == 0)
+                    {
+                        // fi PP.KK.VVVV
+                        aPatternBuf.append( 'D');
+                        nDetected |= 1;
+                    }
+                    break;
+                case 'K':
+                    if ((nDetected & 7) == 1)
+                    {
+                        // fi PP.KK.VVVV
+                        aPatternBuf.append( 'M');
+                        nDetected |= 2;
+                    }
+                    break;
+                case 'V':
+                    if ((nDetected & 7) == 3)
+                    {
+                        // fi PP.KK.VVVV
+                        aPatternBuf.append( 'Y');
+                        nDetected |= 4;
+                    }
+                    break;
+            }
+        }
+        OUString aPattern( aPatternBuf.makeStringAndClear());
+        if (((nDetected & 7) != 7) || aPattern.getLength() < 5)
+        {
+            incErrorStr( "failed to extract full date acceptance pattern", aPattern);
+            fprintf( stderr, "       with DateSeparator '%s' from FormatCode '%s'\n",
+                    OSTR( OUString( cDateSep)), OSTR( sTheDateEditFormat));
+        }
+        else
+            fprintf( stderr, "Generated date acceptance pattern: '%s' from '%s'\n",
+                    OSTR( aPattern), OSTR( sTheDateEditFormat));
+        theDateAcceptancePatterns.push_back( aPattern);
+
+        sal_Int16 nbOfDateAcceptancePatterns = static_cast<sal_Int16>(theDateAcceptancePatterns.size());
+
+        for (sal_Int16 i = 0; i < nbOfDateAcceptancePatterns; ++i)
+        {
+            of.writeParameter("DateAcceptancePattern", theDateAcceptancePatterns[i], i);
+        }
+
+        of.writeAsciiString("static const sal_Int16 DateAcceptancePatternsCount = ");
+        of.writeInt( nbOfDateAcceptancePatterns);
+        of.writeAsciiString(";\n");
+
+        of.writeAsciiString("static const sal_Unicode* DateAcceptancePatternsArray[] = {\n");
+        for (sal_Int16 i = 0; i < nbOfDateAcceptancePatterns; ++i)
+        {
+            of.writeAsciiString("\t");
+            of.writeAsciiString("DateAcceptancePattern");
+            of.writeInt(i);
+            of.writeAsciiString(",\n");
+        }
+        of.writeAsciiString("};\n\n");
+
+        of.writeFunction("getDateAcceptancePatterns_", "DateAcceptancePatternsCount", "DateAcceptancePatternsArray");
+    }
+
     ++mnSection;
 }
 
diff --git a/i18npool/source/localedata/localedata.cxx b/i18npool/source/localedata/localedata.cxx
index 2750297..f26a8fc 100644
--- a/i18npool/source/localedata/localedata.cxx
+++ b/i18npool/source/localedata/localedata.cxx
@@ -793,6 +793,33 @@ LocaleData::getAllFormats( const Locale& rLocale ) throw(RuntimeException)
     return seq;
 }
 
+
+Sequence< OUString > SAL_CALL
+LocaleData::getDateAcceptancePatterns( const Locale& rLocale ) throw(RuntimeException)
+{
+    sal_Unicode **patternsArray = NULL;
+
+    MyFunc_Type func = (MyFunc_Type) getFunctionSymbol( rLocale, "getDateAcceptancePatterns" );
+
+    if (func)
+    {
+        sal_Int16 patternsCount = 0;
+        patternsArray = func( patternsCount );
+        Sequence< OUString > seq( patternsCount );
+        for (sal_Int16 i = 0; i < patternsCount; ++i)
+        {
+            seq[i] = OUString( patternsArray[i] );
+        }
+        return seq;
+    }
+    else
+    {
+        Sequence< OUString > seq(0);
+        return seq;
+    }
+}
+
+
 #define COLLATOR_OFFSET_ALGO    0
 #define COLLATOR_OFFSET_DEFAULT 1
 #define COLLATOR_OFFSET_RULE    2
commit 67edfdce616c6dc56395e05a09e098a0460b9313
Author: Eike Rathke <erack at redhat.com>
Date:   Tue Jan 10 16:42:07 2012 +0100

    made [es-BO] DateSeparator the '/' that is used in FormatCode

diff --git a/i18npool/source/localedata/data/es_BO.xml b/i18npool/source/localedata/data/es_BO.xml
index de7a17f..d6c2eca 100644
--- a/i18npool/source/localedata/data/es_BO.xml
+++ b/i18npool/source/localedata/data/es_BO.xml
@@ -16,7 +16,7 @@
   </LC_INFO>
   <LC_CTYPE>
     <Separators>
-      <DateSeparator>-</DateSeparator>
+      <DateSeparator>/</DateSeparator>
       <ThousandSeparator>.</ThousandSeparator>
       <DecimalSeparator>,</DecimalSeparator>
       <TimeSeparator>:</TimeSeparator>
commit 899fe10b72522c833b7dc03312284db470952a28
Author: Eike Rathke <erack at redhat.com>
Date:   Tue Jan 10 16:42:06 2012 +0100

    made [tr-TR] use the defined '.' DateSeparator in FormatCode

diff --git a/i18npool/source/localedata/data/tr_TR.xml b/i18npool/source/localedata/data/tr_TR.xml
index 6c032ac..e106ab7 100644
--- a/i18npool/source/localedata/data/tr_TR.xml
+++ b/i18npool/source/localedata/data/tr_TR.xml
@@ -87,13 +87,13 @@
       <FormatCode>DD.MM.YY</FormatCode>
     </FormatElement>
     <FormatElement msgid="DateFormatskey9" default="true" type="long" usage="DATE" formatindex="19">
-      <FormatCode>DD/MM/YY</FormatCode>
+      <FormatCode>DD.MM.YY</FormatCode>
     </FormatElement>
     <FormatElement msgid="DateFormatskey8" default="true" type="medium" usage="DATE" formatindex="20">
-      <FormatCode>DD/MM/YY</FormatCode>
+      <FormatCode>DD.MM.YY</FormatCode>
     </FormatElement>
     <FormatElement msgid="DateFormatskey7" default="false" type="medium" usage="DATE" formatindex="21">
-      <FormatCode>DD/MM/YYYY</FormatCode>
+      <FormatCode>DD.MM.YYYY</FormatCode>
     </FormatElement>
     <FormatElement msgid="DateFormatskey10" default="false" type="long" usage="DATE" formatindex="22">
       <FormatCode>D.MMM.YY</FormatCode>
@@ -134,10 +134,10 @@
       <DefaultName>ISO 8601</DefaultName>
     </FormatElement>
     <FormatElement msgid="DateFormatskey3" default="false" type="medium" usage="DATE" formatindex="34">
-      <FormatCode>MM/YY</FormatCode>
+      <FormatCode>MM.YY</FormatCode>
     </FormatElement>
     <FormatElement msgid="DateFormatskey4" default="false" type="medium" usage="DATE" formatindex="35">
-      <FormatCode>DD/MMM</FormatCode>
+      <FormatCode>DD.MMM</FormatCode>
     </FormatElement>
     <FormatElement msgid="DateFormatskey5" default="false" type="medium" usage="DATE" formatindex="36">
       <FormatCode>MMMM</FormatCode>
@@ -170,10 +170,10 @@
       <FormatCode>[HH]:MM:SS,00</FormatCode>
     </FormatElement>
     <FormatElement msgid="DateTimeFormatskey1" default="true" type="medium" usage="DATE_TIME" formatindex="46">
-      <FormatCode>DD/MM/YY HH:MM</FormatCode>
+      <FormatCode>DD.MM.YY HH:MM</FormatCode>
     </FormatElement>
     <FormatElement msgid="DateTimeFormatskey2" default="false" type="medium" usage="DATE_TIME" formatindex="47">
-      <FormatCode>DD/MM/YYYY HH:MM:SS</FormatCode>
+      <FormatCode>DD.MM.YYYY HH:MM:SS</FormatCode>
     </FormatElement>
   </LC_FORMAT>
   <LC_COLLATION ref="en_US" />
commit ed5882352207f25bdc8aeeb515c8d5ed39211ac1
Author: Eike Rathke <erack at redhat.com>
Date:   Tue Jan 10 16:42:05 2012 +0100

    made [ko-KR] DateSeparator the '/' that is used in FormatCode

diff --git a/i18npool/source/localedata/data/ko_KR.xml b/i18npool/source/localedata/data/ko_KR.xml
index 3a3fbb0..7762c6d 100644
--- a/i18npool/source/localedata/data/ko_KR.xml
+++ b/i18npool/source/localedata/data/ko_KR.xml
@@ -13,7 +13,7 @@
   </LC_INFO>
   <LC_CTYPE>
     <Separators>
-      <DateSeparator>-</DateSeparator>
+      <DateSeparator>/</DateSeparator>
       <ThousandSeparator>,</ThousandSeparator>
       <DecimalSeparator>.</DecimalSeparator>
       <TimeSeparator>:</TimeSeparator>
commit 5c81423cc06a907da34d6954732ac5b405ab8296
Author: Eike Rathke <erack at redhat.com>
Date:   Tue Jan 10 16:42:04 2012 +0100

    made [ku-TR] DateSeparator the '/' that is used in FormatCode
    
    though I strongly doubt the entire US-centric approach ...

diff --git a/i18npool/source/localedata/data/ku_TR.xml b/i18npool/source/localedata/data/ku_TR.xml
index 0413353..641f482 100644
--- a/i18npool/source/localedata/data/ku_TR.xml
+++ b/i18npool/source/localedata/data/ku_TR.xml
@@ -13,7 +13,7 @@
   </LC_INFO>
   <LC_CTYPE>
     <Separators>
-      <DateSeparator>.</DateSeparator>
+      <DateSeparator>/</DateSeparator>
       <ThousandSeparator>,</ThousandSeparator>
       <DecimalSeparator>.</DecimalSeparator>
       <TimeSeparator>:</TimeSeparator>
commit 8b7354217de215a972341414c8a693bdfbfd13af
Author: Eike Rathke <erack at redhat.com>
Date:   Tue Jan 10 16:42:03 2012 +0100

    added date acceptance patterns API

diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk
index b6c4bd4..e8f623e 100644
--- a/offapi/UnoApi_offapi.mk
+++ b/offapi/UnoApi_offapi.mk
@@ -2673,6 +2673,7 @@ $(eval $(call gb_UnoApiTarget_add_idlfiles,offapi,offapi/com/sun/star/i18n,\
 	XLocaleData \
 	XLocaleData2 \
 	XLocaleData3 \
+	XLocaleData4 \
 	XNativeNumberSupplier \
 	XNumberFormatCode \
 	XOrdinalSuffix \
diff --git a/offapi/com/sun/star/i18n/LocaleData.idl b/offapi/com/sun/star/i18n/LocaleData.idl
index 5a32dcc..7885741 100644
--- a/offapi/com/sun/star/i18n/LocaleData.idl
+++ b/offapi/com/sun/star/i18n/LocaleData.idl
@@ -30,8 +30,7 @@
 #define __com_sun_star_i18n_LocaleData_idl__
 
 #include <com/sun/star/lang/XServiceInfo.idl>
-#include <com/sun/star/i18n/XLocaleData2.idl>
-#include <com/sun/star/i18n/XLocaleData3.idl>
+#include <com/sun/star/i18n/XLocaleData4.idl>
 
 //============================================================================
 
@@ -47,6 +46,7 @@ published service LocaleData
 
     [optional] interface com::sun::star::i18n::XLocaleData2;
     [optional] interface com::sun::star::i18n::XLocaleData3;
+    [optional] interface com::sun::star::i18n::XLocaleData4;
 };
 
 //============================================================================
diff --git a/offapi/com/sun/star/i18n/XLocaleData4.idl b/offapi/com/sun/star/i18n/XLocaleData4.idl
new file mode 100644
index 0000000..154edfa
--- /dev/null
+++ b/offapi/com/sun/star/i18n/XLocaleData4.idl
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Version: MPL 1.1 / GPLv3+ / LGPLv3+
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License or as specified alternatively below. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * Major Contributor(s):
+ * Copyright (C) 2011 Red Hat, Inc., Eike Rathke <erack at redhat.com>
+ *  (initial developer)
+ *
+ * All Rights Reserved.
+ *
+ * For minor contributions see the git repository.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
+ * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
+ * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
+ * instead of those above.
+ */
+
+#ifndef __com_sun_star_i18n_XLocaleData4_idl__
+#define __com_sun_star_i18n_XLocaleData4_idl__
+
+#include <com/sun/star/i18n/XLocaleData3.idl>
+
+//============================================================================
+
+module com { module sun { module star { module i18n {
+
+//============================================================================
+
+/**
+    Access locale specific data.
+
+    <p> Derived from <type scope="::com::sun::star::i18n">XLocaleData3</type>
+    this provides an additional method to return a sequence of date
+    acceptance patterns for a locale.
+
+    @since LibreOffice 3.6
+ */
+
+published interface XLocaleData4 : com::sun::star::i18n::XLocaleData3
+{
+    /** returns a sequence of date acceptance patterns for a locale
+
+        <p> Patterns with input combinations that are accepted as
+        incomplete date input, such as <b>M/D</b> or <b>D.M.</b>
+     */
+    sequence< string >  getDateAcceptancePatterns(
+                                [in] com::sun::star::lang::Locale aLocale );
+};
+
+//============================================================================
+}; }; }; };
+
+#endif
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit f6c465e3555c8ac9d2902b3ac35f2d4784eed4c9
Author: Eike Rathke <erack at redhat.com>
Date:   Tue Jan 10 16:42:02 2012 +0100

    lower date acceptance with only two numbers

diff --git a/svl/source/numbers/zforfind.cxx b/svl/source/numbers/zforfind.cxx
index 3cf61e1..ba315fc 100644
--- a/svl/source/numbers/zforfind.cxx
+++ b/svl/source/numbers/zforfind.cxx
@@ -2690,6 +2690,23 @@ bool ImpSvNumberInputScan::IsNumberFormat(
                     {
                         if (nAnzNums > 3)
                             res = false;
+                        else if (nAnzNums == 2)
+                        {                           // check locale dependent abbreviation
+                            /* FIXME: here go locale data acceptance patterns
+                             * instead */
+                            // Only one separator, 11/23 yes, 23/11 yes, 11/23/
+                            // no, 23/11/ no, 11-23 yes, 23-11 no?, 11-23- no,
+                            // 23-11- no, 23.11 no, 23.11. yes.
+                            sal_Unicode cDateSep = pFormatter->GetDateSep().GetChar(0);
+                            if (cDateSep == '.' && (nAnzStrings == 3 ||
+                                        (nNums[1]+1 < nAnzStrings &&
+                                         sStrArray[nNums[1]+1].GetChar(0) != cDateSep)))
+                                res = false;
+                            else if ((cDateSep == '/' || cDateSep == '-') &&
+                                    (nNums[1]+1 < nAnzStrings &&
+                                     sStrArray[nNums[1]+1].GetChar(0) == cDateSep))
+                                res = false;
+                        }
                     }
                     break;
 
commit 8298a4741d95034f65c648dddfae081f44cf9455
Author: Eike Rathke <erack at redhat.com>
Date:   Tue Jan 10 16:42:01 2012 +0100

    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.

diff --git a/svl/source/numbers/zforfind.cxx b/svl/source/numbers/zforfind.cxx
index 3df9232..3cf61e1 100644
--- a/svl/source/numbers/zforfind.cxx
+++ b/svl/source/numbers/zforfind.cxx
@@ -145,6 +145,7 @@ void ImpSvNumberInputScan::Reset()
     nMatchedAllStrings = nMatchedVirgin;
     nMayBeIso8601 = 0;
     nTimezonePos = 0;
+    nMayBeMonthDate = 0;
 }
 
 
@@ -995,6 +996,44 @@ bool ImpSvNumberInputScan::MayBeIso8601()
 }
 
 //---------------------------------------------------------------------------
+
+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
 
 bool ImpSvNumberInputScan::GetDateRef( double& fDays, sal_uInt16& nCounter,
@@ -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();
diff --git a/svl/source/numbers/zforfind.hxx b/svl/source/numbers/zforfind.hxx
index 02e08d4..5d6cb85 100644
--- a/svl/source/numbers/zforfind.hxx
+++ b/svl/source/numbers/zforfind.hxx
@@ -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
 
commit 958cdbdcf5e35c04e259a69e972684060f1b3d12
Author: Eike Rathke <erack at redhat.com>
Date:   Tue Jan 10 16:42:01 2012 +0100

    accept and display years < 100
    
    + Years < 100 without being hit by the 2 digits year magic can be entered by
      preceding them with a leading 0 if at least 3 digits are present, e.g.
      099-1-1 is year 99, not 1999.
    + Years < 100 in an yyyy format are always displayed as 0099.

diff --git a/svl/source/numbers/zforfind.cxx b/svl/source/numbers/zforfind.cxx
index ef76fac..3df9232 100644
--- a/svl/source/numbers/zforfind.cxx
+++ b/svl/source/numbers/zforfind.cxx
@@ -957,10 +957,14 @@ sal_uInt16 ImpSvNumberInputScan::ImplGetYear( sal_uInt16 nIndex )
 {
     sal_uInt16 nYear = 0;
 
-    if (sStrArray[nNums[nIndex]].Len() <= 4)
+    xub_StrLen nLen = sStrArray[nNums[nIndex]].Len();
+    if (nLen <= 4)
     {
         nYear = (sal_uInt16) sStrArray[nNums[nIndex]].ToInt32();
-        nYear = SvNumberFormatter::ExpandTwoDigitYear( nYear, nYear2000 );
+        // A year < 100 entered with at least 3 digits with leading 0 is taken
+        // as is without expansion.
+        if (nYear < 100 && nLen < 3)
+            nYear = SvNumberFormatter::ExpandTwoDigitYear( nYear, nYear2000 );
     }
 
     return nYear;
@@ -972,13 +976,22 @@ bool ImpSvNumberInputScan::MayBeIso8601()
 {
     if (nMayBeIso8601 == 0)
     {
-        if (nAnzNums >= 3 && nNums[0] < nAnzStrings &&
-                sStrArray[nNums[0]].ToInt32() > 31)
-            nMayBeIso8601 = 1;
-        else
-            nMayBeIso8601 = 2;
+        nMayBeIso8601 = 1;
+        xub_StrLen nLen = ((nAnzNums >= 1 && nNums[0] < nAnzStrings) ? sStrArray[nNums[0]].Len() : 0);
+        if (nLen)
+        {
+            sal_Int32 n;
+            if (nAnzNums >= 3 && nNums[2] < nAnzStrings &&
+                    sStrArray[nNums[0]+1] == '-' &&                         // separator year-month
+                    (n = sStrArray[nNums[1]].ToInt32()) >= 1 && n <= 12 &&  // month
+                    sStrArray[nNums[1]+1] == '-' &&                         // separator month-day
+                    (n = sStrArray[nNums[2]].ToInt32()) >= 1 && n <= 31)    // day
+                // Year (nNums[0]) value not checked, may be anything, but
+                // length (number of digits) is checked.
+                nMayBeIso8601 = (nLen >= 4 ? 4 : (nLen == 3 ? 3 : (nLen > 0 ? 2 : 0)));
+        }
     }
-    return nMayBeIso8601 == 1;
+    return nMayBeIso8601 > 1;
 }
 
 //---------------------------------------------------------------------------
diff --git a/svl/source/numbers/zforfind.hxx b/svl/source/numbers/zforfind.hxx
index 9c557b7..02e08d4 100644
--- a/svl/source/numbers/zforfind.hxx
+++ b/svl/source/numbers/zforfind.hxx
@@ -69,8 +69,13 @@ public:
     /// get threshold of two-digit year input
     sal_uInt16  GetYear2000() const { return nYear2000; }
 
-    // Whether input may be a ISO 8601 date format, yyyy-mm-dd...
-    // checks if at least 3 numbers and first number>31
+    /** Whether input may be an ISO 8601 date format, yyyy-mm-dd...
+
+        Checks if input has at least 3 numbers for yyyy-mm-dd and the separator
+        is '-', and 1<=mm<=12 and 1<=dd<=31.
+
+        @see nMayBeIso8601
+     */
     bool MayBeIso8601();
 
 private:
@@ -129,7 +134,15 @@ private:
                                                 // number <= nYear2000 => 20xx
                                                 // number >  nYear2000 => 19xx
     sal_uInt16  nTimezonePos;                   // Index of timezone separator (+1)
-    sal_uInt8    nMayBeIso8601;                 // 0:=dontknowyet, 1:=yes, 2:=no
+
+    /** 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
+     */
+    sal_uInt8    nMayBeIso8601;
 
 #ifdef _ZFORFIND_CXX        // methods private to implementation
     void Reset();                               // Reset all variables before start of analysis
diff --git a/svl/source/numbers/zformat.cxx b/svl/source/numbers/zformat.cxx
index d796980..1976ec9 100644
--- a/svl/source/numbers/zformat.cxx
+++ b/svl/source/numbers/zformat.cxx
@@ -3387,8 +3387,19 @@ bool SvNumberformat::ImpGetDateOutput(double fNumber,
             {
                 if ( bOtherCalendar )
                     SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
-                OutString += rCal.getDisplayString(
+                String aYear = rCal.getDisplayString(
                         CalendarDisplayCode::LONG_YEAR, nNatNum );
+                if (aYear.Len() < 4)
+                {
+                    // Ensure that year consists of at least 4 digits, so it
+                    // can be distinguished from 2 digits display and edited
+                    // without suddenly being hit by the 2-digit year magic.
+                    String aZero;
+                    aZero.Fill( 4 - aYear.Len(), sal_Unicode('0'));
+                    ImpTransliterate( aZero, NumFor[nIx].GetNatNum());
+                    aYear.Insert( aZero, 0);
+                }
+                OutString += aYear;
                 if ( bOtherCalendar )
                     SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
             }
@@ -3710,8 +3721,19 @@ bool SvNumberformat::ImpGetDateTimeOutput(double fNumber,
             {
                 if ( bOtherCalendar )
                     SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
-                OutString += rCal.getDisplayString(
+                String aYear = rCal.getDisplayString(
                         CalendarDisplayCode::LONG_YEAR, nNatNum );
+                if (aYear.Len() < 4)
+                {
+                    // Ensure that year consists of at least 4 digits, so it
+                    // can be distinguished from 2 digits display and edited
+                    // without suddenly being hit by the 2-digit year magic.
+                    String aZero;
+                    aZero.Fill( 4 - aYear.Len(), sal_Unicode('0'));
+                    ImpTransliterate( aZero, NumFor[nIx].GetNatNum());
+                    aYear.Insert( aZero, 0);
+                }
+                OutString += aYear;
                 if ( bOtherCalendar )
                     SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
             }


More information about the Libreoffice-commits mailing list