Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ public async Task CheckInstalledStatusArpVersionMatched()
Assert.That(installerInstalledStatus.InstallerInfo.NestedInstallerType, Is.EqualTo(PackageInstallerType.Unknown));
Assert.That(installerInstalledStatus.InstallerInfo.Scope, Is.EqualTo(PackageInstallerScope.Unknown));

// The test manifest does not declare the optional ReleaseDate.
Assert.That(installerInstalledStatus.InstallerInfo.ReleaseDate, Is.Null);

// Installer status
Assert.That(installerInstalledStatus.InstallerInstalledStatus[0].Type, Is.EqualTo(InstalledStatusType.AppsAndFeaturesEntry));
Assert.That(GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[0]), Is.EqualTo(Constants.ErrorCode.S_OK));
Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLIE2ETests/Interop/InstallInterop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,7 @@ public void GetApplicableInstaller()
Assert.That(packageInstallerInfo.NestedInstallerType, Is.EqualTo(PackageInstallerType.Exe));
Assert.That(packageInstallerInfo.Scope, Is.EqualTo(PackageInstallerScope.User));
Assert.That(packageInstallerInfo.Locale, Is.EqualTo("en-US"));
Assert.That(packageInstallerInfo.ReleaseDate, Is.EqualTo(new DateTimeOffset(2024, 6, 15, 0, 0, 0, TimeSpan.Zero)));
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ Installers:
- RelativeFilePath: AppInstallerTestExeInstaller.exe
ElevationRequirement: elevationRequired
InstallerLocale: en-US
ReleaseDate: 2024-06-15
ManifestType: singleton
ManifestVersion: 1.4.0
54 changes: 54 additions & 0 deletions src/AppInstallerCLITests/DateTime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,57 @@ TEST_CASE("ShortFileTime", "[datetime]")
INFO(longTime);
REQUIRE(shortTime.length() < longTime.length());
}

void VerifyTryGetTimePointFromDate(std::string_view date, int year, int month, int day)
{
INFO(date);
auto result = TryGetTimePointFromDate(date);
REQUIRE(result.has_value());

tm time{};
auto tt = system_clock::to_time_t(result.value());
_gmtime64_s(&time, &tt);

REQUIRE(year == time.tm_year + 1900);
REQUIRE(month == time.tm_mon + 1);
REQUIRE(day == time.tm_mday);
REQUIRE(0 == time.tm_hour);
REQUIRE(0 == time.tm_min);
REQUIRE(0 == time.tm_sec);
}

TEST_CASE("TryGetTimePointFromDate", "[datetime]")
{
// Not a YYYY-MM-DD date
REQUIRE_FALSE(TryGetTimePointFromDate("").has_value());
REQUIRE_FALSE(TryGetTimePointFromDate("06/15/2024").has_value());
REQUIRE_FALSE(TryGetTimePointFromDate("2024-06-1a").has_value());
REQUIRE_FALSE(TryGetTimePointFromDate("2024-06-150").has_value());

// Years out of range
REQUIRE_FALSE(TryGetTimePointFromDate("1969-12-31").has_value());
REQUIRE_FALSE(TryGetTimePointFromDate("3001-01-01").has_value());

// Months out of range
REQUIRE_FALSE(TryGetTimePointFromDate("2024-00-15").has_value());
REQUIRE_FALSE(TryGetTimePointFromDate("2024-13-15").has_value());

// Days out of range
REQUIRE_FALSE(TryGetTimePointFromDate("2024-06-00").has_value());
REQUIRE_FALSE(TryGetTimePointFromDate("2024-06-31").has_value());
REQUIRE_FALSE(TryGetTimePointFromDate("2023-02-29").has_value());
REQUIRE_FALSE(TryGetTimePointFromDate("2100-02-29").has_value());

// In range baseline
VerifyTryGetTimePointFromDate("2024-06-15", 2024, 6, 15);

// Leap days
VerifyTryGetTimePointFromDate("2024-02-29", 2024, 2, 29);
VerifyTryGetTimePointFromDate("2000-02-29", 2000, 2, 29);

// Time portion is ignored
VerifyTryGetTimePointFromDate("2024-06-15T00:00:00", 2024, 6, 15);

// Epoch time
REQUIRE(TryGetTimePointFromDate("1970-01-01").value() == system_clock::time_point{});
}
25 changes: 25 additions & 0 deletions src/AppInstallerSharedLib/DateTime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,29 @@ namespace AppInstaller::Utility

return std::chrono::system_clock::from_time_t(_mkgmtime64(&versionTime));
}

