Kaydet (Commit) 6d424f07 authored tarafından Eike Rathke's avatar Eike Rathke

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
üst eace834d
...@@ -24,11 +24,11 @@ ...@@ -24,11 +24,11 @@
#include <com/sun/star/util/Time.hpp> #include <com/sun/star/util/Time.hpp>
#include <com/sun/star/util/DateTime.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); double implTimeSerial( sal_Int16 nHour, sal_Int16 nMinute, sal_Int16 nSecond);
bool implDateTimeSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, bool implDateTimeSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
sal_Int16 nHour, sal_Int16 nMinute, sal_Int16 nSecond, 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 ); sal_Int16 implGetWeekDay( double aDate, bool bFirstDayParam = false, sal_Int16 nFirstDay = 0 );
......
...@@ -1865,7 +1865,7 @@ css::util::Date SbxDateToUNODate( const SbxValue* const pVal ) ...@@ -1865,7 +1865,7 @@ css::util::Date SbxDateToUNODate( const SbxValue* const pVal )
void SbxDateFromUNODate( SbxValue *pVal, const css::util::Date& aUnoDate) void SbxDateFromUNODate( SbxValue *pVal, const css::util::Date& aUnoDate)
{ {
double dDate; 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 ); pVal->PutDate( dDate );
} }
...@@ -1980,7 +1980,7 @@ void SbxDateFromUNODateTime( SbxValue *pVal, const css::util::DateTime& aUnoDT) ...@@ -1980,7 +1980,7 @@ void SbxDateFromUNODateTime( SbxValue *pVal, const css::util::DateTime& aUnoDT)
double dDate(0.0); double dDate(0.0);
if( implDateTimeSerial( aUnoDT.Year, aUnoDT.Month, aUnoDT.Day, if( implDateTimeSerial( aUnoDT.Year, aUnoDT.Month, aUnoDT.Day,
aUnoDT.Hours, aUnoDT.Minutes, aUnoDT.Seconds, aUnoDT.Hours, aUnoDT.Minutes, aUnoDT.Seconds,
false, dDate ) ) false, false, dDate ) )
{ {
pVal->PutDate( dDate ); pVal->PutDate( dDate );
} }
...@@ -2111,7 +2111,7 @@ RTLFUNC(CDateFromIso) ...@@ -2111,7 +2111,7 @@ RTLFUNC(CDateFromIso)
double dDate; double dDate;
if (!implDateSerial( (sal_Int16)(nSign * aYearStr.toInt32()), 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; break;
rPar.Get(0)->PutDate( dDate ); rPar.Get(0)->PutDate( dDate );
...@@ -2143,7 +2143,7 @@ RTLFUNC(DateSerial) ...@@ -2143,7 +2143,7 @@ RTLFUNC(DateSerial)
sal_Int16 nDay = rPar.Get(3)->GetInteger(); sal_Int16 nDay = rPar.Get(3)->GetInteger();
double dDate; double dDate;
if( implDateSerial( nYear, nMonth, nDay, true, dDate ) ) if( implDateSerial( nYear, nMonth, nDay, true, true, dDate ) )
{ {
rPar.Get(0)->PutDate( dDate ); rPar.Get(0)->PutDate( dDate );
} }
...@@ -4912,7 +4912,8 @@ sal_Int16 implGetDateYear( double aDate ) ...@@ -4912,7 +4912,8 @@ sal_Int16 implGetDateYear( double aDate )
return nRet; 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 // 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 // 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 ...@@ -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 ); Date aCurDate( nDay, nMonth, nYear );
/* TODO: also StarBASIC should provide the rollover mechanism, probably we /* TODO: we could enable the same rollover mechanism for StarBASIC to be
* can use tools::Date::AddMonths() and operator+=() for both, VBA and * compatible with VBA (just with our wider supported date range), then
* StarBASIC. If called from CDateFromIso or CDateFromUnoDate it should not * documentation would need to be adapted. As is, the DateSerial() runtime
* rollover. */ * 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 HAVE_FEATURE_SCRIPTING
if ( !SbiRuntime::isVBAEnabled() ) if (!SbiRuntime::isVBAEnabled())
bRollOver = false;
#endif #endif
if (nYear == 0 || (!bRollOver && (nAddMonths || nAddDays || !aCurDate.IsValidDate())))
{ {
if ( nMonth < 1 || nDay < 1 || !aCurDate.IsValidDate() )
{
#if HAVE_FEATURE_SCRIPTING #if HAVE_FEATURE_SCRIPTING
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT ); StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
#endif #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, if (bRollOver)
// 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 aCurDate.Normalize();
if( ( nDay < 1 ) || ( nDay > aCurDate.GetDaysInMonth() ) ) if (nAddMonths)
{ aCurDate.AddMonths( nAddMonths);
aCurDate += nDay - 1; if (nAddDays)
} aCurDate += (long)nAddDays;
else
{
aCurDate.SetDay( nDay );
}
} }
#endif
long nDiffDays = GetDayDiff( aCurDate ); long nDiffDays = GetDayDiff( aCurDate );
rdRet = (double)nDiffDays; rdRet = (double)nDiffDays;
...@@ -5015,10 +5017,10 @@ double implTimeSerial( sal_Int16 nHours, sal_Int16 nMinutes, sal_Int16 nSeconds ...@@ -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, bool implDateTimeSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay,
sal_Int16 nHour, sal_Int16 nMinute, sal_Int16 nSecond, sal_Int16 nHour, sal_Int16 nMinute, sal_Int16 nSecond,
bool bUseTwoDigitYear, double& rdRet ) bool bUseTwoDigitYear, bool bRollOver, double& rdRet )
{ {
double dDate; double dDate;
if(!implDateSerial(nYear, nMonth, nDay, bUseTwoDigitYear, dDate)) if(!implDateSerial(nYear, nMonth, nDay, bUseTwoDigitYear, bRollOver, dDate))
return false; return false;
rdRet += dDate + implTimeSerial(nHour, nMinute, nSecond); rdRet += dDate + implTimeSerial(nHour, nMinute, nSecond);
return true; return true;
......
...@@ -2075,7 +2075,7 @@ RTLFUNC(DateAdd) ...@@ -2075,7 +2075,7 @@ RTLFUNC(DateAdd)
sal_Int32 nTargetYear = lNumber + nYear; sal_Int32 nTargetYear = lNumber + nYear;
nTargetYear16 = limitToINT16( nTargetYear ); nTargetYear16 = limitToINT16( nTargetYear );
nTargetMonth = nMonth; nTargetMonth = nMonth;
bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, dNewDate ); bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, true, dNewDate );
break; break;
} }
case INTERVAL_Q: case INTERVAL_Q:
...@@ -2119,7 +2119,7 @@ RTLFUNC(DateAdd) ...@@ -2119,7 +2119,7 @@ RTLFUNC(DateAdd)
nTargetYear = (sal_Int32)nYear + nYearsAdd; nTargetYear = (sal_Int32)nYear + nYearsAdd;
} }
nTargetYear16 = limitToINT16( nTargetYear ); nTargetYear16 = limitToINT16( nTargetYear );
bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, dNewDate ); bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, true, dNewDate );
break; break;
} }
default: break; default: break;
...@@ -2134,7 +2134,7 @@ RTLFUNC(DateAdd) ...@@ -2134,7 +2134,7 @@ RTLFUNC(DateAdd)
while( nNewMonth > nTargetMonth ) while( nNewMonth > nTargetMonth )
{ {
nCorrectionDay--; nCorrectionDay--;
implDateSerial( nTargetYear16, nTargetMonth, nCorrectionDay, false, dNewDate ); implDateSerial( nTargetYear16, nTargetMonth, nCorrectionDay, false, true, dNewDate );
implGetDayMonthYear( nNewYear, nNewMonth, nNewDay, dNewDate ); implGetDayMonthYear( nNewYear, nNewMonth, nNewDay, dNewDate );
} }
dNewDate += dHoursMinutesSeconds; dNewDate += dHoursMinutesSeconds;
...@@ -2329,7 +2329,7 @@ double implGetDateOfFirstDayInFirstWeek ...@@ -2329,7 +2329,7 @@ double implGetDateOfFirstDayInFirstWeek
nFirstWeekMinDays = 7; // vbFirstFourDays nFirstWeekMinDays = 7; // vbFirstFourDays
double dBaseDate; double dBaseDate;
implDateSerial( nYear, 1, 1, false, dBaseDate ); implDateSerial( nYear, 1, 1, false, false, dBaseDate );
sal_Int16 nWeekDay0101 = implGetWeekDay( dBaseDate ); sal_Int16 nWeekDay0101 = implGetWeekDay( dBaseDate );
sal_Int16 nDayDiff = nWeekDay0101 - nFirstDay; sal_Int16 nDayDiff = nWeekDay0101 - nFirstDay;
...@@ -2392,7 +2392,7 @@ RTLFUNC(DatePart) ...@@ -2392,7 +2392,7 @@ RTLFUNC(DatePart)
{ {
sal_Int16 nYear = implGetDateYear( dDate ); sal_Int16 nYear = implGetDateYear( dDate );
double dBaseDate; double dBaseDate;
implDateSerial( nYear, 1, 1, false, dBaseDate ); implDateSerial( nYear, 1, 1, false, false, dBaseDate );
nRet = 1 + sal_Int32( dDate - dBaseDate ); nRet = 1 + sal_Int32( dDate - dBaseDate );
break; break;
} }
......
...@@ -761,7 +761,7 @@ void SbxValue::Format( OUString& rRes, const OUString* pFmt ) const ...@@ -761,7 +761,7 @@ void SbxValue::Format( OUString& rRes, const OUString* pFmt ) const
{ {
sal_Int16 nYear = implGetDateYear( nNumber ); sal_Int16 nYear = implGetDateYear( nNumber );
double dBaseDate; double dBaseDate;
implDateSerial( nYear, 1, 1, true, dBaseDate ); implDateSerial( nYear, 1, 1, true, false, dBaseDate );
sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate ); sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate );
rRes = OUString::number(nYear32); rRes = OUString::number(nYear32);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment