Resolves: tdf#117612 truncate DateAdd("m",...) to last day of month
... instead of resulting in error because of roll-over not being set.
Fallout from
commit 6d424f0770
CommitDate: Tue May 2 23:12:34 2017 +0200
Replace mouth-painted "inaccurate around leap year" rollover algorithm
that does stricter checking but DateAdd() needs a lax checking
with truncate to last day of month.
Change-Id: I9d6f95ad3ac38257d492019bd621070491e98e76
This commit is contained in:
@@ -24,7 +24,14 @@
|
||||
#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, bool bRollOver, double& rdRet );
|
||||
enum class SbDateCorrection
|
||||
{
|
||||
None,
|
||||
RollOver,
|
||||
TruncateToMonth
|
||||
};
|
||||
|
||||
bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, bool bUseTwoDigitYear, SbDateCorrection eCorr, 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,
|
||||
|
@@ -1725,7 +1725,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, false, dDate ) )
|
||||
if( implDateSerial( aUnoDate.Year, aUnoDate.Month, aUnoDate.Day, false, SbDateCorrection::None, dDate ) )
|
||||
{
|
||||
pVal->PutDate( dDate );
|
||||
}
|
||||
@@ -1955,7 +1955,8 @@ void SbRtl_CDateFromIso(StarBASIC *, SbxArray & rPar, bool)
|
||||
|
||||
double dDate;
|
||||
if (!implDateSerial( static_cast<sal_Int16>(nSign * aYearStr.toInt32()),
|
||||
static_cast<sal_Int16>(aMonthStr.toInt32()), static_cast<sal_Int16>(aDayStr.toInt32()), bUseTwoDigitYear, false, dDate ))
|
||||
static_cast<sal_Int16>(aMonthStr.toInt32()), static_cast<sal_Int16>(aDayStr.toInt32()),
|
||||
bUseTwoDigitYear, SbDateCorrection::None, dDate ))
|
||||
break;
|
||||
|
||||
rPar.Get(0)->PutDate( dDate );
|
||||
@@ -1984,7 +1985,7 @@ void SbRtl_DateSerial(StarBASIC *, SbxArray & rPar, bool)
|
||||
sal_Int16 nDay = rPar.Get(3)->GetInteger();
|
||||
|
||||
double dDate;
|
||||
if( implDateSerial( nYear, nMonth, nDay, true, true, dDate ) )
|
||||
if( implDateSerial( nYear, nMonth, nDay, true, SbDateCorrection::RollOver, dDate ) )
|
||||
{
|
||||
rPar.Get(0)->PutDate( dDate );
|
||||
}
|
||||
@@ -4547,7 +4548,7 @@ sal_Int16 implGetDateYear( double aDate )
|
||||
}
|
||||
|
||||
bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
|
||||
bool bUseTwoDigitYear, bool bRollOver, double& rdRet )
|
||||
bool bUseTwoDigitYear, SbDateCorrection eCorr, 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
|
||||
@@ -4610,14 +4611,14 @@ bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
|
||||
* 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
|
||||
* If change wanted then simply remove overriding RollOver here and adapt
|
||||
* documentation.*/
|
||||
#if HAVE_FEATURE_SCRIPTING
|
||||
if (!SbiRuntime::isVBAEnabled())
|
||||
bRollOver = false;
|
||||
if (eCorr == SbDateCorrection::RollOver && !SbiRuntime::isVBAEnabled())
|
||||
eCorr = SbDateCorrection::None;
|
||||
#endif
|
||||
|
||||
if (nYear == 0 || (!bRollOver && (nAddMonths || nAddDays || !aCurDate.IsValidDate())))
|
||||
if (nYear == 0 || (eCorr == SbDateCorrection::None && (nAddMonths || nAddDays || !aCurDate.IsValidDate())))
|
||||
{
|
||||
#if HAVE_FEATURE_SCRIPTING
|
||||
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
|
||||
@@ -4625,13 +4626,29 @@ bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bRollOver)
|
||||
if (eCorr != SbDateCorrection::None)
|
||||
{
|
||||
aCurDate.Normalize();
|
||||
if (nAddMonths)
|
||||
aCurDate.AddMonths( nAddMonths);
|
||||
if (nAddDays)
|
||||
aCurDate.AddDays( nAddDays);
|
||||
if (eCorr == SbDateCorrection::TruncateToMonth && aCurDate.GetMonth() != nMonth)
|
||||
{
|
||||
if (aCurDate.GetYear() == SAL_MAX_INT16 && nMonth == 12)
|
||||
{
|
||||
// Roll over and back not possible, hard max.
|
||||
aCurDate.SetMonth(12);
|
||||
aCurDate.SetDay(31);
|
||||
}
|
||||
else
|
||||
{
|
||||
aCurDate.SetMonth(nMonth);
|
||||
aCurDate.SetDay(1);
|
||||
aCurDate.AddMonths(1);
|
||||
aCurDate.AddDays(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long nDiffDays = GetDayDiff( aCurDate );
|
||||
@@ -4654,7 +4671,7 @@ bool implDateTimeSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
|
||||
double& rdRet )
|
||||
{
|
||||
double dDate;
|
||||
if(!implDateSerial(nYear, nMonth, nDay, false/*bUseTwoDigitYear*/, false/*bRollOver*/, dDate))
|
||||
if(!implDateSerial(nYear, nMonth, nDay, false/*bUseTwoDigitYear*/, SbDateCorrection::None, dDate))
|
||||
return false;
|
||||
rdRet += dDate + implTimeSerial(nHour, nMinute, nSecond);
|
||||
return true;
|
||||
|
@@ -1898,7 +1898,7 @@ void SbRtl_DateAdd(StarBASIC *, SbxArray & rPar, bool)
|
||||
nTargetYear16 = limitDate( nTargetYear, nMonth, nDay );
|
||||
/* TODO: should the result be error if the date was limited? It never was. */
|
||||
nTargetMonth = nMonth;
|
||||
bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, true, dNewDate );
|
||||
bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, SbDateCorrection::TruncateToMonth, dNewDate );
|
||||
break;
|
||||
}
|
||||
case INTERVAL_Q:
|
||||
@@ -1943,26 +1943,14 @@ void SbRtl_DateAdd(StarBASIC *, SbxArray & rPar, bool)
|
||||
}
|
||||
nTargetYear16 = limitDate( nTargetYear, nTargetMonth, nDay );
|
||||
/* TODO: should the result be error if the date was limited? It never was. */
|
||||
bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, true, dNewDate );
|
||||
bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, SbDateCorrection::TruncateToMonth, dNewDate );
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
if( bOk )
|
||||
{
|
||||
// Overflow?
|
||||
sal_Int16 nNewYear, nNewMonth, nNewDay;
|
||||
implGetDayMonthYear( nNewYear, nNewMonth, nNewDay, dNewDate );
|
||||
sal_Int16 nCorrectionDay = nDay;
|
||||
while( nNewMonth > nTargetMonth )
|
||||
{
|
||||
nCorrectionDay--;
|
||||
implDateSerial( nTargetYear16, nTargetMonth, nCorrectionDay, false, true, dNewDate );
|
||||
implGetDayMonthYear( nNewYear, nNewMonth, nNewDay, dNewDate );
|
||||
}
|
||||
dNewDate += dHoursMinutesSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
rPar.Get(0)->PutDate( dNewDate );
|
||||
@@ -2147,7 +2135,7 @@ double implGetDateOfFirstDayInFirstWeek
|
||||
nFirstWeekMinDays = 7; // vbFirstFourDays
|
||||
|
||||
double dBaseDate;
|
||||
implDateSerial( nYear, 1, 1, false, false, dBaseDate );
|
||||
implDateSerial( nYear, 1, 1, false, SbDateCorrection::None, dBaseDate );
|
||||
|
||||
sal_Int16 nWeekDay0101 = implGetWeekDay( dBaseDate );
|
||||
sal_Int16 nDayDiff = nWeekDay0101 - nFirstDay;
|
||||
@@ -2207,7 +2195,7 @@ void SbRtl_DatePart(StarBASIC *, SbxArray & rPar, bool)
|
||||
{
|
||||
sal_Int16 nYear = implGetDateYear( dDate );
|
||||
double dBaseDate;
|
||||
implDateSerial( nYear, 1, 1, false, false, dBaseDate );
|
||||
implDateSerial( nYear, 1, 1, false, SbDateCorrection::None, dBaseDate );
|
||||
nRet = 1 + sal_Int32( dDate - dBaseDate );
|
||||
break;
|
||||
}
|
||||
|
@@ -774,7 +774,7 @@ void SbxValue::Format( OUString& rRes, const OUString* pFmt ) const
|
||||
{
|
||||
sal_Int16 nYear = implGetDateYear( nNumber );
|
||||
double dBaseDate;
|
||||
implDateSerial( nYear, 1, 1, true, false, dBaseDate );
|
||||
implDateSerial( nYear, 1, 1, true, SbDateCorrection::None, dBaseDate );
|
||||
sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate );
|
||||
rRes = OUString::number(nYear32);
|
||||
}
|
||||
|
Reference in New Issue
Block a user