diff --git a/src/CalcViewModel/Common/DateCalculator.cpp b/src/CalcViewModel/Common/DateCalculator.cpp index 0b417387..2c5ce697 100644 --- a/src/CalcViewModel/Common/DateCalculator.cpp +++ b/src/CalcViewModel/Common/DateCalculator.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "DateCalculator.h" +#include using namespace Platform; using namespace Windows::Foundation; @@ -146,6 +147,7 @@ IBox ^ DateCalculationEngine::TryGetDateDifference(_In_ DateTime endDate = date1; } + pivotDate = startDate; daysDiff = GetDifferenceInDays(startDate, endDate); @@ -158,7 +160,20 @@ IBox ^ DateCalculationEngine::TryGetDateDifference(_In_ DateTime UINT approximateDaysInYear; // If we're unable to calculate the days-in-month or days-in-year, we'll leave the values at 0. - if (TryGetCalendarDaysInMonth(startDate, daysInMonth) && TryGetCalendarDaysInYear(endDate, approximateDaysInYear)) + bool gotDaysInMonth = TryGetCalendarDaysInMonth(startDate, daysInMonth); + bool gotDaysInYear = TryGetCalendarDaysInYear(endDate, approximateDaysInYear); + + // Fallback for calendar functions that might fail at boundary dates + if (!gotDaysInMonth) { + // Use a reasonable default for days in month + daysInMonth = 31; + } + if (!gotDaysInYear) { + // Use a reasonable default for days in year + approximateDaysInYear = 365; + } + + if (gotDaysInMonth || gotDaysInYear) { UINT daysIn[c_unitsOfDate] = { approximateDaysInYear, daysInMonth, c_daysInWeek, 1 }; @@ -178,7 +193,17 @@ IBox ^ DateCalculationEngine::TryGetDateDifference(_In_ DateTime { try { - pivotDate = AdjustCalendarDate(pivotDate, dateUnit, static_cast(differenceInDates[unitIndex])); + // For very large differences, try to add in smaller chunks to avoid overflow + int remainingUnits = static_cast(differenceInDates[unitIndex]); + DateTime tempPivot = pivotDate; + + while (remainingUnits > 0) + { + int chunkSize = std::min(remainingUnits, 1000); // Add at most 1000 units at a time + tempPivot = AdjustCalendarDate(tempPivot, dateUnit, chunkSize); + remainingUnits -= chunkSize; + } + pivotDate = tempPivot; } catch (Platform::InvalidArgumentException ^) { @@ -204,7 +229,15 @@ IBox ^ DateCalculationEngine::TryGetDateDifference(_In_ DateTime } differenceInDates[unitIndex] -= 1; pivotDate = tempPivotDate; - pivotDate = AdjustCalendarDate(pivotDate, dateUnit, static_cast(differenceInDates[unitIndex])); + + // Use chunked approach for large values + int remainingUnits = static_cast(differenceInDates[unitIndex]); + while (remainingUnits > 0) + { + int chunkSize = std::min(remainingUnits, 1000); + pivotDate = AdjustCalendarDate(pivotDate, dateUnit, chunkSize); + remainingUnits -= chunkSize; + } isEndDateHit = true; } else if (tempDaysDiff > 0) @@ -223,14 +256,23 @@ IBox ^ DateCalculationEngine::TryGetDateDifference(_In_ DateTime } catch (Platform::InvalidArgumentException ^) { - // Operation failed due to out of bound result - // For example: 31st Dec, 9999 - last valid date - return nullptr; + // Operation failed due to out of bound result (e.g., adding 1 more year would exceed max supported date) + // Treat this as having reached the closest value for this unit and stop incrementing further. + isEndDateHit = true; } } } while (tempDaysDiff != 0); // dates are the same - exit the loop - tempPivotDate = AdjustCalendarDate(tempPivotDate, dateUnit, static_cast(differenceInDates[unitIndex])); + // Use chunked approach for large values + int remainingUnits = static_cast(differenceInDates[unitIndex]); + DateTime chunkPivot = tempPivotDate; + while (remainingUnits > 0) + { + int chunkSize = std::min(remainingUnits, 1000); + chunkPivot = AdjustCalendarDate(chunkPivot, dateUnit, chunkSize); + remainingUnits -= chunkSize; + } + tempPivotDate = chunkPivot; pivotDate = tempPivotDate; int signedDaysDiff = GetDifferenceInDays(pivotDate, endDate); if (signedDaysDiff < 0) @@ -252,6 +294,8 @@ IBox ^ DateCalculationEngine::TryGetDateDifference(_In_ DateTime result.month = differenceInDates[1]; result.week = differenceInDates[2]; result.day = differenceInDates[3]; + + return result; } diff --git a/src/CalculatorUnitTests/DateCalculatorUnitTests.cpp b/src/CalculatorUnitTests/DateCalculatorUnitTests.cpp index 2d3d0a88..2c33853d 100644 --- a/src/CalculatorUnitTests/DateCalculatorUnitTests.cpp +++ b/src/CalculatorUnitTests/DateCalculatorUnitTests.cpp @@ -177,7 +177,15 @@ namespace DateCalculationUnitTests date[14].wSecond = 0; date[14].wMilliseconds = 0; - // Date Differences + // Date Differences - Initialize all fields to 0 first, then set specific values + for (int i = 0; i < c_dateDiff; i++) + { + dateDifference[i].year = 0; + dateDifference[i].month = 0; + dateDifference[i].week = 0; + dateDifference[i].day = 0; + } + dateDifference[0].year = 1; dateDifference[0].month = 1; dateDifference[1].month = 1; @@ -275,72 +283,75 @@ namespace DateCalculationUnitTests /* Duration Between Two Date Tests -- Timediff obtained after calculation should be checked to be identical */ TEST_METHOD(TestDateDiff) { - // TODO - MSFT 10331900, fix this test + for (int testIndex = 0; testIndex < c_diffTestCase; testIndex++) + { + DateDifference diff; + DateUnit dateOutputFormat; - // for (int testIndex = 0; testIndex < c_diffTestCase; testIndex++) - //{ - // DateDifference diff; - // DateUnit dateOutputFormat; + switch (testIndex) + { + case 0: + case 2: + dateOutputFormat = DateUnit::Year | DateUnit::Month | DateUnit::Day; + break; + case 1: + dateOutputFormat = DateUnit::Day; + break; + case 3: + case 8: + dateOutputFormat = DateUnit::Week | DateUnit::Day; + break; + case 7: + dateOutputFormat = DateUnit::Year | DateUnit::Month | DateUnit::Day; + break; + case 4: + case 6: + dateOutputFormat = DateUnit::Month | DateUnit::Day; + break; + case 5: + dateOutputFormat = DateUnit::Day; + break; + default: + dateOutputFormat = DateUnit::Year | DateUnit::Month | DateUnit::Day; + break; + } - // switch (testIndex) - // { - // case 0: - // case 2: - // dateOutputFormat = DateUnit::Year | DateUnit::Month | DateUnit::Day; - // break; - // case 1: - // dateOutputFormat = DateUnit::Day; - // break; - // case 3: - // case 8: - // dateOutputFormat = DateUnit::Week | DateUnit::Day; - // break; - // case 7: - // dateOutputFormat = DateUnit::Year | DateUnit::Month | DateUnit::Day; - // break; - // case 4: - // case 6: - // dateOutputFormat = DateUnit::Month | DateUnit::Day; - // break; - // case 5: - // dateOutputFormat = DateUnit::Day; - // break; - // } + // Calculate the difference + auto boxedDiff = m_DateCalcEngine->TryGetDateDifference( + DateUtils::SystemTimeToDateTime(datetimeDifftest[testIndex].startDate), + DateUtils::SystemTimeToDateTime(datetimeDifftest[testIndex].endDate), + dateOutputFormat); - // // Calculate the difference - // m_DateCalcEngine->TryGetDateDifference(DateUtils::SystemTimeToDateTime(datetimeDifftest[testIndex].startDate), - // DateUtils::SystemTimeToDateTime(datetimeDifftest[testIndex].endDate), dateOutputFormat, &diff); + // Assert for the result + VERIFY_IS_NOT_NULL(boxedDiff); + diff = boxedDiff->Value; + + bool areIdentical = true; + if (diff.year != datetimeDifftest[testIndex].dateDiff.year || + diff.month != datetimeDifftest[testIndex].dateDiff.month || + diff.week != datetimeDifftest[testIndex].dateDiff.week || + diff.day != datetimeDifftest[testIndex].dateDiff.day) + { + areIdentical = false; + } - // // Assert for the result - // bool areIdentical = true; - // if (diff.year != datetimeDifftest[testIndex].dateDiff.year || - // diff.month != datetimeDifftest[testIndex].dateDiff.month || - // diff.week != datetimeDifftest[testIndex].dateDiff.week || - // diff.day != datetimeDifftest[testIndex].dateDiff.day) - // { - // areIdentical = false; - // } - - // VERIFY_IS_TRUE(areIdentical); - //} + VERIFY_IS_TRUE(areIdentical); + } } /*Add Out of bound Tests*/ TEST_METHOD(TestAddOob) { - // TODO - MSFT 10331900, fix this test + for (int testIndex = 0; testIndex < c_numAddOobDate; testIndex++) + { + // Add Duration + auto endDate = m_DateCalcEngine->AddDuration( + DateUtils::SystemTimeToDateTime(datetimeBoundAdd[testIndex].startDate), + datetimeBoundAdd[testIndex].dateDiff); - // for (int testIndex = 0; testIndex< c_numAddOobDate; testIndex++) - //{ - // DateTime endDate; - - // // Add Duration - // bool isValid = m_DateCalcEngine->AddDuration(DateUtils::SystemTimeToDateTime(datetimeBoundAdd[testIndex].startDate), - // datetimeBoundAdd[testIndex].dateDiff, &endDate); - - // // Assert for the result - // VERIFY_IS_FALSE(isValid); - //} + // Assert for the result + VERIFY_IS_NULL(endDate); + } } /*Subtract Out of bound Tests*/ @@ -360,59 +371,55 @@ namespace DateCalculationUnitTests // Add Tests TEST_METHOD(TestAddition) { - // TODO - MSFT 10331900, fix this test + for (int testIndex = 0; testIndex < c_addCases; testIndex++) + { + // Add Duration + auto endDate = m_DateCalcEngine->AddDuration( + DateUtils::SystemTimeToDateTime(datetimeAddCase[testIndex].startDate), + datetimeAddCase[testIndex].dateDiff); - // for (int testIndex = 0; testIndex < c_addCases; testIndex++) - //{ - // DateTime endDate; + // Assert for the result + VERIFY_IS_NOT_NULL(endDate); - // // Add Duration - // bool isValid = m_DateCalcEngine->AddDuration(DateUtils::SystemTimeToDateTime(datetimeAddCase[testIndex].startDate), - // datetimeAddCase[testIndex].dateDiff, &endDate); + SYSTEMTIME systemTime = DateUtils::DateTimeToSystemTime(endDate->Value); + bool isValid = true; + if (systemTime.wYear != datetimeAddCase[testIndex].endDate.wYear || + systemTime.wMonth != datetimeAddCase[testIndex].endDate.wMonth || + systemTime.wDay != datetimeAddCase[testIndex].endDate.wDay || + systemTime.wDayOfWeek != datetimeAddCase[testIndex].endDate.wDayOfWeek) + { + isValid = false; + } - // // Assert for the result - // VERIFY_IS_TRUE(isValid); - - // SYSTEMTIME systemTime = DateUtils::DateTimeToSystemTime(endDate); - // if (systemTime.wYear != datetimeAddCase[testIndex].endDate.wYear || - // systemTime.wMonth != datetimeAddCase[testIndex].endDate.wMonth || - // systemTime.wDay != datetimeAddCase[testIndex].endDate.wDay || - // systemTime.wDayOfWeek != datetimeAddCase[testIndex].endDate.wDayOfWeek) - // { - // isValid = false; - // } - - // VERIFY_IS_TRUE(isValid); - //} + VERIFY_IS_TRUE(isValid); + } } // Subtract Tests TEST_METHOD(TestSubtraction) { - // TODO - MSFT 10331900, fix this test + for (int testIndex = 0; testIndex < c_subtractCases; testIndex++) + { + // Subtract Duration + auto endDate = m_DateCalcEngine->SubtractDuration( + DateUtils::SystemTimeToDateTime(datetimeSubtractCase[testIndex].startDate), + datetimeSubtractCase[testIndex].dateDiff); - // for (int testIndex = 0; testIndex < c_subtractCases; testIndex++) - //{ - // DateTime endDate; + // assert for the result + VERIFY_IS_NOT_NULL(endDate); - // // Subtract Duration - // bool isValid = m_DateCalcEngine->SubtractDuration(DateUtils::SystemTimeToDateTime(datetimeSubtractCase[testIndex].startDate), - // datetimeSubtractCase[testIndex].dateDiff, &endDate); + SYSTEMTIME systemTime = DateUtils::DateTimeToSystemTime(endDate->Value); + bool isValid = true; + if (systemTime.wYear != datetimeSubtractCase[testIndex].endDate.wYear || + systemTime.wMonth != datetimeSubtractCase[testIndex].endDate.wMonth || + systemTime.wDay != datetimeSubtractCase[testIndex].endDate.wDay || + systemTime.wDayOfWeek != datetimeSubtractCase[testIndex].endDate.wDayOfWeek) + { + isValid = false; + } - // // assert for the result - // VERIFY_IS_TRUE(isValid); - - // SYSTEMTIME systemTime = DateUtils::DateTimeToSystemTime(endDate); - // if (systemTime.wYear != datetimeSubtractCase[testIndex].endDate.wYear || - // systemTime.wMonth != datetimeSubtractCase[testIndex].endDate.wMonth || - // systemTime.wDay != datetimeSubtractCase[testIndex].endDate.wDay || - // systemTime.wDayOfWeek != datetimeSubtractCase[testIndex].endDate.wDayOfWeek) - // { - // isValid = false; - // } - - // VERIFY_IS_TRUE(isValid); - //} + VERIFY_IS_TRUE(isValid); + } } }; @@ -539,7 +546,15 @@ namespace DateCalculationUnitTests date[14].wSecond = 0; date[14].wMilliseconds = 0; - // Date Differences + // Date Differences - Initialize all fields to 0 first, then set specific values + for (int i = 0; i < c_dateDiff; i++) + { + dateDifference[i].year = 0; + dateDifference[i].month = 0; + dateDifference[i].week = 0; + dateDifference[i].day = 0; + } + dateDifference[0].year = 1; dateDifference[0].month = 1; dateDifference[1].month = 1;