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

Eike Rathke erack at redhat.com
Tue May 2 21:13:05 UTC 2017


 basic/source/inc/date.hxx         |    4 -
 basic/source/runtime/methods.cxx  |  100 +++++++++++++++++++-------------------
 basic/source/runtime/methods1.cxx |   10 +--
 basic/source/sbx/sbxscan.cxx      |    2 
 tools/source/datetime/tdate.cxx   |    2 
 5 files changed, 61 insertions(+), 57 deletions(-)

New commits:
commit 6d424f07701bf26d8fb173563b567d5f097c33e2
Author: Eike Rathke <erack at redhat.com>
Date:   Tue May 2 23:08:03 2017 +0200

    Replace mouth-painted "inaccurate around leap year" rollover algorithm
    
    ... with proper tools::Date methods Normalize() and AddMonths().
    
    Also prepare functionality to easily switch on rollover for StarBASIC as well,
    i.e. when called by DateSerial() runtime function.
    
    For StarBASIC, invalid date for day > daysinmonthofyear is now (or better since
    a previous commit 94bb96ada421b423e9ed30526fe5a6aac95f00b9 from today) properly
    detected, not just dumb 1<=day<=31.
    
    Change-Id: Ibb44f7247726f1e1168f0e66c5ae18e073d19f08

diff --git a/basic/source/inc/date.hxx b/basic/source/inc/date.hxx
index eb44aa1ec76c..6173a0b44dd0 100644
--- a/basic/source/inc/date.hxx
+++ b/basic/source/inc/date.hxx
@@ -24,11 +24,11 @@
 #include <com/sun/star/util/Time.hpp>
 #include <com/sun/star/util/DateTime.hpp>
 
-bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, bool bUseTwoDigitYear, double& rdRet );
+bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, bool bUseTwoDigitYear, bool bRollOver, double& rdRet );
 double implTimeSerial( sal_Int16 nHour, sal_Int16 nMinute, sal_Int16 nSecond);
 bool implDateTimeSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
                          sal_Int16 nHour, sal_Int16 nMinute, sal_Int16 nSecond,
-                         bool bUseTwoDigitYear, double& rdRet );
+                         bool bUseTwoDigitYear, bool bRollOver, double& rdRet );
 
 sal_Int16 implGetWeekDay( double aDate, bool bFirstDayParam = false, sal_Int16 nFirstDay = 0 );
 
diff --git a/basic/source/runtime/methods.cxx b/basic/source/runtime/methods.cxx
index 5996d1590903..db6bc2b616cc 100644
--- a/basic/source/runtime/methods.cxx
+++ b/basic/source/runtime/methods.cxx
@@ -1865,7 +1865,7 @@ css::util::Date SbxDateToUNODate( const SbxValue* const pVal )
 void SbxDateFromUNODate( SbxValue *pVal, const css::util::Date& aUnoDate)
 {
     double dDate;
-    if( implDateSerial( aUnoDate.Year, aUnoDate.Month, aUnoDate.Day, false, dDate ) )
+    if( implDateSerial( aUnoDate.Year, aUnoDate.Month, aUnoDate.Day, false, false, dDate ) )
     {
         pVal->PutDate( dDate );
     }
@@ -1980,7 +1980,7 @@ void SbxDateFromUNODateTime( SbxValue *pVal, const css::util::DateTime& aUnoDT)
     double dDate(0.0);
     if( implDateTimeSerial( aUnoDT.Year, aUnoDT.Month, aUnoDT.Day,
                             aUnoDT.Hours, aUnoDT.Minutes, aUnoDT.Seconds,
-                            false, dDate ) )
+                            false, false, dDate ) )
     {
         pVal->PutDate( dDate );
     }
@@ -2111,7 +2111,7 @@ RTLFUNC(CDateFromIso)
 
             double dDate;
             if (!implDateSerial( (sal_Int16)(nSign * aYearStr.toInt32()),
-                        (sal_Int16)aMonthStr.toInt32(), (sal_Int16)aDayStr.toInt32(), false, dDate ))
+                        (sal_Int16)aMonthStr.toInt32(), (sal_Int16)aDayStr.toInt32(), false, false, dDate ))
                 break;
 
             rPar.Get(0)->PutDate( dDate );
@@ -2143,7 +2143,7 @@ RTLFUNC(DateSerial)
     sal_Int16 nDay = rPar.Get(3)->GetInteger();
 
     double dDate;
-    if( implDateSerial( nYear, nMonth, nDay, true, dDate ) )
+    if( implDateSerial( nYear, nMonth, nDay, true, true, dDate ) )
     {
         rPar.Get(0)->PutDate( dDate );
     }
@@ -4912,7 +4912,8 @@ sal_Int16 implGetDateYear( double aDate )
     return nRet;
 }
 
-bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, bool bUseTwoDigitYear, double& rdRet )
+bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
+        bool bUseTwoDigitYear, bool bRollOver, double& rdRet )
 {
     // XXX NOTE: For VBA years<0 are invalid and years in the range 0..29 and
     // 30..99 can not be input as they are 2-digit for 2000..2029 and
@@ -4945,58 +4946,59 @@ bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, bool bUs
         }
     }
 
+    sal_Int32 nAddMonths = 0;
+    sal_Int32 nAddDays = 0;
+    // Always sanitize values to set date and to use for validity detection.
+    if (nMonth < 1 || 12 < nMonth)
+    {
+        sal_Int16 nM = ((nMonth < 1) ? (12 + (nMonth % 12)) : (nMonth % 12));
+        nAddMonths = nMonth - nM;
+        nMonth = nM;
+    }
+    // Day 0 would already be normalized during Date::Normalize(), include
+    // it in negative days, also to detect non-validity. The actual day of
+    // month is 1+(nDay-1)
+    if (nDay < 1)
+    {
+        nAddDays = nDay - 1;
+        nDay = 1;
+    }
+    else if (nDay > 31)
+    {
+        nAddDays = nDay - 31;
+        nDay = 31;
+    }
+
     Date aCurDate( nDay, nMonth, nYear );
 
-    /* TODO: also StarBASIC should provide the rollover mechanism, probably we
-     * can use tools::Date::AddMonths() and operator+=() for both, VBA and
-     * StarBASIC. If called from CDateFromIso or CDateFromUnoDate it should not
-     * rollover. */
+    /* TODO: we could enable the same rollover mechanism for StarBASIC to be
+     * compatible with VBA (just with our wider supported date range), then
+     * documentation would need to be adapted. As is, the DateSerial() runtime
+     * function works as dumb as documented.. (except that the resulting date
+     * is checked for validity now and not just day<=31 and month<=12).
+     * If change wanted then simply remove overriding bRollOver here and adapt
+     * documentation.*/
 #if HAVE_FEATURE_SCRIPTING
-    if ( !SbiRuntime::isVBAEnabled() )
+    if (!SbiRuntime::isVBAEnabled())
+        bRollOver = false;
 #endif
+
+    if (nYear == 0 || (!bRollOver && (nAddMonths || nAddDays || !aCurDate.IsValidDate())))
     {
-        if ( nMonth < 1 || nDay < 1 || !aCurDate.IsValidDate() )
-        {
 #if HAVE_FEATURE_SCRIPTING
-            StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+        StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
 #endif
-            return false;
-        }
+        return false;
     }
-#if HAVE_FEATURE_SCRIPTING
-    else
-    {
-        // grab the year & month
-        aCurDate = Date( 1, (( nMonth % 12 ) > 0 ) ? ( nMonth % 12 ) : 12 + ( nMonth % 12 ), nYear );
-
-        // adjust year based on month value
-        // e.g. 2000, 0, xx = 1999, 12, xx ( or December of the previous year )
-        //      2000, 13, xx = 2001, 1, xx ( or January of the following year )
-        if( ( nMonth < 1 ) || ( nMonth > 12 ) )
-        {
-            // inaccurate around leap year, don't use days to calculate,
-            // just modify the months directory
-            sal_Int16 nYearAdj = ( nMonth /12 ); // default to positive months inputed
-            if ( nMonth <=0 )
-            {
-                nYearAdj = ( ( nMonth -12 ) / 12 );
-            }
-            aCurDate.AddYears( nYearAdj );
-        }
 
-        // adjust day value,
-        // e.g. 2000, 2, 0 = 2000, 1, 31 or the last day of the previous month
-        //      2000, 1, 32 = 2000, 2, 1 or the first day of the following month
-        if( ( nDay < 1 ) || ( nDay > aCurDate.GetDaysInMonth() ) )
-        {
-            aCurDate += nDay - 1;
-        }
-        else
-        {
-            aCurDate.SetDay( nDay );
-        }
+    if (bRollOver)
+    {
+        aCurDate.Normalize();
+        if (nAddMonths)
+            aCurDate.AddMonths( nAddMonths);
+        if (nAddDays)
+            aCurDate += (long)nAddDays;
     }
-#endif
 
     long nDiffDays = GetDayDiff( aCurDate );
     rdRet = (double)nDiffDays;
@@ -5015,10 +5017,10 @@ double implTimeSerial( sal_Int16 nHours, sal_Int16 nMinutes, sal_Int16 nSeconds
 
 bool implDateTimeSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
                          sal_Int16 nHour, sal_Int16 nMinute, sal_Int16 nSecond,
-                         bool bUseTwoDigitYear, double& rdRet )
+                         bool bUseTwoDigitYear, bool bRollOver, double& rdRet )
 {
     double dDate;
-    if(!implDateSerial(nYear, nMonth, nDay, bUseTwoDigitYear, dDate))
+    if(!implDateSerial(nYear, nMonth, nDay, bUseTwoDigitYear, bRollOver, dDate))
         return false;
     rdRet += dDate + implTimeSerial(nHour, nMinute, nSecond);
     return true;
diff --git a/basic/source/runtime/methods1.cxx b/basic/source/runtime/methods1.cxx
index 650c0d92fdc9..f3502fe3465a 100644
--- a/basic/source/runtime/methods1.cxx
+++ b/basic/source/runtime/methods1.cxx
@@ -2075,7 +2075,7 @@ RTLFUNC(DateAdd)
                 sal_Int32 nTargetYear = lNumber + nYear;
                 nTargetYear16 = limitToINT16( nTargetYear );
                 nTargetMonth = nMonth;
-                bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, dNewDate );
+                bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, true, dNewDate );
                 break;
             }
             case INTERVAL_Q:
