mirror of
https://github.com/Microsoft/calculator.git
synced 2025-07-31 03:50:02 -07:00
Hello GitHub
This commit is contained in:
parent
456fe5e355
commit
c13b8a099e
822 changed files with 276650 additions and 75 deletions
392
src/CalcViewModel/DateCalculatorViewModel.cpp
Normal file
392
src/CalcViewModel/DateCalculatorViewModel.cpp
Normal file
|
@ -0,0 +1,392 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "DateCalculatorViewModel.h"
|
||||
#include "Common\LocalizationStringUtil.h"
|
||||
#include "Common\LocalizationService.h"
|
||||
#include "Common\LocalizationSettings.h"
|
||||
#include "Common\CopyPasteManager.h"
|
||||
|
||||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace CalculatorApp::Common::DateCalculation;
|
||||
using namespace CalculatorApp::ViewModel;
|
||||
using namespace Platform;
|
||||
using namespace Platform::Collections;
|
||||
using namespace std;
|
||||
using namespace Windows::ApplicationModel::Resources;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Globalization;
|
||||
using namespace Windows::Globalization::DateTimeFormatting;
|
||||
using namespace Windows::System::UserProfile;
|
||||
|
||||
namespace CalculatorApp::ViewModel::DateCalculatorViewModelProperties
|
||||
{
|
||||
StringReference StrDateDiffResult(L"StrDateDiffResult");
|
||||
StringReference StrDateDiffResultAutomationName(L"StrDateDiffResultAutomationName");
|
||||
StringReference StrDateDiffResultInDays(L"StrDateDiffResultInDays");
|
||||
StringReference StrDateResult(L"StrDateResult");
|
||||
StringReference StrDateResultAutomationName(L"StrDateResultAutomationName");
|
||||
StringReference IsDiffInDays(L"IsDiffInDays");
|
||||
}
|
||||
|
||||
DateCalculatorViewModel::DateCalculatorViewModel() :
|
||||
m_IsDateDiffMode(true),
|
||||
m_IsAddMode(true),
|
||||
m_isOutOfBound(false),
|
||||
m_DaysOffset(0),
|
||||
m_MonthsOffset(0),
|
||||
m_YearsOffset(0),
|
||||
m_StrDateDiffResult(L""),
|
||||
m_StrDateDiffResultAutomationName(L""),
|
||||
m_StrDateDiffResultInDays(L""),
|
||||
m_StrDateResult(L""),
|
||||
m_StrDateResultAutomationName(L""),
|
||||
m_fromDate({ 0 }),
|
||||
m_toDate({ 0 }),
|
||||
m_startDate({ 0 }),
|
||||
m_dateResult({ 0 })
|
||||
{
|
||||
const auto& localizationSettings = LocalizationSettings::GetInstance();
|
||||
|
||||
// Initialize Date Output format instances
|
||||
InitializeDateOutputFormats(localizationSettings.GetCalendarIdentifier());
|
||||
|
||||
// Initialize Date Calc engine
|
||||
m_dateCalcEngine = make_shared<DateCalculationEngine>(localizationSettings.GetCalendarIdentifier());
|
||||
|
||||
// Initialize dates of DatePicker controls to today's date
|
||||
auto calendar = ref new Calendar();
|
||||
auto today = calendar->GetDateTime();
|
||||
|
||||
// FromDate and ToDate should be clipped (adjusted to a consistent hour in UTC)
|
||||
m_fromDate = today;
|
||||
m_toDate = today;
|
||||
FromDate = ClipTime(today);
|
||||
ToDate = ClipTime(today);
|
||||
|
||||
// StartDate should not be clipped
|
||||
StartDate = today;
|
||||
m_dateResult = today;
|
||||
|
||||
// Initialize the list separator delimiter appended with a space at the end, e.g. ", "
|
||||
// This will be used for date difference formatting: Y years, M months, W weeks, D days
|
||||
m_listSeparator = ref new String((localizationSettings.GetListSeparator() + L" ").c_str());
|
||||
|
||||
// Initialize the output results
|
||||
UpdateDisplayResult();
|
||||
|
||||
m_offsetValues = ref new Vector<String^>();
|
||||
for (int i = 0; i <= c_maxOffsetValue; i++)
|
||||
{
|
||||
wstring numberStr(to_wstring(i));
|
||||
localizationSettings.LocalizeDisplayValue(&numberStr);
|
||||
m_offsetValues->Append(ref new String(numberStr.c_str()));
|
||||
}
|
||||
|
||||
/* In the ClipTime function, we used to change timezone to UTC before clipping the time.
|
||||
The comment from the previous delopers said this was done to eliminate the effects of
|
||||
Daylight Savings Time. We can't think of a good reason why this change in timezone is
|
||||
necessary and did find bugs related to the change, therefore, we have removed the
|
||||
change. Just in case, we will see if the clipped time is ever a different day from the
|
||||
original day, which would hopefully indicate the change in timezone was actually
|
||||
necessary. We will collect telemetry if we find this case. If we don't see any
|
||||
telemetry events after the application has been used for some time, we will feel safe
|
||||
and can remove this function. */
|
||||
DayOfWeek trueDayOfWeek = calendar->DayOfWeek;
|
||||
|
||||
DateTime clippedTime = ClipTime(today);
|
||||
calendar->SetDateTime(clippedTime);
|
||||
if (calendar->DayOfWeek != trueDayOfWeek)
|
||||
{
|
||||
calendar->SetDateTime(today);
|
||||
TraceLogger::GetInstance().LogDateClippedTimeDifferenceFound(
|
||||
from_cx<winrt::Windows::Globalization::Calendar>(calendar),
|
||||
winrt::Windows::Foundation::DateTime{ winrt::Windows::Foundation::TimeSpan{ clippedTime.UniversalTime } });
|
||||
}
|
||||
}
|
||||
|
||||
void DateCalculatorViewModel::OnPropertyChanged(_In_ String^ prop)
|
||||
{
|
||||
if (prop == DateCalculatorViewModelProperties::StrDateDiffResult)
|
||||
{
|
||||
UpdateStrDateDiffResultAutomationName();
|
||||
}
|
||||
else if (prop == DateCalculatorViewModelProperties::StrDateResult)
|
||||
{
|
||||
UpdateStrDateResultAutomationName();
|
||||
}
|
||||
else if (prop != DateCalculatorViewModelProperties::StrDateDiffResultAutomationName
|
||||
&& prop != DateCalculatorViewModelProperties::StrDateDiffResultInDays
|
||||
&& prop != DateCalculatorViewModelProperties::StrDateResultAutomationName
|
||||
&& prop != DateCalculatorViewModelProperties::IsDiffInDays)
|
||||
{
|
||||
OnInputsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void DateCalculatorViewModel::OnInputsChanged()
|
||||
{
|
||||
DateDifference dateDiff;
|
||||
|
||||
if (m_IsDateDiffMode)
|
||||
{
|
||||
DateTime clippedFromDate = ClipTime(FromDate);
|
||||
DateTime clippedToDate = ClipTime(ToDate);
|
||||
|
||||
// Calculate difference between two dates
|
||||
m_dateCalcEngine->GetDateDifference(clippedFromDate, clippedToDate, m_allDateUnitsOutputFormat, &dateDiff);
|
||||
DateDiffResult = dateDiff;
|
||||
|
||||
m_dateCalcEngine->GetDateDifference(clippedFromDate, clippedToDate, m_daysOutputFormat, &dateDiff);
|
||||
DateDiffResultInDays = dateDiff;
|
||||
}
|
||||
else
|
||||
{
|
||||
dateDiff.day = DaysOffset;
|
||||
dateDiff.month = MonthsOffset;
|
||||
dateDiff.year = YearsOffset;
|
||||
|
||||
DateTime dateTimeResult;
|
||||
|
||||
if (m_IsAddMode)
|
||||
{
|
||||
// Add number of Days, Months and Years to a Date
|
||||
IsOutOfBound = !m_dateCalcEngine->AddDuration(StartDate, dateDiff, &dateTimeResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtract number of Days, Months and Years from a Date
|
||||
IsOutOfBound = !m_dateCalcEngine->SubtractDuration(StartDate, dateDiff, &dateTimeResult);
|
||||
}
|
||||
|
||||
if (!m_isOutOfBound)
|
||||
{
|
||||
DateResult = dateTimeResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DateCalculatorViewModel::UpdateDisplayResult()
|
||||
{
|
||||
if (m_IsDateDiffMode)
|
||||
{
|
||||
// Are to and from dates the same
|
||||
if (m_dateDiffResultInDays.day == 0)
|
||||
{
|
||||
IsDiffInDays = true;
|
||||
StrDateDiffResultInDays = L"";
|
||||
StrDateDiffResult = AppResourceProvider::GetInstance().GetResourceString(L"Date_SameDates");
|
||||
}
|
||||
else if ((m_dateDiffResult.year == 0) &&
|
||||
(m_dateDiffResult.month == 0) &&
|
||||
(m_dateDiffResult.week == 0))
|
||||
{
|
||||
IsDiffInDays = true;
|
||||
StrDateDiffResultInDays = L"";
|
||||
|
||||
// Display result in number of days
|
||||
StrDateDiffResult = GetDateDiffStringInDays();
|
||||
}
|
||||
else
|
||||
{
|
||||
IsDiffInDays = false;
|
||||
|
||||
// Display result in days, weeks, months and years
|
||||
StrDateDiffResult = GetDateDiffString();
|
||||
|
||||
// Display result in number of days
|
||||
StrDateDiffResultInDays = GetDateDiffStringInDays();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_isOutOfBound)
|
||||
{
|
||||
// Display Date out of bound message
|
||||
StrDateResult = AppResourceProvider::GetInstance().GetResourceString(L"Date_OutOfBoundMessage");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Display the resulting date in long format
|
||||
StrDateResult = m_dateTimeFormatter->Format(DateResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DateCalculatorViewModel::UpdateStrDateDiffResultAutomationName()
|
||||
{
|
||||
String^ automationFormat = AppResourceProvider::GetInstance().GetResourceString(L"Date_DifferenceResultAutomationName");
|
||||
wstring localizedAutomationName = LocalizationStringUtil::GetLocalizedString(automationFormat->Data(), StrDateDiffResult->Data());
|
||||
StrDateDiffResultAutomationName = ref new String(localizedAutomationName.c_str());
|
||||
}
|
||||
|
||||
void DateCalculatorViewModel::UpdateStrDateResultAutomationName()
|
||||
{
|
||||
String^ automationFormat = AppResourceProvider::GetInstance().GetResourceString(L"Date_ResultingDateAutomationName");
|
||||
wstring localizedAutomationName = LocalizationStringUtil::GetLocalizedString(automationFormat->Data(), StrDateResult->Data());
|
||||
StrDateResultAutomationName = ref new String(localizedAutomationName.c_str());
|
||||
}
|
||||
|
||||
void DateCalculatorViewModel::InitializeDateOutputFormats(_In_ String^ calendarIdentifier)
|
||||
{
|
||||
// Format for Add/Subtract days
|
||||
m_dateTimeFormatter = LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(
|
||||
L"longdate",
|
||||
calendarIdentifier,
|
||||
ClockIdentifiers::TwentyFourHour); // Clock Identifier is not used
|
||||
|
||||
// Format for Date Difference
|
||||
m_allDateUnitsOutputFormat = DateUnit::Year | DateUnit::Month | DateUnit::Week | DateUnit::Day;
|
||||
m_daysOutputFormat = DateUnit::Day;
|
||||
}
|
||||
|
||||
String^ DateCalculatorViewModel::GetDateDiffString() const
|
||||
{
|
||||
String^ result = L"";
|
||||
bool addDelimiter = false;
|
||||
AppResourceProvider resourceLoader = AppResourceProvider::GetInstance();
|
||||
|
||||
auto yearCount = m_dateDiffResult.year;
|
||||
if (yearCount > 0)
|
||||
{
|
||||
result = String::Concat(GetLocalizedNumberString(yearCount), L" ");
|
||||
|
||||
if (yearCount > 1)
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Years"));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Year"));
|
||||
}
|
||||
|
||||
// set the flags to add a delimiter whenever the next unit is added
|
||||
addDelimiter = true;
|
||||
}
|
||||
|
||||
auto monthCount = m_dateDiffResult.month;
|
||||
if (monthCount > 0)
|
||||
{
|
||||
if (addDelimiter)
|
||||
{
|
||||
result = String::Concat(result, m_listSeparator);
|
||||
}
|
||||
else
|
||||
{
|
||||
addDelimiter = true;
|
||||
}
|
||||
|
||||
result = String::Concat(result, String::Concat(GetLocalizedNumberString(monthCount), L" "));
|
||||
|
||||
if (monthCount > 1)
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Months"));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Month"));
|
||||
}
|
||||
}
|
||||
|
||||
auto weekCount = m_dateDiffResult.week;
|
||||
if (weekCount > 0)
|
||||
{
|
||||
if (addDelimiter)
|
||||
{
|
||||
result = String::Concat(result, m_listSeparator);
|
||||
}
|
||||
else
|
||||
{
|
||||
addDelimiter = true;
|
||||
}
|
||||
|
||||
result = String::Concat(result, String::Concat(GetLocalizedNumberString(weekCount), L" "));
|
||||
|
||||
if (weekCount > 1)
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Weeks"));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Week"));
|
||||
}
|
||||
}
|
||||
|
||||
auto dayCount = m_dateDiffResult.day;
|
||||
if (dayCount > 0)
|
||||
{
|
||||
if (addDelimiter)
|
||||
{
|
||||
result = String::Concat(result, m_listSeparator);
|
||||
}
|
||||
else
|
||||
{
|
||||
addDelimiter = true;
|
||||
}
|
||||
|
||||
result = String::Concat(result, String::Concat(GetLocalizedNumberString(dayCount), L" "));
|
||||
|
||||
if (dayCount > 1)
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Days"));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = String::Concat(result, resourceLoader.GetResourceString(L"Date_Day"));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
String^ DateCalculatorViewModel::GetDateDiffStringInDays() const
|
||||
{
|
||||
String^ strDateUnit;
|
||||
|
||||
// Display the result as '1 day' or 'N days'
|
||||
if (m_dateDiffResultInDays.day > 1)
|
||||
{
|
||||
strDateUnit = AppResourceProvider::GetInstance().GetResourceString(L"Date_Days");
|
||||
}
|
||||
else
|
||||
{
|
||||
strDateUnit = AppResourceProvider::GetInstance().GetResourceString(L"Date_Day");
|
||||
}
|
||||
|
||||
return String::Concat(GetLocalizedNumberString(m_dateDiffResultInDays.day), String::Concat(L" ", strDateUnit));
|
||||
}
|
||||
|
||||
void DateCalculatorViewModel::OnCopyCommand(Platform::Object^ parameter)
|
||||
{
|
||||
if (m_IsDateDiffMode)
|
||||
{
|
||||
CopyPasteManager::CopyToClipboard(m_StrDateDiffResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
CopyPasteManager::CopyToClipboard(m_StrDateResult);
|
||||
}
|
||||
}
|
||||
|
||||
String^ DateCalculatorViewModel::GetLocalizedNumberString(int value) const
|
||||
{
|
||||
wstring numberStr(to_wstring(value));
|
||||
LocalizationSettings::GetInstance().LocalizeDisplayValue(&numberStr);
|
||||
return ref new String(numberStr.c_str());
|
||||
}
|
||||
|
||||
// Adjusts the given DateTime to 12AM of the same day
|
||||
DateTime DateCalculatorViewModel::ClipTime(DateTime dateTime)
|
||||
{
|
||||
auto calendar = ref new Calendar();
|
||||
calendar->SetDateTime(dateTime);
|
||||
calendar->Period = 1;
|
||||
calendar->Hour = 12;
|
||||
calendar->Minute = 0;
|
||||
calendar->Second = 0;
|
||||
calendar->Nanosecond = 0;
|
||||
|
||||
return calendar->GetDateTime();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue