summaryrefslogtreecommitdiff
path: root/basic/source/runtime
diff options
context:
space:
mode:
authorEike Rathke <erack@redhat.com>2017-05-02 15:53:04 +0200
committerEike Rathke <erack@redhat.com>2017-05-02 15:56:20 +0200
commit1b13548f33720d80f53d493f6d70cbcce6b1a0fb (patch)
tree0b797dde491986b97a94b96ed4fb83a6295c1a72 /basic/source/runtime
parentd2007136587a8722d0e7045828bca6a6d82fd244 (diff)
BASIC: handle the full tools::Date range from -32768-01-01 to 32767-12-31
* Input of two-digit years only possible through CDateFromIso() though to maintain compatibility with previous behavior and also VBA mode. * VBA mode restricted to years 1..9999 Change-Id: Ia9574c3bf136619b4831b349d263c96b162d1ed4
Diffstat (limited to 'basic/source/runtime')
-rw-r--r--basic/source/runtime/methods.cxx105
-rw-r--r--basic/source/runtime/methods1.cxx15
2 files changed, 72 insertions, 48 deletions
diff --git a/basic/source/runtime/methods.cxx b/basic/source/runtime/methods.cxx
index 82bd2ce03134..0b1698b9eabc 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, dDate ) )
+ if( implDateSerial( aUnoDate.Year, aUnoDate.Month, aUnoDate.Day, 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,
- dDate ) )
+ false, dDate ) )
{
pVal->PutDate( dDate );
}
@@ -2021,7 +2021,7 @@ RTLFUNC(CDateFromUnoDateTime)
SbxBase::SetError( ERRCODE_SBX_CONVERSION );
}
-// Function to convert date to ISO 8601 date format
+// Function to convert date to ISO 8601 date format YYYYMMDD
RTLFUNC(CDateToIso)
{
(void)pBasic;
@@ -2031,11 +2031,13 @@ RTLFUNC(CDateToIso)
{
double aDate = rPar.Get(1)->GetDate();
+ // Date may actually even be -YYYYYMMDD
char Buffer[11];
- snprintf( Buffer, sizeof( Buffer ), "%04d%02d%02d",
- implGetDateYear( aDate ),
- implGetDateMonth( aDate ),
- implGetDateDay( aDate ) );
+ sal_Int16 nYear = implGetDateYear( aDate );
+ snprintf( Buffer, sizeof( Buffer ), (nYear < 0 ? "%05d%02d%02d" : "%04d%02d%02d"),
+ static_cast<int>(nYear),
+ static_cast<int>(implGetDateMonth( aDate )),
+ static_cast<int>(implGetDateDay( aDate )) );
OUString aRetStr = OUString::createFromAscii( Buffer );
rPar.Get(0)->PutString( aRetStr );
}
@@ -2056,34 +2058,51 @@ RTLFUNC(CDateFromIso)
do
{
OUString aStr = rPar.Get(1)->GetOUString();
+ if (aStr.isEmpty())
+ break;
+
+ // Valid formats are
+ // YYYYMMDD -YYYMMDD YYYYYMMDD -YYYYYMMDD
+ // YYYY-MM-DD -YYYY-MM-DD YYYYY-MM-DD -YYYYY-MM-DD
+
+ sal_Int32 nSign = 1;
+ if (aStr[0] == '-')
+ {
+ nSign = -1;
+ aStr = aStr.copy(1);
+ }
const sal_Int32 nLen = aStr.getLength();
- if (nLen != 8 && nLen != 10)
+
+ // Now valid
+ // YYYYMMDD YYYYYMMDD
+ // YYYY-MM-DD YYYYY-MM-DD
+ if (nLen < 8 || 11 < nLen)
break;
OUString aYearStr, aMonthStr, aDayStr;
- if (nLen == 8)
+ if (nLen == 8 || nLen == 9)
{
- // YYYYMMDD
+ // (Y)YYYYMMDD
if (!comphelper::string::isdigitAsciiString(aStr))
break;
- aYearStr = aStr.copy( 0, 4 );
- aMonthStr = aStr.copy( 4, 2 );
- aDayStr = aStr.copy( 6, 2 );
+ const sal_Int32 nMonthPos = (nLen == 9 ? 5 : 4);
+ aYearStr = aStr.copy( 0, nMonthPos );
+ aMonthStr = aStr.copy( nMonthPos, 2 );
+ aDayStr = aStr.copy( nMonthPos + 2, 2 );
}
else
{
- // YYYY-MM-DD
- const sal_Int32 nSep1 = aStr.indexOf('-');
- if (nSep1 != 4)
+ // (Y)YYYY-MM-DD
+ const sal_Int32 nMonthSep = (nLen == 11 ? 5 : 4);
+ if (aStr.indexOf('-') != nMonthSep)
break;
- const sal_Int32 nSep2 = aStr.indexOf('-', nSep1+1);
- if (nSep2 != 7)
+ if (aStr.indexOf('-', nMonthSep + 1) != nMonthSep + 3)
break;
- aYearStr = aStr.copy( 0, 4 );
- aMonthStr = aStr.copy( 5, 2 );
- aDayStr = aStr.copy( 8, 2 );
+ aYearStr = aStr.copy( 0, nMonthSep );
+ aMonthStr = aStr.copy( nMonthSep + 1, 2 );
+ aDayStr = aStr.copy( nMonthSep + 4, 2 );
if ( !comphelper::string::isdigitAsciiString(aYearStr) ||
!comphelper::string::isdigitAsciiString(aMonthStr) ||
!comphelper::string::isdigitAsciiString(aDayStr))
@@ -2091,8 +2110,8 @@ RTLFUNC(CDateFromIso)
}
double dDate;
- if (!implDateSerial( (sal_Int16)aYearStr.toInt32(),
- (sal_Int16)aMonthStr.toInt32(), (sal_Int16)aDayStr.toInt32(), dDate ))
+ if (!implDateSerial( (sal_Int16)(nSign * aYearStr.toInt32()),
+ (sal_Int16)aMonthStr.toInt32(), (sal_Int16)aDayStr.toInt32(), false, dDate ))
break;
rPar.Get(0)->PutDate( dDate );
@@ -2124,7 +2143,7 @@ RTLFUNC(DateSerial)
sal_Int16 nDay = rPar.Get(3)->GetInteger();
double dDate;
- if( implDateSerial( nYear, nMonth, nDay, dDate ) )
+ if( implDateSerial( nYear, nMonth, nDay, true, dDate ) )
{
rPar.Get(0)->PutDate( dDate );
}
@@ -4893,36 +4912,46 @@ sal_Int16 implGetDateYear( double aDate )
return nRet;
}
-bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, double& rdRet )
+bool implDateSerial( sal_Int16 nYear, sal_Int16 nMonth, sal_Int16 nDay, bool bUseTwoDigitYear, 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
+ // 1930..1999, VBA mode overrides bUseTwoDigitYear (as if that was always
+ // true). For VBA years > 9999 are invalid.
+ // For StarBASIC, if bUseTwoDigitYear==true then years in the range 0..99
+ // can not be input as they are 2-digit for 1900..1999, years<0 are
+ // accepted. If bUseTwoDigitYear==false then all years are accepted, but
+ // year 0 is invalid (last day BCE -0001-12-31, first day CE 0001-01-01).
#if HAVE_FEATURE_SCRIPTING
- if ( nYear < 30 && SbiRuntime::isVBAEnabled() )
+ if ( (nYear < 0 || 9999 < nYear) && SbiRuntime::isVBAEnabled() )
+ {
+ StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
+ return false;
+ }
+ else if ( nYear < 30 && SbiRuntime::isVBAEnabled() )
{
nYear += 2000;
}
else
#endif
{
- if ( nYear < 100 )
+ if ( 0 <= nYear && nYear < 100 && (bUseTwoDigitYear || SbiRuntime::isVBAEnabled()) )
{
nYear += 1900;
}
}
+
Date aCurDate( nDay, nMonth, nYear );
- if ((nYear < 100 || nYear > 9999) )
- {
-#if HAVE_FEATURE_SCRIPTING
- StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
-#endif
- return false;
- }
+ /* 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. */
#if HAVE_FEATURE_SCRIPTING
if ( !SbiRuntime::isVBAEnabled() )
#endif
{
- if ( (nMonth < 1 || nMonth > 12 )||
- (nDay < 1 || nDay > 31 ) )
+ if ( nMonth < 1 || nDay < 1 || !aCurDate.IsValidDate() )
{
#if HAVE_FEATURE_SCRIPTING
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
@@ -4982,10 +5011,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,
- double& rdRet )
+ bool bUseTwoDigitYear, double& rdRet )
{
double dDate;
- if(!implDateSerial(nYear, nMonth, nDay, dDate))
+ if(!implDateSerial(nYear, nMonth, nDay, bUseTwoDigitYear, 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 f38d47a882bf..650c0d92fdc9 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, dNewDate );
+ bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, dNewDate );
break;
}
case INTERVAL_Q:
@@ -2119,7 +2119,7 @@ RTLFUNC(DateAdd)
nTargetYear = (sal_Int32)nYear + nYearsAdd;
}
nTargetYear16 = limitToINT16( nTargetYear );
- bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, dNewDate );
+ bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, dNewDate );
break;
}
default: break;
@@ -2130,16 +2130,11 @@ RTLFUNC(DateAdd)
// Overflow?
sal_Int16 nNewYear, nNewMonth, nNewDay;
implGetDayMonthYear( nNewYear, nNewMonth, nNewDay, dNewDate );
- if( nNewYear > 9999 || nNewYear < 100 )
- {
- StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
- return;
- }
sal_Int16 nCorrectionDay = nDay;
while( nNewMonth > nTargetMonth )
{
nCorrectionDay--;
- implDateSerial( nTargetYear16, nTargetMonth, nCorrectionDay, dNewDate );
+ implDateSerial( nTargetYear16, nTargetMonth, nCorrectionDay, false, dNewDate );
implGetDayMonthYear( nNewYear, nNewMonth, nNewDay, dNewDate );
}
dNewDate += dHoursMinutesSeconds;
@@ -2334,7 +2329,7 @@ double implGetDateOfFirstDayInFirstWeek
nFirstWeekMinDays = 7; // vbFirstFourDays
double dBaseDate;
- implDateSerial( nYear, 1, 1, dBaseDate );
+ implDateSerial( nYear, 1, 1, false, dBaseDate );
sal_Int16 nWeekDay0101 = implGetWeekDay( dBaseDate );
sal_Int16 nDayDiff = nWeekDay0101 - nFirstDay;
@@ -2397,7 +2392,7 @@ RTLFUNC(DatePart)
{
sal_Int16 nYear = implGetDateYear( dDate );
double dBaseDate;
- implDateSerial( nYear, 1, 1, dBaseDate );
+ implDateSerial( nYear, 1, 1, false, dBaseDate );
nRet = 1 + sal_Int32( dDate - dBaseDate );
break;
}