@@ -2119,7 +2119,7 @@ RTLFUNC(DateAdd)
                     nTargetYear = (sal_Int32)nYear + nYearsAdd;
                 }
                 nTargetYear16 = limitToINT16( nTargetYear );
-                bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, dNewDate );
+                bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, true, dNewDate );
                 break;
             }
             default: break;
@@ -2134,7 +2134,7 @@ RTLFUNC(DateAdd)
             while( nNewMonth > nTargetMonth )
             {
                 nCorrectionDay--;
-                implDateSerial( nTargetYear16, nTargetMonth, nCorrectionDay, false, dNewDate );
+                implDateSerial( nTargetYear16, nTargetMonth, nCorrectionDay, false, true, dNewDate );
                 implGetDayMonthYear( nNewYear, nNewMonth, nNewDay, dNewDate );
             }
             dNewDate += dHoursMinutesSeconds;
@@ -2329,7 +2329,7 @@ double implGetDateOfFirstDayInFirstWeek
         nFirstWeekMinDays = 7;      // vbFirstFourDays
 
     double dBaseDate;
-    implDateSerial( nYear, 1, 1, false, dBaseDate );
+    implDateSerial( nYear, 1, 1, false, false, dBaseDate );
 
     sal_Int16 nWeekDay0101 = implGetWeekDay( dBaseDate );
     sal_Int16 nDayDiff = nWeekDay0101 - nFirstDay;
@@ -2392,7 +2392,7 @@ RTLFUNC(DatePart)
         {
             sal_Int16 nYear = implGetDateYear( dDate );
             double dBaseDate;
-            implDateSerial( nYear, 1, 1, false, dBaseDate );
+            implDateSerial( nYear, 1, 1, false, false, dBaseDate );
             nRet = 1 + sal_Int32( dDate - dBaseDate );
             break;
         }
diff --git a/basic/source/sbx/sbxscan.cxx b/basic/source/sbx/sbxscan.cxx
index dd0a531e964c..5e73e62f2db7 100644
--- a/basic/source/sbx/sbxscan.cxx
+++ b/basic/source/sbx/sbxscan.cxx
@@ -761,7 +761,7 @@ void SbxValue::Format( OUString& rRes, const OUString* pFmt ) const
             {
                 sal_Int16 nYear = implGetDateYear( nNumber );
                 double dBaseDate;
-                implDateSerial( nYear, 1, 1, true, dBaseDate );
+                implDateSerial( nYear, 1, 1, true, false, dBaseDate );
                 sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate );
                 rRes = OUString::number(nYear32);
             }
commit eace834d0830bbf4c89f7f5ea8666fb91527e466
Author: Eike Rathke <erack at redhat.com>
Date:   Tue May 2 22:32:39 2017 +0200

    Assert that nMonth and nDay <100 to be representable
    
    Change-Id: Ie52269478ecac75519d04310a17873904c8167e6

diff --git a/tools/source/datetime/tdate.cxx b/tools/source/datetime/tdate.cxx
index 3dd8f68b3ced..06c1d0e24454 100644
--- a/tools/source/datetime/tdate.cxx
+++ b/tools/source/datetime/tdate.cxx
@@ -104,6 +104,8 @@ inline sal_uInt16 ImplDaysInMonth( sal_uInt16 nMonth, sal_Int16 nYear )
 void Date::setDateFromDMY( sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear )
 {
     SAL_WARN_IF( nYear == 0, "tools.datetime", "Date::setDateFromDMY - sure about 0 year? It's not in the calendar.");
+    assert( nMonth < 100 && "nMonth % 100 not representable" );
+    assert(   nDay < 100 && "nDay % 100 not representable" );
     if (nYear < 0)
         mnDate =
             (static_cast<sal_Int32>( nYear        ) * 10000) -


More information about the Libreoffice-commits mailing list