std::optional<std::chrono::system_clock::time_point> TryGetTimePointFromDate(std::string_view date)
Comment thread
dishahpatel marked this conversation as resolved.
{
tm parsed{};
std::istringstream stream{ std::string{ date } };
stream >> std::get_time(&parsed, "%Y-%m-%d");

// An ISO 8601 date and time is also accepted, but only the date is used.
if (stream.fail() || (stream.peek() != EOF && stream.peek() != 'T'))
{
return std::nullopt;
}

// Limit to the range supported by _mkgmtime64, which is 1970 to 3000.
// _mkgmtime64 rolls days past the end of the month into the next month (2023-02-29 becomes 2023-03-01), so reject those.
int year = parsed.tm_year + 1900;
tm normalized = parsed;
__time64_t epochTime = _mkgmtime64(&normalized);
if (year < 1970 || year > 3000 || normalized.tm_mday != parsed.tm_mday)
{
return std::nullopt;
}

return std::chrono::system_clock::from_time_t(epochTime);
}
}
7 changes: 7 additions & 0 deletions src/AppInstallerSharedLib/Public/AppInstallerDateTime.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
#pragma once
#include <AppInstallerVersions.h>
#include <chrono>
#include <optional>
#include <ostream>
#include <string_view>

namespace AppInstaller::Utility
{
Expand Down Expand Up @@ -67,4 +69,9 @@ namespace AppInstaller::Utility
// Ensure that the package is expected to use this format, or you may get strange times.
// If the version is not convertable, the minimum time is returned.
std::chrono::system_clock::time_point GetTimePointFromVersion(const UInt64Version& version);

// Converts the given ISO 8601 date (YYYY-MM-DD) into a time_point at midnight UTC of that date.
// An ISO 8601 date and time (such as 2024-06-15T12:00:00Z) is also accepted, but the time is ignored.
// If the value is not a valid date between 1970 and 3000, std::nullopt is returned.
std::optional<std::chrono::system_clock::time_point> TryGetTimePointFromDate(std::string_view date);
Comment thread
dishahpatel marked this conversation as resolved.
}
11 changes: 11 additions & 0 deletions src/Microsoft.Management.Deployment/PackageInstallerInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "PackageInstallerInfo.g.cpp"
#include "AuthenticationInfo.h"
#include "Converters.h"
#include <AppInstallerDateTime.h>
#include <wil\cppwinrt_wrl.h>

namespace winrt::Microsoft::Management::Deployment::implementation
Expand Down Expand Up @@ -49,4 +50,14 @@ namespace winrt::Microsoft::Management::Deployment::implementation
});
return m_authenticationInfo;
}
winrt::Windows::Foundation::IReference<winrt::Windows::Foundation::DateTime> PackageInstallerInfo::ReleaseDate()
{
const auto releaseDate = ::AppInstaller::Utility::TryGetTimePointFromDate(m_manifestInstaller.ReleaseDate);
if (!releaseDate)
{
return nullptr;
}

return winrt::clock::from_sys(releaseDate.value());
}
}
2 changes: 2 additions & 0 deletions src/Microsoft.Management.Deployment/PackageInstallerInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation
winrt::Microsoft::Management::Deployment::ElevationRequirement ElevationRequirement();
// Contract 12.0
winrt::Microsoft::Management::Deployment::AuthenticationInfo AuthenticationInfo();
// Contract 31.0
winrt::Windows::Foundation::IReference<winrt::Windows::Foundation::DateTime> ReleaseDate();

#if !defined(INCLUDE_ONLY_INTERFACE_METHODS)
private:
Expand Down
9 changes: 8 additions & 1 deletion src/Microsoft.Management.Deployment/PackageManager.idl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT License.
namespace Microsoft.Management.Deployment
{
[contractversion(30)] // For version 1.30
[contractversion(31)] // For version 1.31
apicontract WindowsPackageManagerContract{};

/// State of the install
Expand Down Expand Up @@ -532,6 +532,13 @@ namespace Microsoft.Management.Deployment
/// Authentication info from the package installer.
AuthenticationInfo AuthenticationInfo { get; };
}

[contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 31)]
{
/// The package installer release date declared in the manifest, as midnight UTC of that date.
/// Null if the manifest does not declare a release date or the value is not a valid date.
Windows.Foundation.IReference<Windows.Foundation.DateTime> ReleaseDate { get; };
}
};

/// The installed status type. The values need to match InstalledStatusType from winget/RepositorySearch.h.
Expand Down
Loading