include/tools/date.hxx          |    5 +++++
 sc/inc/global.hxx               |    1 +
 sc/qa/unit/ucalc.cxx            |   16 ++++++++++++++++
 sc/source/core/data/table4.cxx  |   23 +++++++++++++++++++----
 tools/qa/cppunit/test_date.cxx  |   25 +++++++++++++++++++++++++
 tools/source/datetime/tdate.cxx |   11 +++++++++++
 6 files changed, 77 insertions(+), 4 deletions(-)

New commits:
commit ea61644abe9e7cd35cdb54452dac327782bbc402
Author:     Andreas Heinisch <andreas.heini...@yahoo.de>
AuthorDate: Tue Mar 23 10:54:14 2021 +0100
Commit:     Andreas Heinisch <andreas.heini...@yahoo.de>
CommitDate: Thu Mar 25 09:42:40 2021 +0100

    tdf#58745 - Detect end of month when extending a date list
    
    Change-Id: Icaa64a493598dc4bb8f2d6d076ad4300e2e4dde6
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/112976
    Tested-by: Jenkins
    Reviewed-by: Andreas Heinisch <andreas.heini...@yahoo.de>

diff --git a/include/tools/date.hxx b/include/tools/date.hxx
index cd69d16b10b6..6179d637e8ba 100644
--- a/include/tools/date.hxx
+++ b/include/tools/date.hxx
@@ -188,6 +188,9 @@ public:
         depending on month/year) */
     bool            IsValidDate() const;
 
+    // Returns true, if the date is the end of the month, false otherwise.
+    bool            IsEndOfMonth() const;
+
     /** Normalize date, invalid day or month values are adapted such that they
         carry over to the next month or/and year, for example 1999-02-32
         becomes 1999-03-04, 1999-13-01 becomes 2000-01-01, 1999-13-42 becomes
@@ -239,6 +242,8 @@ public:
     static sal_Int32 DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 
nYear );
     /// Semantically identical to IsValidDate() member method.
     static bool IsValidDate( sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 
nYear );
+    /// Semantically identical to IsEndOfMonth() member method.
+    static bool IsEndOfMonth(sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 
nYear);
     /// Semantically identical to Normalize() member method.
     static bool Normalize( sal_uInt16 & rDay, sal_uInt16 & rMonth, sal_Int16 & 
rYear );
 
diff --git a/sc/inc/global.hxx b/sc/inc/global.hxx
index 3a1429acb0bc..50041b37b323 100644
--- a/sc/inc/global.hxx
+++ b/sc/inc/global.hxx
@@ -344,6 +344,7 @@ enum FillDateCmd
         FILL_DAY,
         FILL_WEEKDAY,
         FILL_MONTH,
+        FILL_END_OF_MONTH,
         FILL_YEAR
     };
 
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index ec599ad7a42d..ec424f97f1d9 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -4962,6 +4962,22 @@ void Test::testAutoFill()
     CPPUNIT_ASSERT_EQUAL( OUString("2012-10-31"), m_pDoc->GetString( 0, 103, 0 
) );
     CPPUNIT_ASSERT_EQUAL( OUString("2012-10-31"), m_pDoc->GetString( 0, 104, 0 
) );
 
+    // Clear column A for a new test.
+    clearRange(m_pDoc, ScRange(0, 0, 0, 0, MAXROW, 0));
+    m_pDoc->SetRowHidden(0, MAXROW, 0, false); // Show all rows.
+
+    m_pDoc->SetString(0, 100, 0, "2019-10-31");
+    m_pDoc->SetString(0, 101, 0, "2019-11-30");
+    m_pDoc->SetString(0, 102, 0, "2019-12-31");
+    m_pDoc->Fill(0, 100, 0, 102, nullptr, aMarkData, 3, FILL_TO_BOTTOM, 
FILL_AUTO);
+
+    // tdf#58745, Without the fix in place, this test would have failed with
+    // - Expected: 2020-01-31
+    // - Actual  : 2019-01-11
+    CPPUNIT_ASSERT_EQUAL(OUString("2020-01-31"), m_pDoc->GetString(0, 103, 0));
+    CPPUNIT_ASSERT_EQUAL(OUString("2020-02-29"), m_pDoc->GetString(0, 104, 0));
+    CPPUNIT_ASSERT_EQUAL(OUString("2020-03-31"), m_pDoc->GetString(0, 105, 0));
+
     // Clear column A for a new test.
     clearRange(m_pDoc, ScRange(0,0,0,0,MAXROW,0));
     m_pDoc->SetRowHidden(0, MAXROW, 0, false); // Show all rows.
diff --git a/sc/source/core/data/table4.cxx b/sc/source/core/data/table4.cxx
index 02e6c1c31d83..e6c29a5a00e7 100644
--- a/sc/source/core/data/table4.cxx
+++ b/sc/source/core/data/table4.cxx
@@ -535,7 +535,12 @@ void ScTable::FillAnalyse( SCCOL nCol1, SCROW nRow1, SCCOL 
nCol2, SCROW nRow2,
                     tools::Long nDDiff = aDate2.GetDay()   - 
static_cast<tools::Long>(aDate1.GetDay());
                     tools::Long nMDiff = aDate2.GetMonth() - 
static_cast<tools::Long>(aDate1.GetMonth());
                     tools::Long nYDiff = aDate2.GetYear()  - 
static_cast<tools::Long>(aDate1.GetYear());
-                    if ( nDDiff )
+                    if (nMDiff && aDate1.IsEndOfMonth() && 
aDate2.IsEndOfMonth())
+                    {
+                        eType = FILL_END_OF_MONTH;
+                        nCmpInc = nMDiff + 12 * nYDiff;
+                    }
+                    else if (nDDiff)
                     {
                         eType = FILL_DAY;
                         nCmpInc = aDate2 - aDate1;
@@ -566,7 +571,8 @@ void ScTable::FillAnalyse( SCCOL nCol1, SCROW nRow1, SCCOL 
nCol2, SCROW nRow2,
                                 nDDiff = aDate2.GetDay()   - 
static_cast<tools::Long>(aDate1.GetDay());
                                 nMDiff = aDate2.GetMonth() - 
static_cast<tools::Long>(aDate1.GetMonth());
                                 nYDiff = aDate2.GetYear()  - 
static_cast<tools::Long>(aDate1.GetYear());
-                                if (nDDiff || ( nMDiff + 12 * nYDiff != 
nCmpInc ))
+                                if ((nDDiff && !aDate1.IsEndOfMonth() && 
!aDate2.IsEndOfMonth())
+                                    || (nMDiff + 12 * nYDiff != nCmpInc))
                                     bVal = false;
                             }
                             aDate1 = aDate2;
@@ -578,7 +584,8 @@ void ScTable::FillAnalyse( SCCOL nCol1, SCROW nRow1, SCCOL 
nCol2, SCROW nRow2,
                     }
                     if (bVal)
                     {
-                        if ( eType == FILL_MONTH && ( nCmpInc % 12 == 0 ) )
+                        if ((eType == FILL_MONTH || eType == FILL_END_OF_MONTH)
+                            && (nCmpInc % 12 == 0))
                         {
                             eType = FILL_YEAR;
                             nCmpInc /= 12;
@@ -1514,6 +1521,7 @@ void ScTable::IncDate(double& rVal, sal_uInt16& 
nDayOfMonth, double nStep, FillD
             }
             break;
         case FILL_MONTH:
+        case FILL_END_OF_MONTH:
             {
                 if ( nDayOfMonth == 0 )
                     nDayOfMonth = aDate.GetDay();       // init
@@ -1549,7 +1557,14 @@ void ScTable::IncDate(double& rVal, sal_uInt16& 
nDayOfMonth, double nStep, FillD
                 {
                     aDate.SetMonth(static_cast<sal_uInt16>(nMonth));
                     aDate.SetYear(static_cast<sal_uInt16>(nYear));
-                    aDate.SetDay( std::min( Date::GetDaysInMonth( nMonth, 
nYear), nDayOfMonth ) );
+                    if (eCmd == FILL_END_OF_MONTH)
+                    {
+                        aDate.SetDay(Date::GetDaysInMonth(nMonth, nYear));
+                    }
+                    else
+                    {
+                        aDate.SetDay(std::min(Date::GetDaysInMonth(nMonth, 
nYear), nDayOfMonth));
+                    }
                 }
             }
             break;
diff --git a/tools/qa/cppunit/test_date.cxx b/tools/qa/cppunit/test_date.cxx
index 9a243cce504c..e11270e6a299 100644
--- a/tools/qa/cppunit/test_date.cxx
+++ b/tools/qa/cppunit/test_date.cxx
@@ -26,6 +26,7 @@ public:
     void testGetDayOfWeek();
     void testGetDaysInMonth();
     void testIsBetween();
+    void testIsEndOfMonth();
 
     CPPUNIT_TEST_SUITE(DateTest);
     CPPUNIT_TEST(testDate);
@@ -37,6 +38,7 @@ public:
     CPPUNIT_TEST(testGetDayOfWeek);
     CPPUNIT_TEST(testGetDaysInMonth);
     CPPUNIT_TEST(testIsBetween);
+    CPPUNIT_TEST(testIsEndOfMonth);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -533,6 +535,29 @@ void DateTest::testIsBetween()
     CPPUNIT_ASSERT(aDate.IsBetween(Date(1, 1, 2018), Date(1, 12, 2018)));
 }
 
+void DateTest::testIsEndOfMonth()
+{
+    {
+        Date aDate(31, 12, 2000);
+        CPPUNIT_ASSERT(aDate.IsEndOfMonth());
+    }
+
+    {
+        Date aDate(30, 12, 2000);
+        CPPUNIT_ASSERT(!aDate.IsEndOfMonth());
+    }
+
+    {
+        Date aDate(29, 2, 2000);
+        CPPUNIT_ASSERT(aDate.IsEndOfMonth());
+    }
+
+    {
+        Date aDate(28, 2, 2000);
+        CPPUNIT_ASSERT(!aDate.IsEndOfMonth());
+    }
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(DateTest);
 }
 
diff --git a/tools/source/datetime/tdate.cxx b/tools/source/datetime/tdate.cxx
index a38fb8e986c3..979611333813 100644
--- a/tools/source/datetime/tdate.cxx
+++ b/tools/source/datetime/tdate.cxx
@@ -452,6 +452,17 @@ bool Date::IsValidDate( sal_uInt16 nDay, sal_uInt16 
nMonth, sal_Int16 nYear )
     return true;
 }
 
+bool Date::IsEndOfMonth() const
+{
+    return IsEndOfMonth(GetDay(), GetMonth(), GetYear());
+}
+
+//static
+bool Date::IsEndOfMonth(sal_uInt16 nDay, sal_uInt16 nMonth, sal_Int16 nYear)
+{
+    return IsValidDate(nDay, nMonth, nYear) && ImplDaysInMonth(nMonth, nYear) 
== nDay;
+}
+
 void Date::Normalize()
 {
     sal_uInt16 nDay   = GetDay();
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to